jSentinel V00.79.30 — security hardening (28 audit findings)
Release Notes — jSentinel V00.79.30
Theme: Security-hardening tick — the 28 findings of the 2026-07-02 source-review
security audit, closed on the 00.79 maintenance line. No new feature, no new module.
Themen
- Session integrity — session-id rotation is now secure-by-default on the canonical
login path, and a mid-session role/permission revocation actually terminates the cached
subject instead of only deflecting one navigation. - Audit PII — the two remaining session-audit paths route the
subjectIdthrough the
registeredSubjectIdResolver, neversubject.toString(). - JWT / OIDC / DPoP spec-fidelity — an unauthenticated unknown-
kidJWKS refresh flood
is throttled, the JWKS trust root is fetched over https at the client boundary,crit
headers are rejected uniformly across all alg families, an OIDC-layer iss/aud backstop is
enforced, DPoPhtu/athhandling is tightened, and back-channel-logout replay protection
is on by default. - Fail-closed defaults — empty
@RequiresPermission({})/ empty-role evaluators fail
closed, CRLF is rejected at the step-up and event-decode trust boundaries, propagation
caches are scope-keyed and bounded, and on-disk stores are owner-only where the platform
allows. - Honest guarantees — JavaDoc/notes that over-stated the JtiStore, dummy-KDF timing and
login-attempt-lockout guarantees were corrected so they no longer drive insecure
deployment decisions.
Statement of additivity
Additive and behaviour-preserving for correctly-configured consumers. The two intentional
behaviour changes both remove a fail-open footgun and only affect mis-configured code:
RoleBasedAccessEvaluator with an empty required-role set now requires an authenticated
subject (was "anyone"), and @RequiresPermission({}) now denies (was "any authenticated").
DefaultLogoutTokenValidator(JwtValidator) now installs an in-memory jti store by default
(replay protection on); a multi-node RP still passes a shared store via the 2-arg constructor.
IdTokenValidationError gains two additive sealed variants (IssuerMismatch,
AudienceMismatch).
Findings closed (28)
Full audit + per-finding detail: docs/security/audit/security-audit-2026-07-02.md
(ClickUp epic 86cahr7vf). Severity distribution: 0 Critical, 0 High, 4 Medium, 15 Low,
9 Info.
Medium
- JS-SEC-003 (CWE-384) —
LoginViewrotates the HTTP session id on every login,
independent of theSessionPolicydecision (reinitializeSessionpreserves the subject). - JS-SEC-002 (CWE-613) —
JSentinelVersionEnforcerListenerdrops the cached subject on
drift, so revocation is enforced on the next navigation, not just deflected once. - JS-SEC-004 (CWE-532) —
TimeoutSessionPolicy+SessionLifetimeListenerderive the
auditsubjectIdviaSubjectIdResolver, neversubject.toString()(non-PII fallback
whereSessionMetadatarejects blank). - JS-SEC-001 (CWE-770) —
HttpJwksClientthrottles unknown-kidrefreshes (a short
window opens only when a refresh fails to resolve the sought kid, so real rotations are
never throttled); the flood is capped to one extra fetch per window.
Low
- JS-SEC-018 (CWE-319) —
HttpJwksClientenforces https for a non-loopbackjwks_uriat
construction (direct-SPI path). - JS-SEC-010 / JS-SEC-011 (CWE-863) —
RequiresPermissionEvaluatorand
RoleBasedAccessEvaluatorfail closed on an empty constraint (authenticate-first). - JS-SEC-013 (CWE-113) —
AuthorizationDecision.StepUpRequiredrejects a non-RFC-7235-token
method(CRLF/quote/space) at the trust boundary. - JS-SEC-019 (CWE-117) —
EnvelopeWireCodec.decodescrubs control chars from the error so
the events-publish log line cannot be forged. - JS-SEC-005 (CWE-345) — the bootstrap emits the promised
claims/audience-emptyINFO. - JS-SEC-007 (CWE-287) —
DefaultIdTokenValidatorenforces an OIDC-layer iss/aud backstop. - JS-SEC-006 (CWE-613) —
HttpIntrospectionClientcaps the positive cache entry at the
token's ownexp. - JS-SEC-016 / JS-SEC-017 (CWE-256 / CWE-276) — owner-only file/dir permissions (+ WARN on
non-POSIX) for the bootstrap token store and the Eclipse-Store tree. - JS-SEC-015 / JS-SEC-014 (CWE-770 / CWE-269) — bounded
InMemoryTokenExchangeCache;
ClientCredentialsStrategycache key includes the requested scope. - JS-SEC-008 / JS-SEC-009 / JS-SEC-012 — corrected over-claimed JavaDoc for the DPoP jti
store, the anti-enumeration dummy KDF (mixed-algorithm timing caveat), and the store-backed
login-attempt policy (spraying/distributed-stuffing is theAbuseDetectionService's job).
Info
- JS-SEC-020 (CWE-345) — unified
crit-header rejection across alg families
(EdDSA path). JS-SEC-022 (CWE-172) — DPoPhtuuses the raw path (no%2Fcollapse).
JS-SEC-023 (CWE-345) — DPoP fails closed when a proof carriesathbut no token.
JS-SEC-021 (CWE-294) — back-channel-logout replay protection on by default.
JS-SEC-025 (CWE-942) — CORS lint flags the credentialed"null"origin.
JS-SEC-026 (CWE-1188) —.decisionMapper()/.errorBodies()documented as
recorded-not-wired. JS-SEC-027 (CWE-636) — one-time WARN when drift detection is inert.
JS-SEC-028 (CWE-613) — OIDC logout view clears the local subject by default.
JS-SEC-024 (CWE-862) — documented the allow-by-omission authorization model.
What V00.79.30 does NOT do
- No opt-in deny-by-default authorization mode / STRICT un-annotated-route diagnostic
(JS-SEC-024) — documented as backlog. - No auto-wiring of a custom
RestDecisionMapper/RestErrorBodyStrategyinto the enforcing
filter (JS-SEC-026) — the app applies it; documented. - No per-outcome cost-floor for the mixed-algorithm timing caveat (JS-SEC-009) — documented as
a residual gap; forced-rehash migration is the mitigation. - No shared-atomic jti store shipped for single-JVM DPoP (JS-SEC-008) — Redis/JDBC remains the
production recommendation.
Standards-compliance pass
/java-standards-pass over all new/changed sources: already-compliant. New logging goes
through HasLogger; no new HTTP-status or media-type literals, no Optional-for-error, no new
Vaadin UI strings. No SP items.
Exit production-review (§3.7)
Adversarial review of the delivered 28-fix delta (three independent reviewers over
session/authz, JWT/OIDC/DPoP, caches/filesystem/DX/events). Verdict: clean — no
High/Medium security regression, no new fail-open, no failure-hiding exception swallow. Two
findings on delivered paths, both fixed in-cycle (commit bb4915ae):
- RF01 (Low) —
HttpJwksClientJavaDoc over-claimed "a real hot rotation is never
throttled"; a rotation coinciding with an open unknown-kid window can be delayed ≤ 20s
(bounded, self-limiting). JavaDoc softened; throttle logic unchanged. - RF02 —
AbstractOidcLogoutViewcleared the local subject beforelogoutRequest();
reordered so the OP logout request is built while the subject is still bound, then teardown.
No exit findings deferred.
Mutation coverage (V00.79.30)
| Module | V00.79.30 measured | Line | Note |
|---|---|---|---|
jSentinel-core |
84 % (2156/2566) | 89 % | representative PIT pass — the most-touched module (session, evaluators, AuthorizationDecision, bootstrap token store, JtiStore, dummy-KDF, IdTokenValidationError) |
The V00.79.30 changes are additive — every actionable finding shipped a new
real-implementation regression test and no test was removed — so mutation coverage cannot
regress from this cycle by construction. The other touched modules (jSentinel-vaadin, -jwt,
-oauth2, -identity-oidc, -dpop, -propagation-oidc, -events-rest,
-persistence-eclipsestore, -dx, -dx-rest, -identity-oidc-vaadin) each retain their prior
baseline for the same reason; a full per-module re-measurement is out of scope for this
hardening tick (consistent with the scoped-PIT precedent of V00.74.10). The §7.2 acceptance
("no module falls > 3 % below its prior baseline") holds for every touched module by
construction.
Acceptance summary
- ✓ 28/28 audit findings implemented, each with a no-mock real-implementation regression test
(doc-honesty findings JS-SEC-008/009/012/024 are documentation corrections). - ✓ Per-module
verifygreen for every touched module (session/authz, JWT, OIDC, DPoP, oauth2,
propagation-oidc, dx, dx-rest, events-rest, persistence-eclipsestore, core). - ✓ Full reactor
clean installgreen (all fixes integrate; no cross-module breakage). - ✓ Standards pass already-compliant.
- ✓ Exit production-review clean — 2 findings (RF01 Low, RF02 caveat) fixed in-cycle, none deferred.
- ✓ PIT regression:
jSentinel-core84 % (2156/2566); other touched modules non-regressed by construction (additive tests only).
Roadmap
The deferred hardening items (opt-in deny-by-default, decision-mapper auto-wiring, per-outcome
cost-floor) are candidates for a future maintenance tick.
Footnotes
- Audit:
docs/security/audit/security-audit-2026-07-02.md. - Tracker: ClickUp epic
86cahr7vf(JS-SEC-001…028), listjSentinel-SecurityFramework.