sandboxedjs 0.1.66 → 0.1.68

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;
@@ -314,6 +318,7 @@ declare const Errno: {
314
318
  readonly ELOOP: 40;
315
319
  readonly ENODATA: 61;
316
320
  readonly ENOTSOCK: 88;
321
+ readonly EPROTONOSUPPORT: 93;
317
322
  readonly EAFNOSUPPORT: 97;
318
323
  readonly EADDRINUSE: 98;
319
324
  readonly EADDRNOTAVAIL: 99;
@@ -562,6 +567,36 @@ interface HostAbiProcess {
562
567
  /** Aborts when the process is killed; wakes anything parked in a call. */
563
568
  readonly signal: AbortSignal;
564
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;
565
600
  }
566
601
  declare function createHostAbiServer(proc: HostAbiProcess): (frame: Uint8Array) => Promise<Uint8Array>;
567
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;
@@ -314,6 +318,7 @@ declare const Errno: {
314
318
  readonly ELOOP: 40;
315
319
  readonly ENODATA: 61;
316
320
  readonly ENOTSOCK: 88;
321
+ readonly EPROTONOSUPPORT: 93;
317
322
  readonly EAFNOSUPPORT: 97;
318
323
  readonly EADDRINUSE: 98;
319
324
  readonly EADDRNOTAVAIL: 99;
@@ -562,6 +567,36 @@ interface HostAbiProcess {
562
567
  /** Aborts when the process is killed; wakes anything parked in a call. */
563
568
  readonly signal: AbortSignal;
564
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;
565
600
  }
566
601
  declare function createHostAbiServer(proc: HostAbiProcess): (frame: Uint8Array) => Promise<Uint8Array>;
567
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,
@@ -84,6 +88,7 @@ var Errno = {
84
88
  ELOOP: 40,
85
89
  ENODATA: 61,
86
90
  ENOTSOCK: 88,
91
+ EPROTONOSUPPORT: 93,
87
92
  EAFNOSUPPORT: 97,
88
93
  EADDRINUSE: 98,
89
94
  EADDRNOTAVAIL: 99,
@@ -1270,6 +1275,9 @@ var MAX_TRANSFER = 8 * 1024 * 1024;
1270
1275
  var POLLIN = 1;
1271
1276
  var POLLOUT = 4;
1272
1277
  var POLLHUP = 16;
1278
+ var AF_INET = 2;
1279
+ var SOCK_STREAM = 1;
1280
+ var IPPROTO_TCP = 6;
1273
1281
  function createHostAbiServer(proc) {
1274
1282
  const files = new FileService(proc.vfs, proc.cred);
1275
1283
  const started = Date.now();
@@ -1507,6 +1515,46 @@ function createHostAbiServer(proc) {
1507
1515
  const joined = Object.entries(proc.env).map(([key, value]) => `${key}=${value}`).join("\0");
1508
1516
  return { status: 0, payload: new Writer().string(joined).finish() };
1509
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
+ }
1510
1558
  /* ---------------------------------------------------------- entropy */
1511
1559
  case Op.getrandom: {
1512
1560
  const length = Math.min(r.u32(), 65536);
@@ -1519,7 +1567,8 @@ function createHostAbiServer(proc) {
1519
1567
  const family = r.u32();
1520
1568
  const type = r.u32();
1521
1569
  const protocol = r.i32();
1522
- if (family !== 2 || (type & 15) !== 1 || protocol !== 0) throw new PosixError(Errno.EAFNOSUPPORT);
1570
+ if (family !== AF_INET || (type & 15) !== SOCK_STREAM) throw new PosixError(Errno.EAFNOSUPPORT);
1571
+ if (protocol !== 0 && protocol !== IPPROTO_TCP) throw new PosixError(Errno.EPROTONOSUPPORT);
1523
1572
  if (!proc.sockets) throw new PosixError(Errno.ENOSYS);
1524
1573
  return { status: proc.table.add(new SocketDescription(new VirtualTcpSocket(proc.sockets))) };
1525
1574
  }