Skip to main content

Recipe: Scoped file access

The problem

The agent needs to read source code and scratch files. It never needs to read /etc/passwd, SSH private keys, or AWS credentials. A vulnerability in the model (or a prompt injection) could otherwise trick the agent into exfiltrating secrets from the filesystem.

The policy

version: '1'
# Scoped file access.
#
# Allow file_read and file_write generally, but explicitly deny known
# dangerous paths. Ordering matters: deny rules evaluate first and win.
#
# The extractor emits `file_read:read` and `file_write:write` for
# every Read/Write/Edit tool call regardless of path. To enforce
# per-path scoping, rule evaluation uses the `path` arg via the
# `when:` condition block.
settings:
default_action: deny
default_on_missing: deny
default_on_tamper: deny
rules:
- id: deny-etc
deny: 'file_read:read'
when:
path: '/etc/*'
reason: 'System config paths are out of scope.'
- id: deny-etc-write
deny: 'file_write:write'
when:
path: '/etc/*'
reason: 'Writes to /etc are never allowed.'
- id: deny-root-home
deny: 'file_read:read'
when:
path: '/root/*'
reason: 'Root home is out of scope.'
- id: deny-ssh
deny: 'file_read:read'
when:
path: '*/.ssh/*'
reason: 'SSH keys are never read by the agent.'
- id: deny-aws-creds
deny: 'file_read:read'
when:
path: '*/.aws/*'
reason: 'AWS credentials are never read by the agent.'
- id: allow-tmp-read
allow: 'file_read:read'
when:
path: '/tmp/*'
reason: 'Scratch space is readable.'
- id: allow-tmp-write
allow: 'file_write:write'
when:
path: '/tmp/*'
reason: 'Scratch space is writable.'
- id: allow-repo-read
allow: 'file_read:read'
when:
path: '/workspace/*'
reason: 'Repo tree is readable.'
- id: allow-repo-write
allow: 'file_write:write'
when:
path: '/workspace/*'
reason: 'Repo tree is writable.'

Why it works

The coding-agent hook receives the Read, Write, and Edit tool calls (and their host-specific aliases) from Claude Code / Cursor / Codex. The extractor normalizes the tool name to file_read or file_write and emits :read or :write as the method. The path argument rides along in the call context; each rule's when: path: glob is matched against it. Rules evaluate top-down, so the explicit deny rules fire before any allow, and sensitive paths are cut off even inside otherwise-allowed trees.

What gets blocked

Agent callExtracted actionDecisionreason_code
Read /etc/passwdfile_read:readdenyRULE_MATCH
Read /root/notes.txtfile_read:readdenyRULE_MATCH
Read ~/.ssh/id_rsafile_read:readdenyRULE_MATCH
Read ~/.aws/credentialsfile_read:readdenyRULE_MATCH
Write /etc/hostsfile_write:writedenyRULE_MATCH
Read /var/log/app.log (no allow rule)file_read:readdenyNO_RULE_MATCH

What gets allowed

Agent callExtracted actionDecisionreason_code
Read /tmp/foo.txtfile_read:readallowRULE_MATCH
Write /tmp/scratch.jsonfile_write:writeallowRULE_MATCH
Read /workspace/src/app.pyfile_read:readallowRULE_MATCH

Test it yourself

curl -O https://docs.controlzero.ai/recipes/scoped-file-access/policy.yaml
curl -O https://docs.controlzero.ai/recipes/scoped-file-access/scenarios.json

# Requires a Python / Node SDK that ships the tool-extractor spec
# (coming in the 228 Phase 3 release). Confirm with:
# controlzero --version
controlzero test-policy policy.yaml --scenarios scenarios.json

Caveats

  • Rules match path against the exact string the hook receives. Different agents pass paths differently: Claude Code passes absolute paths for Read/Write but relative paths for Edit. Point your rules at the form you actually see in the audit log. When in doubt, use a double-prefix pattern (*/.ssh/* catches both /home/me/.ssh/... and ~/.ssh/...).
  • Symbolic links bypass path globs. The agent reading /tmp/link-to-etc-passwd appears as /tmp/... in the path arg. Mitigate at the OS layer (agent user lacks read permission on /etc/passwd) -- governance is a second line of defense, not the only line.
  • Bash-wrapped file access (cat /etc/passwd) routes through the Bash extractor, not the file_read extractor. Pair this recipe with Block outbound network and add explicit Bash:cat / Bash:less rules if that is a concern.