返回 CodeWhale
SANDBOX.md
根目录 / docs / SANDBOX.md
1 # Sandbox threat model
2
3 Codewhale can launch shell commands proposed by a model. Approval policy,
4 workspace-aware tools, and an operating-system command wrapper are separate
5 controls: an approval is not a sandbox, and selecting `workspace-write` does
6 not prove that the current platform has an OS wrapper available.
7
8 This document describes only behavior wired into the command execution path.
9 See [Authorization order](AUTHORIZATION_ORDER.md) for the policy layers that
10 run before execution reaches this boundary.
11
12 ## Platform overview
13
14 | Mechanism | Platform | Selection | What Codewhale reports |
15 |---|---|---|---|
16 | Seatbelt (`sandbox-exec`) | macOS | Automatic when the runtime probe succeeds | `macos-seatbelt` |
17 | Bubblewrap (`/usr/bin/bwrap`) | Linux | `prefer_bwrap = true` and the file is executable | `linux-bwrap` |
18 | No OS wrapper | Linux without usable opt-in bwrap | Default | `none` |
19 | No OS wrapper | Windows | Current implementation | `none` |
20 | OpenSandbox-compatible service | Any supported host | `sandbox_backend = "opensandbox"` | External execution path |
21
22 The repository contains a seccomp implementation module plus a future Windows
23 helper contract. They are not wired into child-command launch, so Codewhale
24 does not advertise them as active sandboxes. Source-only sandbox code is not
25 evidence that a command was restricted.
26
27 ## macOS: Seatbelt
28
29 Codewhale probes `/usr/bin/sandbox-exec` by running a minimal profile. When the
30 probe succeeds and the selected `SandboxPolicy` requests a sandbox, the child
31 command is wrapped with a generated Seatbelt profile.
32
33 The profile can provide:
34
35 - broad filesystem reads;
36 - writes limited by the selected policy, including the workspace and specific
37 runtime/cache paths needed by supported tools;
38 - network access only when the policy enables it.
39
40 If the probe fails or `sandbox-exec` is unavailable, Codewhale reports no OS
41 sandbox and launches the command without a Seatbelt wrapper. It does not set a
42 Seatbelt marker on that fallback.
43
44 ## Linux: opt-in bubblewrap
45
46 Linux command sandboxing is opt-in. Set the top-level configuration key:
47
48 ```toml
49 prefer_bwrap = true
50 ```
51
52 Codewhale selects bubblewrap only when `/usr/bin/bwrap` is a regular executable
53 file. The wrapper derives its mounts and network namespace from the resolved
54 `SandboxPolicy`:
55
56 ```text
57 /usr/bin/bwrap \
58 --unshare-all \
59 [--share-net] \
60 --ro-bind / / \
61 --bind <writable-root> <writable-root> ... \
62 --ro-bind <protected-descendant> <protected-descendant> ... \
63 --chdir <cwd> \
64 -- <program> <args>
65 ```
66
67 That gives the child a read-only root view. For `workspace-write`, every safe,
68 existing policy root is mounted read-write: the working directory, configured
69 additional roots, `/tmp` and `TMPDIR` unless excluded, and verified Git
70 worktree metadata roots. Existing `.codewhale` and `.deepseek` descendants are
71 remounted read-only after their writable parent. Missing paths, non-directory
72 paths, and `/` are not promoted to writable mounts.
73
74 For `read-only`, there are no writable binds, so the working directory remains
75 inside the read-only root view. `--unshare-all` isolates the network namespace
76 by default. Codewhale adds `--share-net` only when the policy's
77 `network_access` is true. `danger-full-access` and `external-sandbox` bypass the
78 local wrapper entirely.
79
80 If the user does not opt in, or `/usr/bin/bwrap` is missing or non-executable,
81 Codewhale reports `none` and launches the command without a Linux OS wrapper.
82 There is no marker-only fallback to a different Linux sandbox.
83
84 Install bubblewrap separately when this opt-in fits the workflow:
85
86 - Ubuntu/Debian: `apt install bubblewrap`
87 - Fedora: `dnf install bubblewrap`
88 - Arch: `pacman -S bubblewrap`
89
90 Codewhale does not vendor bubblewrap.
91
92 ## Windows: no advertised OS sandbox
93
94 The Windows command path currently reports no OS sandbox. The source tree has
95 a future helper contract for Job Object process-tree cleanup, but it is not
96 wired into selection and must not be described as any of the following:
97
98 - read-only filesystem or workspace-write enforcement;
99 - network blocking;
100 - registry isolation;
101 - restricted-token or AppContainer isolation.
102
103 Windows host permissions and approval policy still apply, but they are not a
104 Codewhale OS command sandbox.
105
106 ## Linux process hardening is not a command sandbox
107
108 At startup on Linux, Codewhale best-effort applies `PR_SET_DUMPABLE=0`,
109 `PR_SET_NO_NEW_PRIVS=1`, and `RLIMIT_CORE=0` to its own process. Each failure is
110 logged and startup continues. These controls reduce process-inspection,
111 privilege-escalation, and core-dump risk; they do not create filesystem or
112 network isolation for a child command and are not listed as a sandbox backend.
113
114 ## External OpenSandbox execution
115
116 When `sandbox_backend = "opensandbox"` is configured, shell execution is sent
117 to the configured OpenSandbox-compatible HTTP endpoint instead of starting a
118 local child. Codewhale validates the request/response contract, but isolation
119 guarantees belong to the configured service and its operator.
120
121 ```toml
122 sandbox_backend = "opensandbox"
123 sandbox_url = "http://localhost:8080"
124 sandbox_api_key = "YOUR_API_KEY"
125 ```
126
127 `sandbox_backend = "none"` (or omitting the key) keeps local execution.
128
129 ## Policies and fallbacks
130
131 The local `sandbox_mode` values are:
132
133 ```toml
134 sandbox_mode = "workspace-write" # read-only | workspace-write | danger-full-access | external-sandbox
135 ```
136
137 - `read-only` and `workspace-write` are enforced by Seatbelt or bubblewrap only
138 when that wrapper is selected and available.
139 - `danger-full-access` deliberately bypasses the local OS wrapper.
140 - `external-sandbox` declares that execution is already externally isolated
141 and bypasses a second local wrapper.
142 - When no wrapper is selected, the shell command runs without Codewhale OS
143 isolation. Approval rules and workspace-aware native file tools remain
144 separate controls.
145
146 Canonical environment overrides exist for `sandbox_mode` and the external
147 backend:
148
149 - `CODEWHALE_SANDBOX_MODE`
150 - `CODEWHALE_SANDBOX_BACKEND`
151 - `CODEWHALE_SANDBOX_URL`
152 - `CODEWHALE_SANDBOX_API_KEY`
153
154 There is no `CODEWHALE_PREFER_BWRAP` environment override; use the top-level
155 `prefer_bwrap` config key.
156
157 ## Diagnostics and failure attribution
158
159 `codewhale setup --status`, `codewhale doctor`, `codewhale doctor --json`, and
160 the `diagnostics` tool report the locally available wrapper after applying the
161 resolved bubblewrap preference. An individual command can still bypass that
162 wrapper when its policy does not request sandboxing. On Linux, merely finding
163 a sandbox-related syscall or source module does not make `sandbox_available`
164 true.
165
166 Denial attribution is intentionally conservative:
167
168 - Seatbelt uses its wrapper-specific denial patterns.
169 - Bubblewrap setup errors must be prefixed by `bwrap:`; a read-only-filesystem
170 error from the bwrap filesystem view can also identify the boundary.
171 - A child command's generic `Permission denied` or `Operation not permitted`
172 is not, by itself, proof that Codewhale's sandbox blocked it.
173 - Unsandboxed command failures are never labeled sandbox denials.
174
175 ## Limitations
176
177 - Availability is checked before launch; the selected wrapper can still fail
178 because of host policy, container restrictions, or a race after the probe.
179 - Bubblewrap ignores a configured writable root if it is missing, is not a
180 directory, or canonicalizes to `/`; a path can also disappear between policy
181 resolution and wrapper launch.
182 - Seatbelt profiles are generated at runtime and must be tested against the
183 commands they are expected to support.
184 - No current local wrapper is advertised on Windows.
185 - An external sandbox backend is only as strong as its configured service.
186 - No sandbox protects against kernel vulnerabilities or all resource-exhaustion
187 and side-channel attacks.
188
189 ## Implementation references
190
191 - `crates/tui/src/sandbox/mod.rs` — truthful selection and public capability markers
192 - `crates/tui/src/sandbox/seatbelt.rs` — macOS wrapper and availability probe
193 - `crates/tui/src/sandbox/bwrap.rs` — Linux opt-in wrapper
194 - `crates/tui/src/sandbox/process_hardening.rs` — Linux parent-process hardening
195 - `crates/tui/src/sandbox/backend.rs` — external backend selection
196 - `crates/tui/src/tools/diagnostics.rs` — machine-readable diagnostics
197
197 lines MARKDOWN