Skip to content

backlog: file #1247-#1250, the #1241 amendment, and two banner fixes - #378

Merged
wshallwshall merged 8 commits into
mainfrom
claude/asvs-1247-gate-install-attribution
Aug 13, 2026
Merged

backlog: file #1247-#1250, the #1241 amendment, and two banner fixes#378
wshallwshall merged 8 commits into
mainfrom
claude/asvs-1247-gate-install-attribution

Conversation

@wshallwshall

Copy link
Copy Markdown
Collaborator

Seven commits, docs-only, all gates green. Files #1247, #1248, #1249, #1250, the #1241 amendment, and
two banner fixes.

#1247 records that a gate install leaves no record and its mtime actively lies -- the
machine-global worktree gate was replaced mid-session by a writer none of the three sessions that
examined it performed. install-gate.ps1 uses Copy-Item, which carries the source's timestamp,
so a fresh install produces new content with an old mtime. A session reasoning from the timestamp
retracted a true staleness finding on that basis before re-reversing it.

Pushed by the Lander on the filing session's report; each item's own verification is stated in its
banner.

… actively lies

The installed machine-global worktree gate changed content on this box while
three sessions ran against it, and after all three looked, nobody can say who
wrote it. The change was benign -- it moved the gate forward -- so this is a
governance defect rather than an incident.

install-gate.ps1:420 is a bare Copy-Item -Force. Four mechanisms could have
recorded the write and none is present: no backup of the replaced bytes, no
receipt, no log line, and Copy-Item carries the source file's mtime so the
installed copy inherits a timestamp from whichever checkout it came from.

The mtime is worse than missing because it is misleading. A correct stale-gate
report was retracted on the strength of that timestamp and the retraction reached
three sessions and the owner before a builder's pre-change baseline hash reproved
the original claim. An absent record makes people say unknown; a wrong record
makes them say something false with confidence.

Records the technique that eventually bounded the write, because it is the only
one that worked: a record of the past taken before the change. A re-measurement
cannot distinguish a bad instrument from a changed artifact, since it sees only
the present.

Bounds itself: it is NOT established that install-gate.ps1 performed this write,
only that had it been used it would have left nothing either.
…ide code that notifies

Split out of #1143's research pass so a defect that is real today does not wait
on a design question that is not settled.

Binding a local account to an external identity is the most takeover-relevant
fact that can be written to that account: afterwards, whoever controls the IdP
subject controls the account. _upsert_ad_user writes it with no audit row and no
security notification.

The adjacency is the argument. The binding writes sit at :1109 through :1120; the
nearest _audit is :1130 and _notify_security is :1139, and both belong to the role
resync that follows. So the lower-stakes event is instrumented and the
higher-stakes one is not, in one continuous block, which rules out the mechanism
being unavailable or unknown to the author.

Filed alone rather than folded into #1143 because it is independent of every open
question there: it holds regardless of how the binding is keyed, whether a TOFU
ceremony is adopted, and whether a uniqueness constraint is added. #1143 concluded
hold-at-partial with its ceremony question explicitly unsettled, and this should
not be hostage to it.

Bounds itself: the absence was established by locating every audit and notify call
in the region and comparing positions, not by reading the function narratively.
Whether some other surface records the binding independently is NOT established.
…n-depth

Free pre-work from a design pass landed a security fact that changes why this
item matters, though not how much.

Python's stdlib header guard does not block obs-fold injection. The pattern is
'\n(?![ \t])|\r(?![ \t\n])', so a bare CRLF is caught but CRLF followed by space
or tab passes, as do bare LF plus space, NUL and DEL. That is RFC 7230 obs-fold
working as specified: CRLF plus whitespace is a line continuation, so a value
carrying one ships as an additional CRLF-terminated line. Reproduced twice
independently by reading the pattern out of http.client, with a legitimate value
as a control to show the check discriminates.

So on the header sink this screen is the only control for four byte classes, not
defence-in-depth. The item was filed on the weaker argument, that treatment is
inconsistent within one file; the inconsistency is how the gap survived review
rather than the reason it matters.

Severity deliberately NOT upgraded: conditional_query is operator config, not
message-derived, so it stays below the message-derived sites.

Also corrects two of the item's own citations: the pinned assertion is at :221
not :220, and "at least screens" is stale since limb A now refuses a query
outright, which strengthens the item because reject-not-encode is now the file's
habit at two sites rather than one.
…ing can enable

Found by a builder premise-checking #1114, and it is a better finding than the
item it came from.

The MLLP message pacer is real and reads its settings, and it ships off. It also
cannot be turned on: MLLP() has 26 keyword-only parameters and no **kwargs, and
neither pacer key is among them, while max_connections is -- the positive control
showing the check discriminates. connections.toml routes every settings table
through that same factory, and the comment there says the factory is the schema.
So the dict the pacer reads can never contain the keys.

