Development record
Identity redaction rewrites by string rather than by byte span, so a line carrying both a real leak and a lookalike has the lookalike rewritten too. The detector was taught to leave a dotted namespace component alone, which fixed the corruption of reverse-DNS identifiers. The rewrite did not learn it. Masking for identity kinds replaces every occurrence of the matched string rather than the span the detector found, which is recorded as deliberate because an identity placeholder changes length. So where one line contains a genuine bare mention of the account name and also an identifier that merely begins with it, the first is masked correctly and the second is mangled anyway: a line reading that the crash is in a particular reverse-DNS bundle comes out with the bundle's first component replaced, even though detection refused it. The same mechanism turns an ordinary word containing the account name into a masked fragment mid-word when a real mention appears on the same line, though detection alone leaves that word intact. A whole-string replace cannot express one occurrence masked and another left, so the fix is span-based replacement for identity kinds. That is safe on offsets, because the sealing pass is length-preserving, but it reverses a recorded design choice in the single shared write-time sanitiser that history, memory, capture, ideate, intent, decide and launch all write through, and it reverses it in the fail-open direction. That is a human design decision rather than a patch, which is why it is recorded rather than done. The git-identity and real-name kinds share the mechanism and would corrupt the same way where a name collides with a namespace label.
Identity redaction rewrites by string rather than by byte span, so a line carrying both a real leak and a lookalike has the lookalike rewritten too. The detector was taught to leave a dotted namespace component alone, which fixed the corruption of reverse-DNS identifiers. The rewrite did not learn it. Masking for identity kinds replaces every occurrence of the matched string rather than the span the detector found, which is recorded as deliberate because an identity placeholder changes length. So where one line contains a genuine bare mention of the account name and also an identifier that merely begins with it, the first is masked correctly and the second is mangled anyway: a line reading that the crash is in a particular reverse-DNS bundle comes out with the bundle's first component replaced, even though detection refused it. The same mechanism turns an ordinary word containing the account name into a masked fragment mid-word when a real mention appears on the same line, though detection alone leaves that word intact. A whole-string replace cannot express one occurrence masked and another left, so the fix is span-based replacement for identity kinds. That is safe on offsets, because the sealing pass is length-preserving, but it reverses a recorded design choice in the single shared write-time sanitiser that history, memory, capture, ideate, intent, decide and launch all write through, and it reverses it in the fail-open direction. That is a human design decision rather than a patch, which is why it is recorded rather than done. The git-identity and real-name kinds share the mechanism and would corrupt the same way where a name collides with a namespace label.
Grounds
- pursued: the detector's refusals (a dotted-namespace component, a mid-word collision, an occurrence inside a URL) are only real if the rewrite honours them, so masking by recorded span instead of by whole string stops the corruption; what would show it wrong is a genuine mention the detector does not flag that the whole-string rewrite used to catch — that residue is named in redact.go and pinned by TestSpanMaskingFailsOpenOnClearedLookalikes
Frontmatter
| id | iss-2609120446083912 |
|---|---|
| type | issue |
| lifecycle | resolved |
| severity | major |
.abcd/work/issues/resolved/iss-2609120446083912-identity-redaction-rewrites-by-string.md
open on GitHub ↗ · commit history ↗
mentions1
- dcn-26091500000000012026-09-15 — Identity redaction masks exactly the byte spans the detector flagged, and nothing else on the line (product thinker, ruling on iss-2609120446083912 against the whole-string rewrite that `redact.go` had recorded as deliberate). The detector's refusals — a component of a reverse-DNS identifier, a collision inside a longer word, an occurrence inside a URL span — are only real if the rewrite honours them; the whole-string rewrite overrode every one of them whenever a genuine mention shared the line, corrupting the technical content a record exists to hold. Spans are validated against the line before the secret seal and applied after it (the seal preserves byte length), overlapping spans merge into one cluster masked with the widest member's placeholder, and the line is rebuilt from original bytes so no offset is ever applied against a shifted line. The cost is accepted and named: an occurrence the detector deliberately clears — a login inside a URL, including a credentials URL's userinfo — is no longer masked by accident, and the stage-two re-scan does not flag it either; the caller's own home path is not in that residue because the detector flags every occurrence by the same anchor the literal sweep uses. Rejected: keep masking every copy but exempt the shapes the detector exempts (a second copy of the detector's judgement, which drifts); leave it as it is.