Skip to content

feat(core,cli): M3.5 — sandbox subsystem (macOS sandbox-exec + Linux bwrap)#13

Merged
oratis merged 1 commit into
mainfrom
feat/m3.5-sandbox-macos
May 28, 2026
Merged

feat(core,cli): M3.5 — sandbox subsystem (macOS sandbox-exec + Linux bwrap)#13
oratis merged 1 commit into
mainfrom
feat/m3.5-sandbox-macos

Conversation

@oratis

@oratis oratis commented May 28, 2026

Copy link
Copy Markdown
Owner

Summary

  • macOS sandbox-exec SBPL profile generator with deny-default + system reads + allow/deny path handling
  • Linux bwrap arg generator with system ro mounts + cwd rw + namespace unshare
  • wrapBashCommand() opt-in via settings.sandbox.enabled; honors excludedCommands allowlist
  • Wired end-to-end: tool ctx → agent loop → REPL → settings.json
  • 17 new tests, 308 + 8 skipped total

Deferred

  • Adversarial e2e attack-vector test suite (separate PR)
  • Userspace DNS proxy for fine-grained network policy

Release notes

  • release-notes:feature

Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com

…bwrap)

What ships
----------
- packages/core/src/sandbox/profile.ts
  · detectPlatform() → 'macos' | 'linux' | 'unsupported'
  · buildMacOsProfile(config, cwd) — generates SBPL text:
    - (deny default) baseline
    - allow system reads (/usr /System /Library /private/etc /dev /bin ...)
    - allow allowRead paths (with ~ expansion + SBPL escaping)
    - allow allowWrite paths
    - deny denyRead / denyWrite paths (appended after allows so deny wins)
    - net allow* by default; allowUnixSockets opt-in
  · buildLinuxBwrapArgs(config, cwd) — generates bwrap argv:
    - --ro-bind-try for system dirs
    - --bind for cwd
    - --unshare-pid --unshare-ipc --unshare-uts always
    - --unshare-net iff allowedDomains is explicit empty array

- packages/core/src/sandbox/index.ts
  · wrapBashCommand({ userCommand, cwd, config }) → { command, args }
    Returns sandbox-exec / bwrap wrapping, or /bin/sh unwrapped when:
      · config.enabled is false
      · platform unsupported (Windows)
      · userCommand starts with an excludedCommands entry

- packages/core/src/tools/bash.ts
  · Reads ctx.sandboxConfig (typed in ToolContext)
  · Wraps every Bash invocation under platform sandbox if configured

- packages/core/src/agent.ts
  · runAgent now accepts opts.sandboxConfig; plumbed into ToolContext

- apps/cli/src/repl.ts
  · Passes settings.sandbox to runAgent — fully wired end-to-end

Tests (17 new, 316 total / 308 + 8 skipped)
-------------------------------------------
- sandbox/profile.test.ts (11): platform detect, disabled→empty, system reads,
  allow/deny ordering, SBPL escaping, unix-socket opt-in, bwrap binds + cwd
  rw + unshares + conditional --unshare-net
- sandbox/index.test.ts (6): disabled passthrough, no-config passthrough,
  excludedCommands bypass (prefix + exact), platform-conditional wrapping

Verified
--------
  pnpm typecheck    → green
  pnpm test         → 308 passed / 8 skipped / 0 failed (was 297)
  pnpm format:check → conformant

Deferred (per plan §6 / docs/design/sandbox-plan-worktree.md)
--------------
- Adversarial e2e test suite (fs-traverse / net-exfil / privilege-escalation
  / sandbox-escape fuzzing) — M3.5-attack-suite separate PR
- Userspace DNS proxy for fine-grained allowedDomains enforcement
- docs/security-model.md (threat model + defense coverage)

What this ships safely
----------------------
The sandbox wrapper is opt-in via settings.sandbox.enabled. Default deepcode
behavior (no settings.sandbox) is unchanged — Bash still runs unwrapped on
/bin/sh. Users who enable sandbox get default-deny SBPL on macOS and bwrap on
Linux. The hardening surface (which paths/domains to allow/deny) is theirs to
tune; we don't ship pre-canned policies.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@oratis oratis merged commit 7dba3c8 into main May 28, 2026
@oratis oratis deleted the feat/m3.5-sandbox-macos branch May 28, 2026 04:42
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