Skip to content

sandbox filesystem.allowWrite grants tool permissions instead of restricting writes (contradicts schema description) #54626

Closed

Description

@norrietaylor

Version

gh-aw v0.77.5 (also present on main as of b07b62e4a9), firewall image 0.25.58.

Summary

sandbox.agent.config.filesystem.allowWrite is documented as a restriction but implemented as a permission grant. A workflow author following the schema description will believe they have added a write boundary while having actually widened the agent's tool permissions.

What the schema says

pkg/parser/schemas/main_workflow_schema.json:

allowWrite — "Array of path patterns that allow write access in the sandboxed environment. Paths outside these patterns are read-only."

What actually happens

Adding this to a workflow that already has a network: block (so the AWF sandbox is auto-enabled):

sandbox:
  agent:
    config:
      filesystem:
        allowWrite:
          - /home/runner/work
          - /tmp

compiles cleanly (0 errors) and produces:

  1. No filesystem section in the generated awf-config.json. The runtime config still contains only network, apiProxy and container. Confirmed by reading awf-config.json from a real run's agent artifact both before and after.

  2. Four additional allowed tools. The only functional change in the compiled lock:

+ # - Edit(/home/runner/work/*)
+ # - MultiEdit(/home/runner/work/*)
+ # - Read(/home/runner/work/*)
+ # - Write(/home/runner/work/*)

Tracing it, pkg/workflow/claude_tools.go → appendSandboxWritableTools consumes AllowWrite solely to append Read(p) / Write(p) / Edit(p) / MultiEdit(p) to the agent's allowed-tools list. Nothing outside the list is denied anywhere.

Why this is worse than a no-op

For the Claude engine the compiled allowed-tools already contain unqualified grants:

Edit
MultiEdit
Read
Write

An unqualified Write permits writing anywhere the process can reach, so path-scoped Write(...) entries cannot narrow it — they are strictly additive. The net effect of following the documentation is a permission set that is the same or broader, presented in the diff as hardening.

Expected

Either:

  • Implement the documented behaviour — emit filesystem into awf-config.json so the AWF sandbox enforces the boundary in-kernel (strongest, and what the description implies), or
  • Correct the schema description to say allowWrite grants path-scoped tool permissions and does not restrict anything, so authors do not mistake it for a boundary.

How we found it

We ran it as a deliberate one-workflow canary before fleet rollout, and inspected the compiled lock and the runtime awf-config.json rather than trusting the compile succeeding. Had we merged on the green compile, we would have shipped a change that reads as a write boundary in review and loosens permissions in fact.

Happy to supply the full before/after lock diff if useful.

