@johnhenry/andbox 0.0.2 → 0.0.3
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.
- package/README.md +43 -5
- package/package.json +7 -3
package/README.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# andbox
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/@johnhenry/andbox)
|
|
4
|
+
[](https://github.com/johnhenry/andbox/actions/workflows/ci.yml)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
7
|
+
Full documentation: [opensource.johnhenry.me/andbox](https://opensource.johnhenry.me/andbox/)
|
|
8
|
+
|
|
3
9
|
A separate-context JavaScript runtime with Worker isolation, RPC capabilities, import maps, and timeouts.
|
|
4
10
|
|
|
5
11
|
andbox runs JavaScript in a dedicated Web Worker with a structured bridge back to the host. Code in that Worker can call host-provided "capabilities" via RPC, use import-mapped packages, and define virtual modules -- all with configurable rate limits, timeouts, and hard-kill semantics.
|
|
@@ -8,6 +14,17 @@ andbox runs JavaScript in a dedicated Web Worker with a structured bridge back t
|
|
|
8
14
|
|
|
9
15
|
Zero dependencies. Uses only Web Workers and standard browser APIs.
|
|
10
16
|
|
|
17
|
+
## Contents
|
|
18
|
+
|
|
19
|
+
- [Install](#install)
|
|
20
|
+
- [Quick Start](#quick-start)
|
|
21
|
+
- [Sandbox Modes](#sandbox-modes)
|
|
22
|
+
- [API](#api)
|
|
23
|
+
- [Execution model](#execution-model)
|
|
24
|
+
- [Security model](#security-model)
|
|
25
|
+
- [Family](#family)
|
|
26
|
+
- [License](#license)
|
|
27
|
+
|
|
11
28
|
## Install
|
|
12
29
|
|
|
13
30
|
```bash
|
|
@@ -266,7 +283,18 @@ Code runs inside a Web Worker created from a Blob URL. This gets you, for free,
|
|
|
266
283
|
|
|
267
284
|
## Security model
|
|
268
285
|
|
|
269
|
-
andbox is **not** a boundary against code that is actively trying to escape it. If you're running code you don't fully trust, read this section
|
|
286
|
+
andbox is **not** a boundary against code that is actively trying to escape it. If you're running code you don't fully trust, read this section before you rely on `capabilities`/`policy`/`createNetworkFetch` for anything.
|
|
287
|
+
|
|
288
|
+
**What andbox guarantees:**
|
|
289
|
+
|
|
290
|
+
- **No DOM access.** Worker-mode code executes in a real Worker global scope, which has no `document`, `window`, or other DOM references -- this is a platform property of Workers, not something andbox has to enforce itself.
|
|
291
|
+
- **No implicit host object references.** Only what you explicitly pass in (`capabilities`, `globals`, import map entries) is reachable from sandboxed code -- ordinary (non-adversarial) code cannot accidentally read or mutate host-side state it wasn't given a reference to.
|
|
292
|
+
- **Hard kill on timeout.** `evaluate()` calls that exceed `timeoutMs` `terminate()` the Worker outright and start a fresh one for the next call -- this is a real process-level kill, not a cooperative cancellation the running code could ignore. See "still yours" below for what a kill does *not* undo.
|
|
293
|
+
- **The capability gate cannot be walked around via the prototype chain.** `gateCapabilities()` builds the gated object with `Object.create(null)`, so `host.call('constructor', ...)` cannot resolve through `Object.prototype` to the real global `Object` constructor. Previously fixed; see [andbox#5](https://github.com/johnhenry/andbox/issues/5).
|
|
294
|
+
- **`createNetworkFetch()`'s allowlist is redirect-safe.** Requests are made with `redirect: 'manual'` and any redirect response is rejected outright, so an allowlisted host cannot silently redirect a caller to a non-allowlisted one. Previously fixed; see [andbox#6](https://github.com/johnhenry/andbox/issues/6).
|
|
295
|
+
- **`gateCapabilities()` enforces call/argument-size/concurrency caps per capability**, for cooperative callers that stay within the capabilities you actually granted.
|
|
296
|
+
|
|
297
|
+
**What is still yours:**
|
|
270
298
|
|
|
271
299
|
- **Worker-global APIs are directly reachable, regardless of `capabilities`.** Sandboxed code executes in a real Worker global scope, so `fetch`, `WebSocket`, `Worker` (nested workers), `importScripts`, `indexedDB`, and `self.postMessage` are all callable directly -- omitting a `fetch` capability does not block network access. This is fundamental to how `Function`-based evaluation works and isn't fixable without a different execution strategy (e.g. a cross-origin iframe with a strict CSP, or a Realms/Compartments-based approach). See [andbox#10](https://github.com/johnhenry/andbox/issues/10).
|
|
272
300
|
- **`sandboxImport()` will load and execute an arbitrary remote URL.** Any `http(s)://` specifier is passed straight to `import()` with no allowlist, independent of any network policy configured for capabilities. See [andbox#7](https://github.com/johnhenry/andbox/issues/7).
|
|
@@ -274,12 +302,22 @@ andbox is **not** a boundary against code that is actively trying to escape it.
|
|
|
274
302
|
- **`mode: 'service-worker'` does not provide isolation by merely existing.** It's a hosting mechanism -- a real Service Worker, same-origin by default, serving your `files` map with real fetch/navigation interception. Content served through it can see and touch its own origin exactly like any other same-origin page can; nothing about registering a Service Worker sandboxes what runs inside the pages it serves. If you're hosting content you don't fully trust, point this mode at a genuinely separate origin from day one -- the same recommendation the `fetch`/`WebSocket`/`Worker` item above makes for `worker` mode (a cross-origin iframe with a strict CSP), not something bolted on after the fact. See [andbox#14](https://github.com/johnhenry/andbox/issues/14).
|
|
275
303
|
- **The Service Worker does not control the very first navigation into its scope.** A page/iframe navigation into `scope` that happens *before* the registration has finished activating is a normal, unintercepted network request -- Service Workers never retroactively intercept a request that already went out. `createSandbox({ mode: 'service-worker' })`'s returned promise only resolves once the registration is active (its generated script also calls `clients.claim()` on activate, which helps *already-open* clients but not fresh navigations); the documented, load-bearing contract is: don't navigate anything into `scope` until that promise resolves. Do that and every request is intercepted from the first byte, because the registration already matches `scope` before the navigation request is made. See [andbox#14](https://github.com/johnhenry/andbox/issues/14).
|
|
276
304
|
|
|
277
|
-
|
|
305
|
+
If you need to run untrusted/adversarial code safely, andbox alone is not sufficient -- pair it with OS-level isolation (a separate process/container with its own network and filesystem restrictions) or use a purpose-built sandboxing runtime. Capability gating and rate limits here are for organizing and throttling code you already trust, not for containing code you don't.
|
|
278
306
|
|
|
279
|
-
|
|
280
|
-
- ~~`createNetworkFetch()`'s allowlist was not redirect-safe~~ (it checked the request hostname before the fetch, not the final response URL). Fixed by fetching with `redirect: 'manual'` and rejecting any redirect response outright. See [andbox#6](https://github.com/johnhenry/andbox/issues/6).
|
|
307
|
+
## Family
|
|
281
308
|
|
|
282
|
-
|
|
309
|
+
andbox isn't just a standalone sandbox runtime -- it's the sandboxing engine
|
|
310
|
+
that [aimatey](https://github.com/johnhenry/aimatey) middleware wires in for
|
|
311
|
+
code-based tool execution.
|
|
312
|
+
|
|
313
|
+
- **[`@johnhenry/aimatey-middleware-andbox`](https://github.com/johnhenry/aimatey-middleware-andbox)** --
|
|
314
|
+
wires andbox's `createSandbox()` factory (or a pre-built sandbox) into an
|
|
315
|
+
[aimatey](https://github.com/johnhenry/aimatey) bridge middleware, via
|
|
316
|
+
`toolsToCapabilities()` to convert the middleware's `tools`/`executeToolFn`
|
|
317
|
+
into andbox `capabilities`. This package's own [Security model](#security-model)
|
|
318
|
+
is the source of truth for what that capability gate does and does not
|
|
319
|
+
guarantee -- the middleware's Security model section points back here
|
|
320
|
+
rather than repeating it.
|
|
283
321
|
|
|
284
322
|
## License
|
|
285
323
|
|
package/package.json
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@johnhenry/andbox",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Sandboxed JavaScript runtime with Worker isolation, RPC, import maps, and timeouts",
|
|
6
6
|
"main": "./src/index.mjs",
|
|
7
|
+
"types": "./src/index.d.ts",
|
|
7
8
|
"exports": {
|
|
8
|
-
".":
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./src/index.d.ts",
|
|
11
|
+
"import": "./src/index.mjs"
|
|
12
|
+
}
|
|
9
13
|
},
|
|
10
14
|
"files": [
|
|
11
15
|
"src",
|
|
@@ -36,6 +40,6 @@
|
|
|
36
40
|
},
|
|
37
41
|
"homepage": "https://opensource.johnhenry.me/andbox/",
|
|
38
42
|
"engines": {
|
|
39
|
-
"node": ">=
|
|
43
|
+
"node": ">=26.0.0"
|
|
40
44
|
}
|
|
41
45
|
}
|