Skip to content

f4rkh4d/bun-spawn-safe

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

bun-spawn-safe

tiny hardened wrapper around Bun.spawn. allowlist, timeout, output cap. never invokes a shell.

bun add bun-spawn-safe
import { safeSpawn } from "bun-spawn-safe";

const r = await safeSpawn({
  cmd: "git",
  args: ["log", "--oneline", "-n", "5"],
  timeoutMs: 5_000,
  maxOutputBytes: 64 * 1024,
  allowList: ["git", "ls", "cat"],
});

r.exitCode;   // 0
r.stdout;     // "abc1234 init\n..."
r.truncated;  // false
r.durationMs; // 42

what it is

one function. takes a cmd plus argv. spawns it directly via Bun.spawn. captures stdout/stderr to a byte cap. kills it on a timeout. returns a flat result object.

never passes through /bin/sh, never builds a string, never expands globs. args is argv, not a command line.

what it is not

  • not a shell. you cannot pipe, redirect, or chain commands. write code for that.
  • not a sandbox. it does not jail the filesystem or network. pair with agent-sandbox, bwrap, or your platform of choice if you need isolation.
  • not cross-runtime. Bun only. uses Bun.spawn.

API

interface SpawnOptions {
  cmd: string;
  args?: string[];
  cwd?: string;
  timeoutMs?: number;        // default 30_000
  maxOutputBytes?: number;   // default 65_536, per stream
  allowList?: string[];      // exact-match against cmd
  env?: string[] | Record<string, string>;
  stdin?: string | Uint8Array;
}

interface SpawnResult {
  exitCode: number;          // 124 on timeout
  stdout: string;
  stderr: string;
  killed?: "timeout";
  truncated: boolean;        // true if either stream hit the cap
  durationMs: number;
}

function safeSpawn(opts: SpawnOptions): Promise<SpawnResult>;

class DisallowedCommandError extends Error { cmd: string; allowList: string[]; }
class InvalidCommandError extends Error { cmd: string; reason: string; }

env semantics:

  • undefined: full process.env is forwarded (Bun default)
  • string[]: only those keys are forwarded, missing keys excluded
  • Record<string, string>: that exact env, no merge with process.env

validation

cmd is rejected with InvalidCommandError if it contains /, \, ;, |, &, backtick, $(, or a null byte. this is a sanity check, not a security boundary. the real protection is that args is a real argv, not a string passed to a shell.

if allowList is set, cmd must match one of its entries by string equality. otherwise DisallowedCommandError is thrown.

the f4rkh4d ai-tooling stack

small, focused tools that compose. use any one alone, all together if you need:

license

MIT

About

hardened bun.spawn wrapper. allowlist, timeout, output cap. zero deps.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors