@agentswf/sandbox 0.1.0-beta.0 → 0.1.0-beta.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentswf/sandbox",
3
- "version": "0.1.0-beta.0",
3
+ "version": "0.1.0-beta.2",
4
4
  "description": "agents.wf: the sandboxes a workflow opens, and their providers",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/romanlv/agentswf",
@@ -28,7 +28,7 @@
28
28
  "docker"
29
29
  ],
30
30
  "dependencies": {
31
- "@agentswf/contract": "0.1.0-beta.0"
31
+ "@agentswf/contract": "0.1.0-beta.2"
32
32
  },
33
33
  "publishConfig": {
34
34
  "access": "public"
@@ -133,6 +133,11 @@ async function openBox(
133
133
  }
134
134
  return result.stdout.trim();
135
135
  };
136
+ if (spec.sockets.length > 0) {
137
+ throw new Error(
138
+ `docker: a box cannot reach host sockets (${spec.sockets.join(", ")}); use srt`,
139
+ );
140
+ }
136
141
  const socket = daemonSocket(client.environment);
137
142
  for (const path of [spec.cwd, ...spec.read, ...spec.write]) {
138
143
  // A box that can reach the daemon can start a container with the host mounted.
package/src/resolve.ts CHANGED
@@ -5,7 +5,7 @@ import { isRecord } from "@agentswf/contract";
5
5
  import { SANDBOX_ENVIRONMENTS, type SandboxEnvironmentKey } from "@agentswf/contract/workflow";
6
6
  import type { Gitdir, ResolvedSandbox, SandboxProviders } from "./seam";
7
7
 
8
- const REACH_KEYS = ["read", "write", "network"] as const;
8
+ const REACH_KEYS = ["read", "write", "network", "sockets", "webSearch"] as const;
9
9
 
10
10
  export type SandboxResolution = {
11
11
  /** The environment key the sandbox runs under: the one the spec named, else the default. */
@@ -110,7 +110,7 @@ export async function resolveSandbox(
110
110
  await resolvePath(spec.cwd === undefined ? "." : spec.cwd, options.cwd, home, "cwd"),
111
111
  "cwd",
112
112
  );
113
- const paths = (field: "read" | "write") =>
113
+ const paths = (field: "read" | "write" | "sockets") =>
114
114
  Promise.all(
115
115
  list(spec[field], field).map(async (path) =>
116
116
  forbidden(await resolvePath(path, cwd, home, field), field),
@@ -118,6 +118,10 @@ export async function resolveSandbox(
118
118
  );
119
119
  const write = unique(await paths("write"));
120
120
  const read = unique(await paths("read")).filter((path) => !write.includes(path));
121
+ const sockets = unique(await paths("sockets"));
122
+ if (spec.webSearch !== undefined && typeof spec.webSearch !== "boolean") {
123
+ throw new Error("sandbox webSearch must be true or false");
124
+ }
121
125
  const network = unique(
122
126
  list(spec.network, "network").map((domain) => {
123
127
  if (!isDomain(domain)) throw new Error(`not a sandbox domain: ${JSON.stringify(domain)}`);
@@ -174,6 +178,8 @@ export async function resolveSandbox(
174
178
  read,
175
179
  write,
176
180
  network,
181
+ sockets,
182
+ webSearch: spec.webSearch === true,
177
183
  hidden: holdsRunRoot ? [runRoot] : [],
178
184
  gitdirs: [...gitdirs].map(([path, { own, linked }]): Gitdir => {
179
185
  // A submodule's gitdir is in its superproject's `modules`, which is guarded whole: it is
package/src/srt/index.ts CHANGED
@@ -247,6 +247,8 @@ async function probe(options: SrtOptions, context: SandboxContext): Promise<void
247
247
  read: [],
248
248
  write: [],
249
249
  network: [],
250
+ sockets: [],
251
+ webSearch: false,
250
252
  hidden: [],
251
253
  gitdirs: [],
252
254
  environment: {},
@@ -302,7 +304,8 @@ async function probe(options: SrtOptions, context: SandboxContext): Promise<void
302
304
  }
303
305
 
304
306
  /**
305
- * srt as installed on this machine: 1.0.0 or later on `PATH`. `undefined` when absent. The
307
+ * srt as installed on this machine: the `srt` on `PATH` that answers `--version` (npm releases are
308
+ * 0.0.x; older ones print 1.0.0 outside npm). `undefined` when absent. The
306
309
  * toolchain is what `PATH`'s executables under `~` need: their `bin` directories, and each one's
307
310
  * install tree (X19): a node package's root, a `bin/` beside a `lib/` (a node or a Python venv),
308
311
  * or the executable alone. Never a whole parent of `bin`: `~/.local` holds `state/` and
@@ -324,8 +327,8 @@ export async function findSrt(
324
327
  stderr: "pipe",
325
328
  timeout: VERSION_MS,
326
329
  });
327
- const [major] = version.stdout.toString().trim().split(".").map(Number);
328
- if (version.exitCode !== 0 || major === undefined || !(major >= 1)) return undefined;
330
+ if (version.exitCode !== 0 || !/^\d+\.\d+\.\d+/.test(version.stdout.toString().trim()))
331
+ return undefined;
329
332
  const real = [
330
333
  ...new Set(
331
334
  (await Promise.all(path.map((entry) => realpath(entry).catch(() => undefined)))).filter(
@@ -63,7 +63,11 @@ export function baseProfile(
63
63
  const gitdirs = spec.gitdirs.map((gitdir) => gitdir.path);
64
64
  return {
65
65
  enableWeakerNetworkIsolation: true,
66
- network: { allowedDomains: [...spec.network], deniedDomains: [], allowUnixSockets: [] },
66
+ network: {
67
+ allowedDomains: [...spec.network],
68
+ deniedDomains: [],
69
+ allowUnixSockets: [...spec.sockets],
70
+ },
67
71
  filesystem: {
68
72
  denyRead: unique([host.home, ...DENIED, context.runRoot, ...spec.hidden]),
69
73
  allowRead: unique([
@@ -117,7 +121,11 @@ export function agentProfile(
117
121
  network: {
118
122
  ...base.network,
119
123
  allowedDomains: unique([...base.network.allowedDomains, ...agent.harness.domains]),
120
- allowUnixSockets: [agent.door.endpoint, ...(short ? [short] : [])],
124
+ allowUnixSockets: unique([
125
+ ...base.network.allowUnixSockets,
126
+ agent.door.endpoint,
127
+ ...(short ? [short] : []),
128
+ ]),
121
129
  },
122
130
  filesystem: {
123
131
  ...base.filesystem,