Checking third-party repos for prompt injection before an AI reads them
Before 18 public repos went into a search pool that Claude sessions read, I scanned every text file for hidden text and instruction-shaped strings and read every agent-facing file in full. Nothing malicious turned up, and some harmless files stayed out anyway.
Anything an AI reads is input, and an instruction-shaped string in a search result is still an instruction, wherever it came from. So before 18 public repos went into a Trawl pool that Claude sessions search, I checked them for prompt injection in three steps: scan every text file, read every file written for an agent in full, and keep out anything that reads like an instruction, even when it’s harmless.
Nothing malicious turned up. Some files stayed out anyway.
The scan
It covered 5,861 text files across the 18 repos, plus 1,401 in Mullet’s own code, and looked for:
- hidden Unicode: zero-width, bidirectional and tag characters
- injection phrasing, such as “ignore previous instructions”, “you are now”, notes addressed to an AI, and “don’t tell the user”
- chat-template and harness tags
- hidden HTML comments that mention AI or instructions
- large encoded blobs
Then I read all 36 plugin skill, command, agent and hook files in full. Those are written for an agent to act on, so a pattern match isn’t enough for them.
What it found
No hidden bidirectional or tag characters, no encoded payloads, nothing malicious and no attempts to send secrets out. Most pattern hits were harmless:
- a Windows “Developer Mode enabled” test message
- byte-order marks and zero-width spaces used as test data
- a security comment containing the word “exfiltrate”
- PNG screenshots embedded in HTML pages
The one that looked like an injection
claude-code-router’s own blog quotes a <system-reminder> block that the router adds to requests on purpose, telling the model to proactively run whichever tool suits best. On the blog that’s an explanation of a feature. In a search result it would read like a real instruction to whatever model was reading, so the blog is out of the index. (I’ve paraphrased it here for the same reason.) Files whose code contains literal harness-tag strings, in agent-shim and in the router, are out too.
What else stayed out
- Every repo’s own CLAUDE.md and AGENTS.md. They’re instructions for agents working on those projects, not for whoever searches the pool.
- Two skills with very broad triggers: one sends code to third-party models when everyday words like “fix” or “test” appear, and one tells any reader to route all plugin work through its own tool.
- Internal prompt templates.
- Mullet’s own tests, which contain injection strings as fixtures for its injection guard.
None of these is malicious. They’re out because a model reading a search result can’t reliably tell a quoted instruction from a real one.
If you install rather than read
These aren’t prompt injection, but they matter if you install the tools rather than just read them:
- One plugin’s hooks download an unpinned release binary in the background at session start, and run it on every prompt.
- Another repo ships a project SessionStart hook, which would run if you opened Claude Code inside the clone.
- One troubleshooting guide suggests printing a settings file that holds API keys.
The record
The check, what it found and every exclusion are written down with the pool: in its documents/SOURCES.md, with each repo’s pinned commit and licence, and as comments beside the exclusions in its config.
The method
- Treat anything an AI will read as input.
- Scan every text file before indexing it: hidden Unicode, injection phrasing, harness tags, hidden comments and encoded blobs.
- Read every agent-facing file in full.
- Keep out whatever is instruction-shaped, even when it’s harmless.
More on Mullet
-
Mullet’s code is private now
I’ve made Mullet’s code private while I work on a new idea. Everything I’ve written about it stays here, and if you’d like to know more or ask about…
-
Tuning a search pool: content beat weights
On a Trawl pool of 18 public repos, every search-time setting I tried scored exactly the same, because the reranker sets the final order. What moved…
-
Why a forked worker can wait for ever
A Trawl reindex started through Mullet’s MCP server hung at 0% CPU, because its forked workers inherited a lock the main thread was holding. Pools…
-
Where Mullet goes next
Mullet 2.5.0 is out. What I plan next: a guard that checks a step fits a model’s window, a price ceiling instead of a free switch, more settings and…
-
Eight rules from a game framework’s agent prompts, for web and print design
On 1 November I read the agent prompts of VibeGame, a framework that builds 2D web games, and kept the eight techniques that carry over to web and…
-
What I tried and binned, and what each one cost
Three local-model options I stopped using, with what each cost and the verdict: a 25 GB model that timed out in a job, a Blackwell-only fork ruled…
-
When your plan has no five-hour limit
Mullet 2.5.0 copes with a Claude plan whose usage data has no five-hour window: the HUD drops the five-hour figures and the budget guard paces on…
-
Very cheap is basically free: pricing a coding model per step
A free model id in my config silently had no hosts, and the real price of a cheap coding model depends on which hosts you will accept. The per-step…
Featured projects
-
PrintMD
A Markdown editor that exports print-quality PDF, Word and PowerPoint files, with a small PDF service that runs on your own computer.
-
Solar Calendar
A calendar of 13 identical 28-day months, with each year starting on the first Monday on or after the spring equinox.
-
Where Should I Live
Rank cities around the world by what you’d keep after tax, what a month costs, which visas you can get and how each place fits the way you live.
-
Apple News API
A PHP client for publishing articles to Apple News, with a document builder for the whole Apple News Format.
More writing
-
A PDF service that only runs on your machine
PrintMD’s PDF service turns Markdown into a PDF with one request, and it refuses to be hosted. Why that’s on purpose, and how it stays locked down…
-
Custom sizes, one long page and fit to width
Not every PDF is an A4 page. PrintMD’s page options cover a custom size, one continuous page, scaling wide tables to fit and your own margin, and…
-
Print-quality PDFs from a browser
A browser can print any page to PDF, but a document you send someone needs more. How PrintMD gets fonts, page breaks and images right, and what its…
-
Why I built PrintMD
I write in Markdown, and getting from there to a PDF I’d happily send someone was more work than it should be. So I built an editor that shows the…