Prince Jain 掃描 525 份技能檔案及 153 份 Claude Code、Codex CLI 記錄中的提示詞注入與倉庫開啟陷阱,並發布 repotrap
Scanning 525 Skills and 153 Claude Code & Codex Logs for Prompt-Injection & Repo-Open Traps
Prince Jain 檢查 525 份本地技能 Markdown 文件、16 個工作區倉庫,以及 153 份 Claude Code 和 Codex CLI 對話記錄,排查提示詞注入與倉庫開啟陷阱。
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
來源:DEV Community · dev.to