Skip to content

fix(#6502): frame and sanitize validation feedback in agent prompt - #6510

Merged
waynesun09 merged 2 commits into
mainfrom
agent/6502-frame-sanitize-feedback
Aug 23, 2026
Merged

fix(#6502): frame and sanitize validation feedback in agent prompt#6510
waynesun09 merged 2 commits into
mainfrom
agent/6502-frame-sanitize-feedback

Conversation

@fullsend-ai-coder

Copy link
Copy Markdown
Contributor

Summary

Hardens buildFeedbackPrompt against prompt injection and unicode obfuscation by fencing validation output inside <validation-output> tags with a "treat as data" preamble, sanitizing dangerous unicode characters (tag chars, bidi overrides, zero-width chars, ANSI/OSC escapes) via the existing Go UnicodeNormalizer, and logging when sanitization alters feedback.

Changes

  • Added sanitizeFeedbackUnicode helper that runs feedback through security.NewUnicodeNormalizer().Scan() — the same character classes the PostToolUse hook chain applies to tool results
  • Modified buildFeedbackPrompt to return (string, int) — the prompt and the number of sanitization findings
  • Fenced feedback in <validation-output> / </validation-output> tags with a preamble instructing the model to treat the content as data
  • Escaped occurrences of the closing delimiter inside feedback to prevent breakout
  • Added sanitized-empty path: when sanitization removes all content, the prompt notes it was sanitized away rather than injecting a vacuous fence
  • Updated the caller in runAgent to log a warning when sanitization alters feedback
  • Added 9 new test cases covering data framing, delimiter escape, unicode sanitization, all-sanitized-empty, iteration-1 identity, and sanitizeFeedbackUnicode unit tests
  • Updated 3 existing tests for the new two-return-value signature

Testing

  • All new and existing TestBuildFeedbackPrompt_* and TestSanitizeFeedbackUnicode tests pass
  • 100% coverage on buildFeedbackPrompt and sanitizeFeedbackUnicode
  • go vet and gofmt clean
  • Secret scan passed

Closes #6502

Post-script verification

  • Branch is not main/master (agent/6502-frame-sanitize-feedback)
  • Secret scan passed (gitleaks — d31168a0ce5f0d0f2c10dbbb8ab9d78c8b066167..HEAD)
  • PR body secret scan passed (gitleaks — no-git)

buildFeedbackPrompt injected validation output into the agent prompt
as undelimited prose with no unicode sanitization. A prompt injection
payload or obfuscation sequence in the validator output would arrive
in the next iteration's user turn unmarked, at the same privilege
level as the runner's own instructions.

Three changes:

1. Fence the feedback inside <validation-output> tags with a preamble
   instructing the model to treat the enclosed content as data, not
   as instructions. Occurrences of the closing delimiter inside the
   feedback are escaped to prevent breakout.

2. Run the feedback through the existing Go UnicodeNormalizer (the
   same character classes as the PostToolUse hook chain's scan_text)
   to strip tag characters, bidi overrides, zero-width characters,
   null bytes, and ANSI/OSC escape sequences before they enter the
   prompt. This closes the gap where the sandbox hook path never
   sees runner-assembled prompt text.

3. Log when sanitization alters the feedback so a validator emitting
   escape sequences is visible in the run log rather than silently
   swallowed.

Tests cover data framing, delimiter escape, unicode sanitization
(dangerous chars stripped, CJK/accented text preserved), the
all-sanitized-empty case, and iteration-1 byte-identity with
DefaultAgentPrompt.

Closes #6502
@fullsend-ai-coder
fullsend-ai-coder Bot requested a review from a team as a code owner August 23, 2026 01:22
@fullsend-ai-coder fullsend-ai-coder Bot added the ready-for-review Triggers review agent dispatch label Aug 23, 2026
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 23, 2026

Copy link
Copy Markdown

🤖 Review · ⚠️ Cancelled · Started 1:24 AM UTC · Ended 1:33 AM UTC

Commit: 70ab182 · View workflow run →

@codecov

codecov Bot commented Aug 23, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

`security.NewUnicodeNormalizer().Scan` applies NFKC, so any validator
output carrying fullwidth punctuation, a ligature or a vulgar fraction came
back rewritten — `検証エラー:file ½` became `検証エラー:file 1⁄2` — and every
such run raised a spurious "sanitization altered validation feedback"
warning.

Validation feedback routinely quotes file content the agent then edits, so
handing it a normalized copy invites the agent to write the normalized form
back. The PostToolUse chain made the same call for tool results (#6467):
NFKC is used for detection, never for rewriting. Mirror it here — when the
only finding is the compatibility class, keep the original bytes and report
nothing. Mixed input (a zero-width, bidi, tag character, NUL or escape
alongside compatibility text) still takes the sanitized copy.

Assisted-by: Claude
Signed-off-by: Wayne Sun <gsun@redhat.com>

@waynesun09 waynesun09 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Approving with one fix pushed on top (3ddf7d5b, fast-forward — your commit is untouched).

The change is right

Framing, delimiter escaping, the empty-after-sanitizing branch and the StepWarn when sanitizing fires are all what #6502 asked for, and reusing security.NewUnicodeNormalizer() is the correct instinct: one character-class policy for both the hook chain and the runner, no python dependency on the runner path. Six new tests, all passing, plus the full internal/cli package.

What I fixed

UnicodeNormalizer.Scan applies NFKC (internal/security/unicode.go:217), so any validator output containing compatibility characters came back rewritten. Reproduced:

in:  検証エラー:file 「設定」 が不正です ½ アイウ
out: 検証エラー:file 「設定」 が不正です 1⁄2 アイウ      findings=1 ("fullwidth")

Two consequences: the agent is shown text that differs from what the validator produced — and validation feedback routinely quotes file content the agent then edits, so it can write the normalized form back into the repo — and every run with any non-ASCII output raised a spurious "sanitization altered validation feedback" warning, which trains people to ignore a warning that is supposed to mean something.

This is the same call the PostToolUse chain already made for tool results in #6467: NFKC is used for detection, never for rewriting, precisely because compatibility characters are content. sanitizeFeedbackUnicode now mirrors that — when the only finding is the compatibility class, the original bytes are kept and nothing is reported; mixed input (zero-width, bidi, tag character, NUL or escape alongside compatibility text) still takes the sanitized copy, which carries NFKC folding as the accepted cost of using this normalizer. Two regression tests cover both directions.

I also corrected the doc comment, which claimed parity with the hook chain — accurate now, not before.

Note for the release

This should land before the tag: fullsend-ai/agents#948 turns on feedback_mode: append for code and fix, and release.yml tags the agents repo with the same version, so the two would otherwise ship together with this path unframed.

@fullsend-ai-review

fullsend-ai-review Bot commented Aug 23, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 1:35 AM UTC · Completed 1:46 AM UTC

Commit: 3ddf7d5 · View workflow run →

@waynesun09
waynesun09 added this pull request to the merge queue Aug 23, 2026
Merged via the queue into main with commit dced51c Aug 23, 2026
30 of 31 checks passed
@waynesun09
waynesun09 deleted the agent/6502-frame-sanitize-feedback branch August 23, 2026 01:45
@fullsend-ai-review

Copy link
Copy Markdown

Review skipped — this PR is already merged.

The /fs-review command only reviews open PRs/MRs.

Posted by fullsend post-review check

@fullsend-ai-retro

fullsend-ai-retro Bot commented Aug 23, 2026

Copy link
Copy Markdown

🤖 Finished Retro · ✅ Success · Started 1:47 AM UTC · Completed 2:03 AM UTC

Commit: 3ddf7d5 · View workflow run →

@fullsend-ai-retro

Copy link
Copy Markdown

Retro: PR #6510 — frame and sanitize validation feedback

Overall assessment: This was a well-executed workflow with fast turnaround (~24 min from PR creation to merge). The code agent produced a largely correct implementation on its first iteration, but a nuanced design decision about NFKC normalization required a human fix commit. No new proposals — the main improvement opportunity is already tracked.

Timeline

  1. Issue Validation feedback is injected into the agent prompt unframed and unsanitized (feedback_mode: append) #6502 filed (22:33 UTC) — waynesun09 reported that buildFeedbackPrompt injects validation feedback into the agent prompt without data framing or unicode sanitization.
  2. Triage (22:35–22:39) — Triage agent assessed severity as medium, applied triaged label. Clean run, $0.65. Run 32602765808.
  3. Code dispatch (01:07) — waynesun09 dispatched /fs-code with context referencing PR feat(#6464): add the pi runtime (stream parser, Bootstrap/Run, Vertex provider, enablement) #6467 as the precedent for framing wording and recommending a Go helper rather than shelling out to Python.
  4. Code agent (01:08–01:22) — Completed in 1 iteration, 73 turns, $4.03. Produced commit 70ab182 with sanitizeFeedbackUnicode helper, <validation-output> fencing, delimiter escaping, and 9 new tests. Run 32609609322.
  5. PR fix(#6502): frame and sanitize validation feedback in agent prompt #6510 created (01:22) — First review agent triggered.
  6. Human fix (01:32) — waynesun09 pushed commit 3ddf7d5b fixing the NFKC compatibility character rewriting issue (see below). Added two regression tests.
  7. First review cancelled (01:33) — Superseded by the new commit via concurrency group. ~10 min of compute wasted. Run 32610192327.
  8. Human approved (01:33) — waynesun09 approved with a detailed review explaining the NFKC fix.
  9. Second review (01:35–01:46) — Completed with verdict comment (advisory, non-blocking), 4 findings (1 medium, 3 low). Run 32610626424.
  10. Merged (01:45) — Merged by waynesun09, ~1 minute before the review agent completed.

What the human caught that the code agent missed

The issue body explicitly said to sanitize "matching the PostToolUse chain's character classes" and the dispatch comment referenced PR #6467 as the precedent. The code agent read both the Python PostToolUse chain and the Go UnicodeNormalizer, noted that the Python hook reports but does not remove fullwidth/compatibility characters while the Go normalizer applies full NFKC, and deliberately chose the Go approach because it was "stronger." However, NFKC rewrites compatibility characters (fullwidth punctuation, ligatures, vulgar fractions), causing spurious warnings and potentially incorrect agent output. waynesun09 fixed this by mirroring the PostToolUse chain's policy: NFKC for detection only, never rewriting.

Evidence for existing issues (no new proposals needed)

  • #2185 (code agent should follow cited implementation strategy): This PR is another instance of the exact pattern — the issue and dispatch comment cited a specific precedent (PostToolUse chain behavior from feat(#6464): add the pi runtime (stream parser, Bootstrap/Run, Vertex provider, enablement) #6467), the code agent understood the difference but chose to diverge because it thought its approach was "stronger," and the divergence was wrong. The agent explicitly deliberated on this in its reasoning trace before deciding to use full NFKC instead of matching the cited precedent.
  • #4960 / #5139 (review run debouncing): The first review agent ran ~10 minutes before being cancelled by a concurrency group when waynesun09 pushed the fix commit. Standard case of wasted compute on superseded reviews.

Review agent findings (post-merge, advisory)

The review agent's 4 findings arrived after merge (review completed ~1 min after merge). Of note: the medium finding (delimiter-escape-gap — open delimiter not escaped, only close delimiter escaped) is a valid defense-in-depth observation that wasn't addressed before merge. The low finding about fullwidth characters returning original text was essentially flagging the intentional fix by waynesun09, making it a false positive in context.

Agents repo

Agent definitions resolved from fullsend-ai/agents@main (commit d021f80aea08 for triage, c71d143ec6c2 for review).

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

Labels

ready-for-review Triggers review agent dispatch

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Validation feedback is injected into the agent prompt unframed and unsanitized (feedback_mode: append)

1 participant