test: cover the managed-key lifecycle and the admin session in smoke (T1.2.10) - #50
Merged
Merged
Conversation
…(T1.2.10) The suite never called /api/keys, /api/auth/session, /api/auth/logout, and never completed a successful login. Two claims in SECURITY.md rode on that gap: that a scope is enforced server-side, and that a managed key, even full, cannot manage keys. A destructuring typo on issueSession or sessionPrincipal left the suite green. Fourteen assertions in two blocks. Managed keys, on the bootstrap bearer, because POST /api/keys is admin-only: the create response carries the token once with a prefix and no hash, a read key lists but cannot publish (403), a full managed key cannot list keys (401), a key minted with a past expiresAt never authenticates, disable then re-enable swings both ways, and a revoked key answers 401. The admin session: login sets artifacts_session HttpOnly SameSite=Strict Path=/, the cookie alone reaches /api/artifacts and /api/keys with no Authorization header, /api/auth/session reports it, an unknown username and a wrong password answer identically, and logout drops it. The session block sits before the login burst, which spends the per-IP failure budget for 15 minutes. It skips with a printed line when ARTIFACTS_ADMIN_USERNAME and ARTIFACTS_ADMIN_PASSWORD are not in the environment. Five of the six CI jobs already export them; docker-build now does too, so the one job that boots the shipped image covers the session as well.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The item
T1.2.10, filed 2026-08-07 from the T1.2.2 review.
smoke.shnever called/api/keys,/api/auth/sessionor/api/auth/logout, and never completed a successful login: the only logintraffic was the 40-request failure burst. Two claims in SECURITY.md rode on that gap. Line 11 says a
key acting above its scope is a vulnerability, line 14 says a managed key, even
full, must not beable to manage keys. Neither was tested. A destructuring typo on
issueSessionorsessionPrincipalleaves
undefined, does not crash at boot, and leaves the suite green.What runs now
Fourteen assertions in two blocks, both placed before the login burst, which spends the per-IP
failure budget (10 per 15 minutes) for the rest of the run.
Managed keys, on the bootstrap bearer, because
POST /api/keysis admin-only:keyand aah_prefix and nohashreadkey lists (200) and cannot publish (403)fullmanaged key cannot list keys (401)expiresAt: 2020-01-01never authenticates (401)The admin session:
artifacts_session,HttpOnly,SameSite=Strict,Path=/, read off theSet-Cookieline itself rather than the whole header block
Authorizationheader, reaches/api/artifactsand/api/keys/api/auth/sessionreportsauthenticated:truefor itdecoy-hash timing guard
Skipping, and the one job that used to skip
The session block needs credentials, so it runs when
ARTIFACTS_ADMIN_USERNAMEandARTIFACTS_ADMIN_PASSWORDare in the environment and prints askip:line naming them when they arenot. Five of the six CI jobs already set them at job level.
docker-buildpassed them to thecontainer with
-ebut not to the job shell, so the block would have skipped in the one job thatboots the shipped image. Two lines of
ci.ymlfix that, with the same values the container gets.Tests
npm test: 64 passing, unchanged by this branch.bash .github/workflows/smoke.sh http://localhost:3000 testwith the two admin variables exported:all passing, 176 assertions, 14 of them new.
if (key.disabled) continue;in
lib/auth.js:436and restarting gaveFAIL: disabled key refused: expected 401, got 200. Themutation was reverted before the commit, and the final run above is against the reverted tree.
Review
95 changed lines, which the rule puts in the two-subagent band (adversarial and QA). This session runs
under an instruction not to spawn agents unless asked, so both lenses were run by hand instead and the
call is logged in AGENT-DECISIONS.md. Two findings came out of it and both are already fixed in this
diff:
carrying
path=/could have answered forSet-Cookie. They read theSet-Cookieline now.have passed while proving nothing. It now also requires the body to say
invalid credentials.Other things checked and found fine: the
sedextractors have exactly one match each in the createresponse (
"key":"and"id":"appear once;ci-read-keydoes not contain"key":"), an emptyextraction fails loudly rather than running with an empty bearer, all three keys are deleted at the
end of the block so the later MCP scope section mints into a clean list, and the two failed logins
leave 8 of the 10-per-window budget, which the burst below still exhausts.
QA. Every line of the item's rebuild list is covered except one, deliberately:
/api/auth/passwordis not driven. Changing the seeded admin's password mid-suite would leave a real instance with a
password nobody recorded, and would break the next run against the same instance. The two-replica CI
step already exercises that route against a throwaway pair.
Findings outside the item
None fixed, one recorded: a key create with a duplicate
nameis accepted, so a run that dies betweenminting and deleting leaves
ci-read-keyrecords behind inauth.json. It costs nothing but clutteron a dev instance, and CI starts from an empty one. Naming keys per run would trade that clutter for
noise in the diff.
Merges
git merge-treeagainst currentmainand the one open branch (#43): clean.