sandboxedjs 0.1.92 → 0.1.94

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.
Binary file
@@ -18,9 +18,9 @@
18
18
  "artifacts": {
19
19
  "moduleUrl": "./python.js",
20
20
  "hashes": {
21
- "python.js": "sha256-ec01d17c48c14a6d442bb06e3351570fe3e0d8bfc857ba2c469e6fe58cbe0860",
22
- "python.wasm": "sha256-f6d74973d23afb6401481343eee0190d3617a472c5bd36ed204785b13dcb8ce6",
23
- "python.data": "sha256-69671e9d5e40b71da03fe8e8f9afc97f4563f6f9cadfcad06e8391963b247912",
21
+ "python.js": "sha256-ba225e5b4900449d493f213dc67263ed1afa7497f36582c43d778ccc282fe749",
22
+ "python.wasm": "sha256-a971913c97e671ec281c92c2922e0c64b3e2c9565c58a466a9dda78df5bb8e7a",
23
+ "python.data": "sha256-98d1fd943b6530e6ac7dd816420efe81082510da14c49bdc06fa60f5b08b924e",
24
24
  "python.worker.js": "sha256-63bb1df52c4e3a95eae7b2d77c98554188fcc5925e72628ea0c34d5c573977a5"
25
25
  }
26
26
  },
