Skip to content

docs(attachment): correct the defuse loop's termination argument - #4055

Open
dwin-gharibi wants to merge 1 commit into
docker:mainfrom
dwin-gharibi:fix/attachment-defuse-invariant
Open

docs(attachment): correct the defuse loop's termination argument#4055
dwin-gharibi wants to merge 1 commit into
docker:mainfrom
dwin-gharibi:fix/attachment-defuse-invariant

Conversation

@dwin-gharibi

Copy link
Copy Markdown
Contributor

maxDefusePasses justified its bound with "each pass strictly shortens the body". That is false —
the placeholder is longer than the delimiters it replaces, so a pass usually grows the body.
The loop is fine; the stated reason it works is not.

Closes #4054.

Behaviour is unchanged

No production logic is touched. This corrects reasoning and adds a test that pins it.

What was wrong

One. maxDefusePasses:

Each pass strictly shortens the body (a match is always longer than nothing and is replaced by
a constant)

delimiterPlaceholder is 41 bytes; </document-x> is 13. Measured:

pass 1: len 13 -> 42   (GREW)
pass 1: len 26 -> 43   (GREW)

Two. defuseDelimiters:

</TAG</TAG>> collapses to […removed]> only after the second pass

envelopeTagRe runs to the first >, so that whole prefix is one match and collapses in one
pass. Verified: the residue shape is already stable after pass 1.

The real argument

delimiterPlaceholder contains neither < nor >, so it can never form part of a new match.
Every pass therefore strictly reduces the count of delimiter-shaped tokens in the body, and that
count is a non-negative integer — so the loop reaches a fixed point. The bound is belt-and-braces,
not the guarantee.

Why this matters more than a typo

defuseDelimiters returns whatever it has when the bound is hit — silently, no error. The
placeholder's character set is therefore load-bearing, and nothing said so. A cosmetic change to
something like <removed> would break the invariant, let the loop exhaust its bound, and return
a live delimiter inside the envelope — the exact break-out the escaping exists to prevent.

Tests

A comment can drift again; a test cannot. defuse_convergence_test.go adds:

  • TestDelimiterPlaceholderCannotFormAMatch — fails if the placeholder ever gains angle brackets
    or starts matching envelopeTagRe.
  • TestDefuseDelimitersConverges — 15 adversarial bodies (deep nesting, split brackets, mixed
    case, trailing attributes, self-closing, whitespace padding, prefix-extending tags, a body
    pre-seeded with the placeholder). Each must reach a fixed point inside the bound and leave
    no live delimiter. Convergence is asserted separately from survivor-freedom because hitting the
    bound is silent, so a regression there would otherwise only surface much later as a break-out.

The tripwire was mutation-tested rather than assumed: setting
delimiterPlaceholder = "<docker-agent: envelope delimiter removed>" makes it fail with

delimiterPlaceholder must contain no angle brackets, or the convergence argument
for maxDefusePasses no longer holds

pkg/attachment passes and go vet is clean.

Confirmed not a security fix

Eighteen adversarial inputs were probed before filing; all were fully defused with zero survivors.
There is no break-out to fix here, and this PR should not be read as one.

maxDefusePasses claimed "each pass strictly shortens the body (a match is
always longer than nothing and is replaced by a constant)". That is false:
delimiterPlaceholder is 41 bytes and `</document-x>` is 13, so a pass usually
grows the body. Measured, the first pass on `</document-x>` takes it from 13
to 42 bytes.

Termination actually comes from the placeholder containing neither < nor >, so
it can never form part of a new match and every pass strictly reduces the count
of delimiter-shaped tokens. The bound is belt-and-braces, not the guarantee.

The defuseDelimiters comment was wrong in the other direction: it said
`</TAG</TAG>>` collapses "only after the second pass". The pattern runs to the
first >, so that prefix is a single match and collapses in one pass.

Behavior is unchanged; only the reasoning was wrong, and it is the reasoning
someone would rely on when tuning the constant. Adds a test pinning the real
invariant, since a comment can drift again and a test cannot: it fails if the
placeholder ever gains angle brackets, and checks adversarial bodies converge
inside the bound with no live delimiter left.
Copilot AI lite review requested due to automatic review settings August 24, 2026 19:08
@dwin-gharibi
dwin-gharibi requested a review from a team as a code owner August 24, 2026 19:08

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@aheritier aheritier added area/core Core agent runtime, session management kind/docs Documentation-only changes labels Aug 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/core Core agent runtime, session management kind/docs Documentation-only changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

maxDefusePasses documents a termination argument that is false

3 participants