Activity

  1. locked and limited conversation to collaborators on Aug 21, 2026
  2. unlocked this conversation on Aug 21, 2026
  3. lpcox commented on Aug 22, 2026

    @lpcox
    Collaborator

    🔗 AWF tracking issue: https://github.com/github/gh-aw-firewall/issues/github/gh-aw-firewall#7616

    Generated by Firewall Issue Dispatcher · auto · 25.3 AIC · ⊞ 9.4K · ◷

  4. pelikhan commented on Aug 24, 2026

    @pelikhan
    Collaborator

    /scout review this issue if fixed completely

  5. github-actions commented on Aug 24, 2026

    @github-actions
    Contributor

    🔭 Recon complete! Scout has charted the territory. Map ready! 🗺️

    🔭 Intelligence gathered by Scout

  6. github-actions commented on Aug 24, 2026

    @github-actions
    Contributor

    🔍 Scout Research Report

    Triggered by @pelikhan — "review this issue if fixed completely"

    Executive Summary

    Partially fixed, not completely. A real fix landed on main, but it only closes the gap for sandbox.agent.runtime: cloud-hypervisor. For every other runtime — including the default, which is what the original report used — the exact behavior described in the issue is unchanged: allowWrite still only widens the Claude tool allowlist and never restricts anything. The one improvement for those runtimes is that the compiler now emits an explicit warning instead of compiling silently.

    Click to expand detailed findings

    Research Findings

    What changed

    • pkg/workflow/awf_config.go now has an awfEmitsFilesystemAllowWrite() gate (pkg/workflow/awf_feature_flags.go:117). When sandbox.agent.runtime: cloud-hypervisor is set and the effective AWF version is ≥ v0.28.6 (constants.AWFCloudHypervisorFilesystemAllowWriteMinVersion), a real filesystem section is now written into awf-config.json, so AWF enforces the write boundary in-kernel — this is the "strongest" fix option the issue asked for.

    • pkg/workflow/claude_tools.go:387 (appendSandboxWritableTools) now also branches on AgentRuntimeCloudHypervisor: for that runtime, allowWrite replaces the writable-path set (narrowing), instead of only appending to it.

    • A new compiler warning was added (pkg/workflow/compiler_validators.go:311, backed by declaresIgnoredFilesystemAllowWrite in pkg/workflow/firewall.go:170): if allowWrite is declared and the runtime is anything other than cloud-hypervisor, the compile now prints:

      "sandbox.agent.config.filesystem.allowWrite is ignored for this runtime and was not written to the AWF config. Only sandbox.agent.runtime: cloud-hypervisor enforces the policy without breaking the agent container..."

      This directly addresses the "0 errors, no signal" complaint from the report for the default/docker/gVisor/docker-sbx runtimes — it's no longer a silent compile.

    What did NOT change (the reported bug still reproduces)

    • The reporter's exact repro (no sandbox.agent.runtime override) still hits the non-cloud-hypervisor path. There, appendSandboxWritableTools still only appends Read(p)/Write(p)/Edit(p)/MultiEdit(p) to the allowed-tools list — additive, never restrictive — and awfEmitsFilesystemAllowWrite still returns false, so no filesystem section is ever written to awf-config.json. The only difference from the original report is the new compiler warning; the compiled lock diff itself (four additive Edit/MultiEdit/Read/Write(path) entries, no enforcement) is identical.
    • The schema description in pkg/parser/schemas/main_workflow_schema.json (both the legacy sandbox.config.filesystem.allowWrite at line 3868 and the sandbox.agent.config.filesystem.allowWrite copy) is unchanged: it still reads "Array of path patterns that allow write access... Paths outside these patterns are read-only" with no mention that this is only true for cloud-hypervisor. docs/src/content/docs/reference/frontmatter-full.md has the same unqualified text. The issue's second suggested remedy ("correct the schema description") was not applied.
    • One residual edge case: declaresIgnoredFilesystemAllowWrite returns false whenever runtime: cloud-hypervisor is set, regardless of AWF version. If a workflow sets cloud-hypervisor but pins an AWF version older than v0.28.6, awfEmitsFilesystemAllowWrite will still skip emitting the filesystem section (per awf_config.go:559-561), but the new warning won't fire for that combination since it only checks the runtime, not the version-gated emit decision. Narrow case, but worth knowing about.

    Recommendations

    • Treat this issue as not fully resolved — keep it open (or scope a follow-up), since the "grants instead of restricts" behavior remains the default for every non-cloud-hypervisor runtime.
    • Update main_workflow_schema.json (both allowWrite descriptions) and frontmatter-full.md to state the restriction only applies under sandbox.agent.runtime: cloud-hypervisor with AWF ≥ v0.28.6; for other runtimes, clarify it only grants scoped tool permissions.
    • Consider escalating the compiler message for non-cloud-hypervisor + allowWrite from a warning to a hard error, since the current behavior is still strictly permission-widening despite reading like hardening in a diff.
    • Make declaresIgnoredFilesystemAllowWrite version-aware so the warning also fires when cloud-hypervisor is set but the AWF version is below v0.28.6.

    Key Sources (in-repo)

    • pkg/workflow/awf_feature_flags.go:117 — awfEmitsFilesystemAllowWrite
    • pkg/workflow/awf_config.go:548-565 — filesystem section emission logic
    • pkg/workflow/claude_tools.go:380-410 — appendSandboxWritableTools
    • pkg/workflow/compiler_validators.go:302-319 — emitSandboxRuntimeWarnings
    • pkg/workflow/firewall.go:166-179 — declaresIgnoredFilesystemAllowWrite
    • pkg/parser/schemas/main_workflow_schema.json:3863-3868 and :3783-3788
    • constants.AWFCloudHypervisorFilesystemAllowWriteMinVersion = v0.28.6 (pkg/constants/version_constants.go:155)

    Suggested Next Steps

    1. Decide whether to keep this issue open for the schema/docs correction and the non-cloud-hypervisor default behavior, or file a narrower follow-up issue scoped to just those two gaps.
    2. If cloud-hypervisor-only enforcement is the intended long-term answer, prioritize the doc fix so authors on other runtimes stop reading allowWrite as a boundary.

    🔭 Intelligence gathered by Scout · claude · agent · 113.3 AIC · ⌖ 14.8 AIC · ⊞ 7.4K · ◷
    Comment /scout to run again

  7. pelikhan commented on Aug 25, 2026

    @pelikhan
    Collaborator

    @lpcox fyi

  8. lpcox commented on Aug 27, 2026

    @lpcox
    Collaborator

    Addressed by github/gh-aw-firewall#7634, which enforces filesystem.allowWrite boundaries in AWF sandboxes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions