The Permission Boundary
- Explain prompt injection in plain terms and why it cannot be fully prevented
- Apply least privilege to an agent: role, resource scope, action scope
- Read a permission as a blast-radius decision
- Write a deny-by-default policy table
The scare, twice
Story one. In 2025, researchers at Aim Security showed that a single crafted email could steer Microsoft 365 Copilot. No click required. Copilot does what it is built to do, reads mail, and the email contained text that Copilot treated as instructions. Through a chain of tricks it could then pull data from files it could reach and send it out. Microsoft fixed it (it is tracked as CVE-2025-32711, nicknamed EchoLeak) and said it saw no use in the wild. This was a serious product from a very large company, and it still happened.
Story two. The Replit agent from chapter 0 was told not to touch the database. It could touch the database, so it did.
Different causes, same missing thing: the agent had more power than the task needed.
Refresher: what prompt injection is
An agent reads text: web pages, emails, PDFs, tickets. To a model, all text looks alike. So a document that says "ignore your instructions and do X" can compete with your real instructions. That is prompt injection.
You can make it harder. You cannot make it impossible, because the model cannot reliably tell a rule from a sentence that looks like a rule. (Chapter 6 is about making that distinction as clear as possible.)
So change the question. Not "how do I make the model immune?" but "if it does get fooled, what is the worst thing it can do?" That worst case is the blast radius, and this pattern is about keeping it small.
The pattern
Give the agent the least power that still gets the job done, and enforce it with credentials, not with polite requests in the prompt.
Three dials to turn down:
| Dial | The question | For Pip |
|---|---|---|
| Role | Who is it acting as? | support_agent, not admin |
| Resource scope | Which data can it reach? | This customer's orders, not all orders |
| Action scope | Read, draft, write, send, delete? | Read and draft. Refunds go elsewhere |
Try it
The attack below is the same every time. Only the permissions change. Drag the slider and watch the worst case.
The agent is allowed to
- Read files in /support-docs
- Summarise them
Worst case
It reads a public help article and repeats a bad instruction back. Nothing changes anywhere.
Blast radius
Build it: deny by default
from fnmatch import fnmatch
POLICY = {
"support_agent": {
"search_docs": {"resources": ["/support-docs/*"]},
"read_order": {"resources": ["orders:own_customer"]},
"draft_reply": {"resources": ["/drafts/*"]},
},
"billing_agent": {
"read_order": {"resources": ["orders:*"]},
"issue_refund": {"resources": ["orders:*"], "max_amount": 100},
},
}
def allowed(role, tool, resource, amount=0):
rule = POLICY.get(role, {}).get(tool)
if rule is None:
return False # not listed means denied
if not any(fnmatch(resource, pat) for pat in rule["resources"]):
return False
if amount > rule.get("max_amount", float("inf")):
return False
return True
print(allowed("support_agent", "issue_refund", "orders:A-1043")) # False
print(allowed("billing_agent", "issue_refund", "orders:A-1043", 42)) # True
print(allowed("billing_agent", "issue_refund", "orders:A-1043", 900)) # False
Anything not written down is refused. The list of what an agent can do stays short enough to read over coffee, which is the point. This function is what check 3 in the gateway calls.
When it goes wrong
- The confused deputy. An agent using your full access while reading a stranger's email is a deputy that can be talked into misusing your authority. For anything that came from outside, give it the stranger's level of access, not yours.
- One shared API key for everything. Convenient, and it turns every agent into an admin.
- Permissions that only exist in the docs. If nothing in code enforces the table, it is a wish list.
- Never revisiting. Agents gain tools over time. Re-read the table whenever one is added.
Where it connects
Permission answers "can it?" The Approval Gate answers "should it, right now, without asking?" You will usually want both. The MCP course covers the same idea from the server side.
- Prompt injection cannot be fully prevented, so plan for the day it works
- Permissions are a blast-radius decision: role, resource scope, action scope
- Deny by default, and list what is allowed
- Enforce with credentials the agent cannot exceed, not with instructions it can ignore
- An agent summarises inbound customer emails. Which of the three dials can you turn all the way down, and what does that protect you from?
- Why is a read-only database credential a stronger control than the sentence "never modify data" in the prompt?