Cheat sheets

Prompt Injection Incidents and AI CVEs

Find, trace and block indirect prompt injection. EchoLeak chain, PVE validator, rule detector, tool call firewall.

  • Concepts and terms
  • Tasks and commands
  • Annotated code
  • Diagrams
  • Comparisons
  • Defaults and numbers
  • Troubleshooting
  • Questions and tips

Source

Symptoms, causes and fixes#

SymptomCauseFix
Agent forwards an email to all contactsNaive agent obeys imperatives in the email bodyTag the email untrusted. Ignore its control flow
Filter blocks [SYSTEM: but a polite rewrite passesAdaptive attacker drops the markerUse IFC. Keyword filters fail adaptive attacks
Defense reports near-zero ASR, then breaksTested with static attacks onlyEvaluate with adaptive attacks before you publish
Data leaves through an approved domainOutput crosses the trust boundary in a URLControl retrieval, scope and output one by one
Model already touched the worldAll guardrails run at the endValidate each tool call before it runs
Yesterday's memory note hijacks today's runOvertrust of retrieved memoryRefuse memory writes that look like directives
Regex misses "disregard the prior"One regex, never measuredReport precision and recall per category
load_taxonomy raises FileNotFoundErrorLesson 82 artifact is missingRun lesson 82 main.py first

From Lesson 18.15, 18.15 code/main.py, Lesson 18.25, Lesson 14.27, Lesson 19.83, 19.83 code/main.py

Production incidents#

FactorEchoLeakCamoLeakCopilot RCE
TargetMicrosoft 365 CopilotGitHub Copilot ChatGitHub Copilot
CVECVE-2025-32711Number undisclosedCVE-2025-53773
CVSS9.39.6Details minimal
EntryCrafted email in RAG contextRepository contentCode-suggestion surface
EffectMermaid image on a CSP-approved URLImage loads through the Camo proxyRemote code execution
FixServer-side update, June 2025Image rendering disabledDetails minimal

From Lesson 18.25, Lesson 18.15

EchoLeak attack chain#

1Attackersends an email with a routine subject to anyemployee.2Victimdoes nothing. The attack is zero-click.3CopilotRAG retrieval pulls the email into context for aroutine query.4Modelruns the hidden text. It finds the recent MFAcodes in the inbox.5Rendererloads a Mermaid diagram from a signed URL thatholds the codes.6CSPallows the request because the domain isapproved.7Vendorrated it low first. The MFA demo raised it to9.3.

From Lesson 18.25

Three boundaries and the gate#

emailcontextrender_imageintentratified callsMFA codesclientAttackeremailtrust untrustedprocessRetrievalboundaryenters contextmodelModelreads hiddentextclientOutputboundaryCSP-approved URLclientUser prompttrustedprocessScopeseparationblocks untrustedcallsstoreScopeboundaryprivate inboxemailcontextrender_imageintentratified callsMFA codesclientAttacker emailtrust untrustedprocessRetrievalboundaryenters contextmodelModelreads hidden textclientOutput boundaryCSP-approved URLclientUser prompttrustedprocessScope separationblocks untrustedcallsstoreScope boundaryprivate inbox

From Lesson 18.25, 18.25 code/main.py, Lesson 18.15

Attack surface#

RAG vector
attacker publishes a document. Retrieval puts it in the prompt.
Inbox vector
attacker sends an email. The agent reads the body.
Tool output vector
attacker controls a tool result, for example a web search.
Data theft
agent sends conversation history to an attacker URL.
Worming
injected content tells the agent to embed the exploit in its next output.
Memory poisoning
agent stores the instructions and re-poisons itself next session.
Ecosystem contamination
injected facts spread to other agents through shared memory.
Arbitrary tool use
any tool in the registry becomes attacker-reachable.

From Lesson 18.15, Lesson 14.27

Run the labs#

CommandWhat it does
python3 code/main.py (18.15)Attack naive, filter and IFC agents
python3 code/main.py (18.25)EchoLeak trace, then scope separation
python3 code/main.py (14.27)PVE validator verdicts per call
python3 main.py (19.83)Detector metrics and JSON report
project_test.py <id> --init <dir>Create the workspace. Id is tool-call-firewall
project_test.py <id> --stage 1 --path <dir>Grade one stage
project_test.py <id> --all --solutionCheck the reference. Add --strict

From Lesson 18.15, Lesson 18.25, Lesson 14.27, Lesson 19.83, tool-call-firewall/README.md

PVE validator before the executor#

validator = Validator(
allowed_tools=("search", "send_message", "read_memory"),
sensitive_tools=("send_message",),
)
call = ToolCall("search", {"query": "<q>"}, intent="research")
contents = [Content("<text>", "user_message")]user text is not scanned
ok, reason = validator.assess(call, contents)allowlist, args, retrieved
if ok:
executor.run(call)runs only after allow
looks_like_directive("<text>")marker or None
memory_write_guard(MemoryWrite("<text>"))refuse directive writes

From 14.27 code/main.py

Layered injection detector#

{"name": "ignore-previous", "category": "instruction-override",
"score": 0.85, "substring": "ignore previous"}a rule is data
detector = Detector()compiles regex rules once
v = detector.analyze("<prompt>")raw and normalized text
v.categorytop category, its max score
normalize(text)zero-width, base64, hex, leet, rot13
report = evaluate(detector, fixtures, benign)tp, fp, fn, tn per category
write_report(report)outputs/detector_report.json

From 19.83 code/main.py, 19.83 code/rules.py

Tool call firewall#

TaskCode
Parse the envelopecall(raw, 256)
Decide policydecide(&c) gives Allow, ApprovalRequired or Deny
Reader writes a notereader + write notes.md -> Deny
Editor writes a noteeditor + write notes.md -> ApprovalRequired
Use an approval onceauthorize(request, Some(&mut gate))
Execute with containmentdispatch(root, &request, content, approval)
Log the decisionaudit(&mut log, &c, &d, 10)
Build the CLIrustc --edition=2021 cli.rs -o firewall

From tool-call-firewall/solution, docs/en.md, docs/en.md

Limits and numbers#

SettingDefaultNote
CLI envelope bytes4096Longer input is refused
Write content bytes16384Same cap for reads
Audit max_entries10Duplicate id is refused
JSON schema_version1CLI result JSON
Base64 token length16Shorter tokens are not decoded
Rot13 word hits2Fewer hits keep the input
No rule fires0.0Verdict category is benign
Detector accuracy0.6231 of 50 fixtures

From tool-call-firewall/solution, 19.83 code/main.py, 19.83 outputs/detector_report.json, Lesson 18.15

Questions#

Why do user-input filters miss IPI?
The payload is in retrieved content, not in user input.
Why not filter only keywords?
A benign instruction like "please print Yes" has no keyword.
Does fixing one boundary secure the rest?
No. Prevent each of the three independently.
Is a system prompt rule enough?
No. Instruction-following alone is not enforcement.
Where does the role come from?
The trusted application. Never the model response.
Is the detector the safety gate?
No. It is one signal that the gate composes.
Is the firewall an OS sandbox?
No. Approvals are single-use in memory in one invocation.

From Lesson 18.15, Lesson 18.25, Lesson 14.27, Lesson 19.83, tool-call-firewall/README.md