sandboxedjs 0.1.67 → 0.1.69

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -489,6 +489,9 @@ function normalizeSignal(spec) {
489
489
  const withPrefix = upper.startsWith("SIG") ? upper : "SIG" + upper;
490
490
  return withPrefix in SIGNALS ? withPrefix : null;
491
491
  }
492
+ function signalNumber(name) {
493
+ return SIGNALS[name] ?? 0;
494
+ }
492
495
  function exitCodeForSignal(sig) {
493
496
  return 128 + (SIGNALS[sig] ?? 0);
494
497
  }
@@ -17800,7 +17803,10 @@ var Op = {
17800
17803
  getpeername: 1801,
17801
17804
  setsockopt: 1802,
17802
17805
  getsockopt: 1803,
17803
- socket_close: 1804
17806
+ socket_close: 1804,
17807
+ spawn: 2048,
17808
+ waitpid: 2049,
17809
+ kill: 2050
17804
17810
  };
17805
17811
  var Errno = {
17806
17812
  EPERM: 1,
@@ -17811,6 +17817,7 @@ var Errno = {
17811
17817
  ENXIO: 6,
17812
17818
  E2BIG: 7,
17813
17819
  EBADF: 9,
17820
+ ECHILD: 10,
17814
17821
  EAGAIN: 11,
17815
17822
  ENOMEM: 12,
17816
17823
  EACCES: 13,
@@ -17946,7 +17953,7 @@ var wheels_default = {
17946
17953
 
17947
17954
  // package.json
17948
17955
  var package_default = {
17949
- version: "0.1.67"};
17956
+ version: "0.1.69"};
17950
17957
 
17951
17958
  // src/runtime/python/config.ts
17952
17959
  var bundledManifest = {
@@ -18491,6 +18498,9 @@ function toPosixError(error) {
18491
18498
  };
18492
18499
  return new PosixError(code && mapped[code] ? mapped[code] : Errno.EIO, message);
18493
18500
  }
18501
+
18502
+ // src/runtime/python/backend.ts
18503
+ init_signals();
18494
18504
  var DescriptorTable = class _DescriptorTable {
18495
18505
  slots = /* @__PURE__ */ new Map();
18496
18506
  /** Descriptions this table shares with others, so close counts correctly. */
@@ -19384,6 +19394,46 @@ function createHostAbiServer(proc) {
19384
19394
  const joined = Object.entries(proc.env).map(([key, value]) => `${key}=${value}`).join("\0");
19385
19395
  return { status: 0, payload: new Writer().string(joined).finish() };
19386
19396
  }
19397
+ /* --------------------------------------------------------- processes */
19398
+ case Op.spawn: {
19399
+ const argv = [];
19400
+ for (let i = 0, n = r.u32(); i < n; i++) argv.push(r.string());
19401
+ const env2 = {};
19402
+ for (let i = 0, n = r.u32(); i < n; i++) {
19403
+ const entry = r.string();
19404
+ const split2 = entry.indexOf("=");
19405
+ if (split2 > 0) env2[entry.slice(0, split2)] = entry.slice(split2 + 1);
19406
+ }
19407
+ const cwd = r.string() || proc.cwd;
19408
+ const files2 = /* @__PURE__ */ new Map();
19409
+ for (let i = 0, n = r.u32(); i < n; i++) {
19410
+ const childFd = r.u32() | 0;
19411
+ const parentFd = r.u32() | 0;
19412
+ files2.set(childFd, proc.table.get(parentFd));
19413
+ }
19414
+ if (!proc.processes) throw new PosixError(Errno.ENOSYS);
19415
+ if (argv.length === 0) throw new PosixError(Errno.EINVAL);
19416
+ return { status: await proc.processes.spawn({ argv, env: env2, cwd: resolve3(cwd), files: files2 }) };
19417
+ }
19418
+ case Op.waitpid: {
19419
+ const pid = r.u32() | 0;
19420
+ const options = r.u32() | 0;
19421
+ if (!proc.processes) throw new PosixError(Errno.ENOSYS);
19422
+ const result = await Promise.race([
19423
+ proc.processes.wait(pid, options),
19424
+ aborted().then(() => {
19425
+ throw new PosixError(Errno.EINTR);
19426
+ })
19427
+ ]);
19428
+ return { status: 0, payload: new Writer().i32(result.pid).i32(result.status).finish() };
19429
+ }
19430
+ case Op.kill: {
19431
+ const pid = r.u32() | 0;
19432
+ const signal = r.u32() | 0;
19433
+ if (!proc.processes) throw new PosixError(Errno.ENOSYS);
19434
+ proc.processes.kill(pid, signal);
19435
+ return { status: 0 };
19436
+ }
19387
19437
  /* ---------------------------------------------------------- entropy */
19388
19438
  case Op.getrandom: {
19389
19439
  const length = Math.min(r.u32(), 65536);
@@ -19737,6 +19787,7 @@ async function startPythonProcess(options) {
19737
19787
  table.set(0, stdin);
19738
19788
  table.set(1, stdout);
19739
19789
  table.set(2, stderr);
19790
+ for (const [fd, description] of options.inherit ?? []) table.set(fd, description);
19740
19791
  const controller = new AbortController();
19741
19792
  const abort = () => controller.abort();
19742
19793
  options.signal.addEventListener("abort", abort, { once: true });
@@ -19750,7 +19801,8 @@ async function startPythonProcess(options) {
19750
19801
  env: options.env,
19751
19802
  cwd: options.cwd,
19752
19803
  signal: controller.signal,
19753
- sockets: options.sockets
19804
+ sockets: options.sockets,
19805
+ processes: options.processes
19754
19806
  });
19755
19807
  const buffers = createHostTransportBuffers();
19756
19808
  let fault = null;
@@ -19815,7 +19867,11 @@ async function startPythonProcess(options) {
19815
19867
  pid: options.pid,
19816
19868
  ppid: options.ppid,
19817
19869
  mounts: options.mounts,
19818
- isTty: options.isTty
19870
+ isTty: options.isTty,
19871
+ /* Only beyond the standard three: 0, 1 and 2 are bound as the process's
19872
+ * terminal-or-pipe streams by the worker itself, and installing a second
19873
+ * stream over one of them would leave two objects claiming the number. */
19874
+ inheritFds: [...options.inherit?.keys() ?? []].filter((fd) => fd > 2)
19819
19875
  });
19820
19876
  return {
19821
19877
  write: (data) => stdin.push(data),
@@ -20303,6 +20359,129 @@ raise SystemExit(entry())
20303
20359
  }
20304
20360
 
20305
20361
  // src/runtime/python/backend.ts
20362
+ var PENDING_INHERITANCE = /* @__PURE__ */ new Map();
20363
+ var INHERIT_TOKEN = "SBX_PYTHON_INHERIT";
20364
+ var WNOHANG = 1;
20365
+ function claimInheritance(env2) {
20366
+ const token = env2[INHERIT_TOKEN];
20367
+ if (!token) return void 0;
20368
+ const files = PENDING_INHERITANCE.get(token);
20369
+ PENDING_INHERITANCE.delete(token);
20370
+ delete env2[INHERIT_TOKEN];
20371
+ return files;
20372
+ }
20373
+ function waitStatus(proc) {
20374
+ if (proc.termSignal) return signalNumber(proc.termSignal) & 127;
20375
+ return ((proc.exitCode ?? 0) & 255) << 8;
20376
+ }
20377
+ function outputFor(description, fallback) {
20378
+ if (!description) return fallback;
20379
+ return {
20380
+ write(data) {
20381
+ description.write(typeof data === "string" ? new TextEncoder().encode(data) : data);
20382
+ },
20383
+ end() {
20384
+ },
20385
+ get closed() {
20386
+ return false;
20387
+ },
20388
+ isTTY: fallback.isTTY
20389
+ };
20390
+ }
20391
+ function createProcessService(ctx) {
20392
+ const children = /* @__PURE__ */ new Map();
20393
+ const exited = (proc) => proc.exitCode !== null || proc.termSignal !== null;
20394
+ return {
20395
+ async spawn(request) {
20396
+ const token = `${ctx.proc.pid}:${Math.random().toString(36).slice(2)}`;
20397
+ const env2 = { ...request.env };
20398
+ if (request.files.size > 0) {
20399
+ PENDING_INHERITANCE.set(token, new Map(request.files));
20400
+ env2[INHERIT_TOKEN] = token;
20401
+ }
20402
+ const stdin = request.files.get(0);
20403
+ const argv = [containerProgram(ctx, request.argv[0] ?? ""), ...request.argv.slice(1)];
20404
+ const proc = ctx.kernel.spawn(argv, {
20405
+ cwd: request.cwd,
20406
+ env: env2,
20407
+ cred: ctx.cred,
20408
+ ppid: ctx.proc.pid,
20409
+ stdin: stdin ? descriptionInput(stdin) : void 0,
20410
+ stdout: outputFor(request.files.get(1), ctx.stdout),
20411
+ stderr: outputFor(request.files.get(2), ctx.stderr)
20412
+ });
20413
+ children.set(proc.pid, proc);
20414
+ void proc.wait().finally(() => PENDING_INHERITANCE.delete(token));
20415
+ return proc.pid;
20416
+ },
20417
+ async wait(pid, options) {
20418
+ const candidates = pid > 0 ? [children.get(pid)].filter((p) => p !== void 0) : [...children.values()];
20419
+ if (candidates.length === 0) throw new PosixError(Errno.ECHILD);
20420
+ const reap = (proc) => {
20421
+ children.delete(proc.pid);
20422
+ return { pid: proc.pid, status: waitStatus(proc) };
20423
+ };
20424
+ const done = candidates.find(exited);
20425
+ if (done) return reap(done);
20426
+ if (options & WNOHANG) return { pid: 0, status: 0 };
20427
+ await Promise.race(candidates.map((proc) => proc.wait()));
20428
+ const finished = candidates.find(exited);
20429
+ if (!finished) throw new PosixError(Errno.ECHILD);
20430
+ return reap(finished);
20431
+ },
20432
+ kill(pid, signal) {
20433
+ const proc = children.get(pid) ?? ctx.kernel.procs.get(pid);
20434
+ if (!proc) throw new PosixError(Errno.ESRCH);
20435
+ if (signal === 0) return;
20436
+ const name = SIGNAL_NAMES[signal];
20437
+ if (!name) throw new PosixError(Errno.EINVAL);
20438
+ proc.deliver(name);
20439
+ }
20440
+ };
20441
+ }
20442
+ function containerProgram(ctx, program) {
20443
+ const resolvable = (name) => name !== "" && ctx.kernel.resolveExecutable(name, ctx.cwd, ctx.env, ctx.cred) !== null;
20444
+ if (resolvable(program)) return program;
20445
+ const base2 = program.slice(program.lastIndexOf("/") + 1);
20446
+ if (resolvable(base2)) return base2;
20447
+ if (base2.startsWith("python")) return "python3";
20448
+ return program;
20449
+ }
20450
+ function descriptionInput(description) {
20451
+ const read = async (size) => {
20452
+ for (; ; ) {
20453
+ try {
20454
+ const chunk = description.read(size);
20455
+ if (chunk.length === 0) return null;
20456
+ return chunk;
20457
+ } catch {
20458
+ await description.whenReady();
20459
+ }
20460
+ }
20461
+ };
20462
+ return {
20463
+ isTTY: false,
20464
+ interactive: false,
20465
+ read,
20466
+ async readAll() {
20467
+ const parts = [];
20468
+ for (; ; ) {
20469
+ const chunk = await read(65536);
20470
+ if (chunk === null) break;
20471
+ parts.push(chunk);
20472
+ }
20473
+ let total = 0;
20474
+ for (const part of parts) total += part.length;
20475
+ const out = new Uint8Array(total);
20476
+ let at = 0;
20477
+ for (const part of parts) {
20478
+ out.set(part, at);
20479
+ at += part.length;
20480
+ }
20481
+ return out;
20482
+ }
20483
+ };
20484
+ }
20306
20485
  function withSitePackages(env2) {
20307
20486
  const existing = env2.PYTHONPATH;
20308
20487
  return {
@@ -20343,6 +20522,8 @@ async function runOwnedPython(ctx, argv) {
20343
20522
  const { manifest, workerUrl } = pythonBackend();
20344
20523
  if (!manifest) throw new Error("no Python runtime manifest is configured");
20345
20524
  const mounts = containerMounts(ctx);
20525
+ const env2 = withSitePackages(ctx.env);
20526
+ const inherit = claimInheritance(env2);
20346
20527
  const process2 = await startPythonProcess({
20347
20528
  manifest,
20348
20529
  workerUrl: await resolveWorkerUrl(workerUrl),
@@ -20351,14 +20532,16 @@ async function runOwnedPython(ctx, argv) {
20351
20532
  pid: ctx.proc.pid,
20352
20533
  ppid: ctx.proc.ppid ?? 1,
20353
20534
  argv,
20354
- env: withSitePackages(ctx.env),
20535
+ env: env2,
20355
20536
  cwd: ctx.cwd,
20356
20537
  isTty: isTerminal(ctx),
20357
20538
  mounts,
20358
20539
  onStdout: (data) => ctx.stdout.write(data),
20359
20540
  onStderr: (data) => ctx.stderr.write(data),
20360
20541
  signal: ctx.signal,
20361
- sockets: ctx.kernel.pod.sockets
20542
+ sockets: ctx.kernel.pod.sockets,
20543
+ processes: createProcessService(ctx),
20544
+ inherit
20362
20545
  });
20363
20546
  void pumpStdin(ctx, process2);
20364
20547
  return process2.wait();