@zhivex-ai/sandbox-docker 0.0.1 → 0.2.0
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/README.md +89 -1
- package/dist/docker-client.d.ts +92 -0
- package/dist/docker-client.d.ts.map +1 -0
- package/dist/docker-client.js +464 -0
- package/dist/docker-client.js.map +1 -0
- package/dist/environment.d.ts +119 -0
- package/dist/environment.d.ts.map +1 -0
- package/dist/environment.js +1056 -0
- package/dist/environment.js.map +1 -0
- package/dist/errors.d.ts +7 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +9 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/memory-docker-client.d.ts +13 -0
- package/dist/memory-docker-client.d.ts.map +1 -0
- package/dist/memory-docker-client.js +348 -0
- package/dist/memory-docker-client.js.map +1 -0
- package/dist/network.d.ts +43 -0
- package/dist/network.d.ts.map +1 -0
- package/dist/network.js +323 -0
- package/dist/network.js.map +1 -0
- package/dist/paths.d.ts +7 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +58 -0
- package/dist/paths.js.map +1 -0
- package/dist/stores.d.ts +42 -0
- package/dist/stores.d.ts.map +1 -0
- package/dist/stores.js +396 -0
- package/dist/stores.js.map +1 -0
- package/dist/stream.d.ts +11 -0
- package/dist/stream.d.ts.map +1 -0
- package/dist/stream.js +75 -0
- package/dist/stream.js.map +1 -0
- package/dist/tar.d.ts +13 -0
- package/dist/tar.d.ts.map +1 -0
- package/dist/tar.js +230 -0
- package/dist/tar.js.map +1 -0
- package/dist/tools.d.ts +35 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +90 -0
- package/dist/tools.js.map +1 -0
- package/docker/Dockerfile +11 -0
- package/package.json +46 -4
package/dist/network.js
ADDED
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
import { createServer } from "node:http";
|
|
2
|
+
import { connect as connectNet, isIP } from "node:net";
|
|
3
|
+
import { lookup } from "node:dns/promises";
|
|
4
|
+
import { isPrivateNetworkHostname } from "@zhivex-ai/core";
|
|
5
|
+
import { SandboxError } from "./errors.js";
|
|
6
|
+
const CONNECT_TIMEOUT_MS = 10_000;
|
|
7
|
+
/** Host-side limits for one allowlist proxy. These are enforced before any tunnel bytes flow. */
|
|
8
|
+
export const allowlistProxyQuotaDefaults = Object.freeze({
|
|
9
|
+
maxConnections: 16,
|
|
10
|
+
maxInFlight: 4,
|
|
11
|
+
maxRequests: 128,
|
|
12
|
+
maxBytes: 8_388_608,
|
|
13
|
+
idleMs: 30_000,
|
|
14
|
+
tunnelMs: 60_000
|
|
15
|
+
});
|
|
16
|
+
const bareAddress = (address) => address.trim().toLowerCase().replace(/^\[/, "").replace(/\]$/, "").split("%")[0] ?? "";
|
|
17
|
+
const dottedIPv4 = (value) => {
|
|
18
|
+
const parts = value.split(".");
|
|
19
|
+
if (parts.length !== 4 || parts.some(part => !/^\d{1,3}$/.test(part)))
|
|
20
|
+
return undefined;
|
|
21
|
+
const numbers = parts.map(Number);
|
|
22
|
+
if (numbers.some(part => part > 255))
|
|
23
|
+
return undefined;
|
|
24
|
+
return [(numbers[0] << 8) | numbers[1], (numbers[2] << 8) | numbers[3]];
|
|
25
|
+
};
|
|
26
|
+
/** Parse an IPv6 literal to 16 bytes, including dotted and hex IPv4 tails. */
|
|
27
|
+
const ipv6Bytes = (address) => {
|
|
28
|
+
let value = bareAddress(address);
|
|
29
|
+
if (!value.includes(":") || value.includes(":::"))
|
|
30
|
+
return undefined;
|
|
31
|
+
const compression = value.indexOf("::");
|
|
32
|
+
if (compression !== value.lastIndexOf("::"))
|
|
33
|
+
return undefined;
|
|
34
|
+
const expand = (side) => {
|
|
35
|
+
if (side === "")
|
|
36
|
+
return [];
|
|
37
|
+
const labels = side.split(":");
|
|
38
|
+
const groups = [];
|
|
39
|
+
for (let index = 0; index < labels.length; index += 1) {
|
|
40
|
+
const label = labels[index];
|
|
41
|
+
if (label.includes(".")) {
|
|
42
|
+
if (index !== labels.length - 1)
|
|
43
|
+
return undefined;
|
|
44
|
+
const tail = dottedIPv4(label);
|
|
45
|
+
if (!tail)
|
|
46
|
+
return undefined;
|
|
47
|
+
groups.push(tail[0], tail[1]);
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (!/^[0-9a-f]{1,4}$/.test(label))
|
|
51
|
+
return undefined;
|
|
52
|
+
groups.push(Number.parseInt(label, 16));
|
|
53
|
+
}
|
|
54
|
+
return groups;
|
|
55
|
+
};
|
|
56
|
+
let groups;
|
|
57
|
+
if (compression === -1) {
|
|
58
|
+
const parsed = expand(value);
|
|
59
|
+
if (!parsed || parsed.length !== 8)
|
|
60
|
+
return undefined;
|
|
61
|
+
groups = parsed;
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
const left = expand(value.slice(0, compression));
|
|
65
|
+
const right = expand(value.slice(compression + 2));
|
|
66
|
+
if (!left || !right || left.length + right.length > 7)
|
|
67
|
+
return undefined;
|
|
68
|
+
groups = [...left, ...new Array(8 - left.length - right.length).fill(0), ...right];
|
|
69
|
+
}
|
|
70
|
+
const bytes = new Uint8Array(16);
|
|
71
|
+
groups.forEach((group, index) => {
|
|
72
|
+
bytes[index * 2] = (group >> 8) & 0xff;
|
|
73
|
+
bytes[index * 2 + 1] = group & 0xff;
|
|
74
|
+
});
|
|
75
|
+
return bytes;
|
|
76
|
+
};
|
|
77
|
+
const isDocumentationIPv4 = (address) => {
|
|
78
|
+
const parts = address.split(".");
|
|
79
|
+
if (parts.length !== 4 || parts.some(part => !/^\d{1,3}$/.test(part)))
|
|
80
|
+
return false;
|
|
81
|
+
const numbers = parts.map(Number);
|
|
82
|
+
if (numbers.some(part => part > 255))
|
|
83
|
+
return false;
|
|
84
|
+
const [a, b, c] = numbers;
|
|
85
|
+
return (a === 198 && b === 51 && c === 100) || (a === 203 && b === 0 && c === 113);
|
|
86
|
+
};
|
|
87
|
+
const prefix = (bytes, expected) => expected.every((value, index) => bytes[index] === value);
|
|
88
|
+
/** Documentation, discard-only, and deprecated site-local ranges the shared classifier does not cover. */
|
|
89
|
+
const isSpecialIPv6 = (bytes) => prefix(bytes, [0x20, 0x01, 0x0d, 0xb8]) ||
|
|
90
|
+
prefix(bytes, [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]) ||
|
|
91
|
+
prefix(bytes, [0x20, 0x01, 0x00, 0x02, 0x00, 0x00]) ||
|
|
92
|
+
(bytes[0] === 0xfe && (bytes[1] & 0xc0) === 0xc0);
|
|
93
|
+
/**
|
|
94
|
+
* True for non-global addresses. Unparseable input is rejected.
|
|
95
|
+
* Classification of private, mapped, compatible, NAT64, 6to4, and Teredo
|
|
96
|
+
* addresses goes through the shared hostname helper, then local special ranges.
|
|
97
|
+
*/
|
|
98
|
+
export const isPrivateAddress = (address) => {
|
|
99
|
+
const bare = bareAddress(address);
|
|
100
|
+
if (!bare || isPrivateNetworkHostname(bare))
|
|
101
|
+
return true;
|
|
102
|
+
const version = isIP(bare);
|
|
103
|
+
if (version === 4)
|
|
104
|
+
return isDocumentationIPv4(bare);
|
|
105
|
+
if (version !== 6)
|
|
106
|
+
return true;
|
|
107
|
+
const bytes = ipv6Bytes(bare);
|
|
108
|
+
if (!bytes)
|
|
109
|
+
return true;
|
|
110
|
+
return isSpecialIPv6(bytes);
|
|
111
|
+
};
|
|
112
|
+
const domainPattern = /^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}$/i;
|
|
113
|
+
export const normalizeAllowlist = (policy) => {
|
|
114
|
+
const domains = [...new Set(policy.allowedDomains.map(domain => domain.toLowerCase()))].sort();
|
|
115
|
+
const ports = [...new Set(policy.allowedPorts)].sort((left, right) => left - right);
|
|
116
|
+
if (domains.length === 0 || ports.length === 0) {
|
|
117
|
+
throw new SandboxError("NETWORK_ALLOWLIST_EMPTY", "Network allowlist requires at least one domain and one port.");
|
|
118
|
+
}
|
|
119
|
+
for (const domain of domains) {
|
|
120
|
+
if (domain === "localhost" || !domainPattern.test(domain) || isIP(domain)) {
|
|
121
|
+
throw new SandboxError("NETWORK_ALLOWLIST_DOMAIN", `Allowlist domain "${domain}" is not a public DNS name.`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
for (const port of ports) {
|
|
125
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
126
|
+
throw new SandboxError("NETWORK_ALLOWLIST_PORT", `Allowlist port "${String(port)}" is outside 1-65535.`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return { domains, ports };
|
|
130
|
+
};
|
|
131
|
+
const resolveQuotas = (input) => {
|
|
132
|
+
const quotas = { ...allowlistProxyQuotaDefaults, ...input };
|
|
133
|
+
for (const [name, value] of Object.entries(quotas)) {
|
|
134
|
+
if (!Number.isSafeInteger(value) || value < 1) {
|
|
135
|
+
throw new SandboxError("LIMIT", `Allowlist proxy quota ${name} must be a positive integer.`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return quotas;
|
|
139
|
+
};
|
|
140
|
+
export const startAllowlistProxy = async (options) => {
|
|
141
|
+
const normalized = normalizeAllowlist(options.policy);
|
|
142
|
+
const domains = new Set(normalized.domains);
|
|
143
|
+
const ports = new Set(normalized.ports);
|
|
144
|
+
const resolveHost = options.resolveHost ?? (async (hostname) => {
|
|
145
|
+
const records = await lookup(hostname, { all: true, verbatim: true });
|
|
146
|
+
return records.map(record => record.address);
|
|
147
|
+
});
|
|
148
|
+
const authorize = async (host, port) => {
|
|
149
|
+
if (!domains.has(host.toLowerCase()) || !ports.has(port)) {
|
|
150
|
+
throw new SandboxError("NETWORK_DENIED", `Egress to ${host}:${port} is not on the sandbox allowlist.`);
|
|
151
|
+
}
|
|
152
|
+
const addresses = await resolveWithin(resolveHost(host));
|
|
153
|
+
if (addresses.length === 0) {
|
|
154
|
+
throw new SandboxError("NETWORK_DENIED", `Allowlist host ${host} did not resolve.`);
|
|
155
|
+
}
|
|
156
|
+
if (!options.policy.allowPrivateNetworks && addresses.some(isPrivateAddress)) {
|
|
157
|
+
throw new SandboxError("NETWORK_DENIED", `Allowlist host ${host} resolves to a private address.`);
|
|
158
|
+
}
|
|
159
|
+
return addresses;
|
|
160
|
+
};
|
|
161
|
+
const quotas = resolveQuotas(options.quotas);
|
|
162
|
+
const tracked = new Set();
|
|
163
|
+
let inFlight = 0;
|
|
164
|
+
let requests = 0;
|
|
165
|
+
let transferred = 0;
|
|
166
|
+
const server = createServer((_request, response) => {
|
|
167
|
+
response.writeHead(405);
|
|
168
|
+
response.end("Sandbox allowlist proxy accepts CONNECT only.");
|
|
169
|
+
});
|
|
170
|
+
server.on("connection", socket => {
|
|
171
|
+
if (tracked.size >= quotas.maxConnections) {
|
|
172
|
+
socket.end("HTTP/1.1 403 Forbidden\r\n\r\n");
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
tracked.add(socket);
|
|
176
|
+
socket.setTimeout(quotas.idleMs);
|
|
177
|
+
socket.on("timeout", () => socket.destroy());
|
|
178
|
+
socket.on("data", () => socket.setTimeout(quotas.idleMs));
|
|
179
|
+
socket.on("close", () => tracked.delete(socket));
|
|
180
|
+
});
|
|
181
|
+
server.on("connect", (request, clientSocket, head) => {
|
|
182
|
+
const socket = clientSocket;
|
|
183
|
+
socket.on("error", () => undefined);
|
|
184
|
+
void (async () => {
|
|
185
|
+
const target = connectTarget(request.url ?? "");
|
|
186
|
+
let upstream;
|
|
187
|
+
let counted = false;
|
|
188
|
+
let tunnelTimer;
|
|
189
|
+
const closeBoth = () => {
|
|
190
|
+
if (tunnelTimer)
|
|
191
|
+
clearTimeout(tunnelTimer);
|
|
192
|
+
if (!socket.destroyed)
|
|
193
|
+
socket.destroy();
|
|
194
|
+
if (upstream && !upstream.destroyed)
|
|
195
|
+
upstream.destroy();
|
|
196
|
+
};
|
|
197
|
+
try {
|
|
198
|
+
if (!tracked.has(socket)) {
|
|
199
|
+
socket.destroy();
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
requests += 1;
|
|
203
|
+
if (requests > quotas.maxRequests || transferred >= quotas.maxBytes) {
|
|
204
|
+
throw new SandboxError("NETWORK_DENIED", "Allowlist proxy quota is exhausted.");
|
|
205
|
+
}
|
|
206
|
+
if (inFlight >= quotas.maxInFlight) {
|
|
207
|
+
throw new SandboxError("NETWORK_DENIED", "Allowlist proxy connection limit reached.");
|
|
208
|
+
}
|
|
209
|
+
if (!target)
|
|
210
|
+
throw new SandboxError("NETWORK_DENIED", "CONNECT target is missing a host.");
|
|
211
|
+
inFlight += 1;
|
|
212
|
+
counted = true;
|
|
213
|
+
await authorize(target.host, target.port);
|
|
214
|
+
const fresh = await resolveWithin(resolveHost(target.host));
|
|
215
|
+
const chosen = fresh[0];
|
|
216
|
+
if (!chosen || (!options.policy.allowPrivateNetworks && fresh.some(isPrivateAddress))) {
|
|
217
|
+
throw new SandboxError("NETWORK_DENIED", `Allowlist host ${target.host} resolves to a private address.`);
|
|
218
|
+
}
|
|
219
|
+
upstream = connectNet(target.port, chosen);
|
|
220
|
+
upstream.on("error", () => undefined);
|
|
221
|
+
await new Promise((resolve, reject) => {
|
|
222
|
+
const timer = setTimeout(() => {
|
|
223
|
+
upstream?.destroy();
|
|
224
|
+
reject(new SandboxError("NETWORK_DENIED", "Allowlist connection timed out."));
|
|
225
|
+
}, CONNECT_TIMEOUT_MS);
|
|
226
|
+
upstream?.once("connect", () => {
|
|
227
|
+
clearTimeout(timer);
|
|
228
|
+
resolve();
|
|
229
|
+
});
|
|
230
|
+
upstream?.once("error", error => {
|
|
231
|
+
clearTimeout(timer);
|
|
232
|
+
reject(error);
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
if (!options.policy.allowPrivateNetworks && isPrivateAddress(chosen)) {
|
|
236
|
+
throw new SandboxError("NETWORK_DENIED", `Allowlist host ${target.host} resolves to a private address.`);
|
|
237
|
+
}
|
|
238
|
+
inFlight -= 1;
|
|
239
|
+
counted = false;
|
|
240
|
+
socket.write("HTTP/1.1 200 Connection Established\r\n\r\n");
|
|
241
|
+
if (head.length) {
|
|
242
|
+
transferred += head.length;
|
|
243
|
+
if (transferred > quotas.maxBytes)
|
|
244
|
+
throw new SandboxError("NETWORK_DENIED", "Allowlist proxy byte quota is exhausted.");
|
|
245
|
+
upstream.write(head);
|
|
246
|
+
}
|
|
247
|
+
tunnelTimer = setTimeout(closeBoth, quotas.tunnelMs);
|
|
248
|
+
const relay = (from, to) => {
|
|
249
|
+
from.on("data", (chunk) => {
|
|
250
|
+
transferred += chunk.length;
|
|
251
|
+
if (transferred > quotas.maxBytes) {
|
|
252
|
+
closeBoth();
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
from.setTimeout(quotas.idleMs);
|
|
256
|
+
if (!to.write(chunk))
|
|
257
|
+
from.pause();
|
|
258
|
+
});
|
|
259
|
+
to.on("drain", () => from.resume());
|
|
260
|
+
};
|
|
261
|
+
upstream.setTimeout(quotas.idleMs);
|
|
262
|
+
upstream.on("timeout", closeBoth);
|
|
263
|
+
relay(socket, upstream);
|
|
264
|
+
relay(upstream, socket);
|
|
265
|
+
socket.on("close", () => upstream?.destroy());
|
|
266
|
+
upstream.on("close", () => socket.destroy());
|
|
267
|
+
socket.on("error", closeBoth);
|
|
268
|
+
upstream.on("error", closeBoth);
|
|
269
|
+
}
|
|
270
|
+
catch {
|
|
271
|
+
if (counted)
|
|
272
|
+
inFlight -= 1;
|
|
273
|
+
upstream?.destroy();
|
|
274
|
+
if (!socket.destroyed)
|
|
275
|
+
socket.end("HTTP/1.1 403 Forbidden\r\n\r\n");
|
|
276
|
+
}
|
|
277
|
+
})();
|
|
278
|
+
});
|
|
279
|
+
await new Promise((resolve, reject) => {
|
|
280
|
+
server.once("error", reject);
|
|
281
|
+
server.listen(0, options.host ?? "127.0.0.1", () => resolve());
|
|
282
|
+
});
|
|
283
|
+
const address = server.address();
|
|
284
|
+
if (!address || typeof address === "string") {
|
|
285
|
+
throw new SandboxError("NETWORK_PROXY", "Allowlist proxy did not bind a TCP port.");
|
|
286
|
+
}
|
|
287
|
+
return {
|
|
288
|
+
port: address.port,
|
|
289
|
+
close: () => new Promise(resolve => {
|
|
290
|
+
for (const socket of [...tracked])
|
|
291
|
+
socket.destroy();
|
|
292
|
+
server.close(() => resolve());
|
|
293
|
+
})
|
|
294
|
+
};
|
|
295
|
+
};
|
|
296
|
+
const connectTarget = (value) => {
|
|
297
|
+
const target = value.trim();
|
|
298
|
+
if (target.startsWith("[")) {
|
|
299
|
+
const end = target.indexOf("]");
|
|
300
|
+
if (end <= 1 || target[end + 1] !== ":")
|
|
301
|
+
return undefined;
|
|
302
|
+
const port = Number(target.slice(end + 2));
|
|
303
|
+
return { host: target.slice(1, end), port };
|
|
304
|
+
}
|
|
305
|
+
const colon = target.lastIndexOf(":");
|
|
306
|
+
if (colon <= 0)
|
|
307
|
+
return undefined;
|
|
308
|
+
return { host: target.slice(0, colon), port: Number(target.slice(colon + 1)) };
|
|
309
|
+
};
|
|
310
|
+
const resolveWithin = async (pending) => {
|
|
311
|
+
let timer;
|
|
312
|
+
try {
|
|
313
|
+
return await new Promise((resolve, reject) => {
|
|
314
|
+
timer = setTimeout(() => reject(new SandboxError("NETWORK_DENIED", "Allowlist DNS lookup exceeded its deadline.")), CONNECT_TIMEOUT_MS);
|
|
315
|
+
pending.then(resolve, reject);
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
finally {
|
|
319
|
+
if (timer)
|
|
320
|
+
clearTimeout(timer);
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
//# sourceMappingURL=network.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"network.js","sourceRoot":"","sources":["../src/network.ts"],"names":[],"mappings":"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"}
|
package/dist/paths.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const assertWorkspaceRoot: (root: string) => string;
|
|
2
|
+
/** Resolve a caller path and reject escapes outside the sandbox workspace. */
|
|
3
|
+
export declare const resolveWorkspacePath: (root: string, input: string) => string;
|
|
4
|
+
export declare const assertRelativeWorkspacePath: (input: string) => string;
|
|
5
|
+
/** Allow argv0 as a bare name or an executable under /bin or /usr/bin. */
|
|
6
|
+
export declare const commandBaseName: (argv0: string) => string;
|
|
7
|
+
//# sourceMappingURL=paths.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,mBAAmB,SAAU,MAAM,KAAG,MAQlD,CAAC;AAEF,8EAA8E;AAC9E,eAAO,MAAM,oBAAoB,SAAU,MAAM,SAAS,MAAM,KAAG,MAmBlE,CAAC;AAEF,eAAO,MAAM,2BAA2B,UAAW,MAAM,KAAG,MAQ3D,CAAC;AAEF,0EAA0E;AAC1E,eAAO,MAAM,eAAe,UAAW,MAAM,KAAG,MAgB/C,CAAC"}
|
package/dist/paths.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { SandboxError } from "./errors.js";
|
|
2
|
+
const WORKSPACE_ROOT = "/workspace";
|
|
3
|
+
export const assertWorkspaceRoot = (root) => {
|
|
4
|
+
if (root !== WORKSPACE_ROOT) {
|
|
5
|
+
throw new SandboxError("WORKSPACE_ROOT", `Sandbox workspace root must be ${WORKSPACE_ROOT}.`);
|
|
6
|
+
}
|
|
7
|
+
return root;
|
|
8
|
+
};
|
|
9
|
+
/** Resolve a caller path and reject escapes outside the sandbox workspace. */
|
|
10
|
+
export const resolveWorkspacePath = (root, input) => {
|
|
11
|
+
assertWorkspaceRoot(root);
|
|
12
|
+
if (typeof input !== "string" || input.length === 0 || input.length > 4096 || input.includes("\0")) {
|
|
13
|
+
throw new SandboxError("PATH_INVALID", "Sandbox path must be a non-empty string without NUL bytes.");
|
|
14
|
+
}
|
|
15
|
+
const candidate = input.startsWith("/") ? input : `${root}/${input}`;
|
|
16
|
+
const parts = [];
|
|
17
|
+
for (const part of candidate.split("/")) {
|
|
18
|
+
if (part === "" || part === ".")
|
|
19
|
+
continue;
|
|
20
|
+
if (part === "..") {
|
|
21
|
+
throw new SandboxError("PATH_ESCAPE", "Sandbox path cannot contain '..'.");
|
|
22
|
+
}
|
|
23
|
+
parts.push(part);
|
|
24
|
+
}
|
|
25
|
+
const resolved = `/${parts.join("/")}`;
|
|
26
|
+
if (resolved !== root && !resolved.startsWith(`${root}/`)) {
|
|
27
|
+
throw new SandboxError("PATH_ESCAPE", "Sandbox path is outside the workspace.");
|
|
28
|
+
}
|
|
29
|
+
return resolved;
|
|
30
|
+
};
|
|
31
|
+
export const assertRelativeWorkspacePath = (input) => {
|
|
32
|
+
if (typeof input !== "string" || input.length === 0 || input.length > 4096 || input.includes("\0")) {
|
|
33
|
+
throw new SandboxError("PATH_INVALID", "Declared workspace path must be a non-empty relative string.");
|
|
34
|
+
}
|
|
35
|
+
if (input.startsWith("/") || input.split("/").some(part => part === "" || part === "." || part === "..")) {
|
|
36
|
+
throw new SandboxError("PATH_ESCAPE", `Declared workspace path "${input}" must stay inside the workspace.`);
|
|
37
|
+
}
|
|
38
|
+
return input;
|
|
39
|
+
};
|
|
40
|
+
/** Allow argv0 as a bare name or an executable under /bin or /usr/bin. */
|
|
41
|
+
export const commandBaseName = (argv0) => {
|
|
42
|
+
if (argv0.length === 0 || argv0.includes("\0") || argv0.includes("..") || argv0.startsWith("-")) {
|
|
43
|
+
throw new SandboxError("COMMAND_INVALID", "Sandbox command is not an allowlisted executable name.");
|
|
44
|
+
}
|
|
45
|
+
if (argv0.startsWith("/")) {
|
|
46
|
+
const slash = argv0.lastIndexOf("/");
|
|
47
|
+
const directory = argv0.slice(0, slash);
|
|
48
|
+
if (directory !== "/bin" && directory !== "/usr/bin") {
|
|
49
|
+
throw new SandboxError("COMMAND_INVALID", "Sandbox command must live in /bin or /usr/bin.");
|
|
50
|
+
}
|
|
51
|
+
return argv0.slice(slash + 1);
|
|
52
|
+
}
|
|
53
|
+
if (argv0.includes("/")) {
|
|
54
|
+
throw new SandboxError("COMMAND_INVALID", "Sandbox command cannot be a relative path.");
|
|
55
|
+
}
|
|
56
|
+
return argv0;
|
|
57
|
+
};
|
|
58
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"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"}
|
package/dist/stores.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export interface SandboxLease {
|
|
2
|
+
schemaVersion: 1;
|
|
3
|
+
runId: string;
|
|
4
|
+
scopeKey: string;
|
|
5
|
+
environmentId: string;
|
|
6
|
+
fingerprint: string;
|
|
7
|
+
workspaceId?: string;
|
|
8
|
+
containerId: string;
|
|
9
|
+
networkId?: string;
|
|
10
|
+
snapshotId?: string;
|
|
11
|
+
revision: number;
|
|
12
|
+
status: "active" | "released" | "expired";
|
|
13
|
+
createdAt: number;
|
|
14
|
+
expiresAt: number;
|
|
15
|
+
}
|
|
16
|
+
export interface SandboxSnapshot {
|
|
17
|
+
schemaVersion: 1;
|
|
18
|
+
id: string;
|
|
19
|
+
runId: string;
|
|
20
|
+
scopeKey: string;
|
|
21
|
+
mode: "workspace-tar" | "docker-commit";
|
|
22
|
+
imageId?: string;
|
|
23
|
+
/** Images still to delete after a replacement. Empty once every deletion has succeeded. */
|
|
24
|
+
pendingImageIds?: string[];
|
|
25
|
+
workspaceTar: Uint8Array;
|
|
26
|
+
createdAt: number;
|
|
27
|
+
}
|
|
28
|
+
export interface SandboxLeaseStore {
|
|
29
|
+
load(runId: string, scopeKey: string): Promise<SandboxLease | undefined>;
|
|
30
|
+
compareAndSwap(lease: SandboxLease, expectedRevision: number | undefined): Promise<SandboxLease>;
|
|
31
|
+
list(): Promise<SandboxLease[]>;
|
|
32
|
+
}
|
|
33
|
+
export interface SandboxSnapshotStore {
|
|
34
|
+
load(runId: string, scopeKey: string): Promise<SandboxSnapshot | undefined>;
|
|
35
|
+
save(snapshot: SandboxSnapshot): Promise<void>;
|
|
36
|
+
remove(runId: string, scopeKey: string): Promise<void>;
|
|
37
|
+
}
|
|
38
|
+
export declare const createMemorySandboxLeaseStore: () => SandboxLeaseStore;
|
|
39
|
+
export declare const createMemorySandboxSnapshotStore: () => SandboxSnapshotStore;
|
|
40
|
+
export declare const createFileSandboxLeaseStore: (directory: string) => SandboxLeaseStore;
|
|
41
|
+
export declare const createFileSandboxSnapshotStore: (directory: string) => SandboxSnapshotStore;
|
|
42
|
+
//# sourceMappingURL=stores.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stores.d.ts","sourceRoot":"","sources":["../src/stores.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,YAAY;IAC3B,aAAa,EAAE,CAAC,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAC;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,aAAa,EAAE,CAAC,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,eAAe,GAAG,eAAe,CAAC;IACxC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2FAA2F;IAC3F,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,YAAY,EAAE,UAAU,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,CAAC;IACzE,cAAc,CAAC,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IACjG,IAAI,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;CACjC;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC,CAAC;IAC5E,IAAI,CAAC,QAAQ,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxD;AAgBD,eAAO,MAAM,6BAA6B,QAAO,iBA2BhD,CAAC;AAEF,eAAO,MAAM,gCAAgC,QAAO,oBA+BnD,CAAC;AA0MF,eAAO,MAAM,2BAA2B,cAAe,MAAM,KAAG,iBAyD/D,CAAC;AAEF,eAAO,MAAM,8BAA8B,cAAe,MAAM,KAAG,oBA6ClE,CAAC"}
|