The defect is not that the control ships off. It is that SECURITY.md:1633 lists
both keys in the operator control table with state "off", which a reader takes as
an instruction to set it on. That is a compensating control resting on a false
premise, in the document where that rule matters most, and "off" is worse than
absent because absent prompts a question while off prompts a config edit that
cannot succeed.

Two opposite fixes -- expose the keys, or delete the row -- so the choice is an
owner call and the item does not pre-decide it.

Records that a pacing sweep over all 25 transports modules found 14 hits in
mllp.py and zero elsewhere, so ten inbound modules have no rate control at all.
And corrects #1114 in the direction that flatters the engine: four size and
concurrency bounds do ship on, so "no message-rate bound" is right and "unbounded
intake" is false.
…rsed as CLOSED

The open-count control caught it: filing one item moved items +1 but open +0.
A no-entry glyph used as prose emphasis inside the item's blockquote body is read
by parse_items as a status banner, so a just-filed live item parsed as CLOSED --
removed from the queue, which is the direction where nobody looks again.

Second time today I have made this exact mistake, both times writing emphasis
rather than a banner, and both times caught only by diffing the counts rather
than by reading the diff. docs/LEDGER-GATE.md documents the control; this commit
is the evidence that documenting it is not the same as not needing it.
…can hold a ledger

Owner-ruled that the backlog belongs in the vault, and ruled in the same breath
that moving it now is not safe. This item holds the decision until the
preconditions land.

The argument is aggregation, not any single entry. ASVS is a closed public
standard, so a map of our code against it discloses the gaps by subtraction. One
public item describing one defect is ordinary practice, but the aggregate already
exists and nobody assembled it deliberately: 103 open items carry ASVS in the
heading, in one file, retrievable with one search. Filing consistently produced a
ranked weakness map as a side effect.

The cost is asymmetric, which is the urgency. Over-restricting is recoverable;
over-publishing is not, because a public history cannot be unpublished and has
already shipped in sdists. At 103 items this is a review; at 200 it is a
migration.

It must not be done today because the destination is currently worse than the
source: the vault ledger gate is inert, so two sessions can take the same number
and merge clean in the security record. Moving a ledger whose purpose is
collision-free allocation into the one repo that cannot enforce it trades
disclosure for corruption.

Blast radius measured rather than estimated: 66 tracked files reference the file,
including the pre-commit allocation gate, parse_items, a CI workflow, the
allocator, and 13 test files that read it from disk.

Records that I am not a neutral party: I filed twelve ASVS items into the public
backlog tonight on the reading this item acts against.
…t today

Filing one item moved items +1 and open +0 again. Two no-entry glyphs used as
prose emphasis inside the item body parsed as CLOSED banners, so a just-filed
item recording an owner ruling was recorded as already done.

Third time today, all three mine, all three the same mechanism: I reach for that
glyph as emphasis because root CLAUDE.md uses it that way in prose, and in
docs/BACKLOG.md the identical character is a status banner. Knowing the rule has
not helped, documenting the control in docs/LEDGER-GATE.md has not helped, and
writing it to memory has not helped -- only the count diff catches it, every time.

Adopting the absolute form rather than the careful one: no closed-alphabet glyph
appears in a backlog item body at all, in any position. Say the word.
@wshallwshall
wshallwshall enabled auto-merge (squash) August 13, 2026 16:41
Resolves the docs/BACKLOG.md conflict. Ownership table recomputed over all 280
items from the three refs, not a hand-picked list:

  1239  base == ours, main differs -> only MAIN changed -> took main
  1241  base == main, ours differs -> only OURS changed -> took ours
  1247  added by both, bodies identical -> appears once
  1248 1249 1250  branch-only adds -> kept
  274 others plus the 825-line preamble: byte-identical on all three sides

Resolution is the branch file verbatim with 1239's body replaced by main's;
every other item was already correct in the branch's own copy.

No uniform rule is correct here, and each fails silently: prefer-ours reverts
main's 1239 amendment, prefer-theirs reverts the branch's 1241 amendment,
keep-both duplicates 1247. An item-count check cannot see the first two,
because a reverted body changes no heading -- so this was verified by per-item
body hash, each equality paired with a discrimination guard asserting the side
taken differs from base. Without that pair the 1241 check is a tautology, since
ours == base there.

Guards against this tree: 280 items, 0 duplicate numbers, 0 conflict markers,
1247 once, 1239 == main, 1241 == branch, preamble == main, 0 collateral drift,
0 deletions against main.
@wshallwshall
wshallwshall merged commit 6879776 into main Aug 13, 2026
33 of 34 checks passed
@wshallwshall
wshallwshall deleted the claude/asvs-1247-gate-install-attribution branch August 13, 2026 18:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant