Skip to content

Repository files navigation

quickjs-sandbox

A synchronous, capability-empty QuickJS-NG runtime for PHP, built as a native extension in Rust. It is designed for running untrusted JavaScript orchestration code whose only authority comes from PHP callbacks explicitly registered by the host application.

Security properties

  • Mandatory positive CPU, memory, and native stack budgets
  • No filesystem, network, process, environment, or standard QuickJS std/os
  • No module loader, dynamic native modules, Promise job loop, or bytecode API
  • Explicit, bounded PHP-to-JavaScript and JavaScript-to-PHP conversion
  • Circular-reference, conversion-depth, and conversion-size rejection
  • Fresh QuickJS runtime and global object for every Sandbox
  • Typed PHP exceptions for syntax, runtime, timeout, memory, stack, conversion, and callback failures

This is an in-process language sandbox, not an operating-system isolation boundary. Read THREAT_MODEL.md before exposing it to users.

Requirements

  • PHP 8.4 on Linux x86_64 (NTS or ZTS), or PHP 8.5 NTS on macOS ARM64
  • Rust 1.87+ when building from source

Release artifacts include the PHP minor version and thread-safety ABI in the filename. Both must match php-config --version and the Thread Safety row in php -i; an NTS extension cannot load into ZTS PHP or vice versa.

PHP API

$sandbox = new \QuickJS\Sandbox(
    memory_limit: 32 * 1024 * 1024,
    cpu_limit: 5.0,
    stack_limit: 512 * 1024,
);

$sandbox->setGlobal('ctx', ['run_number' => 4]);
$sandbox->register('__host', [
    'greet' => fn (string $name): string => "Hello, {$name}!",
]);

$script = $sandbox->load(<<<'JS'
    return {
        run: ctx.run_number,
        greeting: __host.greet('OpenCompany'),
    };
JS, 'automation.js');

var_dump($script());

load() compiles the source as a strict JavaScript function body. Top-level return works, and invocation arguments are available through arguments. Source is capped at 4 MiB before the extension creates its wrapped function.

QuickJS\Sandbox

Method Meaning
__construct(int $memory_limit = 33554432, float $cpu_limit = 5.0, int $stack_limit = 524288) Create one isolated runtime. All limits must be positive.
load(string $source, ?string $name = null): QuickJS\Script Compile a strict function body.
setGlobal(string $name, mixed $value): void Inject structured data.
register(string $namespace, array $callbacks): void Register explicit PHP capabilities.
memoryUsage(): int Current allocator usage.
peakMemoryUsage(): int Highest observed allocator usage at a host boundary.
memoryStats(): array QuickJS allocator counters.
cpuUsage(): float CPU seconds used by the last script/host operation.
version(): array Extension and engine binding versions.

Conversion contract

  • null, booleans, floats, UTF-8 strings, lists, and associative arrays cross the boundary directly.
  • PHP integers outside JavaScript's safe Number range become BigInt.
  • JavaScript BigInt values must fit in a PHP 64-bit integer when returned.
  • Only JavaScript arrays and plain/null-prototype objects with enumerable string keys become PHP arrays; symbol and non-enumerable properties are rejected.
  • Functions, symbols, promises, proxies, Dates, Maps, Sets, cycles, invalid UTF-8, and oversized/deep graphs are rejected explicitly.

Build and test

cargo build --release
./scripts/run-php-tests.sh target/release/libquickjs_sandbox.dylib

On Linux the artifact ends in .so.

Installation

Download the release artifact matching both your platform and PHP minor ABI, copy it into php-config --extension-dir, and enable it:

curl -fLO https://github.com/OpenCompanyApp/quickjs-sandbox/releases/download/v1.0.1/quickjs_sandbox-php85-nts-macos-aarch64.so
curl -fLO https://github.com/OpenCompanyApp/quickjs-sandbox/releases/download/v1.0.1/quickjs_sandbox-php85-nts-macos-aarch64.so.sha256
shasum -a 256 -c quickjs_sandbox-php85-nts-macos-aarch64.so.sha256
install -m 0755 quickjs_sandbox-php85-nts-macos-aarch64.so "$(php-config --extension-dir)/quickjs_sandbox.so"
printf '%s\n' 'extension=quickjs_sandbox.so' > "$(php-config --ini-dir)/quickjs-sandbox.ini"
php --ri quickjs_sandbox

License

MIT

About

Capability-empty QuickJS-NG sandbox for PHP, built in Rust

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages