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.
@@ -275,6 +275,9 @@ declare const Op: {
275
275
  readonly setsockopt: 1802;
276
276
  readonly getsockopt: 1803;
277
277
  readonly socket_close: 1804;
278
+ readonly spawn: 2048;
279
+ readonly waitpid: 2049;
280
+ readonly kill: 2050;
278
281
  };
279
282
  type OpCode = (typeof Op)[keyof typeof Op];
280
283
  /** Canonical (Linux/musl) errno values. A negative status is `-Errno.X`. */
@@ -287,6 +290,7 @@ declare const Errno: {
287
290
  readonly ENXIO: 6;
288
291
  readonly E2BIG: 7;
289
292
  readonly EBADF: 9;
293
+ readonly ECHILD: 10;
290
294
  readonly EAGAIN: 11;
291
295
  readonly ENOMEM: 12;
292
296
  readonly EACCES: 13;
@@ -563,6 +567,36 @@ interface HostAbiProcess {
563
567
  /** Aborts when the process is killed; wakes anything parked in a call. */
564
568
  readonly signal: AbortSignal;
565
569
  readonly sockets?: VirtualTcpNetwork;
570
+ /**
571
+ * How this process makes another one.
572
+ *
573
+ * There is no fork in WebAssembly, so process creation cannot be a libc
574
+ * detail the guest performs on its own — it is a service, like the
575
+ * filesystem. A host that does not supply this reports ENOSYS to every
576
+ * spawn, which is the honest answer and not a crash: a container with no
577
+ * process table genuinely cannot start a child.
578
+ */
579
+ readonly processes?: ProcessService;
580
+ }
581
+ /** The descriptors a child is to be given, by the numbers it will see. */
582
+ interface SpawnRequest {
583
+ readonly argv: string[];
584
+ readonly env: Record<string, string>;
585
+ readonly cwd: string;
586
+ /** `child fd` → the parent's open file description behind it. */
587
+ readonly files: Map<number, OpenFileDescription>;
588
+ }
589
+ interface ProcessService {
590
+ spawn(request: SpawnRequest): Promise<number>;
591
+ /**
592
+ * Reap one child. Returns the pid and its wait status, or a pid of 0 when
593
+ * `WNOHANG` was asked for and nothing has exited yet.
594
+ */
595
+ wait(pid: number, options: number): Promise<{
596
+ pid: number;
597
+ status: number;
598
+ }>;
599
+ kill(pid: number, signal: number): void;
566
600
  }
567
601
  declare function createHostAbiServer(proc: HostAbiProcess): (frame: Uint8Array) => Promise<Uint8Array>;
568
602
 
@@ -275,6 +275,9 @@ declare const Op: {
275
275
  readonly setsockopt: 1802;
276
276
  readonly getsockopt: 1803;
277
277
  readonly socket_close: 1804;
278
+ readonly spawn: 2048;
279
+ readonly waitpid: 2049;
280
+ readonly kill: 2050;
278
281
  };
279
282
  type OpCode = (typeof Op)[keyof typeof Op];
280
283
  /** Canonical (Linux/musl) errno values. A negative status is `-Errno.X`. */
@@ -287,6 +290,7 @@ declare const Errno: {
287
290
  readonly ENXIO: 6;
288
291
  readonly E2BIG: 7;
289
292
  readonly EBADF: 9;
293
+ readonly ECHILD: 10;
290
294
  readonly EAGAIN: 11;
291
295
  readonly ENOMEM: 12;
292
296
  readonly EACCES: 13;
@@ -563,6 +567,36 @@ interface HostAbiProcess {
563
567
  /** Aborts when the process is killed; wakes anything parked in a call. */
564
568
  readonly signal: AbortSignal;
565
569
  readonly sockets?: VirtualTcpNetwork;
570
+ /**
571
+ * How this process makes another one.
572
+ *
573
+ * There is no fork in WebAssembly, so process creation cannot be a libc
574
+ * detail the guest performs on its own — it is a service, like the
575
+ * filesystem. A host that does not supply this reports ENOSYS to every
576
+ * spawn, which is the honest answer and not a crash: a container with no
577
+ * process table genuinely cannot start a child.
578
+ */
579
+ readonly processes?: ProcessService;
580
+ }
581
+ /** The descriptors a child is to be given, by the numbers it will see. */
582
+ interface SpawnRequest {
583
+ readonly argv: string[];
584
+ readonly env: Record<string, string>;
585
+ readonly cwd: string;
586
+ /** `child fd` → the parent's open file description behind it. */
587
+ readonly files: Map<number, OpenFileDescription>;
588
+ }
589
+ interface ProcessService {
590
+ spawn(request: SpawnRequest): Promise<number>;
591
+ /**
592
+ * Reap one child. Returns the pid and its wait status, or a pid of 0 when
593
+ * `WNOHANG` was asked for and nothing has exited yet.
594
+ */
595
+ wait(pid: number, options: number): Promise<{
596
+ pid: number;
597
+ status: number;
598
+ }>;
599
+ kill(pid: number, signal: number): void;
566
600
  }
567
601
  declare function createHostAbiServer(proc: HostAbiProcess): (frame: Uint8Array) => Promise<Uint8Array>;
568
602
 
@@ -46,7 +46,10 @@ var Op = {
46
46
  getpeername: 1801,
47
47
  setsockopt: 1802,
48
48
  getsockopt: 1803,
49
- socket_close: 1804
49
+ socket_close: 1804,
50
+ spawn: 2048,
51
+ waitpid: 2049,
52
+ kill: 2050
50
53
  };
51
54
  var Errno = {
52
55
  EPERM: 1,
@@ -57,6 +60,7 @@ var Errno = {
57
60
  ENXIO: 6,
58
61
  E2BIG: 7,
59
62
  EBADF: 9,
63
+ ECHILD: 10,
60
64
  EAGAIN: 11,
61
65
  ENOMEM: 12,
62
66
  EACCES: 13,
@@ -1511,6 +1515,46 @@ function createHostAbiServer(proc) {
1511
1515
  const joined = Object.entries(proc.env).map(([key, value]) => `${key}=${value}`).join("\0");
1512
1516
  return { status: 0, payload: new Writer().string(joined).finish() };
1513
1517
  }
1518
+ /* --------------------------------------------------------- processes */
1519
+ case Op.spawn: {
1520
+ const argv = [];
1521
+ for (let i = 0, n = r.u32(); i < n; i++) argv.push(r.string());
1522
+ const env = {};
1523
+ for (let i = 0, n = r.u32(); i < n; i++) {
1524
+ const entry = r.string();
1525
+ const split = entry.indexOf("=");
1526
+ if (split > 0) env[entry.slice(0, split)] = entry.slice(split + 1);
1527
+ }
1528
+ const cwd = r.string() || proc.cwd;
1529
+ const files2 = /* @__PURE__ */ new Map();
1530
+ for (let i = 0, n = r.u32(); i < n; i++) {
1531
+ const childFd = r.u32() | 0;
1532
+ const parentFd = r.u32() | 0;
1533
+ files2.set(childFd, proc.table.get(parentFd));
1534
+ }
1535
+ if (!proc.processes) throw new PosixError(Errno.ENOSYS);
1536
+ if (argv.length === 0) throw new PosixError(Errno.EINVAL);
1537
+ return { status: await proc.processes.spawn({ argv, env, cwd: resolve2(cwd), files: files2 }) };
1538
+ }
1539
+ case Op.waitpid: {
1540
+ const pid = r.u32() | 0;
1541
+ const options = r.u32() | 0;
1542
+ if (!proc.processes) throw new PosixError(Errno.ENOSYS);
1543
+ const result = await Promise.race([
1544
+ proc.processes.wait(pid, options),
1545
+ aborted().then(() => {
1546
+ throw new PosixError(Errno.EINTR);
1547
+ })
1548
+ ]);
1549
+ return { status: 0, payload: new Writer().i32(result.pid).i32(result.status).finish() };
1550
+ }
1551
+ case Op.kill: {
1552
+ const pid = r.u32() | 0;
1553
+ const signal = r.u32() | 0;
1554
+ if (!proc.processes) throw new PosixError(Errno.ENOSYS);
1555
+ proc.processes.kill(pid, signal);
1556
+ return { status: 0 };
1557
+ }
1514
1558
  /* ---------------------------------------------------------- entropy */
1515
1559
  case Op.getrandom: {
1516
1560
  const length = Math.min(r.u32(), 65536);