Skip to content

Commit 408fc85

Browse files
committed
docs: refactor docs
1 parent 5706a0f commit 408fc85

5 files changed

Lines changed: 1723 additions & 152 deletions

File tree

.claude/skills/hacker/SKILL.md

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
---
2+
name: hacker
3+
description: >
4+
Red team agent for vm2 sandbox escape testing. Systematically attempts to break out of the
5+
vm2 JavaScript sandbox by exploiting known and novel attack vectors. Use this skill whenever
6+
the user makes changes to vm2's sandbox code (bridge.js, setup-sandbox.js, setup-node-sandbox.js,
7+
vm.js, nodevm.js, transformer.js) and wants to verify the sandbox still holds. Also use when
8+
the user asks to "hack", "attack", "test security", "try to escape", "red team", or "pentest"
9+
the sandbox. Trigger on any request to find sandbox escapes or verify sandbox integrity.
10+
---
11+
12+
# Hacker - vm2 Sandbox Red Team Agent
13+
14+
## Purpose
15+
16+
Act as a persistent adversary trying to escape the vm2 sandbox. After every code change to the sandbox, systematically attempt known and novel escape vectors to verify the sandbox holds.
17+
18+
## Before Starting
19+
20+
1. Read `docs/ATTACKS.md` -- the full catalog of attack patterns, fundamentals, and defense table.
21+
2. Read `lib/bridge.js` and `lib/setup-sandbox.js` to understand the current defenses.
22+
3. Read the specific file(s) that were changed to understand what was modified.
23+
24+
## Attack Methodology
25+
26+
### Phase 1: Understand the Change
27+
28+
Analyze the diff or changed code to understand:
29+
- What new surface area is exposed?
30+
- What invariants were relaxed or tightened?
31+
- What assumptions does the change make?
32+
33+
### Phase 2: Replay Known Attacks
34+
35+
Run through all attack categories from `docs/ATTACKS.md` against the modified code. The document is organized into three tiers (Primitives, Techniques, Compound Attacks) with canonical examples containing executable payloads.
36+
37+
### Phase 3: Synthesize Novel Attacks
38+
39+
Combine attack primitives to create compound attacks targeting the specific change:
40+
- New property/method exposed: try constructor chain, prototype traversal, symbol extraction
41+
- Async code modified: try Promise species, TOCTOU, static method stealing, Reflect.construct bypass
42+
- Proxy handling changes: try trap exploitation, duck-typing, showProxy exposure
43+
- Error handling changes: try prepareStackTrace, stack overflow, Symbol-name TypeError
44+
- Property access changes: try descriptor extraction, Object.entries unwrapping, nested getOwnPropertyDescriptors
45+
46+
### Phase 4: Write Attack Tests
47+
48+
Write each escape attempt as a Mocha test in `test/vm.js`:
49+
50+
```javascript
51+
it('attack name - description', () => {
52+
const vm2 = new VM();
53+
assert.doesNotThrow(() => vm2.run(`
54+
// ... attack code ...
55+
`), 'description of what should be prevented');
56+
57+
// Or for attacks that should throw:
58+
assert.throws(() => vm2.run(`
59+
// ... attack code ...
60+
`), /expected error pattern/, 'description');
61+
});
62+
```
63+
64+
For async attacks:
65+
```javascript
66+
it('async attack name', async () => {
67+
const vm2 = new VM({allowAsync: true});
68+
let escaped = false;
69+
global.escapeMarker = () => { escaped = true; };
70+
await new Promise((resolve) => {
71+
vm2.run(`
72+
// ... async attack code ...
73+
// If escape works: escapeMarker()
74+
`);
75+
setTimeout(() => {
76+
delete global.escapeMarker;
77+
assert.strictEqual(escaped, false, 'Sandbox escape should be prevented');
78+
resolve();
79+
}, 200);
80+
});
81+
});
82+
```
83+
84+
Use `it.cond(name, condition, fn)` to guard tests requiring specific Node versions.
85+
86+
## Thinking Like an Attacker
87+
88+
When analyzing a code change, ask:
89+
1. Does this introduce a new way to get a reference to a host object?
90+
2. Can I make the bridge call my code with unsanitized arguments?
91+
3. Can I intercept an internal operation between check and use (TOCTOU)?
92+
4. Can I override something the bridge relies on before it caches it?
93+
5. Can I cause an error that leaks host realm objects?
94+
6. Can I make the bridge skip a security check via type confusion?
95+
7. Can I reach a code path where values cross the boundary unsanitized?
96+
8. Can I combine two individually-safe operations into an unsafe compound?
97+
98+
## Output
99+
100+
After each attack session, produce:
101+
1. Summary of which attack categories were tested
102+
2. Any new vulnerabilities discovered (with proof-of-concept code)
103+
3. Mocha tests for each attempted attack (both successful and prevented)
104+
4. Recommendations for fixes if escapes were found

