Start deletes request attempt cookies that are no live attempt, then the live ones expiring first until the new attempt and the survivors fit 5120 bytes, so the newest attempt always completes and the header stays under nginx's 8 KB. Behavioural tests drive a cookie jar past the bound and read the served page's form fields and action through the test host.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
HttpOnly, Secure and SameSite on a deletion decide nothing (a cookie is matched on name and path); Stryker found those two booleans surviving. The deletion is pinned by path, expiry and the absence of Max-Age.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
d695674 does not build (two string literals split across lines); this commit repairs it. cf4e5b4 inherits the break.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A committed harness (Fixtures/complete-page-harness.cjs) executes the served page's inline script against a simulated browser; tests pin that the fragment is dropped before the post, an error status never submits, and the code is read only from the fragment. Node is required on PATH; a missing Node fails, it does not skip.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The HeimdallAuthService client redacts x-heimdall-app-secret from HTTP client logging at every level; a Trace canary test pins it.
- The attempt cookie's deletion carries its Path and no Max-Age (which outranks the 1970 expiry); pinned on success and refusal paths.
- LocalReturnPath is at most 256 characters, else /App, so the attempt cookie stays far below the browser limit; pinned at the bound and with the heaviest-escaping path.
- A set BaseUrl with a blank AppSlug is refused at Start before any Heimdall call; behavioural test.
Builds untested until the verification job at the end of this batch.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The key lookup's second cache check after taking the gate changed nothing the refetch throttle did not
already decide, so it is removed. Mutation testing of the diff found the three behaviours above unpinned.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Start puts a per-attempt nonce in the returnTo it sends Heimdall and keeps the attempt (provider,
member flag, local return path, expiry) in a Data Protection cookie named for that nonce. Heimdall's
completion page sends the browser to /auth/heimdall/complete with a code in the URL fragment; the
page posts it back, Bifrost redeems it at Heimdall, requires the redeemed returnTo to name the
cookie's nonce, verifies the access token for the redeemed account, and signs that browser in.
Deleted: the callback route, the wait route, the server-side attempt store, the pending state and
the backend_callback handoff. Added: LocalReturnPath for return targets, Heimdall:AppSecret sent as
x-heimdall-app-secret (member starts are refused without it), a short timeout on the key fetch, and
a key lookup that does not hold the fetch gate for cached keys. Refusals at the completion route all
answer one redirect and log a fixed reason.
Tests are rewritten against a fake Heimdall with start and redeem routes. Not compile-checked by this
commit alone; later commits record the verification.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The forgery and wrong-claim tests now assert which rule refused, start failures,
blank attempt ids, capability shapes and date extremes are covered. A token whose
exp is the largest representable date no longer overflows the clock-skew sum, and
the unreachable blank-return-url fallback is removed (Program defaults it).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Behaviour is unchanged. The catch-and-refuse blocks sit in their own small
methods so the mutation tool can compile mutants of the rest of the verifier.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The verifier was registered as a typed client, which is transient, so each request
would have started with an empty key cache. It is now a singleton that takes its
HTTP client from the factory per fetch. Test hosts share one in-memory database
root so EF keeps a single service provider.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The callback verifies the EdDSA access token against Heimdall's JWKS and takes the
account, display name and member access from its claims; attempts expire, are
bounded, are completed only if issued, and are redeemed once with a per-attempt
binding cookie. Not yet built or tested; the first verification run follows.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Drops antiforgery from the anonymous vote flow: no antiforgery cookie, no hidden token.
CastRequestGuard is the one decider; VoteModel.OnPageHandlerSelected is the one gate and
answers 4xx when no handler is selected (was a NullReferenceException 500).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Empty, NUL and unreadable paths, malformed UTF-8 and lone surrogates refuse
with the command's exit 1. The manifest is bounded (1 MiB, 1000 ships) and
duplicate JSON properties are refused. Titles refuse format, line and
paragraph separator characters and edge whitespace. Naming an already
retired key is a recorded no-op, so a rerun converges.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
UseConceptTallyCors replaces UseCors and appends Vary: Origin to every
response under the export route, preflights and 404s included, keeping any
Vary already set. A single-origin ASP.NET policy never adds it itself.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Stryker flagged ParseManifest, ReadString and RunAsync as compile errors in safe
mode (out vars and try-assigned locals), leaving them unmutated. Behaviour unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
One owner for the route and the policy, so Stryker can mutate them: it
flags every mutant of Program.cs as a compile error (safe mode), which
left the export untested by mutation. Program.cs keeps UseCors and the
two registration calls.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GET /public/concepts/{collection}/tallies returns per-live-subject up, down and
member counts only, anonymous, Cache-Control public max-age=60. CORS policy
ConceptTallies is built from ConceptVotes:AllowedOrigins, GET only, no
credentials, required on this route alone. Production startup refuses an
allowed origin that is not an exact https origin.
Not yet built or tested when committed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ConceptSubjectRegistry is the only writer of ConceptSubject. ConceptKey is the
one key grammar (anchored with \z), shared with ConceptAppraisalService.
Not yet built or tested at this commit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>