2.7 KiB
ModuleResolver
(no submodules)
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/unregisterfor custom modules.isBlocked/isAllowed/getBlockedModulesintrospection helpers.BLOCKED_MODULESandBUILTIN_SAFE_MODULESexported for downstream composition.
Usage: Basic
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:
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— throwsSandboxError(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.