00.79.41
Release date: 2026-07-06 Previous release: 00.79.40 Maven coordinates (parent):
com.svenruppert.jsentinel:jSentinel-parent:00.79.41Full changelog: GitHub release v00.79.41
A multi-angle adversarial source-review (explicitly excluding the 38 findings already closed in 00.79.30 + 00.79.40) confirmed 20 distinct findings; a completeness critic flagged a further coverage gap that was verified and promoted to a 21st, Medium-severity finding.
Two Medium findings
- JS-SEC-039 (CWE-290) — the GitHub identity mapper anchored the subject to the mutable
login; it now anchors to the immutable numeric account id (github#12345), so a renamed or recycled GitHub login can no longer inherit another user’s grants. - JS-SEC-059 (CWE-287/294) — the OIDC
noncewas computed and sent to the OP, then discarded — unreachable at callback, so the replay/login-CSRF defence couldn’t be enforced.AuthorizationCodeFlow.handleCallbacknow returns aCallbackResultcarrying the storednonceandresumeTargetback to the caller; both callback adapters (REST, Vaadin) were threaded through.
Sixteen Low + three Info findings
- Identity & authorization — class-level restriction annotations (
@RequiresRoleand five siblings) are now@Inherited, so a subclass of a protected base is no longer silently unprotected (JS-SEC-040); the Keycloak roles mapper defaults to client-scoped, so a foreign client’sadminrole no longer becomes an unqualifiedadmin(JS-SEC-042); back-channel-logout replay detection anchors on first-seen wall-clock, not the token’s owniat(JS-SEC-041); token-introspectionaudtolerates both string and array form (JS-SEC-043); only forwardable token types (BearerToken,OidcAccessToken) are ever propagated as an OAuth2 subject token (JS-SEC-044); HIBP enforces https-or-loopback and fails closed on an empty response (JS-SEC-046). - Log injection & secret hygiene — one shared
LogFieldScrubberreplaces three divergent copies and reaches a sink that previously had none (JS-SEC-045);PasswordNormalizerzeroes its pass-through buffer in afinally(JS-SEC-047). - Resource bounds — login-attempt, nonce, replay and dead-letter stores are capacity-bounded with eviction that never drops an in-force lockout (JS-SEC-048/050/051/052); the JWS validator caps compact-token input before decoding, matching the JWE decoder (JS-SEC-053);
ConsumePipeline.verify()is now total (fails closed instead of throwing) (JS-SEC-054); the SSE handler caps concurrent streams with a 503 + Retry-After instead of starving the server (JS-SEC-049); an envelope-store cursor guards againstLong.MAX_VALUEoverflow (JS-SEC-058). - DX honesty & STRICT consistency — the recorded-not-wired rate-limit/API-key/refresh-token diagnostics stop over-claiming and now fail STRICT boots (JS-SEC-055); a public OAuth2 client without PKCE and a disabled OIDC nonce become STRICT/PRODUCTION boot gates, consistent with the existing JWKS-https gate (JS-SEC-056); the one remaining un-migrated token service (
StoreBackedRememberMeService) now takes aTokenHasher(JS-SEC-057).
Three shared homes established (anti-drift)
CapacityBound(jSentinel-core) — the single home for the bound value and guard used by every capacity-limited store.LogFieldScrubber(jSentinel-core) — control-char and space neutralisation, one implementation for every log sink.JoseLimits(jSentinel-jwt, package-private) — the shared compact-token size ceiling for the JWT validator and both JWE paths.
Additivity
Three deliberate exceptions, all on @ExperimentalJSentinelApi surfaces or
STRICT-only: the handleCallback return-type change (JS-SEC-059, above);
IntrospectionResult.audience() changes from Optional<String> to
Set<String> to preserve an array-form aud (JS-SEC-043); and three
previously-silent conditions now fail the STRICT/PRODUCTION boot (recorded-
not-wired rate-limit/API-keys/refresh-tokens, public-client-without-PKCE,
disabled-nonce) — DEVELOPMENT / COMMUNITY_DEFAULTS keep them as
WARNING/INFO, so only an app that deliberately opted out of a secure default
under STRICT is affected. Everything else is additive.
Exit production-review
A workflow-backed exit review over the full 21-finding delta (47 files) returned SHIP — no high/critical finding on any delivered path across all seven checked high-risk interaction areas. Three low, non-blocking notes were folded in-cycle (a more precise HIBP failure label; a documented eviction tradeoff; confirmation that the brute-force lockout invariant holds under flood).
What it does not do
Three of four audit coverage gaps stay a scoped follow-up — none judged an
as-shipped exploitable bug: REST callback CSRF default, propagation-core
audience binding, security-annotation-processor coverage, RP-initiated-logout
Location validation. No new feature module; 00.80.00 stays
feature-reserved.
Quality
jSentinel-core (the most-touched module) re-measured at 84 %
(2230/2658) mutation / 89 % line — +329 absolute kills versus the 00.71
baseline. Untouched modules retain their 00.79.40 baseline by construction.
Full library-reactor clean install green; every finding carries a
no-mock regression test (~1000 insertions across 47 files). No mocks.
Migration
None. Just bump the version to 00.79.41.