first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,70 @@
# ModuleResolver
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`ModuleResolver` is the gatekeeper between user code and the host's module surface. It maintains an allowlist of modules the sandbox may consult, refuses to register anything on the denylist (`fs`, `net`, `child_process`, `cluster`, `dgram`, `dns`, `http`, `https`, `http2`, `tls`, `worker_threads`, `process`, `os`, `path`, `readline`, `repl`, `vm`), and ships three built-in safe modules (`Math`, `JSON`, `Date`) auto-loaded when their names appear in the allowed list.
The resolver is the only place in the package where module names are reasoned about. Engines consume `resolver.getModules()` and inject the returned exports into the execution context; they never reason about names directly. This makes the security boundary auditable in one file rather than spread across four engine implementations.
## Features
- Hard-coded denylist of 17 dangerous Node module names.
- Three built-in safe modules with curated exports.
- `register` / `unregister` for custom modules.
- `isBlocked` / `isAllowed` / `getBlockedModules` introspection helpers.
- `BLOCKED_MODULES` and `BUILTIN_SAFE_MODULES` exported for downstream
composition.
## Usage: Basic
```ts
import { ModuleResolver } from "@suaveplan/sandbox/module-resolver";
const resolver = new ModuleResolver(["Math"]);
resolver.register({ name: "config", exports: { factor: 21 } });
console.log(resolver.getModules().length); // 2
```
## Usage: Advanced
Surface the denylist to a UI that shows users why their module registration
was rejected:
```ts
import {
BLOCKED_MODULES,
ModuleResolver,
} from "@suaveplan/sandbox/module-resolver";
function offerSuggestions(name: string): string {
if (BLOCKED_MODULES.has(name)) {
return `"${name}" is denylisted — use the sandbox's host bridge instead.`;
}
return "";
}
```
## API Reference
- `new ModuleResolver(allowedNames?: readonly string[])`.
- `register(module: SandboxModule): void` — throws `SandboxError(BLOCKED_MODULE)`.
- `unregister(name: string): boolean`.
- `getModules(): readonly SandboxModule[]`.
- `isBlocked(name: string): boolean`.
- `isAllowed(name: string): boolean`.
- `getBlockedModules(): readonly string[]`.
- `BLOCKED_MODULES: ReadonlySet<string>`.
- `BUILTIN_SAFE_MODULES: Readonly<Record<string, () => SandboxModule>>`.
## Implementation Notes
The denylist is hard-coded rather than configurable because it lists Node
intrinsics whose mere presence inside a sandbox compromises isolation
regardless of whether the engine is a vm context, a worker, or a child
process. The `BUILTIN_SAFE_MODULES` factory returns a fresh module per call so
custom rebinding by one sandbox does not leak into another sharing the
resolver constructor.