@@ -49,6 +49,7 @@ var Op = {
49
49
  setsockopt: 1802,
50
50
  getsockopt: 1803,
51
51
  socket_close: 1804,
52
+ resolve: 1805,
52
53
  spawn: 2048,
53
54
  waitpid: 2049,
54
55
  kill: 2050
@@ -1120,6 +1121,8 @@ var VirtualTcpSocket = class _VirtualTcpSocket {
1120
1121
  network;
1121
1122
  listenerValue = null;
1122
1123
  connectionValue = null;
1124
+ /** Held so closing the guest's socket closes the host's, not just the queues. */
1125
+ hostConnection = null;
1123
1126
  boundPort = 0;
1124
1127
  boundAddress = "0.0.0.0";
1125
1128
  bind(address, port) {
@@ -1137,13 +1140,35 @@ var VirtualTcpSocket = class _VirtualTcpSocket {
1137
1140
  }
1138
1141
  this.listenerValue = this.network.listen(this.boundPort, this.boundAddress, backlog);
1139
1142
  }
1140
- connect(port, address = "127.0.0.1") {
1143
+ /**
1144
+ * Connect, in the container or out of it.
1145
+ *
1146
+ * The destination used to be discarded, which quietly turned every connect
1147
+ * into a loopback connect: a guest dialling example.com:443 would reach a
1148
+ * container server listening on 443 and believe it had reached the internet.
1149
+ * Loopback is still routed inside the container; anything else goes to the
1150
+ * host's outbound stack, which owns the names and the real sockets, and
1151
+ * which refuses with a reason when the host has no way out.
1152
+ */
1153
+ async connect(port, address = "127.0.0.1") {
1141
1154
  if (this.listenerValue || this.connectionValue) throw new PosixError(Errno.EISCONN);
1142
1155
  if (!isLoopback(address)) {
1143
- throw new PosixError(
1144
- Errno.ENETUNREACH,
1145
- `cannot reach ${address}:${port}: outbound network access is not available through raw sockets in this container; only loopback is routed`
1146
- );
1156
+ const outbound = this.network.outbound;
1157
+ if (!outbound) {
1158
+ throw new PosixError(
1159
+ Errno.ENETUNREACH,
1160
+ `cannot reach ${address}:${port}: outbound network access is not available through raw sockets in this container; only loopback is routed`
1161
+ );
1162
+ }
1163
+ const dialled = await outbound.connect(address, port, {
1164
+ address: this.boundAddress,
1165
+ port: this.boundPort
1166
+ });
1167
+ this.connectionValue = dialled.connection;
1168
+ this.hostConnection = dialled.host;
1169
+ this.boundPort = dialled.connection.local.port;
1170
+ this.boundAddress = dialled.connection.local.address;
1171
+ return;
1147
1172
  }
1148
1173
  this.connectionValue = this.network.connect(port, this.boundPort, this.boundAddress);
1149
1174
  this.boundPort = this.connectionValue.local.port;
@@ -1194,6 +1219,8 @@ var VirtualTcpSocket = class _VirtualTcpSocket {
1194
1219
  close() {
1195
1220
  if (this.listenerValue) this.network.close(this.listenerValue);
1196
1221
  this.connectionValue?.close();
1222
+ this.hostConnection?.close();
1223
+ this.hostConnection = null;
1197
1224
  this.listenerValue = null;
1198
1225
  this.connectionValue = null;
1199
1226
  }
@@ -1258,7 +1285,7 @@ var SocketDescription = class _SocketDescription {
1258
1285
  this.resource.listen(backlog);
1259
1286
  }
1260
1287
  connect(address, port) {
1261
- this.resource.connect(port, address);
1288
+ return this.resource.connect(port, address);
1262
1289
  }
1263
1290
  accept() {
1264
1291
  return new _SocketDescription(this.resource.accept(), this.flags);
@@ -1270,7 +1297,11 @@ var SocketDescription = class _SocketDescription {
1270
1297
  this.options.set(`${level}:${option}`, value.slice());
1271
1298
  }
1272
1299
  getOption(level, option) {
1273
- return this.options.get(`${level}:${option}`)?.slice() ?? new Uint8Array(4);
1300
+ const stored = this.options.get(`${level}:${option}`);
1301
+ if (stored) return stored.slice();
1302
+ if (level === SOL_SOCKET && option === SO_TYPE) return int32(SOCK_STREAM);
1303
+ if (level === SOL_SOCKET && option === SO_ERROR) return int32(0);
1304
+ return new Uint8Array(4);
1274
1305
  }
1275
1306
  localAddress() {
1276
1307
  return this.resource.local;
@@ -1281,6 +1312,15 @@ var SocketDescription = class _SocketDescription {
1281
1312
  return peer;
1282
1313
  }
1283
1314
  };
1315
+ var SOL_SOCKET = 1;
1316
+ var SO_ERROR = 4;
1317
+ var SO_TYPE = 3;
1318
+ var SOCK_STREAM = 1;
1319
+ function int32(value) {
1320
+ const bytes = new Uint8Array(4);
1321
+ new DataView(bytes.buffer).setInt32(0, value, true);
1322
+ return bytes;
1323
+ }
1284
1324
  function isLoopback(address) {
1285
1325
  if (address === "" || address === "localhost" || address === "0.0.0.0") return true;
1286
1326
  if (address === "::1" || address === "::" || address === "[::1]") return true;
@@ -1293,7 +1333,7 @@ var POLLIN = 1;
1293
1333
  var POLLOUT = 4;
1294
1334
  var POLLHUP = 16;
1295
1335
  var AF_INET = 2;
1296
- var SOCK_STREAM = 1;
1336
+ var SOCK_STREAM2 = 1;
1297
1337
  var IPPROTO_TCP = 6;
1298
1338
  function createHostAbiServer(proc) {
1299
1339
  const files = new FileService(proc.vfs, proc.cred);
@@ -1584,7 +1624,7 @@ function createHostAbiServer(proc) {
1584
1624
  const family = r.u32();
1585
1625
  const type = r.u32();
1586
1626
  const protocol = r.i32();
1587
- if (family !== AF_INET || (type & 15) !== SOCK_STREAM) throw new PosixError(Errno.EAFNOSUPPORT);
1627
+ if (family !== AF_INET || (type & 15) !== SOCK_STREAM2) throw new PosixError(Errno.EAFNOSUPPORT);
1588
1628
  if (protocol !== 0 && protocol !== IPPROTO_TCP) throw new PosixError(Errno.EPROTONOSUPPORT);
1589
1629
  if (!proc.sockets) throw new PosixError(Errno.ENOSYS);
1590
1630
  return { status: proc.table.add(new SocketDescription(new VirtualTcpSocket(proc.sockets))) };
@@ -1608,9 +1648,15 @@ function createHostAbiServer(proc) {
1608
1648
  }
1609
1649
  case Op.connect: {
1610
1650
  const description = socketDescription(proc.table.get(r.i32()));
1611
- description.connect(r.string(), r.u32());
1651
+ await description.connect(r.string(), r.u32());
1612
1652
  return { status: 0 };
1613
1653
  }
1654
+ case Op.resolve: {
1655
+ const sockets = proc.sockets;
1656
+ if (!sockets?.outbound) throw new PosixError(Errno.ENOSYS);
1657
+ const address = sockets.outbound.resolve(r.string());
1658
+ return { status: 0, payload: new Writer().string(address).finish() };
1659
+ }
1614
1660
  case Op.send: {
1615
1661
  const description = socketDescription(proc.table.get(r.i32()));
1616
1662
  return { status: await writeBlocking(description, r.bytes32()) };
@@ -1,6 +1,6 @@
1
- import { V as Vfs, C as Cred, a as VirtualTcpNetwork } from './contracts-BkWBTH8E.cjs';
2
- export { m as ROOT_CRED, v as makeCred } from './contracts-BkWBTH8E.cjs';
3
- export { M as MemoryVolume } from './memory-volume-DCm-DiXH.cjs';
1
+ import { V as Vfs, C as Cred, a as VirtualTcpNetwork } from './contracts-B6SHFjma.cjs';
2
+ export { m as ROOT_CRED, v as makeCred } from './contracts-B6SHFjma.cjs';
3
+ export { M as MemoryVolume } from './memory-volume-CRhXUyCN.cjs';
4
4
 
5
5
  /**
6
6
  * Open-file descriptions: the thing a descriptor points *at*.
@@ -275,6 +275,7 @@ declare const Op: {
275
275
  readonly setsockopt: 1802;
276
276
  readonly getsockopt: 1803;
277
277
  readonly socket_close: 1804;
278
+ readonly resolve: 1805;
278
279
  readonly spawn: 2048;
279
280
  readonly waitpid: 2049;
280
281
  readonly kill: 2050;
@@ -1,6 +1,6 @@
1
- import { V as Vfs, C as Cred, a as VirtualTcpNetwork } from './contracts-BkWBTH8E.js';
2
- export { m as ROOT_CRED, v as makeCred } from './contracts-BkWBTH8E.js';
3
- export { M as MemoryVolume } from './memory-volume-DJA-vFYW.js';
1
+ import { V as Vfs, C as Cred, a as VirtualTcpNetwork } from './contracts-B6SHFjma.js';
2
+ export { m as ROOT_CRED, v as makeCred } from './contracts-B6SHFjma.js';
3
+ export { M as MemoryVolume } from './memory-volume-CwiXvRfs.js';
4
4
 
5
5
  /**
6
6
  * Open-file descriptions: the thing a descriptor points *at*.
@@ -275,6 +275,7 @@ declare const Op: {
275
275
  readonly setsockopt: 1802;
276
276
  readonly getsockopt: 1803;
277
277
  readonly socket_close: 1804;
278
+ readonly resolve: 1805;
278
279
  readonly spawn: 2048;
279
280
  readonly waitpid: 2049;
280
281
  readonly kill: 2050;
@@ -47,6 +47,7 @@ var Op = {
47
47
  setsockopt: 1802,
48
48
  getsockopt: 1803,
49
49
  socket_close: 1804,
50
+ resolve: 1805,
50
51
  spawn: 2048,
51
52
  waitpid: 2049,
52
53
  kill: 2050
@@ -1118,6 +1119,8 @@ var VirtualTcpSocket = class _VirtualTcpSocket {
1118
1119
  network;
1119
1120
  listenerValue = null;
1120
1121
  connectionValue = null;
1122
+ /** Held so closing the guest's socket closes the host's, not just the queues. */
1123
+ hostConnection = null;
1121
1124
  boundPort = 0;
1122
1125
  boundAddress = "0.0.0.0";
1123
1126
  bind(address, port) {
@@ -1135,13 +1138,35 @@ var VirtualTcpSocket = class _VirtualTcpSocket {
1135
1138
  }
1136
1139
  this.listenerValue = this.network.listen(this.boundPort, this.boundAddress, backlog);
1137
1140
  }
1138
- connect(port, address = "127.0.0.1") {
1141
+ /**
1142
+ * Connect, in the container or out of it.
1143
+ *
1144
+ * The destination used to be discarded, which quietly turned every connect
1145
+ * into a loopback connect: a guest dialling example.com:443 would reach a
1146
+ * container server listening on 443 and believe it had reached the internet.
1147
+ * Loopback is still routed inside the container; anything else goes to the
1148
+ * host's outbound stack, which owns the names and the real sockets, and
1149
+ * which refuses with a reason when the host has no way out.
1150
+ */
1151
+ async connect(port, address = "127.0.0.1") {
1139
1152
  if (this.listenerValue || this.connectionValue) throw new PosixError(Errno.EISCONN);
1140
1153
  if (!isLoopback(address)) {
1141
- throw new PosixError(
1142
- Errno.ENETUNREACH,
1143
- `cannot reach ${address}:${port}: outbound network access is not available through raw sockets in this container; only loopback is routed`
1144
- );
1154
+ const outbound = this.network.outbound;
1155
+ if (!outbound) {
1156
+ throw new PosixError(
1157
+ Errno.ENETUNREACH,
1158
+ `cannot reach ${address}:${port}: outbound network access is not available through raw sockets in this container; only loopback is routed`
1159
+ );
1160
+ }
1161
+ const dialled = await outbound.connect(address, port, {
1162
+ address: this.boundAddress,
1163
+ port: this.boundPort
1164
+ });
1165
+ this.connectionValue = dialled.connection;
1166
+ this.hostConnection = dialled.host;
1167
+ this.boundPort = dialled.connection.local.port;
1168
+ this.boundAddress = dialled.connection.local.address;
1169
+ return;
1145
1170
  }
1146
1171
  this.connectionValue = this.network.connect(port, this.boundPort, this.boundAddress);
1147
1172
  this.boundPort = this.connectionValue.local.port;
@@ -1192,6 +1217,8 @@ var VirtualTcpSocket = class _VirtualTcpSocket {
1192
1217
  close() {
1193
1218
  if (this.listenerValue) this.network.close(this.listenerValue);
1194
1219
  this.connectionValue?.close();
1220
+ this.hostConnection?.close();
1221
+ this.hostConnection = null;
1195
1222
  this.listenerValue = null;
1196
1223
  this.connectionValue = null;
1197
1224
  }
@@ -1256,7 +1283,7 @@ var SocketDescription = class _SocketDescription {
1256
1283
  this.resource.listen(backlog);
1257
1284
  }
1258
1285
  connect(address, port) {
1259
- this.resource.connect(port, address);
1286
+ return this.resource.connect(port, address);
1260
1287
  }
1261
1288
  accept() {
1262
1289
  return new _SocketDescription(this.resource.accept(), this.flags);
@@ -1268,7 +1295,11 @@ var SocketDescription = class _SocketDescription {
1268
1295
  this.options.set(`${level}:${option}`, value.slice());
1269
1296
  }
1270
1297
  getOption(level, option) {
1271
- return this.options.get(`${level}:${option}`)?.slice() ?? new Uint8Array(4);
1298
+ const stored = this.options.get(`${level}:${option}`);
1299
+ if (stored) return stored.slice();
1300
+ if (level === SOL_SOCKET && option === SO_TYPE) return int32(SOCK_STREAM);
1301
+ if (level === SOL_SOCKET && option === SO_ERROR) return int32(0);
1302
+ return new Uint8Array(4);
1272
1303
  }
1273
1304
  localAddress() {
1274
1305
  return this.resource.local;
@@ -1279,6 +1310,15 @@ var SocketDescription = class _SocketDescription {
1279
1310
  return peer;
1280
1311
  }
1281
1312
  };
1313
+ var SOL_SOCKET = 1;
1314
+ var SO_ERROR = 4;
1315
+ var SO_TYPE = 3;
1316
+ var SOCK_STREAM = 1;
1317
+ function int32(value) {
1318
+ const bytes = new Uint8Array(4);
1319
+ new DataView(bytes.buffer).setInt32(0, value, true);
1320
+ return bytes;
1321
+ }
1282
1322
  function isLoopback(address) {
1283
1323
  if (address === "" || address === "localhost" || address === "0.0.0.0") return true;
1284
1324
  if (address === "::1" || address === "::" || address === "[::1]") return true;
@@ -1291,7 +1331,7 @@ var POLLIN = 1;
1291
1331
  var POLLOUT = 4;
1292
1332
  var POLLHUP = 16;
1293
1333
  var AF_INET = 2;
1294
- var SOCK_STREAM = 1;
1334
+ var SOCK_STREAM2 = 1;
1295
1335
  var IPPROTO_TCP = 6;
1296
1336
  function createHostAbiServer(proc) {
1297
1337
  const files = new FileService(proc.vfs, proc.cred);
@@ -1582,7 +1622,7 @@ function createHostAbiServer(proc) {
1582
1622
  const family = r.u32();
1583
1623
  const type = r.u32();
1584
1624
  const protocol = r.i32();
1585
- if (family !== AF_INET || (type & 15) !== SOCK_STREAM) throw new PosixError(Errno.EAFNOSUPPORT);
1625
+ if (family !== AF_INET || (type & 15) !== SOCK_STREAM2) throw new PosixError(Errno.EAFNOSUPPORT);
1586
1626
  if (protocol !== 0 && protocol !== IPPROTO_TCP) throw new PosixError(Errno.EPROTONOSUPPORT);
1587
1627
  if (!proc.sockets) throw new PosixError(Errno.ENOSYS);
1588
1628
  return { status: proc.table.add(new SocketDescription(new VirtualTcpSocket(proc.sockets))) };
@@ -1606,9 +1646,15 @@ function createHostAbiServer(proc) {
1606
1646
  }
1607
1647
  case Op.connect: {
1608
1648
  const description = socketDescription(proc.table.get(r.i32()));
1609
- description.connect(r.string(), r.u32());
1649
+ await description.connect(r.string(), r.u32());
1610
1650
  return { status: 0 };
1611
1651
  }
1652
+ case Op.resolve: {
1653
+ const sockets = proc.sockets;
1654
+ if (!sockets?.outbound) throw new PosixError(Errno.ENOSYS);
1655
+ const address = sockets.outbound.resolve(r.string());
1656
+ return { status: 0, payload: new Writer().string(address).finish() };
1657
+ }
1612
1658
  case Op.send: {
1613
1659
  const description = socketDescription(proc.table.get(r.i32()));
1614
1660
  return { status: await writeBlocking(description, r.bytes32()) };
@@ -47,6 +47,7 @@ var Op = {
47
47
  setsockopt: 1802,
48
48
  getsockopt: 1803,
49
49
  socket_close: 1804,
50
+ resolve: 1805,
50
51
  spawn: 2048,
51
52
  waitpid: 2049,
52
53
  kill: 2050
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sandboxedjs",
3
- "version": "0.1.92",
3
+ "version": "0.1.94",
4
4
  "description": "A Linux-like container that runs entirely inside Node.js — POSIX shell, ~140 coreutils, Node.js and Python runtimes, virtual filesystem and networking. No Docker, no VM, no native modules.",
5
5
  "type": "module",
6
6
  "license": "MIT",