fix(exec): avoid splitting surrogate pairs in approval display#99566
Conversation
|
Codex review: needs maintainer review before merge. Reviewed July 3, 2026, 7:28 PM ET / 23:28 UTC. Summary PR surface: Source +2, Tests +47. Total +49 across 3 files. Reproducibility: yes. Source inspection shows current main still uses raw UTF-16 slice at the 16 KiB display cap, and the PR body plus tests exercise a command where an emoji crosses that boundary and previously could leave a lone surrogate. Review metrics: none identified. Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Merge the focused central sanitizer hardening after maintainer review and required checks complete; keep broad boundary-safety guard work and channel-specific preview fixes separate. Do we have a high-confidence way to reproduce the issue? Yes. Source inspection shows current main still uses raw UTF-16 slice at the 16 KiB display cap, and the PR body plus tests exercise a command where an emoji crosses that boundary and previously could leave a lone surrogate. Is this the best way to solve the issue? Yes. The central approval-display sanitizer is the narrowest maintainable owner because Gateway approval registration, lookup, and broadcast payloads flow through it; reusing the existing dependency-free UTF-16 helper avoids a duplicate local slicing implementation. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against c680312ef6f3. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +2, Tests +47. Total +49 across 3 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
Use the shared UTF-16 safe truncation helper for exec approval display caps so long sanitized command previews remain well-formed when the cutoff lands inside a surrogate pair. Co-Authored-By: Claude <[email protected]>
809ce40 to
1d96d5b
Compare
|
Maintainer proof for exact head
The first local source-checkout Gateway attempt was stopped and cleaned up because missing worktree package-local dependencies stalled sidecar startup; the installed Testbox lane completed the real Gateway E2E. Screenshots are not useful for this nonvisual encoding boundary. The local prepared and pushed trees are identical ( |
|
Merged via squash.
|
…law#99566) * fix(exec): avoid splitting surrogate pairs in approval display Use the shared UTF-16 safe truncation helper for exec approval display caps so long sanitized command previews remain well-formed when the cutoff lands inside a surrogate pair. Co-Authored-By: Claude <[email protected]> * fix(exec): sanitize malformed approval text --------- Co-authored-by: Claude <[email protected]> Co-authored-by: Peter Steinberger <[email protected]>
What Problem This Solves
Exec approval command display text is part of the security decision UI. The sanitizer already handles invisible Unicode characters by full code point, but its final display cap used raw
String.prototype.slice. If the 16 KiB cap lands between the high and low surrogate of a supplementary-plane character, the approval preview can contain malformed UTF-16 such as a dangling\uD83Dbefore…[truncated].That can make the command text rendered in approval prompts differ at the character level from the sanitized string the code intended to show, and can break downstream UI/payload encoding for the approval prompt.
Summary
Evidence
"a".repeat(16 * 1024 - 1) + "😀tail"returned{ "truncated": true, "hasLoneSurrogate": false, "encodable": true, "tail": "aaaaaaaaaaaa…[truncated]" }.node scripts/run-vitest.mjs run src/infra/exec-approval-command-display.test.tsexited 0.node_modules/.bin/vitest run src/infra/exec-approval-command-display.test.tsreported 1 file passed / 33 tests passed.pnpm exec oxfmt --check --threads=1 src/infra/exec-approval-command-display.ts src/infra/exec-approval-command-display.test.tsexited 0. Local pnpm emitted the existing Node engine warning because the machine is on Node v22.17.0 while package metadata asks for >=22.19.0.pnpm exec oxlint src/infra/exec-approval-command-display.ts src/infra/exec-approval-command-display.test.tsexited 0.git diff --checkexited 0.Risk
Low. This only changes the existing display truncation boundary to back off from split surrogate pairs. It does not change redaction patterns, the display size constant, the truncation marker, warning/command marker strings, or the hard input cap behavior.
🤖 Generated with Claude Code