feat(quality): the username-as-access-key screen now runs, and can fail on a new site (BACKLOG #1226) - #561
Open
wshallwshall wants to merge 17 commits into
Open
feat(quality): the username-as-access-key screen now runs, and can fail on a new site (BACKLOG #1226)#561wshallwshall wants to merge 17 commits into
wshallwshall wants to merge 17 commits into
Conversation
…il on a new site (BACKLOG #1226)
The screen shipped with 281 lines and five test arms and NOTHING INVOKED IT. Measured with a
control before touching anything:
git grep -l username_access_key origin/main -> BACKLOG.md, the screen, 2 test files
git grep -l control_char_check origin/main -> ci.yml AND .pre-commit-config.yaml
A wired screen appears in those two files. This one appeared in neither, so it read as coverage
and produced none -- which is the same false-green family as the four checks.py sites.
WIRING IT AS-IS WOULD HAVE INSTALLED A STEP THAT CANNOT FAIL. main() returns 0 whatever it
finds, deliberately: several candidates are correct code, and the item forbids turning the
screen into a verdict-emitter. So the design the item's own re-score asks for is a shape that
fails on a NEW key without judging any of them.
--baseline is that shape. It is silent on judged sites and red on a key nobody has read yet.
`Candidate.key` was already written stable -- `callee::slot`, no line number, documented as
"stable across rebases" -- and had ZERO consumers. This completes what its author started
rather than inventing a mechanism.
THE BASELINE RECORDS THAT A HUMAN LOOKED, NEVER THAT A SITE IS CORRECT. One of its eight
entries is a confirmed defect, which is exactly why it must not be read as an approval list.
JUDGED THE CANDIDATE THE ROW NAMES, and it is real. `GET /me/security-events` reaches
`store.security_events_for_user`, whose SQL is:
SELECT ts, action, detail FROM audit_log WHERE actor = ? AND action LIKE 'auth.%'
The username is not labelling a row there -- it IS the WHERE clause deciding which rows the
caller sees, which is this item's definition verbatim. The tell the row predicted holds: the
sibling route two lines up scopes on user_id.
NOT FIXED HERE, and the reason is measured rather than assumed: audit_log has NO immutable
actor id. Its columns are id/ts/actor/action/channel_id/detail/client/row_hash, and a grep for
user_id against the audit store returns ZERO against a positive control of 3 for actor. The fix
is a schema change to a table carrying a row_hash tamper-evidence chain. Named as a subject,
not a number, because none is allocated.
CONDITIONAL SEVERITY (section 0 -- zero deployments): on a first deployment, an operator who
deleted a user and recreated the same username would hand the new holder the previous holder's
sign-ins, lockouts and password changes.
10 passed. The wiring-parity arm uses control_char_check as its positive control, so a broken
reading fails the control first.
The CONFIRMED INSTANCE block said the sibling route scopes on `identity.user_id` "two lines above". Two things were wrong with that sentence and neither changes the finding. IT IS THIRTEEN LINES, NOT TWO, AND IT IS THE PRECEDING ROUTE HANDLER rather than an adjacent line. The author inherited "two lines" from a dispatch brief and wrote it as confirmed. In a security claim, in a public repository. AND THE CONTRAST IS NOT EVIDENCE OF AN OVERSIGHT, which is how "the tell the item predicted holds" reads. Measured on main: 9 `FROM audit_log` sites in store.py, ZERO keyed on `user_id`, against a positive control of 1 keyed on `actor = ?`. The sessions table has a user id; audit_log does not. The author of the second site had no immutable key available, so the two routes differ because the schemas differ. That is a better account than the original and it makes the finding cleaner. The defect stands on the SQL and the schema alone; the contrast was corroboration and it was overstated. Two deviations from the replacement text as supplied, both to fit the file's own rules rather than to change its meaning: NO LINE NUMBER. The file's header says the key carries no line numbers BY CONSTRUCTION, because the same site sits at different lines depending on your base. Prose citing auth_routes.py:464 would drift for exactly that reason, so the route is named instead. THE POSITIVE CONTROL IS LABELLED WITH WHAT IT COUNTS. The block already carries a positive control of 3 for `actor` over a different sweep, so an unqualified second "positive control 1 for actor" would have read as a contradiction. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
wshallwshall
enabled auto-merge (squash)
August 23, 2026 20:42
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 username-as-access-key screen existed on main and ran nowhere. This wires it, and gives it a way to fail.
It was unwired, measured with a control
A wired screen appears in those two files. This one appeared in neither. The control is what makes the zero mean "unwired" rather than "my search was broken".
Wiring it as-is would have installed a step that cannot fail
main()returns 0 whatever it finds, deliberately — several candidates are correct code, and the item forbids the screen from issuing verdicts.--baselineis the shape the item's own re-score asks for: silent on judged sites, red on a key nobody has read.Candidate.keywas already written stable (callee::slot, no line number, documented "stable across rebases") with zero consumers, so this completes an existing design rather than inventing one.The finding, and it is the candidate the row predicted
GET /me/security-eventsreachesstore.security_events_for_user, whose SQL is, verbatim on main at store.py:7493:with
(username, limit). The username is not labelling a row — it is the WHERE clause deciding which rows the caller sees. That is the item's definition of the defect.The schema agrees.
audit_log.actoris declaredTEXTand its own column comment reads "who: a username or 'system'". The table's columns areid, ts, actor, action, channel_id, detail, client, row_hash— there is no immutable user id to key on.Conditional severity, per CLAUDE.md section 0 — there are zero deployments and nothing is exposed today. On a first deployment, an operator who deleted a user and recreated the same username would hand the new holder the previous holder's sign-ins, lockouts and password changes.
One supporting claim was overstated, and correcting it improved the finding
The baseline originally said the sibling route scopes on
user_id"two lines above", offered as the tell that this site is anomalous. Two things were wrong with that, and a second commit (5be1e508) fixes them.It is thirteen lines, and it is the preceding route handler — not an adjacent line. The author inherited "two lines" from a dispatch brief and wrote it as confirmed.
And the contrast is not evidence of an oversight, which is how "the tell the item predicted holds" reads. I first looked in
store.pyand could not reproduce anyuser_idscoping at all; the author then pointed atauth_routes.py, where the contrast is real:We had measured different files and both readings were correct — the same shape as the two rescue-ref namespaces earlier today.
The measurement explains why the two routes differ: 9
FROM audit_logsites instore.py, zero keyed onuser_id, against a positive control of 1 keyed onactor = ?. The sessions table has a user id andaudit_logdoes not, so the author of the second site had no immutable key available. The schemas differ, not the care taken.That is a better account than the original. The defect stands on the SQL and the schema alone; the contrast was corroboration and it was overstated.
Two deviations from the author's supplied replacement text, both to fit the file's own rules: no line number is cited, because the file's header says keys carry none by construction and prose would drift for the same reason; and the new positive control is labelled with what it counts, since the block already carries a different positive control of 3 for
actorover a broader sweep.Not fixed here, and that is the right call
audit_logcarries arow_hashtamper-evidence chain over(prev_hash + this row). Adding an immutable actor id is a schema change to a hash-chained table — different work with different risks, and it is named as a subject rather than a number, since none is allocated.Verification
19 passed, screen suite plus the tooling partition.
ruffandmypyclean. Both YAML files parse. The control-character regex was verified byte-identical after the edit. The new hook ran on its own installing commit and passed.The parity arm uses
control_char_checkas its positive control, so a broken reading of the two wiring files fails the control first rather than passing quietly.backlog-hygienedoes not fire:touches_codeglobs onlymessagefoundry/,ide/andmessagefoundry_webconsole/, and this touchesscripts/quality/,.github/,.pre-commit-config.yamlandtests/.Co-Authored-By: Claude Opus 5 noreply@anthropic.com