backlog: close #1106 (already shipped) and file #1223 -- http_auth_user leaks verbatim - #332
Merged
Merged
Conversation
…oving it TWO CORRECTIONS, AND THE FIRST IS MINE. #1106's banner read 'not started'. It shipped 2026-08-09 as e756608 (PR #300): _is_secret_setting names sign_private_key and sign_private_key_password explicitly at config/wiring.py:830, and I confirmed both return *** from both serializers on main. The lane also verified it is PINNED rather than incidentally green -- reverting that arm turns four tests red. I READ 'not started' AND BELIEVED IT, then dispatched a lane to build something already on main for two days. That is the same error class as the #1219 duplicate, one level up, and I committed it AFTER writing the memory entry about exactly this -- a status field believed instead of measured. The banner is a machine-written field; whether the work exists is not something it tracks. The lane recovered the cost by finding a real defect while proving the item was already done, but that was luck rather than design, and the banner now says so. #1223 -- http_auth_user LEAKS VERBATIM. Measured by running the shipped redactor on main, not inferred: basic_user -> *** proxy_user -> *** username -> *** http_auth_password -> *** http_auth_user -> SYNTHETIC-ACCOUNT <- verbatim, both serializers It is the SIXTH member of a FIVE-member class: wiring.py:847 carries {username, basic_user, proxy_user, ws_username, credential_username}, masked defence-in-depth because a username names a principal. http_auth_ user is written at transports/http_auth.py:477 across the SAME parameter-to-setting boundary #1106 was entirely about. THE STRUCTURAL HALF IS WHY BOTH GUARDS WERE GREEN: CREDENTIAL_ISH, the tuple deciding where a sentinel is INJECTED, lacked 'user', so _is_credential_param('user') is False and no guard had ever injected into a username parameter at all -- credential_username matched only by accident via 'credential'. The mapping test inherits the same blindness by importing the same predicate, so two independent-looking checks share one blind spot. Proved by mutation: widening the tuple by one word reds exactly with_http_digest -> http_auth_user and nothing else. AND THE INVERSION WAS EXAMINED AND REJECTED ON EVIDENCE rather than adopted because it sounded structural. Over the real 189-key set it over-redacts 14 (paths, URLs, a DB column, keyword-name indirections) and under-redacts 4 it currently masks -- and decisively, IT WOULD NOT HAVE CAUGHT THIS DEFECT, since http_auth_user contains none of its six substrings. Allowlist and shape rule fail on the same key, because the defect was never that the list is a list; it was that nobody derived the domain. Gates: 488 items each declaring exactly one status; ledger_check clean; doc guards 48 passed. Live 252, open 184.
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.
Closes BACKLOG #1106 (already shipped -- see below) and files BACKLOG #1223.
#1223 --
http_auth_userleaks verbatim, and the guard's vocabulary cannot see itConfirmed by running the shipped redactor on
main, not by reading it:config/wiring.py:847carries{username, basic_user, proxy_user, ws_username, credential_username}--masked defence-in-depth, on the stated ground that a username names a principal and can leak
directory structure.
http_auth_useris written attransports/http_auth.py:477, across the sameparameter-to-setting boundary
#1106was entirely about:with_signingrenamesprivate_keytosign_private_key;with_http_digestrenamesusertohttp_auth_user. Same seam, same omission,different key. It is the sixth member of a five-member class.
Stated in the conditional (this is a beta with zero deployments): on a first deployment this would
emit an HTTP auth username verbatim from both serializers. It is a defence-in-depth leak of a principal
name, not of a password --
http_auth_passwordis correctly masked.The structural half: why two independent-looking guards were both green
The tuple deciding where a sentinel gets injected was:
So
_is_credential_param("user")isFalse,_is_credential_param("username")isFalse, and no guardhad ever injected into a username parameter at all.
credential_usernamematched only by accident,through
credential.And
test_credential_parameter_mapping.pyinherits the same blindness by importing the same predicate-- two independent-looking checks that are blind in exactly the same place by construction, because
they share the real predicate rather than a copy.
Proved by mutation, not by reading: widening the tuple by the single word
userreds exactlywith_http_digest -> http_auth_userand nothing else.The inversion was examined and rejected on evidence
The lane was dispatched believing deny-by-default was probably the structural answer. Measured over the
real 189-key set it is worse in both directions: it over-redacts 14 (four file paths, two URLs, a DB
column name, the
odbc_*_keykeyword indirections) and under-redacts 4 that are currently masked. Anddecisively -- it would not have caught this defect:
http_auth_usercontains none of its sixsubstrings.
So the allowlist and the shape rule fail on the same key. The defect was never "the list is a list";
it is that nobody derived the domain. The fix that actually stops the next omission is a test deriving
the injection vocabulary from
_SECRET_SETTING_KEYSitself -- red today without the widening, namingall six usernames, and unable to narrow silently the way a hand-maintained tuple can.
#1106 was already fixed, and the banner says so in those words
e7566082(PR #300) landed 2026-08-09;_is_secret_settingnamessign_private_keyandsign_private_key_passwordexplicitly atconfig/wiring.py:830. Verified onmain: both redact fromboth serializers, and it is pinned rather than incidentally green -- reverting that arm turns four
tests red.
The banner read "not started" and a lane was dispatched to build it. A machine-written status field
tracks status, not whether the work exists. The corrected banner records that plainly, and records that
the lane recovering the cost by finding
#1223was luck, not design.Ledger-only. Verified:
#1223free in the live ledger and the archive;#1106flips to a single closedbanner;
parse_itemsreads 252 items, 184 open; merges clean onto currentmain.