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