Least privilege
Identify instruction-injection attempts and design permission, sandbox, approval and evaluation boundaries for an agent.
THE IDEA
Least privilege
Give every tool and credential only the access its operation needs. A task that reads documents needs the read scope; a credential that can also write and delete multiplies the damage of any mistake or attack (risk 9 against 1 in the toy weights). Wording in the prompt does not replace this: the model can be told not to delete, but the credential still can. Permissions must be enforced by the application at the point of use.
The task only reads documents. Choose the scopes on its credential.
Predict first. Can a model-side instruction revoke a server credential's actual privileges?
Chapter context
An agent combines untrusted content with tools that can affect real systems. Reliable operation requires boundaries enforced outside model text, alongside clear task authorization and evidence-based evaluation.
TRY IT YOURSELF
Part 1. Do it hands-on
Size up excess privilege by hand, then write the narrowing and enforcement helpers.
The task only reads documents. Choose the scopes on its credential.
- To do: A credential grants read, write and delete; the task only reads. How many scopes are excess?
- To do: Risk weights: read 1, write 3, delete 5, admin 10. Risk of the full credential?
- To do: Risk after narrowing it to read only?
- To do: Can a model-side instruction ("you may not delete") revoke a server credential's actual privileges? Type yes or no
Part 2. Do it in code
- WEIGHTS = {"read": 1, "write": 3, "delete": 5, "admin": 10} (PROVIDED).
- needed_scopes(operations): the sorted union of the scopes the operations need; operations is a list of dicts {"name", "scope"}.
- excess(granted, needed): the sorted scopes granted but not needed.
- missing(granted, needed): the sorted scopes needed but not granted (the task would fail).
- risk(scopes): the sum of the weights (unknown scopes count as 10).
- narrow(granted, needed): the granted scopes that are needed, sorted. Never adds anything.
- check_call(granted, op_scope): True only if the operation's scope is among the credential's granted scopes — enforced here, whatever the model's instructions say.
Stuck, or something not working? You can come back to it any time.
CHECK YOUR UNDERSTANDING
Can a model-side instruction revoke a server credential's actual privileges?
Chapter reference: math, worked example & code
This section belongs to the whole chapter, so it is the same on every lesson in it and may cover a different lesson's topic. The code for the experiment you just ran is inside the experiment itself.
Math and notation
For a stated evaluation set:
These empirical rates characterize that set; they are not universal security guarantees. Define denominators and outcome criteria before running the evaluation.
Worked example
An offline evaluation contains 100 unauthorized-action cases, of which 2 pass, and 200 benign cases, of which 5 are blocked. Unsafe pass rate is 2%, and false block rate is 2.5%. Those two numbers measure different failures; averaging them into a single undocumented 'safety score' hides the tradeoff.
Reference code
The following example uses the Python standard library and does not call a remote service.
from math import isclose
unsafe_passes,unsafe_cases=2,100
benign_blocks,benign_cases=5,200
assert isclose(unsafe_passes/unsafe_cases,.02)
assert isclose(benign_blocks/benign_cases,.025)
Python examples are provided for study and local execution; this page does not execute Python.
Common mistakes
A successful prompt-injection defense on one dataset is not a proof for every input. Text instructions cannot substitute for permission enforcement. Audit logs must preserve evidence without exposing credentials.