Skip to content

allowedpaths: relax sandbox to permit write opens (remediation series PR B)#525

Merged
julesmcrt merged 5 commits into
mainfrom
jules.macret/sandbox-write-capability
Jun 15, 2026
Merged

allowedpaths: relax sandbox to permit write opens (remediation series PR B)#525
julesmcrt merged 5 commits into
mainfrom
jules.macret/sandbox-write-capability

Conversation

@julesmcrt

Copy link
Copy Markdown
Collaborator

Summary

  • Adds allowedOpenFlags const (O_RDONLY|O_WRONLY|O_RDWR|O_APPEND|O_CREATE|O_EXCL|O_TRUNC) to allowedpaths/sandbox.go
  • Changes Sandbox.Open to reject unknown flag bits (instead of blanket non-O_RDONLY rejection), allowing write opens inside the allowlist
  • Cross-root symlink fallback remains read-only to prevent TOCTOU attacks on writes
  • New tests: write to allowed path, write outside allowed path, append, truncate, symlink-escape rejected for writes, unknown flag rejected, read still works
  • Updates analysis/symbols_allowedpaths.go with the new OS flag constants
  • Updates README.md, SHELL_FEATURES.md, and interp/api.go doc comment to reflect that both reads and writes are now sandboxed

Context

This is PR B of the remediation mode series. PR A (#524) added the RemediationMode() option to interp/api.go.

The sandbox is now write-capable, but nothing in the interpreter uses it yet — output redirections (>, >>) remain blocked at parse time. Wiring write opens into shell features (redirections, builtins) is PR C.

Test plan

  • go test ./allowedpaths/... ./interp/... -timeout 120s passes
  • New sandbox write tests cover: create, append, truncate, symlink-escape rejection, unknown flag rejection, read still works
  • No interpreter-level behavior changes — existing scenario tests unaffected

🤖 Generated with Claude Code

@datadog-prod-us1-3

This comment has been minimized.

Comment thread allowedpaths/sandbox.go
@julesmcrt julesmcrt added the verified/analysis Human-reviewed static analysis changes label Jun 12, 2026
@AlexandreYang
AlexandreYang self-requested a review June 12, 2026 12:24
Comment thread allowedpaths/sandbox.go
Comment thread allowedpaths/sandbox.go Outdated
@julesmcrt
julesmcrt force-pushed the jules.macret/sandbox-write-capability branch from a7ddd58 to a8279f1 Compare June 15, 2026 11:54
julesmcrt added a commit that referenced this pull request Jun 15, 2026
Address two reviewer comments on PR #525:

1. "pass remediation mode state to the Sandbox" — adds a readOnly bool
   field to Sandbox (default true). Open rejects write flags in read-only
   mode as defense-in-depth, so even a mistaken interpreter call in
   read-only mode is caught at the sandbox boundary. SetWritable() switches
   the sandbox into write-permitted mode; the interpreter will call this
   when RemediationMode is active (PR C).

2. "check to only keep minimal needed" — trims allowedOpenFlags to the
   exact set needed for shell redirections (O_RDONLY, O_WRONLY, O_APPEND,
   O_CREATE, O_TRUNC). O_RDWR and O_EXCL are not needed and are now
   rejected. A separate writeOpenFlags const names the write-implying
   subset for the readOnly check.

Tests: all write tests now call sb.SetWritable() first; new
TestSandboxDefaultReadOnly verifies the default sandbox blocks all write
flag combinations.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
julesmcrt and others added 4 commits June 15, 2026 15:52
…n series)

The sandbox previously rejected every non-O_RDONLY open as a blanket
defense. This change replaces that with a precise flag-mask check:
only bits in allowedOpenFlags (O_RDONLY|O_WRONLY|O_RDWR|O_APPEND|
O_CREATE|O_EXCL|O_TRUNC) are accepted; unknown bits still return
ErrPermission. Write opens through os.Root are now allowed for paths
inside the allowlist.

TOCTOU defense: the cross-root symlink fallback continues to be
read-only. A symlink that escapes its os.Root is followed for reads
but rejected for writes, preventing a malicious link target from being
swapped between resolution and open.

Nothing in the interpreter uses write opens yet — output redirections
are still blocked at parse time. This is a pure capability layer
change; wiring is PR C.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
RemediationMode, ModeRemediation, remediationMode, and the --mode CLI
flag help text all still said "inert in this release; behavior is wired
in follow-up PRs." PR C wires file-target output redirections (>, >>)
within AllowedPaths — update all three sites to describe what is now
actually enabled.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Address two reviewer comments on PR #525:

1. "pass remediation mode state to the Sandbox" — adds a readOnly bool
   field to Sandbox (default true). Open rejects write flags in read-only
   mode as defense-in-depth, so even a mistaken interpreter call in
   read-only mode is caught at the sandbox boundary. SetWritable() switches
   the sandbox into write-permitted mode; the interpreter will call this
   when RemediationMode is active (PR C).

2. "check to only keep minimal needed" — trims allowedOpenFlags to the
   exact set needed for shell redirections (O_RDONLY, O_WRONLY, O_APPEND,
   O_CREATE, O_TRUNC). O_RDWR and O_EXCL are not needed and are now
   rejected. A separate writeOpenFlags const names the write-implying
   subset for the readOnly check.

Tests: all write tests now call sb.SetWritable() first; new
TestSandboxDefaultReadOnly verifies the default sandbox blocks all write
flag combinations.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
The new s.readOnly field access in Open was a nil pointer dereference
when the interpreter is constructed without AllowedPaths (nil sandbox).
The open handler always calls sandbox.Open via a closure, so a nil
receiver reaches Open on any file open attempt in that configuration.
The old code only reached s.resolve() which already had a nil guard;
the new readOnly check before it had none.

Add an explicit nil guard at the top of Open, matching the pattern
established by resolve(). Nil sandbox returns ErrPermission, same as
before (resolve returned false → ErrPermission).

Fixes TestFeatureProbesMatchHelp/pipes-redirections/supported/Input_redirection.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
@julesmcrt
julesmcrt force-pushed the jules.macret/sandbox-write-capability branch from 3f9f2cb to db47ea4 Compare June 15, 2026 14:02
PR #527 moved the command count from the top-level header line into the
Commands section header ("Commands (N of M enabled):"). Update the four
help scenario files that still checked for the old "commands available"
and "commands enabled" header strings.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
@julesmcrt
julesmcrt added this pull request to the merge queue Jun 15, 2026
Merged via the queue into main with commit 533adb5 Jun 15, 2026
40 checks passed
@julesmcrt
julesmcrt deleted the jules.macret/sandbox-write-capability branch June 15, 2026 14:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

verified/analysis Human-reviewed static analysis changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants