@carderne/sandbox-runtime 0.0.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +684 -0
  3. package/dist/cli.d.ts +3 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +163 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/index.d.ts +11 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +9 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/sandbox/generate-seccomp-filter.d.ts +71 -0
  12. package/dist/sandbox/generate-seccomp-filter.d.ts.map +1 -0
  13. package/dist/sandbox/generate-seccomp-filter.js +263 -0
  14. package/dist/sandbox/generate-seccomp-filter.js.map +1 -0
  15. package/dist/sandbox/http-proxy.d.ts +13 -0
  16. package/dist/sandbox/http-proxy.d.ts.map +1 -0
  17. package/dist/sandbox/http-proxy.js +217 -0
  18. package/dist/sandbox/http-proxy.js.map +1 -0
  19. package/dist/sandbox/linux-sandbox-utils.d.ts +158 -0
  20. package/dist/sandbox/linux-sandbox-utils.d.ts.map +1 -0
  21. package/dist/sandbox/linux-sandbox-utils.js +875 -0
  22. package/dist/sandbox/linux-sandbox-utils.js.map +1 -0
  23. package/dist/sandbox/macos-sandbox-utils.d.ts +40 -0
  24. package/dist/sandbox/macos-sandbox-utils.d.ts.map +1 -0
  25. package/dist/sandbox/macos-sandbox-utils.js +623 -0
  26. package/dist/sandbox/macos-sandbox-utils.js.map +1 -0
  27. package/dist/sandbox/sandbox-config.d.ts +288 -0
  28. package/dist/sandbox/sandbox-config.d.ts.map +1 -0
  29. package/dist/sandbox/sandbox-config.js +178 -0
  30. package/dist/sandbox/sandbox-config.js.map +1 -0
  31. package/dist/sandbox/sandbox-manager.d.ts +42 -0
  32. package/dist/sandbox/sandbox-manager.d.ts.map +1 -0
  33. package/dist/sandbox/sandbox-manager.js +786 -0
  34. package/dist/sandbox/sandbox-manager.js.map +1 -0
  35. package/dist/sandbox/sandbox-schemas.d.ts +57 -0
  36. package/dist/sandbox/sandbox-schemas.d.ts.map +1 -0
  37. package/dist/sandbox/sandbox-schemas.js +3 -0
  38. package/dist/sandbox/sandbox-schemas.js.map +1 -0
  39. package/dist/sandbox/sandbox-utils.d.ts +109 -0
  40. package/dist/sandbox/sandbox-utils.d.ts.map +1 -0
  41. package/dist/sandbox/sandbox-utils.js +429 -0
  42. package/dist/sandbox/sandbox-utils.js.map +1 -0
  43. package/dist/sandbox/sandbox-violation-store.d.ts +19 -0
  44. package/dist/sandbox/sandbox-violation-store.d.ts.map +1 -0
  45. package/dist/sandbox/sandbox-violation-store.js +54 -0
  46. package/dist/sandbox/sandbox-violation-store.js.map +1 -0
  47. package/dist/sandbox/socks-proxy.d.ts +13 -0
  48. package/dist/sandbox/socks-proxy.d.ts.map +1 -0
  49. package/dist/sandbox/socks-proxy.js +95 -0
  50. package/dist/sandbox/socks-proxy.js.map +1 -0
  51. package/dist/utils/config-loader.d.ts +11 -0
  52. package/dist/utils/config-loader.d.ts.map +1 -0
  53. package/dist/utils/config-loader.js +60 -0
  54. package/dist/utils/config-loader.js.map +1 -0
  55. package/dist/utils/debug.d.ts +7 -0
  56. package/dist/utils/debug.d.ts.map +1 -0
  57. package/dist/utils/debug.js +25 -0
  58. package/dist/utils/debug.js.map +1 -0
  59. package/dist/utils/platform.d.ts +15 -0
  60. package/dist/utils/platform.d.ts.map +1 -0
  61. package/dist/utils/platform.js +49 -0
  62. package/dist/utils/platform.js.map +1 -0
  63. package/dist/utils/ripgrep.d.ts +20 -0
  64. package/dist/utils/ripgrep.d.ts.map +1 -0
  65. package/dist/utils/ripgrep.js +42 -0
  66. package/dist/utils/ripgrep.js.map +1 -0
  67. package/dist/utils/which.d.ts +9 -0
  68. package/dist/utils/which.d.ts.map +1 -0
  69. package/dist/utils/which.js +25 -0
  70. package/dist/utils/which.js.map +1 -0
  71. package/dist/vendor/seccomp/arm64/apply-seccomp +0 -0
  72. package/dist/vendor/seccomp/arm64/unix-block.bpf +0 -0
  73. package/dist/vendor/seccomp/x64/apply-seccomp +0 -0
  74. package/dist/vendor/seccomp/x64/unix-block.bpf +0 -0
  75. package/dist/vendor/seccomp-src/apply-seccomp.c +98 -0
  76. package/dist/vendor/seccomp-src/seccomp-unix-block.c +97 -0
  77. package/package.json +88 -0
  78. package/vendor/seccomp/arm64/apply-seccomp +0 -0
  79. package/vendor/seccomp/arm64/unix-block.bpf +0 -0
  80. package/vendor/seccomp/x64/apply-seccomp +0 -0
  81. package/vendor/seccomp/x64/unix-block.bpf +0 -0
  82. package/vendor/seccomp-src/apply-seccomp.c +98 -0
  83. package/vendor/seccomp-src/seccomp-unix-block.c +97 -0
package/README.md ADDED
@@ -0,0 +1,684 @@
1
+ # Anthropic Sandbox Runtime (srt)
2
+
3
+ ## Forked from [anthropic-experimental/sandbox-runtime](https://github.com/anthropic-experimental/sandbox-runtime) with two changes:
4
+
5
+ - **`allowRead` config option**: A new optional `allowRead` field re-allows read access within regions blocked by `denyRead`. `allowRead` takes precedence over `denyRead` (intentionally the opposite of write, where `denyWrite` takes precedence over `allowWrite`). This enables workspace-only access patterns like `denyRead: ["/Users"], allowRead: ["."]` without breaking system paths. The field is optional — existing configs work identically.
6
+
7
+ - **`enableWeakerNetworkIsolation` now allows `configd` access**: When `enableWeakerNetworkIsolation` is enabled, the macOS sandbox now also permits mach-lookup to `com.apple.SystemConfiguration.configd`. This is needed for Rust/Go programs (e.g. `uv`, `cargo`) that query system proxy/network configuration via `SCDynamicStoreCreate` on startup. The service is read-only (exposes proxy settings, DNS servers, interface info) but cannot modify system settings. **Security note:** this exposes host network configuration to the sandboxed process.
8
+
9
+ ## Introduction
10
+
11
+ A lightweight sandboxing tool for enforcing filesystem and network restrictions on arbitrary processes at the OS level, without requiring a container.
12
+
13
+ `srt` uses native OS sandboxing primitives (`sandbox-exec` on macOS, `bubblewrap` on Linux) and proxy-based network filtering. It can be used to sandbox the behaviour of agents, local MCP servers, bash commands and arbitrary processes.
14
+
15
+ > **Beta Research Preview**
16
+ >
17
+ > The Sandbox Runtime is a research preview developed for [Claude Code](https://www.claude.com/product/claude-code) to enable safer AI agents. It's being made available as an early open source preview to help the broader ecosystem build more secure agentic systems. As this is an early research preview, APIs and configuration formats may evolve. We welcome feedback and contributions to make AI agents safer by default!
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ npm install -g @anthropic-ai/sandbox-runtime
23
+ ```
24
+
25
+ ## Basic Usage
26
+
27
+ ```bash
28
+ # Network restrictions
29
+ $ srt "curl anthropic.com"
30
+ Running: curl anthropic.com
31
+ <html>...</html> # Request succeeds
32
+
33
+ $ srt "curl example.com"
34
+ Running: curl example.com
35
+ Connection blocked by network allowlist # Request blocked
36
+
37
+ # Filesystem restrictions
38
+ $ srt "cat README.md"
39
+ Running: cat README.md
40
+ # Anthropic Sandb... # Current directory access allowed
41
+
42
+ $ srt "cat ~/.ssh/id_rsa"
43
+ Running: cat ~/.ssh/id_rsa
44
+ cat: /Users/ollie/.ssh/id_rsa: Operation not permitted # Specific file blocked
45
+ ```
46
+
47
+ ## Overview
48
+
49
+ This package provides a standalone sandbox implementation that can be used as both a CLI tool and a library. It's designed with a **secure-by-default** philosophy tailored for common developer use cases: processes start with minimal access, and you explicitly poke only the holes you need.
50
+
51
+ **Key capabilities:**
52
+
53
+ - **Network restrictions**: Control which hosts/domains can be accessed via HTTP/HTTPS and other protocols
54
+ - **Filesystem restrictions**: Control which files/directories can be read/written
55
+ - **Unix socket restrictions**: Control access to local IPC sockets
56
+ - **Violation monitoring**: On macOS, tap into the system's sandbox violation log store for real-time alerts
57
+
58
+ ### Example Use Case: Sandboxing MCP Servers
59
+
60
+ A key use case is sandboxing Model Context Protocol (MCP) servers to restrict their capabilities. For example, to sandbox the filesystem MCP server:
61
+
62
+ **Without sandboxing** (`.mcp.json`):
63
+
64
+ ```json
65
+ {
66
+ "mcpServers": {
67
+ "filesystem": {
68
+ "command": "npx",
69
+ "args": ["-y", "@modelcontextprotocol/server-filesystem"]
70
+ }
71
+ }
72
+ }
73
+ ```
74
+
75
+ **With sandboxing** (`.mcp.json`):
76
+
77
+ ```json
78
+ {
79
+ "mcpServers": {
80
+ "filesystem": {
81
+ "command": "srt",
82
+ "args": ["npx", "-y", "@modelcontextprotocol/server-filesystem"]
83
+ }
84
+ }
85
+ }
86
+ ```
87
+
88
+ Then configure restrictions in `~/.srt-settings.json`:
89
+
90
+ ```json
91
+ {
92
+ "filesystem": {
93
+ "denyRead": [],
94
+ "allowWrite": ["."],
95
+ "denyWrite": ["~/sensitive-folder"]
96
+ },
97
+ "network": {
98
+ "allowedDomains": [],
99
+ "deniedDomains": []
100
+ }
101
+ }
102
+ ```
103
+
104
+ Now the MCP server will be blocked from writing to the denied path:
105
+
106
+ ```
107
+ > Write a file to ~/sensitive-folder
108
+ ✗ Error: EPERM: operation not permitted, open '/Users/ollie/sensitive-folder/test.txt'
109
+ ```
110
+
111
+ ## How It Works
112
+
113
+ The sandbox uses OS-level primitives to enforce restrictions that apply to the entire process tree:
114
+
115
+ - **macOS**: Uses `sandbox-exec` with dynamically generated [Seatbelt profiles](https://reverse.put.as/wp-content/uploads/2011/09/Apple-Sandbox-Guide-v1.0.pdf)
116
+ - **Linux**: Uses [bubblewrap](https://github.com/containers/bubblewrap) for containerization with network namespace isolation
117
+
118
+ ![0d1c612947c798aef48e6ab4beb7e8544da9d41a-4096x2305](https://github.com/user-attachments/assets/76c838a9-19ef-4d0b-90bb-cbe1917b3551)
119
+
120
+ ### Dual Isolation Model
121
+
122
+ Both filesystem and network isolation are required for effective sandboxing. Without file isolation, a compromised process could exfiltrate SSH keys or other sensitive files. Without network isolation, a process could escape the sandbox and gain unrestricted network access.
123
+
124
+ **Filesystem Isolation** enforces read and write restrictions:
125
+
126
+ - **Read** (deny-then-allow pattern): By default, read access is allowed everywhere. You can deny broad regions (e.g., `/Users`) and then re-allow specific paths within them (e.g., `.`). `allowRead` takes precedence over `denyRead` — the opposite of write, where `denyWrite` takes precedence over `allowWrite`.
127
+ - **Write** (allow-only pattern): By default, write access is denied everywhere. You must explicitly allow paths (e.g., `.`, `/tmp`). An empty allow list means no write access.
128
+
129
+ **Network Isolation** (allow-only pattern): By default, all network access is denied. You must explicitly allow domains. An empty allowedDomains list means no network access. Network traffic is routed through proxy servers running on the host:
130
+
131
+ - **Linux**: Requests are routed via the filesystem over a Unix domain socket. The network namespace of the sandboxed process is removed entirely, so all network traffic must go through the proxies running on the host (listening on Unix sockets that are bind-mounted into the sandbox)
132
+
133
+ - **macOS**: The Seatbelt profile allows communication only to a specific localhost port. The proxies listen on this port, creating a controlled channel for all network access
134
+
135
+ Both HTTP/HTTPS (via HTTP proxy) and other TCP traffic (via SOCKS5 proxy) are mediated by these proxies, which enforce your domain allowlists and denylists.
136
+
137
+ For more details on sandboxing in Claude Code, see:
138
+
139
+ - [Claude Code Sandboxing Documentation](https://docs.claude.com/en/docs/claude-code/sandboxing)
140
+ - [Beyond Permission Prompts: Making Claude Code More Secure and Autonomous](https://www.anthropic.com/engineering/claude-code-sandboxing)
141
+
142
+ ## Architecture
143
+
144
+ ```
145
+ src/
146
+ ├── index.ts # Library exports
147
+ ├── cli.ts # CLI entrypoint (srt command)
148
+ ├── utils/ # Shared utilities
149
+ │ ├── debug.ts # Debug logging
150
+ │ ├── settings.ts # Settings reader (permissions + sandbox config)
151
+ │ ├── platform.ts # Platform detection
152
+ │ └── exec.ts # Command execution utilities
153
+ └── sandbox/ # Sandbox implementation
154
+ ├── sandbox-manager.ts # Main sandbox manager
155
+ ├── sandbox-schemas.ts # Zod schemas for validation
156
+ ├── sandbox-violation-store.ts # Violation tracking
157
+ ├── sandbox-utils.ts # Shared sandbox utilities
158
+ ├── http-proxy.ts # HTTP/HTTPS proxy for network filtering
159
+ ├── socks-proxy.ts # SOCKS5 proxy for network filtering
160
+ ├── linux-sandbox-utils.ts # Linux bubblewrap sandboxing
161
+ └── macos-sandbox-utils.ts # macOS sandbox-exec sandboxing
162
+ ```
163
+
164
+ ## Usage
165
+
166
+ ### As a CLI tool
167
+
168
+ The `srt` command (Anthropic Sandbox Runtime) wraps any command with security boundaries:
169
+
170
+ ```bash
171
+ # Run a command in the sandbox
172
+ srt echo "hello world"
173
+
174
+ # With debug logging
175
+ srt --debug curl https://example.com
176
+
177
+ # Specify custom settings file
178
+ srt --settings /path/to/srt-settings.json npm install
179
+ ```
180
+
181
+ ### As a library
182
+
183
+ ```typescript
184
+ import {
185
+ SandboxManager,
186
+ type SandboxRuntimeConfig,
187
+ } from '@anthropic-ai/sandbox-runtime'
188
+ import { spawn } from 'child_process'
189
+
190
+ // Define your sandbox configuration
191
+ const config: SandboxRuntimeConfig = {
192
+ network: {
193
+ allowedDomains: ['example.com', 'api.github.com'],
194
+ deniedDomains: [],
195
+ },
196
+ filesystem: {
197
+ denyRead: ['~/.ssh'],
198
+ allowWrite: ['.', '/tmp'],
199
+ denyWrite: ['.env'],
200
+ },
201
+ }
202
+
203
+ // Initialize the sandbox (starts proxy servers, etc.)
204
+ await SandboxManager.initialize(config)
205
+
206
+ // Wrap a command with sandbox restrictions
207
+ const sandboxedCommand = await SandboxManager.wrapWithSandbox(
208
+ 'curl https://example.com',
209
+ )
210
+
211
+ // Execute the sandboxed command
212
+ const child = spawn(sandboxedCommand, { shell: true, stdio: 'inherit' })
213
+
214
+ // Handle exit and cleanup after child process completes
215
+ child.on('exit', async code => {
216
+ console.log(`Command exited with code ${code}`)
217
+ // Cleanup when done (optional, happens automatically on process exit)
218
+ await SandboxManager.reset()
219
+ })
220
+ ```
221
+
222
+ #### Available exports
223
+
224
+ ```typescript
225
+ // Main sandbox manager
226
+ export { SandboxManager } from '@anthropic-ai/sandbox-runtime'
227
+
228
+ // Violation tracking
229
+ export { SandboxViolationStore } from '@anthropic-ai/sandbox-runtime'
230
+
231
+ // TypeScript types
232
+ export type {
233
+ SandboxRuntimeConfig,
234
+ NetworkConfig,
235
+ FilesystemConfig,
236
+ IgnoreViolationsConfig,
237
+ SandboxAskCallback,
238
+ FsReadRestrictionConfig,
239
+ FsWriteRestrictionConfig,
240
+ NetworkRestrictionConfig,
241
+ } from '@anthropic-ai/sandbox-runtime'
242
+ ```
243
+
244
+ ## Configuration
245
+
246
+ ### Settings File Location
247
+
248
+ By default, the sandbox runtime looks for configuration at `~/.srt-settings.json`. You can specify a custom path using the `--settings` flag:
249
+
250
+ ```bash
251
+ srt --settings /path/to/srt-settings.json <command>
252
+ ```
253
+
254
+ ### Complete Configuration Example
255
+
256
+ ```json
257
+ {
258
+ "network": {
259
+ "allowedDomains": [
260
+ "github.com",
261
+ "*.github.com",
262
+ "lfs.github.com",
263
+ "api.github.com",
264
+ "npmjs.org",
265
+ "*.npmjs.org"
266
+ ],
267
+ "deniedDomains": ["malicious.com"],
268
+ "allowUnixSockets": ["/var/run/docker.sock"],
269
+ "allowLocalBinding": false
270
+ },
271
+ "filesystem": {
272
+ "denyRead": ["~/.ssh"],
273
+ "allowRead": [],
274
+ "allowWrite": [".", "src/", "test/", "/tmp"],
275
+ "denyWrite": [".env", "config/production.json"]
276
+ },
277
+ "ignoreViolations": {
278
+ "*": ["/usr/bin", "/System"],
279
+ "git push": ["/usr/bin/nc"],
280
+ "npm": ["/private/tmp"]
281
+ },
282
+ "enableWeakerNestedSandbox": false,
283
+ "enableWeakerNetworkIsolation": false
284
+ }
285
+ ```
286
+
287
+ ### Configuration Options
288
+
289
+ #### Network Configuration
290
+
291
+ Uses an **allow-only pattern** - all network access is denied by default.
292
+
293
+ - `network.allowedDomains` - Array of allowed domains (supports wildcards like `*.example.com`). Empty array = no network access.
294
+ - `network.deniedDomains` - Array of denied domains (checked first, takes precedence over allowedDomains)
295
+ - `network.allowLocalBinding` - Allow binding to local ports (boolean, default: false)
296
+
297
+ **Unix Socket Settings** (platform-specific behavior):
298
+
299
+ | Setting | macOS | Linux |
300
+ |---------|-------|-------|
301
+ | `allowUnixSockets: string[]` | Allowlist of socket paths | *Ignored* (seccomp can't filter by path) |
302
+ | `allowAllUnixSockets: boolean` | Allow all sockets | Disable seccomp blocking |
303
+
304
+ Unix sockets are **blocked by default** on both platforms.
305
+
306
+ - **macOS**: Use `allowUnixSockets` to allow specific paths (e.g., `["/var/run/docker.sock"]`), or `allowAllUnixSockets: true` to allow all.
307
+ - **Linux**: Blocking uses seccomp filters (x64/arm64 only). If seccomp isn't available, sockets are unrestricted and a warning is shown. Use `allowAllUnixSockets: true` to explicitly disable blocking.
308
+
309
+ #### Filesystem Configuration
310
+
311
+ Uses two different patterns:
312
+
313
+ **Read restrictions** (deny-then-allow pattern) - all reads allowed by default:
314
+
315
+ - `filesystem.denyRead` - Array of paths to deny read access. Empty array = full read access.
316
+ - `filesystem.allowRead` - Array of paths to re-allow read access within denied regions (takes precedence over denyRead). **Note:** this is the opposite of write, where `denyWrite` takes precedence over `allowWrite`.
317
+
318
+ **Write restrictions** (allow-only pattern) - all writes denied by default:
319
+
320
+ - `filesystem.allowWrite` - Array of paths to allow write access. Empty array = no write access.
321
+ - `filesystem.denyWrite` - Array of paths to deny write access within allowed paths (takes precedence over allowWrite)
322
+
323
+ **Path Syntax (macOS):**
324
+
325
+ Paths support git-style glob patterns on macOS, similar to `.gitignore` syntax:
326
+
327
+ - `*` - Matches any characters except `/` (e.g., `*.ts` matches `foo.ts` but not `foo/bar.ts`)
328
+ - `**` - Matches any characters including `/` (e.g., `src/**/*.ts` matches all `.ts` files in `src/`)
329
+ - `?` - Matches any single character except `/` (e.g., `file?.txt` matches `file1.txt`)
330
+ - `[abc]` - Matches any character in the set (e.g., `file[0-9].txt` matches `file3.txt`)
331
+
332
+ Examples:
333
+
334
+ - `"allowWrite": ["src/"]` - Allow write to entire `src/` directory
335
+ - `"allowWrite": ["src/**/*.ts"]` - Allow write to all `.ts` files in `src/` and subdirectories
336
+ - `"denyRead": ["~/.ssh"]` - Deny read to SSH directory
337
+ - `"denyRead": ["/Users"], "allowRead": ["."]` - Deny read to all of `/Users`, but re-allow the current directory
338
+ - `"denyWrite": [".env"]` - Deny write to `.env` file (even if current directory is allowed)
339
+
340
+ **Path Syntax (Linux):**
341
+
342
+ **Linux currently does not support glob matching.** Use literal paths only:
343
+
344
+ - `"allowWrite": ["src/"]` - Allow write to `src/` directory
345
+ - `"denyRead": ["/home/user/.ssh"]` - Deny read to SSH directory
346
+ - `"denyRead": ["/home"], "allowRead": ["."]` - Deny read to all of `/home`, but re-allow the current directory
347
+
348
+ **All platforms:**
349
+
350
+ - Paths can be absolute (e.g., `/home/user/.ssh`) or relative to the current working directory (e.g., `./src`)
351
+ - `~` expands to the user's home directory
352
+
353
+ #### Other Configuration
354
+
355
+ - `ignoreViolations` - Object mapping command patterns to arrays of paths where violations should be ignored
356
+ - `enableWeakerNestedSandbox` - Enable weaker sandbox mode for Docker environments (boolean, default: false)
357
+ - `enableWeakerNetworkIsolation` - Allow access to `com.apple.trustd.agent` and `com.apple.SystemConfiguration.configd` in the macOS sandbox (boolean, default: false). This is needed for Go programs (`gh`, `gcloud`, `terraform`, `kubectl`, etc.) to verify TLS certificates when using `httpProxyPort` with a MITM proxy and custom CA, and for Rust/Go programs (`uv`, `cargo`) that query system proxy/network configuration via `SCDynamicStoreCreate`. **Security warning:** enabling this opens a potential data exfiltration vector through the trustd service and exposes host network configuration (proxy settings, DNS servers) through configd.
358
+
359
+ ### Common Configuration Recipes
360
+
361
+ **Allow GitHub access** (all necessary endpoints):
362
+
363
+ ```json
364
+ {
365
+ "network": {
366
+ "allowedDomains": [
367
+ "github.com",
368
+ "*.github.com",
369
+ "lfs.github.com",
370
+ "api.github.com"
371
+ ],
372
+ "deniedDomains": []
373
+ },
374
+ "filesystem": {
375
+ "denyRead": [],
376
+ "allowWrite": ["."],
377
+ "denyWrite": []
378
+ }
379
+ }
380
+ ```
381
+
382
+ **Restrict to specific directories:**
383
+
384
+ ```json
385
+ {
386
+ "network": {
387
+ "allowedDomains": [],
388
+ "deniedDomains": []
389
+ },
390
+ "filesystem": {
391
+ "denyRead": ["~/.ssh"],
392
+ "allowWrite": [".", "src/", "test/"],
393
+ "denyWrite": [".env", "secrets/"]
394
+ }
395
+ }
396
+ ```
397
+
398
+ **Workspace-only filesystem access** (deny reads outside the workspace):
399
+
400
+ ```json
401
+ {
402
+ "network": {
403
+ "allowedDomains": [],
404
+ "deniedDomains": []
405
+ },
406
+ "filesystem": {
407
+ "denyRead": ["/Users"],
408
+ "allowRead": ["."],
409
+ "allowWrite": ["."],
410
+ "denyWrite": []
411
+ }
412
+ }
413
+ ```
414
+
415
+ This denies reading anything under `/Users` (or `/home` on Linux), then re-allows the current working directory. System paths (`/usr`, `/lib`, etc.) remain readable.
416
+
417
+ ### Common Issues and Tips
418
+
419
+ **Running Jest:** Use `--no-watchman` flag to avoid sandbox violations:
420
+
421
+ ```bash
422
+ srt "jest --no-watchman"
423
+ ```
424
+
425
+ Watchman accesses files outside the sandbox boundaries, which will trigger permission errors. Disabling it allows Jest to run with the built-in file watcher instead.
426
+
427
+ ## Platform Support
428
+
429
+ - **macOS**: Uses `sandbox-exec` with custom profiles (no additional dependencies)
430
+ - **Linux**: Uses `bubblewrap` (bwrap) for containerization
431
+ - **Windows**: Not yet supported
432
+
433
+ ### Platform-Specific Dependencies
434
+
435
+ **Linux requires:**
436
+
437
+ - `bubblewrap` - Container runtime
438
+ - Ubuntu/Debian: `apt-get install bubblewrap`
439
+ - Fedora: `dnf install bubblewrap`
440
+ - Arch: `pacman -S bubblewrap`
441
+ - `socat` - Socket relay for proxy bridging
442
+ - Ubuntu/Debian: `apt-get install socat`
443
+ - Fedora: `dnf install socat`
444
+ - Arch: `pacman -S socat`
445
+ - `ripgrep` - Fast search tool for deny path detection
446
+ - Ubuntu/Debian: `apt-get install ripgrep`
447
+ - Fedora: `dnf install ripgrep`
448
+ - Arch: `pacman -S ripgrep`
449
+
450
+ **Optional Linux dependencies (for seccomp fallback):**
451
+
452
+ The package includes pre-generated seccomp BPF filters for x86-64 and arm architectures. These dependencies are only needed if you are on a different architecture where pre-generated filters are not available:
453
+
454
+ - `gcc` or `clang` - C compiler
455
+ - `libseccomp-dev` - Seccomp library development files
456
+ - Ubuntu/Debian: `apt-get install gcc libseccomp-dev`
457
+ - Fedora: `dnf install gcc libseccomp-devel`
458
+ - Arch: `pacman -S gcc libseccomp`
459
+
460
+ **macOS requires:**
461
+
462
+ - `ripgrep` - Fast search tool for deny path detection
463
+ - Install via Homebrew: `brew install ripgrep`
464
+ - Or download from: https://github.com/BurntSushi/ripgrep/releases
465
+
466
+ ## Development
467
+
468
+ ```bash
469
+ # Install dependencies
470
+ npm install
471
+
472
+ # Build the project
473
+ npm run build
474
+
475
+ # Build seccomp binaries (requires Docker)
476
+ npm run build:seccomp
477
+
478
+ # Run tests
479
+ npm test
480
+
481
+ # Run integration tests
482
+ npm run test:integration
483
+
484
+ # Type checking
485
+ npm run typecheck
486
+
487
+ # Lint code
488
+ npm run lint
489
+
490
+ # Format code
491
+ npm run format
492
+ ```
493
+
494
+ ### Building Seccomp Binaries
495
+
496
+ The pre-generated BPF filters are included in the repository, but you can rebuild them if needed:
497
+
498
+ ```bash
499
+ npm run build:seccomp
500
+ ```
501
+
502
+ This script uses Docker to cross-compile seccomp binaries for multiple architectures:
503
+
504
+ - x64 (x86-64)
505
+ - arm64 (aarch64)
506
+
507
+ The script builds static generator binaries, generates the BPF filters (~104 bytes each), and stores them in `vendor/seccomp/x64/` and `vendor/seccomp/arm64/`. The generator binaries are removed to keep the package size small.
508
+
509
+ ## Implementation Details
510
+
511
+ ### Network Isolation Architecture
512
+
513
+ The sandbox runs HTTP and SOCKS5 proxy servers on the host machine that filter all network requests based on permission rules:
514
+
515
+ 1. **HTTP/HTTPS Traffic**: An HTTP proxy server intercepts requests and validates them against allowed/denied domains
516
+ 2. **Other Network Traffic**: A SOCKS5 proxy handles all other TCP connections (SSH, database connections, etc.)
517
+ 3. **Permission Enforcement**: The proxies enforce the `permissions` rules from your configuration
518
+
519
+ **Platform-specific proxy communication:**
520
+
521
+ - **Linux**: Requests are routed via the filesystem over Unix domain sockets (using `socat` for bridging). The network namespace is removed from the bubblewrap container, ensuring all network traffic must go through the proxies.
522
+
523
+ - **macOS**: The Seatbelt profile allows communication only to specific localhost ports where the proxies listen. All other network access is blocked.
524
+
525
+ ### Filesystem Isolation
526
+
527
+ Filesystem restrictions are enforced at the OS level:
528
+
529
+ - **macOS**: Uses `sandbox-exec` with dynamically generated Seatbelt profiles that specify allowed read/write paths
530
+ - **Linux**: Uses `bubblewrap` with bind mounts, marking directories as read-only or read-write based on configuration
531
+
532
+ **Default filesystem permissions:**
533
+
534
+ - **Read** (deny-then-allow): Allowed everywhere by default. You can deny broad regions, then re-allow specific paths within them. `allowRead` takes precedence over `denyRead`.
535
+
536
+ - Example: `denyRead: ["~/.ssh"]` to block access to SSH keys
537
+ - Example: `denyRead: ["/Users"], allowRead: ["."]` to block all of `/Users` except the workspace
538
+ - Empty `denyRead: []` = full read access (nothing denied)
539
+
540
+ - **Write** (allow-only): Denied everywhere by default. You must explicitly allow paths.
541
+ - Example: `allowWrite: [".", "/tmp"]` to allow writes to current directory and /tmp
542
+ - Empty `allowWrite: []` = no write access (nothing allowed)
543
+ - `denyWrite` creates exceptions within allowed paths (deny takes precedence)
544
+
545
+ **Precedence is intentionally opposite for reads vs writes:** `allowRead` overrides `denyRead`, while `denyWrite` overrides `allowWrite`. This lets you carve out readable regions within denied areas, and carve out protected regions within writable areas.
546
+
547
+ ### Mandatory Deny Paths (Auto-Protected Files)
548
+
549
+ Certain sensitive files and directories are **always blocked from writes**, even if they fall within an allowed write path. This provides defense-in-depth against sandbox escapes and configuration tampering.
550
+
551
+ **Always-blocked files:**
552
+
553
+ - Shell config files: `.bashrc`, `.bash_profile`, `.zshrc`, `.zprofile`, `.profile`
554
+ - Git config files: `.gitconfig`, `.gitmodules`
555
+ - Other sensitive files: `.ripgreprc`, `.mcp.json`
556
+
557
+ **Always-blocked directories:**
558
+
559
+ - IDE directories: `.vscode/`, `.idea/`
560
+ - Claude config directories: `.claude/commands/`, `.claude/agents/`
561
+ - Git hooks and config: `.git/hooks/`, `.git/config`
562
+
563
+ These paths are blocked automatically - you don't need to add them to `denyWrite`. For example, even with `allowWrite: ["."]`, writing to `.bashrc` or `.git/hooks/pre-commit` will fail:
564
+
565
+ ```bash
566
+ $ srt 'echo "malicious" >> .bashrc'
567
+ /bin/bash: .bashrc: Operation not permitted
568
+
569
+ $ srt 'echo "bad" > .git/hooks/pre-commit'
570
+ /bin/bash: .git/hooks/pre-commit: Operation not permitted
571
+ ```
572
+
573
+ **Note (Linux):** On Linux, mandatory deny paths only block files that already exist. Non-existent files in these patterns cannot be blocked by bubblewrap's bind-mount approach. macOS uses glob patterns which block both existing and new files.
574
+
575
+ **Linux search depth:** On Linux, the sandbox uses `ripgrep` to scan for dangerous files in subdirectories within allowed write paths. By default, it searches up to 3 levels deep for performance. You can configure this with `mandatoryDenySearchDepth`:
576
+
577
+ ```json
578
+ {
579
+ "mandatoryDenySearchDepth": 5,
580
+ "filesystem": {
581
+ "allowWrite": ["."]
582
+ }
583
+ }
584
+ ```
585
+
586
+ - Default: `3` (searches up to 3 levels deep)
587
+ - Range: `1` to `10`
588
+ - Higher values provide more protection but slower performance
589
+ - Files in CWD (depth 0) are always protected regardless of this setting
590
+
591
+ ### Unix Socket Restrictions (Linux)
592
+
593
+ On Linux, the sandbox uses **seccomp BPF (Berkeley Packet Filter)** to block Unix domain socket creation at the syscall level. This provides an additional layer of security to prevent processes from creating new Unix domain sockets for local IPC (unless explicitly allowed).
594
+
595
+ **How it works:**
596
+
597
+ 1. **Pre-generated BPF filters**: The package includes pre-compiled BPF filters for different architectures (x64, ARM64). These are ~104 bytes each and stored in `vendor/seccomp/`. The filters are architecture-specific but libc-independent, so they work with both glibc and musl.
598
+
599
+ 2. **Runtime detection**: The sandbox automatically detects your system's architecture and loads the appropriate pre-generated BPF filter.
600
+
601
+ 3. **Syscall filtering**: The BPF filter intercepts the `socket()` syscall and blocks creation of `AF_UNIX` sockets by returning `EPERM`. This prevents sandboxed code from creating new Unix domain sockets.
602
+
603
+ 4. **Two-stage application using apply-seccomp binary**:
604
+ - Outer bwrap creates the sandbox with filesystem, network, and PID namespace restrictions
605
+ - Network bridging processes (socat) start inside the sandbox (need Unix sockets)
606
+ - apply-seccomp binary applies the seccomp filter via `prctl()`
607
+ - apply-seccomp execs the user command with seccomp active
608
+ - User command runs with all sandbox restrictions plus Unix socket creation blocking
609
+
610
+ **Security limitations**: The filter only blocks `socket(AF_UNIX, ...)` syscalls. It does not prevent operations on Unix socket file descriptors inherited from parent processes or passed via `SCM_RIGHTS`. For most sandboxing scenarios, blocking socket creation is sufficient to prevent unauthorized IPC.
611
+
612
+ **Zero runtime dependencies**: Pre-built static apply-seccomp binaries and pre-generated BPF filters are included for x64 and arm64 architectures. No compilation tools or external dependencies required at runtime.
613
+
614
+ **Architecture support**: x64 and arm64 are fully supported with pre-built binaries. Other architectures are not currently supported. To use sandboxing without Unix socket blocking on unsupported architectures, set `allowAllUnixSockets: true` in your configuration.
615
+
616
+ ### Violation Detection and Monitoring
617
+
618
+ When a sandboxed process attempts to access a restricted resource:
619
+
620
+ 1. **Blocks the operation** at the OS level (returns `EPERM` error)
621
+ 2. **Logs the violation** (platform-specific mechanisms)
622
+ 3. **Notifies the user** (in Claude Code, this triggers a permission prompt)
623
+
624
+ **macOS**: The sandbox runtime taps into macOS's system sandbox violation log store. This provides real-time notifications with detailed information about what was attempted and why it was blocked. This is the same mechanism Claude Code uses for violation detection.
625
+
626
+ ```bash
627
+ # View sandbox violations in real-time
628
+ log stream --predicate 'process == "sandbox-exec"' --style syslog
629
+ ```
630
+
631
+ **Linux**: Bubblewrap doesn't provide built-in violation reporting. Use `strace` to trace system calls and identify blocked operations:
632
+
633
+ ```bash
634
+ # Trace all denied operations
635
+ strace -f srt <your-command> 2>&1 | grep EPERM
636
+
637
+ # Trace specific file operations
638
+ strace -f -e trace=open,openat,stat,access srt <your-command> 2>&1 | grep EPERM
639
+
640
+ # Trace network operations
641
+ strace -f -e trace=network srt <your-command> 2>&1 | grep EPERM
642
+ ```
643
+
644
+ ### Advanced: Bring Your Own Proxy
645
+
646
+ For more sophisticated network filtering, you can configure the sandbox to use your own proxy instead of the built-in ones. This enables:
647
+
648
+ - **Traffic inspection**: Use tools like [mitmproxy](https://mitmproxy.org/) to inspect and modify traffic
649
+ - **Custom filtering logic**: Implement complex rules beyond simple domain allowlists
650
+ - **Audit logging**: Log all network requests for compliance or debugging
651
+
652
+ **Example with mitmproxy:**
653
+
654
+ ```bash
655
+ # Start mitmproxy with custom filtering script
656
+ mitmproxy -s custom_filter.py --listen-port 8888
657
+ ```
658
+
659
+ Note: Custom proxy configuration is not yet supported in the new configuration format. This feature will be added in a future release.
660
+
661
+ **Important security consideration:** Even with domain allowlists, exfiltration vectors may exist. For example, allowing `github.com` lets a process push to any repository. With a custom MITM proxy and proper certificate setup, you can inspect and filter specific API calls to prevent this.
662
+
663
+ ### Security Limitations
664
+
665
+ - Network Sandboxing Limitations: The network filtering system operates by restricting the domains that processes are allowed to connect to. It does not otherwise inspect the traffic passing through the proxy and users are responsible for ensuring they only allow trusted domains in their policy.
666
+
667
+ <Warning>
668
+ Users should be aware of potential risks that come from allowing broad domains like `github.com` that may allow for data exfiltration. Also, in some cases it may be possible to bypass the network filtering through [domain fronting](https://en.wikipedia.org/wiki/Domain_fronting).
669
+ </Warning>
670
+
671
+ - Privilege Escalation via Unix Sockets: The `allowUnixSockets` configuration can inadvertently grant access to powerful system services that could lead to sandbox bypasses. For example, if it is used to allow access to `/var/run/docker.sock` this would effectively grant access to the host system through exploiting the docker socket. Users are encouraged to carefully consider any unix sockets that they allow through the sandbox.
672
+ - Filesystem Permission Escalation: Overly broad filesystem write permissions can enable privilege escalation attacks. Allowing writes to directories containing executables in `$PATH`, system configuration directories, or user shell configuration files (`.bashrc`, `.zshrc`) can lead to code execution in different security contexts when other users or system processes access these files.
673
+ - Linux Sandbox Strength: The Linux implementation provides strong filesystem and network isolation but includes an `enableWeakerNestedSandbox` mode that enables it to work inside of Docker environments without privileged namespaces. This option considerably weakens security and should only be used incases where additional isolation is otherwise enforced.
674
+ - Weaker Network Isolation (macOS): The `enableWeakerNetworkIsolation` option re-enables access to `com.apple.trustd.agent` and `com.apple.SystemConfiguration.configd`. The former is needed for Go programs to verify TLS certificates via the macOS Security framework; the latter is needed for Rust/Go programs (e.g. `uv`, `cargo`) that query system proxy/network configuration on startup. This opens a potential data exfiltration vector through the trustd service and exposes read-only host network configuration (proxy settings, DNS servers) through configd. Only enable when needed.
675
+
676
+ ### Known Limitations and Future Work
677
+
678
+ **Linux proxy bypass**: Currently uses environment variables (`HTTP_PROXY`, `HTTPS_PROXY`, `ALL_PROXY`) to direct traffic through proxies. This works for most applications but may be ignored by programs that don't respect these variables, leading to them being unable to connect to the internet.
679
+
680
+ **Future improvements:**
681
+
682
+ - **Proxychains support**: Add support for `proxychains` with `LD_PRELOAD` on Linux to intercept network calls at a lower level, making bypass more difficult
683
+
684
+ - **Linux violation monitoring**: Implement automatic `strace`-based violation detection for Linux, integrated with the violation store. Currently, Linux users must manually run `strace` to see violations, unlike macOS which has automatic violation monitoring via the system log store
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map