Skip to content

Let policy config allow every program and set read roots - #192

Merged
pbdeuchler merged 1 commit into
masterfrom
outpost/019fa592-baf8-7852-a4f2-d88bb457df94
Jul 27, 2026
Merged

Let policy config allow every program and set read roots#192
pbdeuchler merged 1 commit into
masterfrom
outpost/019fa592-baf8-7852-a4f2-d88bb457df94

Conversation

@cowboycodebot

@cowboycodebot cowboycodebot Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Summary

Two related gaps in the policy surface:

  1. policy.shell.allow had no wildcard. policy.network.allowed_hosts accepts *; the shell allowlist did not. A deployment that wants arbitrary shell execution had to allowlist bash and route every command through bash -c — a bypass dressed up as an allowlist entry, which teaches agents a worse habit than simply saying "any program" and makes the profile's real surface depend on knowing the trick.

  2. Three PolicySettings fields were unreachable from config. policy_from_config pinned allowed_read_roots, sensitive_path_patterns, and shell_mode to PolicySettings::default(), with a comment marking it as pending work. An embedder could therefore state the exact filesystem an agent may write, but never the one it may read — read roots stayed at the working directory plus the temp directory forever, regardless of what the deployment intended.

Changes

  • allowed_shell_commands short-circuits on a * entry, mirroring allowed_hosts. ShellMode remains the only shell restriction under a wildcard, and an empty list still denies every external command.
  • policy.allowed_read_roots — empty (the default) keeps the built-in roots; a non-empty list replaces them. Configured allowed_write_roots stay readable either way, so an agent allowed to modify a tree can still inspect it.
  • policy.sensitive_path_patterns — absent keeps the built-in globs; an explicit list replaces them, and [] disables the check for deliberately unguarded deployments.
  • policy.shell.mode"strict" (default) or "relaxed", mapping to the existing ShellMode.
  • policy_from_config now sets every field explicitly, so the ..defaults struct update is gone and a future field addition is a compile error rather than a silent default.

Every new knob's unset form reproduces current behavior exactly, so this is additive for existing configs. deny_unknown_fields still applies, so the new keys are opt-in by name.

Verification

cargo fmt --all --check, cargo clippy --workspace --all-targets --all-features -- -D warnings, and cargo test --workspace --all-features all pass (with PLAYWRIGHT_DRIVER_VERSION set, per the CI workaround).

New tests: wildcard accepts any program while strict mode still rejects eval; an empty allowlist denies every external command but still permits bare assignments; and a table over policy_from_config covering unset/explicit read roots, unset/empty/explicit sensitive patterns, and both shell modes.

Downstream

Outpost's unrestricted tool-access profile is the motivating consumer — it currently allowlists bash, and its Halter sessions read from a different tree than its Claude Code sessions. It is pinned to this commit by rev until a release contains it.

`policy.shell.allow = ["*"]` now allows every program, mirroring the
wildcard `policy.network.allowed_hosts` already accepts. Deployments that
want arbitrary shell execution had to allowlist `bash` and route every
command through `bash -c` — a bypass dressed up as an allowlist entry,
which taught agents a worse habit than simply saying "any program".

The builder also stopped pinning the last three `PolicySettings` fields to
their defaults. `policy.allowed_read_roots`, `policy.sensitive_path_patterns`,
and `policy.shell.mode` are configurable, so an embedder can state the exact
filesystem an agent sees instead of inheriting the working directory and
temp directory forever. Unset keeps today's behavior in every case.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@pbdeuchler
pbdeuchler merged commit 11279d7 into master Jul 27, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant