Phase 06 — Permissions and Security
Part of Harness engineering.
A harness must stay safe when the model is wrong or hijacked. This phase builds the layers: a permission gate with a fixed rule order, hooks that run as code, a settings file that declares both, and a test for prompt injection that can fail. Each lesson says what its layer cannot stop, so you know why you need the next one.
Lessons
- Permission Gate: Allow, Ask, Deny — rules, modes and approvals, with deny checked first.
- Hooks: Rules That Run as Code — the stdin JSON and exit-code protocol, with a working
.envhook. - Settings.json: Declare the Safety Layer — a real config file and a lint that can fail.
- Untrusted Content: Treat What the Agent Reads as Data — fence, gate, egress guard, redaction, and an eval that measures side effects.
Where the depth lives
| If you want | Read |
|---|---|
| Permissions and blast radius for tools | Permissions, blast radius and the trust boundary |
| The threat model and guardrails for AI products | The threat model and guardrails |
| Access control for agents (identity, roles, policy) | Access control for AI agents |
| Safety, security and governance of agentic products | Safety, security and governance |
Test yourself
Bash(aws s3 ls)is allowed andBash(aws *)is denied. Doesaws s3 lsrun? <details><summary>Answer</summary>No. Rules run in the order deny, ask, allow, and the first match wins. Specificity does not change the order, so a narrow allow cannot carve an exception out of a deny. (<a href=“./01-permission-gate/docs/en.md”>Lesson 1</a>)</details>- The rule
Bash(git status *)is allowed. Why doesgit status && git push originnot run without a prompt? <details><summary>Answer</summary>The gate splits the command into subcommands. An allow rule must cover every part, so the unmatchedgit pushmakes the call ask. A deny rule ongit pushwould block the whole call. (<a href=“./01-permission-gate/docs/en.md”>Lesson 1</a>)</details> - A PreToolUse hook crashes with exit code 1. Does the tool call run? How do you write a hook that fails closed? <details><summary>Answer</summary>Yes, the call runs, because only exit 2 (or a deny decision in JSON) blocks. A safe hook exits 2 itself when it cannot parse its input or reach a verdict. (<a href=“./02-hooks/docs/en.md”>Lesson 2</a>)</details>
- A hook returns
permissionDecision: "allow", but a deny rule matches the call. What happens? <details><summary>Answer</summary>The deny rule wins. A hook allow does not bypass deny or ask rules. A hook that exits 2 blocks the call before the rules even run. (<a href=“./02-hooks/docs/en.md”>Lesson 2</a>)</details> - Why does
Write(src/**)in a permissions list do nothing? <details><summary>Answer</summary>Claude Code consults onlyEdit(path)andRead(path)for file paths. Rules for Write, Glob, NotebookEdit and MultiEdit are accepted and never used, so you writeEdit(src/**)instead. (<a href=“./03-settings-json/docs/en.md”>Lesson 3</a>)</details> - Why is a
Bash(rm *)deny rule not a security boundary, and what do you add? <details><summary>Answer</summary>It matches command text, so/bin/rmandbash -c 'rm ...'slip past it. Add a PreToolUse hook that inspects the command and the OS sandbox, which enforces limits whatever the text says. (<a href=“./03-settings-json/docs/en.md”>Lesson 3</a>)</details> - The injection eval scores a bare harness at 0.0. Why keep that case? <details><summary>Answer</summary>It proves the eval can fail, so a score of 1.0 means something. Removing any single layer must also lower the score, which shows each layer is doing real work. (<a href=“./04-untrusted-content/docs/en.md”>Lesson 4</a>)</details>
- A secret was printed into a transcript, and redaction is now on. Is the problem solved? <details><summary>Answer</summary>No. Redaction stops the next leak, not the last one. Rotate the exposed secret. (<a href=“./04-untrusted-content/docs/en.md”>Lesson 4</a>)</details>
