The discussion on r/ClaudeAI (1xozfcv) about why 98% of the 16,060 open GitHub issues on anthropics/claude-code cannot simply be handed to autonomous triage agents hit on a real engineering problem: untrusted input poisoning.
Whenever you point Claude Code or Codex CLI at an external GitHub issue (gh issue view), a cloned PR branch, or a third-party SKILL.md file, four classes of traps can hijack the agent:
-
Repo-open config hooks:
.claude/settings.json(hooks,apiKeyHelper,statusLine),.mcp.json, or.vscode/tasks.json("runOn": "folderOpen") inside a cloned repo that run commands on startup. -
Invisible Unicode steganography: Unicode Tag characters (
U+E0000toU+E007F), bidirectional overrides (U+202AtoU+202E), and standalone zero-width spaces (U+200B). -
Spoofed
<system-reminder>and</tool_result>delimiters inside untrusted issue bodies or web responses. -
Hidden HTML comment instructions & remote shell pipes (hidden HTML comment blocks with override instructions or
curl ... | bashinside skill files).
Three false-positive traps and blind spots in my first scanner
When I ran my first scanner script across 525 local skill markdown files, 16 workspace repos, and 153 local Claude Code and Codex CLI transcripts, three things broke:
-
Claude Code's
1,526nativetype: "attachment"<system-reminder>rows: Scanning raw.jsonllines for<system-reminder>to detect delimiter spoofing flagged1,526rows across125sessions because Claude Code writes its own internal system reminders astype: "attachment"andtype: "api-request-*"records. Filtering out native attachment records (RTR012) isolated untrustedtool_resultpayloads (RTR008). -
Codex CLI's
custom_tool_call_outputnested object payloads (142zero-width characters across5sessions): Unlikefunction_call_output(wherepayload.outputis a string), Codex CLI'scustom_tool_call_outputstorespayload.outputas a nested JSON object. My initialString(payload.output)call turned the tool output into"[object Object]"and missed138of the142standaloneU+200BandU+200Echaracters ingested across15tool outputs (RTR011). -
Composite Emoji
U+200DZWJ vs standaloneU+200B(RTR014): A raw zero-width regex flagged composite emojis (1innotebook-guidance/SKILL.md) until I separated Emoji Zero-Width Joiner sequences (<Emoji> U+200D <Emoji>) from standaloneU+200Bspaces (2insigns-of-ai-writing.md,RTR007) and1livecurl -fsSL https://composio.dev/install | bashremote shell pipe incomposio-cli/SKILL.md(RTR005).
Running repotrap
I published the zero-dependency scanner as repotrap (npx repotrap):
npx repotrap
You can also pre-scan a live GitHub issue or PR via the gh-issue option (npx repotrap with gh-issue owner/repo#123) before letting an agent read it, or run emit-hook mode to generate a deterministic PostToolUse sanitizer hook for ~/.claude/settings.json.
Limitations
-
repotrapdeterministically catches invisible Unicode (U+E0000tags, Bidi overrides, standaloneU+200B), spoofed XML/system delimiters, hidden HTML comment instructions,curl | shskill pipes, and.claude/.mcp.json/.vscoderepo-open hooks. It cannot prove whether plain natural-language prose without structural markers or override phrases will sway a model.
GitHub: https://github.com/princejain756/repotrap
npm: https://www.npmjs.com/package/repotrap
Top comments (1)
An AGENTS.md checked into the repo is a supply chain input, not documentation. Treating agent instructions as untrusted content and pinning the hash of that file in CI would catch the class of attack described here before the agent ever executes it.