0004. Grant provider subprocesses no tools, and run them in an empty directory¶
- Status: Accepted
- Date: 2026-09
Context¶
ADR 0003 means draft puts the text of a file
the user downloaded from the internet into a prompt for an agent running on
their machine, with their credentials. That is the canonical
indirect-prompt-injection shape, and CSA's 2026 research names PDFs explicitly
as an in-the-wild vector — the shift from theoretical to operational happened
once agentic CLIs began executing shell commands rather than emitting
suggestions.
An audit found four things had composed into a live path:
- The extraction prompt appended source text with no delimiter and no statement that it was data.
- No
cmd.Dirorcmd.Envanywhere, so every provider inherited the user's working directory and full environment. copilotwas invoked with--allow-all-tools;cursor-agentwith--force, which its own help documents as an alias for--yolo.- Default
automode could select either without the user choosing it.
A crafted claim would be dropped by the grounding gate — but the tool call has already happened by then.
Decision¶
Bound the blast radius rather than trying to win an adversarial text game:
- No provider is invoked with a tool-granting flag. A test pins the table against a list of known ones.
- Each call runs in a fresh empty temporary directory, removed on return. Failure to create it fails the call rather than falling back to the CWD.
DRAFT_*is stripped from the child environment.- Source text, the ledger and the draft under review are fenced in a nonce-delimited block with an explicit data-not-instructions instruction.
Alternatives considered¶
- Fencing alone. No prompt-level measure wins outright; it is defence in depth, not the control.
- A strict environment allowlist. Rejected: every provider finds its credentials somewhere different, so a wrong list breaks authentication and surfaces as a confusing provider error rather than a security decision.
- Refusing untrusted sources. Every source is untrusted. That is the tool.
- Ollama only for untrusted material. Still the right advice for genuinely hostile input, and the README says so — but it cannot be the only answer, because users will not classify their inputs.
Consequences¶
- A crafted document reaches an agent that has no tools, no project context and an empty working directory. It can still influence text; it cannot act.
- Not loading project agent configuration also removes MCP server startup from every one of the dozen-plus calls a paper costs.
copilotandcursor-agentlost flags they were previously given. This only reduces privilege, but both need re-verifying end to end.- The ledger is fenced too, because a
SOURCE_QUOTEis verbatim source by construction — whoever controls the PDF controls what reaches the writer.