Files
skills/docs/devtools/tooling/sandbox/src/module-resolver/module-resolver.md
2026-05-29 08:20:56 +01:00

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 / unregister for custom modules.
  • isBlocked / isAllowed / getBlockedModules introspection helpers.
  • BLOCKED_MODULES and BUILTIN_SAFE_MODULES exported 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 — 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.