test(corpus): sweep for vacuous assertions and settle carried findings - #151
Conversation
Census the 135-file / 1364-method API Sheriff test corpus for assertions that cannot support the behaviour their names claim, publish the full classification as a checked-in note, and strengthen a capped subset highest-risk-first. Note (new): doc/development/test-corpus-integrity.adoc, indexed from doc/development/README.adoc. It carries the marker censuses with their complete-coverage evidence, the four vacuity shapes and three verdicts, the two selection gates and every higher-density file they excluded, one verdict row per flagged method with a file:line citation, and the standing backlog counted as 43 files carrying 140 marker occurrences. Strengthened: 18 methods (8 mandatory + 10 discretionary) against a cap of 20. The six BenignPassThrough tests in PassthroughHostGuardStageTest are both parameterized (closing java:S5976) and strengthened, since parameterizing alone would turn Sonar green while leaving all six vacuous. One test is deleted as strictly subsumed by a stronger sibling. Findings recorded in the note: marker density is a screen, not a verdict (three high-density files yielded zero strengthenings); EgressPolicy equality does not carry the host allowlist; TokenValidator exposes no view of its issuer configs. No production file is modified by this deliverable. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NJ5pHcAVTxjFJneB7cyke8
Three transient production mutations, one per strengthening technique, were applied together and reverted after the run. Each broke exactly the strengthened assertions it targeted, and none is visible to the pre-change form of the same test - which is the contrast the sweep exists to establish. The audit ran after the deliverable was committed so the revert is a checkout against committed content rather than a destructive discard of uncommitted work. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NJ5pHcAVTxjFJneB7cyke8
Read the authoritative cuioss SonarCloud gate BEFORE any code change. All four PLAN-05 findings were genuinely open, with the re-anchored line numbers exact: java:S3776 at ClientHelloSniParser.java:80 (cognitive complexity 21 > 15), java:S7467 at :171, and the two java:S5976 rows at PassthroughHostGuardStageTest.java:116 and :146. None had been resolved without a code change, so there is no silent-dismissal finding to report. java:S3776 - the while(true) reassembly loop interleaved four concerns. Each is now a named private helper returning the terminal Result it implies, or null to consume the record: recordHeaderVerdict, recordBodyVerdict, reassembledVerdict. parse() reads as the reassembly loop it is. The duplicated tail check after an INCOMPLETE span is removed because it restates the loop head's own first check, so looping back is byte-for-byte equivalent. Every fail-closed Result.parsed(null) path survives unchanged. java:S7467 - the unused catch binding becomes the unnamed variable. Structure-only: ClientHelloSniParserTest passes with ZERO edits, which is the behaviour-preservation harness this deliverable is verified by. The two S5976 rows were closed by deliverable 1's parameterize-and-strengthen pass. Four further live-gate findings are open and outside this plan's scope; they are reported in decision.log, not absorbed: java:S6539 RouteTableBuilder.java:80, java:S5738 ConfigFailFastTest.java:59, java:S5738 ConfigProducerTest.java:179, java:S135 BackchannelLogoutEndpoint.java:137. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NJ5pHcAVTxjFJneB7cyke8
Both consumer sweeps were re-confirmed against the current tree searching BOTH the call form and the ::name method-reference form, which is what decides them in opposite directions. no-arg FramingGate() - REMOVED. Sweeping `new FramingGate()` together with `FramingGate::` over 926 files finds exactly one consumer, FramingGateTest:40, and no production one; the only production instantiation is the boolean constructor at GatewayEdgeRoute. All four clean-break conditions hold, so under the plan-level breaking compatibility and the pre-1.0 remove-never-deprecate rule it goes directly, with no deprecation marker and no transitionary comment. The single call site becomes new FramingGate(false), which is value-identical: the removed constructor delegated to this(false), so the strict default posture is preserved exactly and no assertion changes. SealedSessionCookieCodec.Unsealed - KEPT, with the reason recorded in its Javadoc. It is the payload wrapper of unseal()'s return, consumed in production by the readSealedValue(...).flatMap(codec::unseal).filter(...).map(...) chain in CookieSessionBinding.resolve. That site is the METHOD-REFERENCE form, so a call-form-only search reports this API as unused - a false verdict that would have justified an unsafe removal. The Javadoc now records both that consumer chain and the ADR-0033 computed-vs-stored Optional distinction, so a future sweep does not re-flag Optional<Unsealed> as PLAN-36 residue. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NJ5pHcAVTxjFJneB7cyke8
The phase-5 worktree is cut from main's committed HEAD, so it does not inherit main's uncommitted working-tree changes. This re-applies the pending .plan/marshal.json edit inside the worktree so it ships rather than being silently lost. Carried as a patch (git diff --output, then git apply) from the main checkout, never hand-retyped, and the main checkout was not written to. Three hunks: - default:lessons-capture and default:finalize-step-preference-emitter are relocated to sit after default:branch-cleanup. A canonical key re-ordering with byte-identical values, semantically a no-op. - provisioned_version 0.1.1286 -> 0.1.1288. REPORTED, not corrected: provisioned_version 0.1.1288 is already stale against the installed plan-marshall bundle 0.1.1292. Re-deriving it would mean running marshall-steward, which is outside this plan's scope, so it is carried verbatim. REPORTED, needs operator confirmation: the operator-supplied backup of this diff carried default:lessons-capture at lane "off", while the main checkout's current working tree carries it at lane "minimal" - main drifted after that backup was taken. D4's success criteria name the main checkout as the source of truth, so "minimal" is what shipped here. If "off" was intended, this hunk is wrong and must be corrected before merge. See decision.log. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NJ5pHcAVTxjFJneB7cyke8
…g change Corrects the one hunk of the D4 carry that was taken from a rewritten source. The carry in 9907dc0 read .plan/marshal.json from the main checkout, which D4's success criteria name as the source of truth, and shipped default:lessons-capture at lane "minimal". Main's working tree had been REWRITTEN mid-run: the orchestrator's init-time capture of the same pending change is blob 474e4cc..60ed916 and carries lane "off", while main now shows 474e4cc..a84f140 with "minimal". The plan's own handshake rows corroborate a mid-run config write (config_hash 93acf2ec -> 6951111f -> 577b902 -> e8e8b3e). "Matches main" was therefore not the same as "matches the operator's change". The operator's init-time intent is authoritative, so lane "off" is what ships. Verification is exact rather than asserted: after this change .plan/marshal.json hashes to blob 60ed916 - byte-identical to the post-image of the init-time capture. Applied through the manage-config step setter, so no JSON was hand-retyped and no other key was touched; the diff is one line. Everything else in the D4 carry is unchanged: the block relocation, finalize-step-preference-emitter at preference_min_recurrence 2 / lane minimal, and provisioned_version verbatim at 0.1.1288. No marshall-steward run. REPORTED, unchanged: provisioned_version 0.1.1288 is stale against the installed plan-marshall bundle 0.1.1292; re-deriving it is out of scope. REPORTED, new: main's .plan/marshal.json was mutated mid-run, so this branch deliberately differs from main's CURRENT on-disk value for lessons-capture.lane. Anyone diffing the branch against main's working tree later would otherwise read that difference as an error in this PR. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NJ5pHcAVTxjFJneB7cyke8
…nert NOSONAR marker Post-implementation simplification sweep over the plan's changeset. TokenValidatorProducerTest: allowlistSurvivesTheProducerPath carried two near-identical ~13-line builder blocks differing only by the .allowedEgressHosts(...) call. Collapsed into a producerPathEgressPolicy helper alongside the existing egressPolicyFor. Both assertions and every explanatory comment are preserved verbatim; no assertion was weakened. TlsEdgeProducerTest: awaitNotListening carried two suppression mechanisms for java:S2925 — an inert "// NOSONAR java:S2925" on a standalone comment line (NOSONAR only suppresses on the line it sits on, and the Thread.sleep is six lines below) plus the @SuppressWarnings that actually suppresses. Dropped the inert marker, kept the rationale prose and the annotation. Two anti-patterns were reported rather than fixed, both out of scope: a naming shadow at ClientHelloSniParser.java:113 (a rename would re-open D2's behaviour-preserving, zero-test-edit constraint for cosmetic churn), and pre-existing five-argument call duplication in RouteRuntimeAssemblerTest that predates this plan. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NJ5pHcAVTxjFJneB7cyke8
There was a problem hiding this comment.
Sorry @cuioss-oliver, you have reached your weekly rate limit of 500000 diff characters.
Please try again later or upgrade to continue using Sourcery
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository: cuioss/coderabbit/.coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
🚧 Files skipped from review as they are similar to previous changes (3)
📝 WalkthroughWalkthroughThe change refactors TLS validation, removes the default ChangesGateway validation and test integrity
Workflow metadata
Estimated code review effort: 3 (Moderate) | ~25 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 2✅ Passed checks (2 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
PR Reviewer Guide 🔍(Review updated until commit 5742019)
|
There was a problem hiding this comment.
Actionable comments posted: 2
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository: cuioss/coderabbit/.coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 0fb87f82-8542-448b-9476-b7bda3bc1c12
📒 Files selected for processing (16)
.plan/marshal.jsonapi-sheriff/src/main/java/de/cuioss/sheriff/gateway/bff/cookie/SealedSessionCookieCodec.javaapi-sheriff/src/main/java/de/cuioss/sheriff/gateway/pipeline/FramingGate.javaapi-sheriff/src/main/java/de/cuioss/sheriff/gateway/tls/ClientHelloSniParser.javaapi-sheriff/src/test/java/de/cuioss/sheriff/gateway/auth/AuthenticationStageTest.javaapi-sheriff/src/test/java/de/cuioss/sheriff/gateway/auth/TokenValidatorProducerTest.javaapi-sheriff/src/test/java/de/cuioss/sheriff/gateway/edge/RouteRuntimeAssemblerTest.javaapi-sheriff/src/test/java/de/cuioss/sheriff/gateway/pipeline/BasicChecksStageTest.javaapi-sheriff/src/test/java/de/cuioss/sheriff/gateway/pipeline/FramingGateTest.javaapi-sheriff/src/test/java/de/cuioss/sheriff/gateway/pipeline/PassthroughHostGuardStageTest.javaapi-sheriff/src/test/java/de/cuioss/sheriff/gateway/pipeline/ThoroughChecksStageTest.javaapi-sheriff/src/test/java/de/cuioss/sheriff/gateway/quarkus/BffRuntimeProducerTest.javaapi-sheriff/src/test/java/de/cuioss/sheriff/gateway/quarkus/ConfigProducerTest.javaapi-sheriff/src/test/java/de/cuioss/sheriff/gateway/tls/TlsEdgeProducerTest.javadoc/development/README.adocdoc/development/test-corpus-integrity.adoc
💤 Files with no reviewable changes (2)
- api-sheriff/src/main/java/de/cuioss/sheriff/gateway/pipeline/FramingGate.java
- api-sheriff/src/test/java/de/cuioss/sheriff/gateway/auth/AuthenticationStageTest.java
The cuioss SonarCloud gate failed at f802043 with three new-code findings. All three are closed here; the bug-class one was analysed before it was touched. javabugs:S6466 (ClientHelloSniParser.java:253, BUG) - re-derived every call path into uint16 and uint24 rather than trusting the earlier "the header guard already returned null" claim. Three call sites exist and every one is guarded: * parse() reaches uint16(bytes, pos + 3) only after recordHeaderVerdict returned null, which happens solely when bytes.length - pos >= RECORD_HEADER_LENGTH, so pos + 4 <= bytes.length - 1. pos starts at 0 and only advances to a recordEnd that recordBodyVerdict already bounded by MAX_CLIENT_HELLO_BYTES, so it is never negative and cannot overflow. * Cursor.readUint16() reaches uint16(data, position) only after require(2) asserted position + 2 <= data.length, raising MalformedHelloException otherwise. position starts at HANDSHAKE_HEADER_LENGTH and never decreases - seek() refuses a backwards target - so it is never negative. * completeHandshake() reaches uint24(handshake, 1) only after returning INCOMPLETE for handshake.length < HANDSHAKE_HEADER_LENGTH, so offsets 1..3 are always inside the buffer. The out-of-bounds access is therefore unreachable: no defect shipped and no behavioural fix is warranted. What the S3776 split did change is where the guards live - they moved from inline in the reassembly loop into named helpers, which is exactly what put them out of reach of the symbolic-execution engine. Suppressed in-code with // NOSONAR javabugs:S6466 on the two flagged reads, with the full per-call-site bounds contract written up as Javadoc on uint16 (and the matching one-liner on uint24) so a reviewer can challenge the claim rather than take it. Two regression tests turn that prose into executable assertions, both pinning the guard at the exact one-byte-short-of-a-record-header boundary - index 4 present in neither case, which is the precise read Sonar flagged: one on the first record (pos == 0) and one on the loop-back to a later record (pos > 0), the tail check the S3776 split delegated to the loop head. Remove the length guard and both throw ArrayIndexOutOfBoundsException instead of asking for more data. java:S7467 (TlsEdgeProducerTest.java:201, :213) - both catch bindings are unused, so both become the unnamed pattern. The refused-connection branch keeps its meaning in a comment now that the binding name no longer carries it. No assertion was weakened; these are the D1-strengthened tests and their strength is untouched. Both gates green with zero errors: verify -Ppre-commit and verify. Neither left any rewrite churn outside the three-file footprint. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Triage dispositionsIn reply to comment_id:
|
…ount
PR 151 CodeRabbit inline comment 28625f on ClientHelloSniParser.java:119.
recordHeaderVerdict decided "keep buffering vs fail closed" with overBound(pos),
where pos counts only the record bytes already CONSUMED. The invariant
MAX_CLIENT_HELLO_BYTES declares is on the ACCUMULATED buffer: "the hard upper
bound on the bytes buffered while reassembling a single ClientHello ... anything
larger is treated as complete-with-no-SNI (fail closed) rather than buffered
further".
Those two are not the same number. recordBodyVerdict admits a record whose
recordEnd equals MAX_CLIENT_HELLO_BYTES, so pos can advance to MAX - 1. The
header branch fires while bytes.length - pos < RECORD_HEADER_LENGTH, so
bytes.length can reach pos + 4 = MAX + 3 with overBound(pos) still false and
parse() still returning needMoreData(). The caller was therefore told to keep
buffering a buffer that had already passed the declared hard bound.
The overshoot is self-terminating - one more byte completes the record header
and recordBodyVerdict fails closed - so the class-level "buffering can never
grow unbounded" claim always held. What did not hold is the constant's own
contract, and a guard whose predicate does not match the scenario it exists for
is a defect whether or not the blast radius is currently bounded.
Fixed by anchoring the test on overBound(bytes.length). Since pos <= bytes.length
always holds (pos only ever advances to a recordEnd that recordBodyVerdict has
already confirmed is within the buffer), the buffer-anchored test subsumes the
position-anchored one: every input that used to fail closed still does, and the
1..3 byte overshoot window now fails closed too. The rationale is written up on
recordHeaderVerdict so a reviewer can challenge it rather than take it.
This is NOT the javabugs:S6466 finding closed earlier in this PR. That one was
about a uint16 read running past the array and was closed UNREACHABLE with a
per-call-site bounds contract; index safety is untouched here. This is the SIZE
bound, a distinct concern on the same method.
Two regression tests pin the guard as a matched pair, both built from one
fixture that puts an incomplete handshake record ahead of a partial next-record
header - the shape that separates the two candidate anchors, because the bytes
consumed stop at the first record's end while the bytes buffered run on:
* a buffer of exactly MAX_CLIENT_HELLO_BYTES fails closed, though only MAX - 1
bytes have been consumed. This is the case the old anchor got wrong; it fails
against the previous guard.
* the identical shape one byte smaller still asks for more data, pinning the
give-up point to MAX_CLIENT_HELLO_BYTES exactly so the assertion above cannot
be satisfied by a guard that gives up early on every fragmented ClientHello.
The pair is what makes either test meaningful: they differ by one buffered byte
and disagree on the verdict, while the consumed position is under the bound in
both - precisely the distinction the old predicate could not draw.
Both gates green with zero errors: verify -Ppre-commit and verify. Neither left
any rewrite churn outside the footprint.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NJ5pHcAVTxjFJneB7cyke8
PR 151 CodeRabbit inline comment 094c4b on RouteRuntimeAssemblerTest.java:194.
The gRPC leg of shouldAssembleSessionRoutes ended on
assertNotNull(grpc.getHttpClient(), "a gRPC route carries the forced-h2 upstream client")
The message claims forced-h2; the assertion only proved non-null. The test's
client factory is
clientFactory = _ -> vertx.createHttpClient();
which DISCARDS its UpstreamTarget, so the leg passed identically whether or not
the assembler asked for a forced-h2 client. The only thing that could have failed
it is the factory returning null, which it never does. That is exactly the
weak-assertion class this plan exists to remove, reintroduced by the very hunk
that replaced the leg's original assertDoesNotThrow - so it is in scope and
authored by this PR, not pre-existing.
RouteRuntimeAssembler builds UpstreamTarget.of(resolvedUpstream, route.protocol()
== Protocol.GRPC) at line 159, so forcedHttp2 is the observable that proves the
gRPC branch ran. A capturing UpstreamClientFactory records the target the
assembler hands it, which is the direct, non-reflective way to assert on the
factory's INPUT rather than on its never-null output. The gRPC leg now asserts
exactly one client was resolved and that its target carries forcedHttp2().
The WebSocket leg becomes the matched negative control: the identical capture
over a non-gRPC route asserts forcedHttp2() is false. Without it the new
assertion would be only half-pinned - assertTrue on a flag would still pass if
the flag were hardcoded true, or if forced-h2 were requested for every protocol.
The two legs assert opposite values on the same field through the same capture
path, so neither can be vacuous.
Nothing was weakened or deleted: the original assertNotNull and both existing
assertEquals assertions on the WebSocket leg are preserved verbatim, with the
new assertions added alongside them.
Both gates green with zero errors: verify -Ppre-commit and verify.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NJ5pHcAVTxjFJneB7cyke8
|
/review |
Summary
Censuses the API Sheriff test corpus for tests whose assertions cannot support the behaviour they are
credited with covering, publishes the full classification as a checked-in note
(
doc/development/test-corpus-integrity.adoc), strengthens a capped subset highest-risk-first, andsettles four carried items in the same PR: the four PLAN-05 Sonar
new_codefindings, the two publicAPI elements whose load-bearing status was undetermined, and the pending
.plan/marshal.jsonconfiguration change.
The method is deliberately census-then-targeted-read, not a naive 135-file linear pass. A linear
pass over a corpus this size degrades into spot-checks — which is the exact apparent-vs-real-coverage
failure this sweep exists to catch. Every coverage claim below is reproducible from a published
census count; nowhere does this PR assert "the whole corpus was reviewed", because no such claim
could be verified.
Changes
D1 — Corpus sweep, classification, and the capped strengthening
Declared surface (the denominator)
@Test@ParameterizedTestBoth annotations are part of the denominator. A
@Test-only count under-states the realtest-method population by 97 methods (7.1%). After this change the union is 1364 (−5
@Testconverted to +2
@ParameterizedTestinPassthroughHostGuardStageTest, −1 for the singledeleteverdict); the file count is unchanged at 135. Every census ran under complete coverage: 300 files
scanned, 0 unreadable, not truncated, 0 elided.
The stated cap, and why it was deliberately under-filled
The cap is 20 test methods — 8 mandatory + up to 12 discretionary — stated up front, before
selection.
Actually strengthened: 18 (8 mandatory + 10 discretionary), plus 1
delete.The discretionary budget was not filled to twelve because the discretionary pool yielded only ten
strengthenverdicts; every remaining flagged method in the pool earnedkeepon thematched-control test. Padding to twelve would have meant rewriting tests that were already correctly
scoped — churn, not integrity. This under-fill is a deliberate verdict-driven outcome, not an
omission or an unfinished quota.
Classification — one row per flagged method, exactly one verdict each
56 methods classified across 15 files (12 mutation-pool files + 3 control-sample files), every row
carrying a
file:linecitation.TokenValidatorProducerTest— 7 flaggedbuildsFromHttpIssuerWithAudiencebuildsFromHttpIssuerWithoutAudiencelistedHostIsExemptedexemptionIsScopedToTheListedHost:211severalHostsAreAllowlistedexemptionIsScopedToTheListedHost:211allowlistSurvivesTheProducerPathassertNotNull(validator)shows only that a validator was builtomittedProfileNeverConsultsTheResolvercompletesForAValidlyConfiguredValidatorforcesEagerAssemblyByInvokingTheValidator:398PassthroughHostGuardStageTest— 7 flaggedpassesBenignHostjava:S5976triple 1passesNullHostjava:S5976triple 1passesSuffixLookalikeHostjava:S5976triple 1passesHostWithNonNumericPortjava:S5976triple 2passesHostWithEmptyPortjava:S5976triple 2passesHostWithMultipleColonsjava:S5976triple 2inertWhenPassthroughSetEmptyrejectsSmuggledHost:54ThoroughChecksStageTest— 9 flaggedacceptsLegitimateRequestUnderDivergentFilterrejectsDivergentFilterViolation:80skipsReRunWhenRouteConfigEqualsDefaultfallsBackToBaselineWhenRouteDeclaresNoConfigadmitsWildcardWhitelistMatchrejectsWildcardSegmentCountMismatch:180acceptsParameterValidationTheModeDisablesstrictRouteStillRejectsWhatMinimalAccepts:355skipsPipelineReRunstrictRouteStillRejectsWhatMinimalAccepts:355skipsHeaderNameValidationUnderMinimalrejectsHeaderNameUnderDivergentConfig:387skipsHeaderNameReRunWhenConfigEqualsBaselineadmitsLongCookieOnCookieModeGatewaybearerOnlyGatewayStillRejectsLongCookie:539TlsEdgeProducerTest— 3 flagged (the sharpest instance in the corpus: all three test methodswholly vacuous, none with a control)
startsAndStopsFrontListenernoFrontListenerWhenPassthroughEmptyskipsUnresolvedAliasBasicChecksStageTest— 7 flaggedacceptsLegitimatePathassertNotNullnever says whichdoesNotValidateParameterNamesOrValuesacceptsCookieHeaderAtBudgetrejectsCookieHeaderBeyondCap:213strictBaselineAcceptsCookieHeaderAtBudgetstrictBaselineRejectsCookieHeaderBeyondCap:226admitsBearerTokenAboveBaselineCaprejectsAuthorizationBeyondConfiguredCap:315matchesHeaderNameCaseInsensitivelyextendedAsciiIsAdmittedUnderAPermissiveBaselineConfigProducerTest— 3 flaggedshouldBootWhenTheDeclaredBodyCapEqualsTheFrameworkLimitshouldRefuseBootWhenADeclaredBodyCapExceedsTheFrameworkLimit:239 — a boundary pair, the #148 precedentshouldAssembleEagerlyOnStartupForValidConfigonStartupfrom lazy assembly at the latergatewayConfig()callshouldResolvePassthroughSniAliasValueRatherThanHostKeyshouldFailBootWhenPassthroughSniAliasIsUnresolvable:346BffRuntimeProducerTest— 1 flaggedshouldAssembleWithoutDiscoveryRouteRuntimeAssemblerTest— 1 flagged (F2, partially vacuous)shouldAssembleSessionRoutesassertDoesNotThrowAuthenticationStageTest— 3 flaggedpassesRequireNonepassesRequireNoneWithoutResolvingValidator:77 — same arrange plus a throwing validator providerpassesRequireNoneWithoutResolvingValidatoracceptsValidBearerTokenCsrfDefenceTest— 4 flagged, allkeepsafeMethodsBypassbothHeadersAbsentRejected:126unsafeMethodTrustedOriginAcceptedunsafeMethodUntrustedOriginRejected:92trustedOriginMatchIsCaseInsensitiveabsentOriginSameOriginFetchAcceptedabsentOriginCrossSiteFetchRejected:118GatewayEdgeRouteTest— 5 flagged, allkeepbootsCleanlyOverEmptyRouteTableregistersCatchAllRoute:132 asserts on the assembled edgebootsSessionAuthRoutebootsGrpcProtocolbootsWebSocketProtocolbootsSessionAuthWebSocketRouteSessionAuthenticationStageTest— 0 flagged. FiveassertDoesNotThrowoccurrences, every onefollowed by a substantive
assertEquals. Clears the density gate; contributes nothing to thevacuity population.
Control-sample rows (3 un-ranked files read in full) — 6 flagged, all
keepacceptsWellFramedPostFramingGateTestonAdmitsGetWithContentLengthBodyFramingGateTestonAdmitsCleanGetFramingGateTestemptyAllowlistEnforcesNothingOriginValidationStageTestforeignOriginRejected:92allowlistedOriginProceedsOriginValidationStageTestforeignOriginRejected:92matchingIsCaseInsensitiveOnHostOriginValidationStageTestClassification totals
strengthenkeepdeleteThe countable backlog
43 files carrying 140 vacuous-shape marker occurrences were censused but not individually
classified. That is the standing backlog and the honest measure of what this sweep did not cover.
It is derived arithmetically from the published census, not estimated:
assertDoesNotThrow∪assertNotNull)Every backlog entry is enumerated by name in the note with its measured marker density and the gate
that excluded it (24 files / 73 occurrences under
api-sheriff, 19 files / 67 occurrences underintegration-tests). It is not "various remaining tests" — draining it is bounded, countable work.The inversion audit
A strengthened assertion is worth the diff only if it fails when the behaviour it claims is broken.
Three transient production mutations were applied, the affected suites run, and the mutations
reverted — each targeting a different strengthening technique:
PassthroughHostGuardStage.normalizestrips any:suffixrather than only a numeric portPASSTHROUGH_HOST_SMUGGLED; the other rows and every attribution control correctly stayed greenTlsEdgeProducer.onStartupreturns before creating the front listenerstartsAndStopsFrontListenerfailed: expected<true>but was<false>TokenValidatorProducer.toValidationIssuerdrops theaudienceValidationDisabled(true)opt-outaudienceLessIssuerDisablesAudienceValidationfailed with the exact library refusal the opt-out branch exists to preventEach mutation is invisible to the pre-change form of the same tests — all of them stay green
under all three mutations. That contrast is the point of the sweep.
D2 — All four carried PLAN-05 Sonar findings, disposed
"Still open" was not a permitted outcome. All four are fixed in code, none suppressed, none
marked won't-fix/false-positive in the Sonar UI:
java:S3776cognitive complexityClientHelloSniParser.parsejava:S7467unnamed patternClientHelloSniParsercatch clause_appliedjava:S5976(triple 1)PassthroughHostGuardStageTestjava:S5976(triple 2)PassthroughHostGuardStageTestThe two
java:S5976rows required both operations. Parameterizing alone would have closed theSonar rule while leaving all six methods vacuous under D1's own taxonomy — the gate would go green
and the rows would look handled. That is precisely the failure mode this sweep exists to catch, so
parameterize-and-strengthen was treated as one indivisible fix.
The live gate was read first (a code check alone cannot prove gate state). Both
ClientHelloSniParserline anchors carried by the source premises were off by one (
parseat 80 not 79; the catch at 171not 170) — the constructs were present and the findings were not stale; only the line numbers had
drifted.
Four ADDITIONAL live-gate findings — logged, deliberately NOT fixed
Reading the live gate surfaced four findings beyond the four carried ones. All four sit outside this
plan's write boundary and are reported here rather than absorbed; absorbing them would have been
a scope breach, not a judgement call:
java:S6539RouteTableBuilder:80java:S5738ConfigFailFastTest:59java:S5738ConfigProducerTest:179java:S135BackchannelLogoutEndpoint:137D3 — The two undetermined public API elements, per-element verdicts
Both branches (remove / keep-with-recorded-rationale) were live. They resolved differently:
FramingGate()(the no-arg constructor) — REMOVED.Zero production consumers; its sole caller was its own test. Deleted outright per the pre-1.0 rule
(remove, never deprecate).
FramingGateTestwas updated to the boot-resolved constructor, and themodule compiling with the no-arg form gone is itself the proof that no consumer survived.
SealedSessionCookieCodec.Unsealed— KEPT, with the rationale recorded in-source.It is load-bearing: it is the payload wrapper of the return type of the public
Optional<Unsealed> unseal(String), consumed via the method referencecodec::unsealinsideCookieSessionBinding.resolve()(lines 110–113). Two facts are now written into its Javadoc so afuture sweep cannot re-open the question from the same wrong evidence:
as unconsumed (see the methodology note below);
Optional<Unsealed>return is not PLAN-36 residue — ADR-0033 retiresOptionalonly fromstored positions (fields, declared parameters, record components); a computed method return
is explicitly sanctioned.
D4 — The carried
.plan/marshal.jsonconfiguration changeThe pending configuration change is carried into this PR as committed hunks on this branch. Two
caveats, both load-bearing for anyone reviewing or later diffing this file:
(a)
main's working-tree copy of.plan/marshal.jsonwas MUTATED MID-RUN.This branch deliberately ships the lessons-capture lane as
"off"— the operator's init-timeintent, captured verbatim at plan-init as blob
60ed916. Meanwhilemain's current on-diskworking-tree value reads
"minimal". Anyone diffing this branch againstmain's working tree laterwould otherwise read that difference as a defect introduced by this PR. It is not: the branch value
is the intended one, and the divergence is an artefact of the mid-run mutation of the main checkout.
(b)
provisioned_versionships verbatim at0.1.1288and is STALE on arrival.The installed bundle is
0.1.1292. The value is carried verbatim by operator ruling; re-derivingit would mean running
marshall-steward, which was explicitly out of this plan's scope. It isrecorded here so the staleness reads as a known, deliberate carry rather than an oversight.
Methodology note — a census on
name(misses::nameThis generalises well beyond this PR and is now a standing rule for the corpus.
Every symbol-reachability census must search both the call form
name(and themethod-reference form
::namebefore any "unused" / "unreached" verdict is recorded.This bit concretely during this plan. A literal
unseal(search reported no production consumerfor
SealedSessionCookieCodec.unseal, while the real production call site iscodec::unsealinsideCookieSessionBinding.resolve(). Acting on that census would have removed a live public symbol —the exact apparent-vs-real failure this sweep is about, turned on the sweep's own method. The
constraint is written into the note as load-bearing, not as an aside.
Reported, NOT fixed — two production defects outside the write boundary
This sweep's production write boundary is exactly three files
(
ClientHelloSniParser.java,SealedSessionCookieCodec.java,FramingGate.java). Two genuineproduction defects were found outside it. Absorbing either would have been a scope breach, so
both are reported here and in the note:
1.
EgressPolicy.equalsdoes not carry the host allowlist.An
EgressPolicybuilt withallowedEgressHost("localhost")compares equal toEgressPolicy.secureDefault()— both render asEgressPolicy(allowLoopback=false). The allowlist isinvisible to
equals/toString. Consequence:assertEquals(EgressPolicy.secureDefault(), …)cannotdetect an unintended widening, and the inverse assertion cannot detect a dropped allowlist at all.
The type is third-party (
de.cuioss.sheriff.token.commons.transport). The strengthened row thereforeasserts behaviourally via
policy.check(uri)rather than by equality.2.
TokenValidatorexposes no view of its issuer configs.Its public surface is
createAccessToken/createIdToken/createRefreshToken,close,getSecurityEventCounter,getPerformanceMonitor— there is no accessor for theIssuerConfiglistit was built from. The producer's audience decision (expected audience vs the explicit opt-out) is
therefore structurally unobservable on the built validator. That is why the strengthened
TokenValidatorProducerTestpair asserts the audience posture behaviourally, by validating areal generated token over an offline
fileJWKS source, rather than structurally.Production write boundary held. D1 modified no production file at all.
Test Plan
test -pl api-sheriff -am— greenverify -Ppre-commit— zero errors/warningsverify— greenassertion red, and were reverted (see D1 above)
ClientHelloSniParserTestpasses unmodified, proving thejava:S3776refactor isbehaviour-preserving
unreadable, 0 elided): declared surface 1368 → 1364
Related Issues
None.
Intent
The problem. A large share of this corpus asserts nothing that can fail. A test that calls
production code and asserts only
assertDoesNotThrow(...)orassertNotNull(result)reports ascoverage while proving almost nothing: it stays green when the behaviour its
@DisplayNamepromisesis deleted, inverted, or never implemented. The highest-risk shape is a test named or documented
for a behaviour its assertions do not exercise — the name reads as a specification and the green
result reads as its proof. The corpus is large enough that "we read everything and it looked fine" is
not a claim anyone can check.
The chosen approach, and why that shape. Census-then-targeted-read, not a linear 135-file pass. A
linear pass over a corpus this size degrades into spot-checks — which would reproduce the exact
apparent-vs-real-coverage failure this sweep exists to catch. So: mechanical censuses with published,
reproducible counts; a targeted read of 100% of the ranked candidates plus a control sample of the
un-ranked remainder to test whether the ranking actually selected the vacuous ones; one terminal
verdict per flagged method with a
file:linecitation; a cap stated before selection; and theun-covered remainder recorded as a countable backlog with every file named, rather than as an
anecdote. A
keepverdict must be attributable — a matched negative control or a[Intent truncated — 1396 of 2280 characters shown; full outline in the plan workspace]
Summary by CodeRabbit
Security & Reliability
Configuration
Documentation