Skip to content

fix(#24): redact marker must have no shell metacharacters#53

Merged
dwgx merged 1 commit intodwgx:masterfrom
aict666:fix/redaction-marker-shell-safe
Apr 24, 2026
Merged

fix(#24): redact marker must have no shell metacharacters#53
dwgx merged 1 commit intodwgx:masterfrom
aict666:fix/redaction-marker-shell-safe

Conversation

@aict666
Copy link
Copy Markdown
Contributor

@aict666 aict666 commented Apr 24, 2026

Problem

Claude Code v2.1.114 (Opus 4.7) routed through the proxy still derails on tool use — not because of the old preamble (PR #51 fixed that), but because the model echoes the redaction marker (internal path redacted) into a bash tool call:

⏺ Bash(cd (internal path redacted) && git branch -a && git log --all --oneline -30)
  ⏎ Error: Exit code 1
     (eval):1: unknown file attribute: i

zsh parses cd word(qualifier) as glob-qualifier syntax, reads the i of internal as a qualifier flag, and dies with the cryptic unknown file attribute: i. The model can't recover from that error shape and starts replying with confused meta-text ("the user seems to have pasted a system prompt along with a shell error...") instead of retrying the tool.

Root cause

Commit 9120b8b picked (internal path redacted) with the rationale "a multi-word parenthesised phrase cannot be tokenised as a path or identifier." True for file APIs — but parens are first-class shell metacharacters (subshell in POSIX sh/bash, glob qualifier in zsh). The marker has to satisfy file-API safety AND shell safety AND anti-loop shape simultaneously; the old marker only cleared the first two.

Fix

Drop every shell metacharacter from the marker. Use a plain ASCII multi-word phrase:

const REDACTED_PATH = 'redacted internal path';
  • No character in ()[]{}<>|&;$`"'\*? → no shell parser treats it specially.
  • No /, no . → does not look like a path, so the sonnet/opus drift-probe regressions (./tail, [internal], <redacted-path>) do not come back.
  • Contains whitespace, multi-word → cannot be tokenised as a single identifier / file_path argument.
  • If a model still inlines it in shell, the words split into separate argv entries and cd / Read fails with a clean recoverable error (cd: too many arguments, single ENOENT) — never the cryptic glob-qualifier error.

No other changes: the patterns, the streaming cut-point logic, and the sanitizeToolCall shape are untouched.

Regression guard

Adds a new suite REDACTED_PATH marker shape (shell-safety regression) with two asserts:

  1. No shell metacharacters. The marker must not match /[()\[\]{}<>|&;$`\\"'*?]/.
  2. Not a path or identifier. No /, no \\, contains whitespace, multi-word.

Both guards run against the actual output of sanitizeText('/tmp/windsurf-workspace'), so any future marker change must pass them or break the build.

Tests

npm test61/61 pass (59 existing + 2 new marker-shape guards).

Risk

Low. Only the display string changes. All 19 call sites pass the string through opaquely; none parse or pattern-match on the marker value. Clients that previously rendered (internal path redacted) in transcripts will now render redacted internal path — the information conveyed is identical and the wording reads as prose in both English and CJK contexts.

Reproducer: Claude Code v2.1.114 (Opus 4.7) routed through the proxy
emits a bash tool call whose command includes the redaction marker
verbatim, e.g.:

    cd (internal path redacted) && git log --all --oneline -30

zsh parses `cd word(qualifier)` as glob-qualifier syntax, reads the
first char of "internal" as a qualifier flag, and fails with:

    (eval):1: unknown file attribute: i

The model sees the cryptic error, can't recover, and starts replying
with confused meta-text ("the user seems to have pasted a system
prompt along with a shell error about an invalid file test operator")
instead of retrying.

Root cause: commit 9120b8b picked `(internal path redacted)` because
"a multi-word parenthesised phrase cannot be tokenised as a path or
identifier." True for file APIs — but parens are first-class shell
metacharacters (subshell, glob qualifier). The marker had to satisfy
file-API safety AND shell safety AND anti-loop shape at once; the
old marker only satisfied the first two.

Fix: drop every shell metacharacter from the marker. Use a plain
ASCII multi-word phrase: "redacted internal path". If a model still
echoes it into shell the words become separate argv entries and `cd`
or `Read` fails with a clean, recoverable error (too many arguments
/ single ENOENT) — never the cryptic glob-qualifier error. The
marker still cannot be parsed as a path (no `/`, no `.`) or as an
identifier (contains whitespace), so the drift-probe regressions
(./tail, [internal], <redacted-path>) remain fixed.

Adds a REDACTED_PATH marker-shape regression suite asserting:
1. No character in `()[]{}<>|&;$\`"'\\*?` appears in the marker.
2. The marker has no `/` or `\\`, contains whitespace, and is
   multi-word — so it cannot reappear as a path or identifier.

Tests: 61/61 pass (59 existing + 2 new marker-shape guards).
@dwgx dwgx merged commit be39c35 into dwgx:master Apr 24, 2026
2 checks passed
dwgx added a commit that referenced this pull request Apr 25, 2026
…ly-zhang to S+

- baily-zhang PR #61 (Opus 4.7 multimodal context bloat) — third major
  contribution after #36 and #45, now de-facto maintainer of the
  reuse-fingerprint / trajectory-offset machinery
- abwuge PR #58 (docker/nginx deploy fix) — first-time contributor,
  +3/-2 surgical, unblocked the docker-compose Restart loop
- aict666 PR #54 (tool preamble slimming + redact marker 6th-gen U+2026
  ellipsis + identity coverage extension) — fourth major contribution
- aict666 PR #53 (redact marker shell-safety regression) — second
  contribution, was missing from the prior credits update
- baily-zhang upgraded from S to S+ (parity with aict666)
dwgx added a commit that referenced this pull request Apr 25, 2026
The Pages site at dwgx.github.io/WindsurfAPI/ had only 4 names listed
in the footer (dd373156, colin1112a, motto1, youfak). 8 contributors
were missing from the public site even though most of them landed
S+/S level fixes (aict666 #44/#51/#53/#54, baily-zhang #36/#45/#61,
smeinecke #43, abwuge #58).

Adds a dedicated `#contributors` section before the footer with one
card per contributor: avatar, GitHub link, weight badge (S+/S/A+/A/B+),
PR list, and a one-paragraph 繁體中文 description of what each fix
actually solved. Cards reuse the existing panel-card warm/coral
palette to fit the site's aesthetic.

Footer one-liner is also expanded to all 8 names ordered by weight,
with a "完整名單 ↑" anchor back to the new section.

CSS additions: contrib-grid, contrib-card, contrib-avatar,
contrib-weight + 5 weight-tier classes (-S-plus, -S, -A-plus, -A,
-B-plus). All gradient/hover behaviour matches the existing
panel-card styling.
@aict666 aict666 deleted the fix/redaction-marker-shell-safe branch April 26, 2026 00:10
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.

2 participants