`slopjail` is a sandbox for running untrusted JavaScript in the browser. It can be used to safely implement a code execution tool for browser-based AI agents, among other use cases.
npm install slopjailslopjail is a sandbox for running untrusted JavaScript in the browser. It can be used to safely implement a code execution tool for browser-based AI agents, among other use cases.
> Status: Alpha âš¡
- Tiny (~3 KB gzipped)
- Simple API: createSandbox() → run() → dispose().
- Expose variables and functions for untrusted code to access.
- Pretty good security:
* Code runs in a Web Worker on an opaque origin: no access to the host page's storage, cookies, or DOM.
* Network access is blocked by default using a strict Content-Security-Policy.
* Disables APIs like navigator to resist device fingerprinting.
Install:
```
npm install slopjail
Create a sandbox, run some code, and clean up:
`typescript
import { createSandbox } from 'slopjail'
const sandbox = await createSandbox({
globals: {
twenty: 20,
add: (a: number, b: number) => a + b,
},
})
try {
await sandbox.run('console.log(await add(twenty, 5))') // 25
} finally {
sandbox.dispose()
}
`
slopjail creates a hidden
``
Host (main thread)
└─ iframe (sandbox="allow-scripts", opaque origin, strict CSP)
└─ Worker (runs untrusted code)
└─ RPC proxy functions → call back to host via MessagePort
Objects are traversed recursively, so nested functions work the same way:
`typescript
const sandbox = await createSandbox({
globals: {
math: {
add: (a: number, b: number) => a + b,
multiply: (a: number, b: number) => a * b,
},
version: '1.0.0',
},
})
try {
await sandbox.run(
const sum = await math.add(2, 3)
const product = await math.multiply(sum, 4)
console.log(version, product) // "1.0.0" 20
)`
} finally {
sandbox.dispose()
}
run() enforces a 3-second execution timeout by default. If the code doesn't finish in time, the returned promise rejects with an error. You can override it per call:
`typescript`
await sandbox.run(code, { timeout: 10_000 }) // 10 seconds`
By default, the sandbox blocks all network access and resource loading:
Use the contentSecurityPolicy option to relax specific directives:
`typescript
const sandbox = await createSandbox({
// Allow access to the GitHub API
contentSecurityPolicy: "connect-src https://api.github.com",
})
await sandbox.run(
const res = await fetch('https://api.github.com/zen')
console.log(await res.text()))`
Give a sandbox a name for easier debugging:
`typescriptai-code-tool-${Date.now()}
const sandbox = await createSandbox({
name: ,`
})
Sandbox implements Symbol.dispose, so you can use using to automatically clean up when leaving scope:
`typescript`
using sandbox = await createSandbox()
await sandbox.run(code)
Which is equivalent to:
`typescript`
const sandbox = await createSandbox()
try {
await sandbox.run(code)
} finally {
sandbox.dispose()
}
Simply expose your own console global to capture them:
`typescript
const sandbox = await createSandbox({
globals: {
console: {
log: (...args: unknown[]) => {
document.getElementById('output')!.textContent += args.join(' ') + '\n'
},
},
},
})
try {
await sandbox.run('console.log("hello from the sandbox!")')
} finally {
sandbox.dispose()
}
`
run() returns the return value of the code, so you can use a return statement:
`typescript
const sandbox = await createSandbox({
globals: { fruit: ['apple', 'banana'] },
})
try {
// First run some untrusted code that may modify the state
await sandbox.run('fruit.push("cherry")')
// Then query the updated state
const updatedState = await sandbox.run('return fruit')
console.log(updatedState); // ['apple', 'banana', 'cherry']
} finally {
sandbox.dispose()
}
`
Create a new sandboxed execution environment.
Options:
| Option | Type | Description |
|---|---|---|
| globals | Record | Variables and functions to expose inside the sandbox. |contentSecurityPolicy
| | string | Additional CSP directives appended to the default policy. |name
| | string | Name for debugging. |
| Method | Description |
|---|---|
| run(code: string, options?): Promise | Execute JavaScript inside the sandbox. Supports top-level await. Returns the return value of the executed code. |dispose(): void
| | Terminate the worker and clean up all resources. |
Run options:
| Option | Type | Description |
|---|---|---|
| timeout | number | Maximum time in milliseconds to wait before rejecting with a timeout error. Defaults to 3000`. |