Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
8c177d7
refactor(bff): extract a mode-neutral SessionBinding seam
cuioss-oliver Jul 27, 2026
6973ea3
feat(bff): add the AES-256-GCM sealed-cookie crypto core
cuioss-oliver Jul 27, 2026
17b87d4
feat(bff): add decrypt-only previous_key rotation
cuioss-oliver Jul 27, 2026
7bfd044
feat(bff): add two-mode cookie key material
cuioss-oliver Jul 27, 2026
366d442
feat(bff): re-seal cookie sessions on token refresh
cuioss-oliver Jul 27, 2026
d652b59
feat(bff): gate the back-channel logout endpoint to 404 in cookie mode
cuioss-oliver Jul 27, 2026
93aaffe
test(bff): add in-module cookie-mode boot fixture and generate-on-sta…
cuioss-oliver Jul 27, 2026
1419cd1
test(bff): add cookie-mode deployment descriptors and the activation …
cuioss-oliver Jul 27, 2026
ae6bff3
test(bff): prove the sealed cookie session across two instances end t…
cuioss-oliver Jul 27, 2026
fcff170
docs(bff): record the accepted @Nullable record-component layout
cuioss-oliver Jul 27, 2026
9d7e130
style(bff): apply the quality gate's in-place normalizations
cuioss-oliver Jul 27, 2026
2ad42c5
fix(bff): drive the pre-route Cookie cap and the seal budget from one…
cuioss-oliver Jul 27, 2026
94eaffe
chore(simplify): collapse accidental complexity in plan-07a-bff-cooki…
cuioss-oliver Jul 27, 2026
a949748
fix(edge): cap the reserved-path request body (finding 0ced30)
cuioss-oliver Jul 27, 2026
2872e0f
fix(bff): decode out-of-range epoch seconds as no session
cuioss-oliver Jul 27, 2026
ff4d5ff
fix(bff): log the back-channel capability gate at DEBUG, not WARN
cuioss-oliver Jul 27, 2026
45ff640
fix(bff): retain every Set-Cookie value on the response
cuioss-oliver Jul 27, 2026
b0bbb52
fix(bff): map an unbindable session to a clean 500 callback outcome
cuioss-oliver Jul 27, 2026
185b787
fix(config): canonicalize oidc.session.mode once, share one predicate
cuioss-oliver Jul 27, 2026
0010b3a
fix(edge): retire the connection after a reserved-body 413
cuioss-oliver Jul 27, 2026
efec078
refactor(bff): use unnamed patterns for unread unseal catch parameters
cuioss-oliver Jul 27, 2026
001581b
docs(bff): correct cookie-mode statements and bound the rollover INFO
cuioss-oliver Jul 27, 2026
1f40359
test(bff): assert the real key length and clear S5778/S6213 findings
cuioss-oliver Jul 27, 2026
ba3edbc
style: apply formatter reflow from the quality gate
cuioss-oliver Jul 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions api-sheriff/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,22 @@
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<!-- The D4 secrets rule requires every secret-classified config field to be a
bare ${VAR} reference — a literal is refused at load time — and a bare
reference naming an undefined variable aborts the boot. CookieModeBootTest
boots the gateway from a real YAML fixture, so its oidc.client_secret must
resolve from the environment. This is a throwaway test-only value that
authenticates to nothing: the boot fixture reaches no IdP (OIDC discovery is
lazy) and no network. -->
<environmentVariables>
<COOKIE_BOOT_CLIENT_SECRET>cookie-boot-test-secret</COOKIE_BOOT_CLIENT_SECRET>
</environmentVariables>
</configuration>
</plugin>
</plugins>
</build>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
* assertable. This catalogue's identifier ranges are disjoint from
* {@link de.cuioss.sheriff.gateway.config.ConfigLogMessages}'s (the boot-time configuration
* subsystem catalogue), which shares the same {@code ApiSheriff} prefix: {@code 1} / {@code 4} /
* {@code 6-7} / {@code 100} / {@code 103-108} here vs {@code 2-3} / {@code 101-102} / {@code 200-201}
* {@code 6-7} / {@code 100} / {@code 103-109} here vs {@code 2-3} / {@code 101-102} / {@code 200-201}
* there — never renumber one catalogue without checking the other for a collision.
* Security-relevant {@code WARN}s record only the failure <em>type</em> and route id —
* never the raw offending payload. {@code DEBUG} / {@code TRACE} diagnostics use the logger
Expand Down Expand Up @@ -146,5 +146,17 @@ public static final class WARN {
.identifier(108)
.template("Host-vs-SNI smuggle rejected before route selection: %s")
.build();

/**
* A gateway-terminated reserved POST path exceeded the edge's reserved-body byte ceiling and
* was rejected {@code 413}. Records the ceiling and a fixed disposition
* ({@code declared-content-length} or {@code streamed-body}) only — never the offending body,
* which is precisely the unauthenticated payload this bound exists to refuse.
*/
public static final LogRecord RESERVED_BODY_TOO_LARGE = LogRecordModel.builder()
.prefix(PREFIX)
.identifier(109)
.template("Reserved-path request body exceeded the %s byte ceiling (%s) — rejected 413")
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
* {@code 103-106}) and the configuration subsystem
* ({@link de.cuioss.sheriff.gateway.config.ConfigLogMessages}: {@code 2-3} / {@code 101-102} /
* {@code 200-201}), which share the same {@code ApiSheriff} prefix — this BFF catalogue owns
* {@code 10-14} (INFO) and {@code 110-112} (WARN). Never renumber one catalogue without checking
* {@code 10-17} (INFO) and {@code 110-114} (WARN). Never renumber one catalogue without checking
* the others for a collision.
* <p>
* <strong>No sensitive data is logged.</strong> Session subjects ({@code sub}), IdP session ids
Expand All @@ -51,7 +51,7 @@ public final class BffLogMessages {
private static final String PREFIX = "ApiSheriff";

/**
* Info-level messages (INFO range 1-99; this catalogue owns 10-14).
* Info-level messages (INFO range 1-99; this catalogue owns 10-17).
*/
@UtilityClass
public static final class INFO {
Expand Down Expand Up @@ -97,10 +97,43 @@ public static final class INFO {
.identifier(14)
.template("RP-initiated logout completed for a require:session route")
.build();

/**
* A cookie-mode session was sealed into its {@code Set-Cookie}. The template carries only
* the bounded sealed-value length — never the sealed value, the key, or any token material.
*/
public static final LogRecord COOKIE_SESSION_SEALED = LogRecordModel.builder()
.prefix(PREFIX)
.identifier(15)
.template("Cookie-mode session sealed (%s bytes)")
.build();

/**
* No {@code encryption_key} was configured, so a cookie-mode sealing key was generated at
* startup. Records only the non-sensitive fact and the affected scope — never key material.
*/
public static final LogRecord COOKIE_KEY_GENERATED = LogRecordModel.builder()
.prefix(PREFIX)
.identifier(16)
.template("Cookie-mode sealing key generated at startup (%s) — sessions do not survive a restart")
.build();

/**
* A cookie still sealed under the decrypt-only {@code previous_key} was observed, so a key
* rotation is in progress: each such session is re-sealed under the current key on its next
* write. Recorded once per process — the condition is gateway-wide, and the detection recurs
* on every request for an unrotated session. Records only the bounded disposition.
*/
public static final LogRecord COOKIE_ROLLOVER_IN_PROGRESS = LogRecordModel.builder()
.prefix(PREFIX)
.identifier(17)
.template("Cookie-mode previous-key rotation in progress (%s) — affected sessions "
+ "re-seal under the current key on their next write")
.build();
}

/**
* Warn-level messages (WARN range 100-199; this catalogue owns 110-112).
* Warn-level messages (WARN range 100-199; this catalogue owns 110-114).
*/
@UtilityClass
public static final class WARN {
Expand Down Expand Up @@ -136,5 +169,27 @@ public static final class WARN {
.identifier(112)
.template("Back-channel logout token rejected: %s")
.build();

/**
* A sealed session cookie failed to unseal and was treated as "no session". Records the
* non-sensitive rejection disposition ({@code malformed} / {@code unknown-version} /
* {@code unknown-key-id} / {@code authentication-tag} / {@code payload-format}) only —
* never the offending cookie value or any key material.
*/
public static final LogRecord COOKIE_UNSEAL_REJECTED = LogRecordModel.builder()
.prefix(PREFIX)
.identifier(113)
.template("Sealed session cookie rejected: %s")
.build();

/**
* A sealed session cookie exceeded the browser-safe size budget, so the seal failed rather
* than emitting a value the browser would silently drop. Records only the bounded length.
*/
public static final LogRecord COOKIE_SIZE_BUDGET_EXCEEDED = LogRecordModel.builder()
.prefix(PREFIX)
.identifier(114)
.template("Sealed session cookie exceeds the size budget (%s bytes) — seal refused")
.build();
}
}
Loading
Loading