00.82.00
Release date: 2026-09-01 Previous release: 00.81.20 Maven coordinates (parent):
eu.jsentinel:jCustos-community-parent:00.82.00Full changelog: GitHub release v00.82.00
Additive throughout — no API was broken, and nothing changes behaviour until you opt in.
What was closed
Unprotected REST handlers reported at startup (CWE-862)
Deny-by-default already refused a handler carrying no security annotation, but a
REST application only found out when a request arrived — Vaadin reports it at
startup because its router can enumerate routes; REST has no such registry.
RestHandlerDiscovery closes it; the application names its handler classes,
since it is the only party that knows them:
RestSecurity.bootstrap()
.discoverHandlers(new ClassScanningRestHandlerDiscovery(DocumentHandlers.class))
.mode(JCustosBootstrapMode.STRICT)
.install();Three findings keep “could not check” distinct from “checked and clean”:
deny-by-default/unannotated-handler (unreachable), …/discovery-unavailable
(the check could not run) and …/discovery-disabled (no enumeration
configured).
The relying party can vet the logout redirect (CWE-601)
post_logout_redirect_uri was forwarded to the provider unchecked. When the URI
comes from the request, an attacker picks where the user lands:
new RpInitiatedLogoutInitiator(
PostLogoutRedirectValidator.allowOnly(URI.create("https://app.example.com/bye")));allowOnly compares scheme, host, port and path exactly — a prefix check on
https://app.example.com also accepts https://app.example.com.attacker.test.
A rejected URI throws rather than being dropped. The no-arg constructor is
unchanged, so upgrading changes nothing until you pass a validator.
HTTP body reads are bounded in time, not just in size (CWE-400)
Every HTTP client used ofInputStream() then readNBytes(MAX + 1). The size
cap worked; the time bound did not — HttpRequest.timeout covers the exchange
up to the response headers, and the body was read afterwards, outside any
deadline. BoundedHttpBody hands send() a materialising, limiting subscriber,
so the request timeout covers the whole exchange. Seven clients carried the
pattern: JWKS, token endpoint, OAuth2 form post, OIDC discovery, userinfo, the
propagation token client and the HIBP checker.
Every password verification costs the same (CWE-208)
The dummy KDF ran the preferred algorithm, so a stored hash on a different one verified at a different cost — measurable from outside during a lazy migration, separating “existing but unmigrated” from “no such user”. A verification against a non-preferred envelope now runs an additional preferred-cost dummy KDF; the cost is one extra KDF per unmigrated account and disappears as the migration completes.
Also in this release
- Full-backup export —
EclipseStoreJCustosStorage.issueFullBackup(Path)writes a bootable copy of the live storage while it keeps serving. - Javadoc references repaired: two
@links pointed at packages not on their module’s classpath and failed javadoc generation during the 00.81.20 build.
Deferred
CSRF / web-adapter hardening stays open. Login-CSRF is covered
(CallbackStateBinding, 00.81.00), but general CSRF protection needs a concept
rather than a patch: it only affects cookie-based authentication, while the
documented jCustos REST path uses bearer tokens, where a CSRF token would be
ceremony without effect. Half a CSRF defence is worse than none.
Verification
Community 6600+ tests, enterprise 474 — no failures. Every security fix has a test that was confirmed to fail when the fix is disabled; a test that never goes red proves nothing. No mocks.
Upgrade
Drop-in. No API was broken and no default changed.