Skip to content

fix(redact): strip complete CSI sequences before prefix-mask scan - #81083

Closed
Enough1122 wants to merge 2 commits into
NousResearch:mainfrom
Enough1122:fix/81012-redact-csi-prefix-masking
Closed

fix(redact): strip complete CSI sequences before prefix-mask scan#81083
Enough1122 wants to merge 2 commits into
NousResearch:mainfrom
Enough1122:fix/81012-redact-csi-prefix-masking

Conversation

@Enough1122

Copy link
Copy Markdown
Contributor

Fixes #81012

_mask_control_split_tokens stripped only the bare ESC byte, leaving the CSI terminator letter (m / K / ...) glued to the token head in the shadow copy. The prefix-regex lookbehind (?<![A-Za-z0-9_-]) then failed and the whole token leaked verbatim through logs (\x1b[32msk-...\x1b[0m).

Fix: eat COMPLETE CSI sequences (ESC + [ + params + terminator) before the prefix scan, and track the eaten positions so the body-safe check accepts them when they fall inside an original mask span.

Also closes the second gap from the same report: a split that crosses \n AND has a self-matching prefix on one side used to skip the cross-line mask (legacy line-boundary guard). When that happens, fall back to per-line masking so the prefix-bearing line is still masked instead of the whole token leaking.

Regression tests cover the real leak shape (\x1b[32mtok\x1b[0m, no space), the same shape with a space, and the ESC + newline split case.

…usResearch#81012)

Bare-ESC stripping left CSI terminator bytes (`m` / `K` / ...)
glued to the token head in the stripped copy, which defeated the
`(?<![A-Za-z0-9_-])` prefix-regex lookbehind and let whole tokens
leak through (`\x1b[32msk-...\x1b[0m`).

Fix: in `_mask_control_split_tokens`, eat COMPLETE CSI sequences
(ESC + `[` + params + terminator letter) instead of just the bare
ESC byte. Track eaten positions so the body-safe check accepts the
CSI bytes when they fall inside an original mask span.

Also handles the second gap from the same report: a split that
crosses \n AND has a self-matching fragment on one side used to
skip the cross-line mask entirely (the legacy line-boundary
guard). When that happens, fall back to per-line masking so the
prefix-bearing line is still masked instead of the whole token
leaking.

Regression tests cover the real leak shape (`\x1b[32mtok\x1b[0m`,
no space between the CSI terminator and the token), the same
shape with a space, and the ESC + newline split case.

Fixes NousResearch#81012
@Enough1122
Enough1122 force-pushed the fix/81012-redact-csi-prefix-masking branch from c448f81 to 8f07f93 Compare August 7, 2026 14:09
The locally re-declared control-char regex had its \uXXXX escapes
mangled into literals, forming an unintended \x7f-\u200f range that
matched Latin-1 high bytes (é, £, ...) as "control" chars. Reuse the
module-level _CONTROL_CHARS_RE (already correct) for the shadow-copy
strip and body-safe check so the two cannot drift.
@alt-glitch alt-glitch added type/security Security vulnerability or hardening P3 Low — cosmetic, nice to have comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint labels Aug 7, 2026
@Enough1122

Copy link
Copy Markdown
Contributor Author

Duplicate of #81060 (same issue). Closing in favor of the earlier PR to avoid double-fixing the same issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P3 Low — cosmetic, nice to have type/security Security vulnerability or hardening

Projects

None yet

Development

Successfully merging this pull request may close these issues.

redact: complete CSI/SGR sequences defeat prefix masking (ESC-byte-only stripping leaves 'm'-glue)

2 participants