CLAUDE.md

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# vm2
2+
3+
vm2 safely sandboxes untrusted JavaScript code running in Node.js. This is a security-critical project -- every change must be evaluated for sandbox escape potential.
4+
5+
## Public API
6+
7+
Exported from `lib/main.js` via `index.js`:
8+
9+
- **`VM`** -- isolated context for synchronous execution without `require`.
10+
- **`NodeVM`** -- sandbox emulating Node's module loader with fine-grained `require` controls.
11+
- **`VMScript`** -- compilation, caching, and compiler selection wrapper.
12+
- **`VMFileSystem`** -- controls sandbox filesystem access.
13+
- **`VMError`** -- raised when sandbox policy is violated.
14+
15+
## Architecture
16+
17+
### The Boundary (Security-Critical)
18+
19+
| File | Role |
20+
|------|------|
21+
| `lib/bridge.js` | Core proxy layer. Paired proxies for every value crossing host/sandbox. WeakMap caches for identity. Proxy trap handlers (`BaseHandler`, `ProtectedHandler`, `ReadOnlyHandler`). |
22+
| `lib/setup-sandbox.js` | Sandbox bootstrap for `VM`. `handleException`, Promise wrapping, `Symbol.for` override, symbol filtering, `Error.prepareStackTrace` safe default, `WebAssembly.JSTag` deletion. |
23+
| `lib/setup-node-sandbox.js` | Sandbox bootstrap for `NodeVM`. `require`, module resolution, console redirection. |
24+
| `lib/transformer.js` | Acorn parser instrumenting `catch` blocks and `with` statements. **Limitation**: `ecmaVersion: 2022` -- post-ES2022 syntax (e.g., `using`) is invisible. |
25+
26+
### Other Files
27+
28+
| File | Role |
29+
|------|------|
30+
| `lib/vm.js` | `VM` class. Compiles via `vm.Script`, applies transformer, enforces timeout/async. |
31+
| `lib/nodevm.js` | `NodeVM` class. Module loader, external module access, console redirection. |
32+
| `lib/script.js` | `VMScript`. Options, compilers, caching, async detection. |
33+
| `lib/compiler.js` | Compiler resolution (JS passthrough, optional CoffeeScript/TypeScript). |
34+
| `lib/filesystem.js` | `VMFileSystem` implementation. |
35+
| `lib/resolver.js` | Module resolution for `NodeVM`. |
36+
| `lib/events.js` | Sandbox-safe copy of Node's `events`. |
37+
38+
### CLI
39+
40+
```sh
41+
npx vm2 path/to/script.js # NodeVM with external modules, verbose logging
42+
```
43+
44+
## Security
45+
46+
See [`docs/ATTACKS.md`](docs/ATTACKS.md) for the full catalog of attack patterns, fundamentals of realm separation and V8 internals, and the defense table.
47+
48+
**Key insight**: V8 internal algorithms (ArraySpeciesCreate, FormatStackTrace, PromiseResolveThenableJob) operate on raw objects, bypassing proxy traps. Defenses must neutralize raw objects directly, not just intercept via proxies.
49+
50+
### Principles
51+
52+
1. Never expose host constructors or prototypes.
53+
2. Rebound every function crossing the bridge to the destination realm.
54+
3. Freeze or proxy shared mutable state.
55+
4. Filter property access via `Object.getOwnPropertyDescriptor`. Never `for...in` or spread on host objects.
56+
5. Treat new symbols and language features as suspect until vetted.
57+
6. Cache `Reflect.*` and other critical references at init time.
58+
59+
### Checklist for Boundary Changes
60+
61+
1. Does this expose any new return path for host objects?
62+
2. Can sandbox code call this method directly (not through a proxy)?
63+
3. Does this accept attacker-controlled parameters?
64+
4. Are all `Reflect.*` calls using cached references?
65+
5. Could V8 internal algorithms trigger this path (bypassing traps)?
66+
6. Does this handle host-realm errors that could be thrown?
67+
7. Are there new well-known symbols that need filtering?
68+
69+
## Tests
70+
71+
```sh
72+
npm test # Main suite (Mocha)
73+
npm run test:compilers # Optional (needs typescript, coffee-script)
74+
npm run lint # ESLint
75+
```
76+
77+
- `test/vm.js` -- Main sandbox tests. Uses `makeHelpers()` for proxy boundary checks, `it.cond(name, condition, fn)` for version-gated tests.
78+
- `test/nodevm.js` -- NodeVM module loading tests.
79+
- `test/escape-scanner.js` -- Automated escape scanner. Serializable, runs inside `vm.run()`.
80+
81+
Every security fix must include tests that reproduce the attack, verify the defense, and test bypass variants. Use `it.cond()` for Node version requirements.
82+
83+
Use the `/hacker` skill after making security-related changes to systematically red-team the sandbox and verify it still holds.
84+
85+
## Updating ATTACKS.md
86+
87+
**Every time the library is patched**, update [`docs/ATTACKS.md`](docs/ATTACKS.md):
88+
89+
- Add the attack to the appropriate tier/category (or create a new one).
90+
- Document: attack flow, canonical example, why it works, mitigation, detection rules.
91+
- Update the Compound Attack Patterns and "How The Bridge Defends" table.
92+
- Add new APIs/features to "Considered Attack Surfaces" or "Future Risks".
93+
94+
## Workflow
95+
96+
- Keep changes small and focused. Security-sensitive code discourages large refactors.
97+
- Include threat model reasoning and escape-prevention tests for boundary changes.
98+
- Follow existing ESLint code style.
99+
- Vulnerabilities: follow `SECURITY.md`, not public issues.

CONTRIBUTING.md

Lines changed: 0 additions & 150 deletions
This file was deleted.

0 commit comments

Comments
 (0)