Commit graph Bifrost/tests/Bifrost.Web.Tests
Author SHA1 Message Date
781e6eabf6 Desk: pin vectors to CultLib de8a062a (#45) 2026-11-11 02:16:28 +00:00
05dcceb203 Desk: a stale answer on an oversize fork is 422 fork_too_large (#44) 2026-11-11 01:54:04 +00:00
815304568f Desk: pin vectors to CultLib f6c9bf5f (#43) 2026-11-11 01:49:37 +00:00
8385eca00f Desk: a read of an oversize fork is 422 fork_too_large (#42) 2026-11-11 01:35:07 +00:00
7e9ba5796b Desk: the fork desk's Bifrost half (#40) 2026-11-11 01:02:36 +00:00
12a153f66f Mill votes: ranked, paged read and not-yet-voted-by-this-network filter (#41) 2026-11-10 21:06:56 +00:00
Heimdall
5ba6571fa6 Test the whole reserved set through both paths and pin case-insensitive matching
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-10 14:30:50 +00:00
Heimdall
8955279ef1 Reserve Matrix localparts and system names at identity creation
The identity becomes the Matrix localpart at SSO, so a new account must not mint a name that
a Matrix account already holds (the 13 Synapse localparts) or a system name. Native register
refuses a reserved name; a derived identity takes the next free suffix. Lookups of existing
accounts and existing rows are untouched; there is no exemption path.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-10 14:06:22 +00:00
800486a9a0 Merge pull request 'Pin the vote page busy-refusal branches' (#14) from heimdall/pin-vote-refusal-branches into main 2026-11-10 14:02:29 +00:00
Heimdall
012d1cb926 Test: a second simultaneous close of a Standard motion must not throw
Fails at this commit with DbUpdateConcurrencyException from CloseMotionAsync:
Motion.Status became a concurrency token in BifrostDbContext.cs:288.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-09 22:13:59 +00:00
Heimdall
0d7c3ec078 Mind intake: nonce prune is best-effort and never fails a valid request; test the five-lost-races contended branch; doc the retried reveal's phase judgement
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-09 20:55:03 +00:00
Heimdall
1c9cb9937e Signed Mind intake: verify before revealing state, dummy verify, issuedAt window and nonce pruning, per-source rate limit and body cap, one account per persona, DbUpdateException is replay only for the nonce
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-09 20:54:06 +00:00
Heimdall
8e6afc43ed Signed Mind intake for sealed motions: Ed25519 envelopes, registered public keys, nonce replay refusal, persona bound into the commitment
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-09 20:54:06 +00:00
Heimdall
55b7f79c13 Sealed ballot: retry a reveal that loses a RevealSeq race while the window is open, \z in hex patterns, doc the contended 409
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-09 20:47:23 +00:00
Heimdall
264233e08f Sealed ballot: freeze the reveal set at close (RevealSeq token), HTTP tests for replay/non-voter/late reveal/close, doc corrected on the ballot schema
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-09 20:40:54 +00:00
Heimdall
96da1b7a8c Sealed ballot: race-safe close, close sweep, endpoint tests, backup reader and auth findings in the doc
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-09 20:30:35 +00:00
Heimdall
d5a71eb639 Sealed-ballot motions: commit-reveal over typed gamecult.sealed_ballot.v0 documents
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-09 20:24:49 +00:00
Heimdall
58fa80bc5b Make the attempt cookie's deletion Secure so a browser honours it
A __Host- cookie is only removed by a Set-Cookie that is itself Secure. The
hardening test now checks Secure on every header, deletion included, and the
completion tests pin that success and refusal both clear the attempt cookie
with Secure, Path=/ and no Domain.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-09 11:24:02 +00:00
Heimdall
7cfce18fcf Pin the vote page's two unpinned busy-refusal branches
A signed-in retract on a full account table must say busy with no sign-in offer (the
retract door passes its voter to Throttled), and a spent cast budget must say only
"too fast", for anonymous and signed-in voters alike. Both mutants survived Soul s4.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-08 21:28:41 +00:00
Heimdall
c757acf525 Make every cookie host-only: __Host- names, Secure, Path=/, no Domain
Session, OAuth correlation, antiforgery, voter and Heimdall attempt cookies all take the
__Host- prefix. The prefix forbids a narrower Path, so the voter (was /mill) and attempt
(was /auth/heimdall) cookies move to Path=/.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-07 18:42:43 +00:00
Meta Vens
37c8fe1a72 Logout tests: pin the loop's own tick-failure catch now that subject faults are isolated
A pass that cannot read its subjects (a scope factory failing like a database outage) is logged
by exception type only and retried after the interval, never in a tight loop. The earlier loop
test reached this catch through a per-subject exception, which the isolation now absorbs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 01:57:31 +01:00
Meta Vens
ec66fba5ca Logout client: no auto-redirect, pin the redirect rule and the 10 s timeout
The named logout HttpClient's primary handler disables auto-redirect, so a 3xx from MAS is a
non-delivery retried like any non-2xx instead of being followed to a final 2xx that revokes.
Pinned through the real DI registration against a real loopback listener answering 302, and
the registered client's Timeout is pinned to exactly 10 s. The unexpected-exception test is
replaced by the isolation test (its fix is the next commit) and the cancellation test stands alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 01:50:53 +01:00
Meta Vens
51fe807b66 Logout loop test: the tick failure is logged by exception type only
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 01:03:02 +01:00
Meta Vens
af64868326 Logout tests: loop, log content, 2xx range, backoff reset, exception and cancellation paths
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 00:56:18 +01:00
Meta Vens
69b670e3af Fix logout test support compile errors
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 00:38:56 +01:00
Meta Vens
404dafdd40 OIDC back-channel logout: a reconciler ends the sessions of subjects who are not Active
OidcLogoutReconciler derives pending logouts from valid OpenIddict authorizations x
membership, POSTs one signed logout token per non-Active subject to MAS, and revokes
the authorizations only after a 2xx. Registered only when
Bifrost:Oidc:Clients:Mas:BackchannelLogoutUri is set. The active-member reading is
the endpoints' existing one, now shared.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 00:38:15 +01:00
Meta Vens
83daabe512 Bifrost as OIDC issuer for the Matrix chat (gamecult-chat cut bifrost-oidc-issuer, squash of 1ef289a..ec4acfa and abd12b3..)
One configured client (stale clients removed with their tokens and authorizations), Active membership re-checked at token and userinfo through IsStillActiveMemberAsync, PKCE required by the server option, OpenIddict.Server.AspNetCore + EntityFrameworkCore 7.7.1 with no vulnerable transitives. Soul s2, s3 and s4 hold; Low conformance gaps in gamecult-chat:follow_up:oidc-issuer-spec-conformance.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 04:57:16 +01:00
Meta Vens
d7d4b21c4b Commit NuGet lockfiles for the web and test projects
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-22 23:29:46 +02:00
Meta Vens
b2110f53fa Merge hands/heimdall-callback-signed
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-22 18:11:28 +02:00
Meta Vens
d743e4f363 Heimdall tests: an unreadable form with a genuine attempt cookie present is still no attempt
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-11 23:00:06 +02:00
Meta Vens
efb278a910 Heimdall tests: the no-attempt-field refusal carries a genuine code, so only the missing nonce can refuse it
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-11 22:57:26 +02:00
Meta Vens
d463336206 Heimdall tests: a genuine attempt cookie under a name that is no slot is no attempt
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-11 22:41:31 +02:00
Meta Vens
b54e8d5dc8 Heimdall tests: fix a string literal in the parallel-starts test
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-11 22:32:07 +02:00
Meta Vens
053272a0f3 Heimdall tests: re-point the cookie-ring tests to the slot cookie; a refusal with no matching attempt deletes nothing
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-11 22:31:25 +02:00
Meta Vens
15f96c408c Heimdall sign-in: attempt cookies live in a ring of two fixed slots, so the Cookie header is bounded by construction; pin the completion form's id
Start takes a free slot or overwrites the one expiring first; parallel starts carry the same request
cookies, choose the same slot and overwrite each other. Complete finds its attempt by scanning the
slots for the posted nonce; an overwritten attempt finds none and is refused like any other. The
per-request counting sweep, its byte budget and the nonce-named cookie are deleted.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-11 22:25:24 +02:00
Meta Vens
8186d6f8fa Heimdall tests: drop the exact-byte boundary test; the 5120-byte bound has a 3 KB margin, and no two attempt cookies can reach it
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-11 21:33:17 +02:00
Meta Vens
21f2c75c78 Heimdall tests: name the sums near the bound when the boundary pair is missing
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-11 21:32:31 +02:00
Meta Vens
ec2f812b59 Heimdall attempt cookie bound: pin the exact byte boundary and that unrelated cookies are never swept
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-11 21:26:31 +02:00
Meta Vens
4a28cea1f9 Heimdall sign-in: Start sweeps dead and oldest attempt cookies to keep the Cookie header bounded; pin the complete page's real form
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>
2026-10-11 20:41:41 +02:00
Meta Vens
cf74019194 Mill votes: the very-busy refusal offers sign-in to a voter who is not signed in
Accounts have their own table, so signing in works while the cast or mint
table is full. A signed-in voter is not told to sign in. Pinned through the
full pipeline at a small cap, for both audiences.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-11 20:23:59 +02:00
Meta Vens
a9b9b29bf9 Pin the account table's own cap
The cap-not-wired-accounts hand mutant survived: no test filled the account table.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-11 19:58:09 +02:00
Meta Vens
912d083894 Fix a string literal broken across lines in the table-full test
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-11 19:17:27 +02:00
Meta Vens
436a02864f Vote budgets: at the cap a new key evicts the least recently used key with permits left
Ruling budget-table-evict-oldest. FixedWindowBudget keeps two linked lists (window start, last use among keys
with permits left) so eviction and expiry are O(1); a spent key is never evicted, and a table full of spent keys
refuses a new key as TableFull with the true wait. Accounts get their own cast table. The vote page checks the
subject before spending any key, and the refusal names its reason: cast budget, mint budget, or a busy table.
Not verified by a build yet at this commit.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-11 19:16:30 +02:00
Meta Vens
8f57dc89f0 Pin the no-client-address bucket of the vote budgets
Kills the Stryker survivor on the null-client key: a request with no address shares one
bucket and does not throw. Builds with a8c3ecf; test added after the main verify job.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-11 17:38:56 +02:00
Meta Vens
a8c3ecf495 Vote budgets: monotonic windows, a key cap, 30 mints an hour and a readable 429
FixedWindowBudget times windows on the clock's monotonic timestamps so a wall-clock
step cannot stretch or reset one, expires windows from a start-ordered queue instead
of a periodic sweep, and holds at most MaxTrackedKeys keys: a new key at the cap is
denied with the true wait and a held key is never evicted. MintPermits is 30 per hour
per the operator ruling, and a 429 now carries a plain-text reason; a refused new
device is told it can sign in. Not yet built or tested at this commit.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-11 17:32:01 +02:00
Meta Vens
2c8384e44a Pin that a signed-in retract spends the account budget
Hand mutant retract-key-ignores-signed-in-voter survived the first table.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 09:46:45 +02:00
Meta Vens
9fcef927fd Pin the budget sweep and Retry-After rounding; drop a redundant Math.Max
Survivors of the first Stryker pass on FixedWindowBudget: the sweep dropping
live windows, sweeping every call, and the exact window boundary (masked by
the sweep in the first test).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 09:35:50 +02:00
Meta Vens
7f375ecf8f Pin the empty Origin branch with a server-built request
HttpClient drops an empty header value, so the first version of the test
never sent the header; the request is now built on the TestServer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 09:02:01 +02:00
Meta Vens
0f71a9f3ca Heimdall tests: fix two log-join string literals broken by a newline
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>
2026-10-11 09:01:06 +02:00
Meta Vens
fbf6e43955 Concept vote rate limits: cast and mint budgets per account or network prefix
One FixedWindowBudget (injectable clock) owned by ConceptVoteRateLimits;
the vote page 429s with Retry-After before any row or cookie. Prefix
lengths and budgets are validated in every environment. Pins the empty
Origin branch of CastRequestGuard.

Unverified; not yet built.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 09:00:23 +02:00