@celestea/tools 2.7.1
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/LICENSE +21 -0
- package/dist/args.d.ts +15 -0
- package/dist/args.js +55 -0
- package/dist/attachments/image-header.d.ts +28 -0
- package/dist/attachments/image-header.js +148 -0
- package/dist/attachments/store.d.ts +56 -0
- package/dist/attachments/store.js +148 -0
- package/dist/browser/cdp.d.ts +162 -0
- package/dist/browser/cdp.js +269 -0
- package/dist/browser/launch.d.ts +92 -0
- package/dist/browser/launch.js +232 -0
- package/dist/browser/memory-guard.d.ts +55 -0
- package/dist/browser/memory-guard.js +210 -0
- package/dist/browser/session.d.ts +146 -0
- package/dist/browser/session.js +371 -0
- package/dist/browser/snapshot.d.ts +69 -0
- package/dist/browser/snapshot.js +163 -0
- package/dist/browser/types.d.ts +51 -0
- package/dist/browser/types.js +8 -0
- package/dist/builtin.d.ts +54 -0
- package/dist/builtin.js +77 -0
- package/dist/desc.d.ts +20 -0
- package/dist/desc.js +22 -0
- package/dist/disclosure.d.ts +113 -0
- package/dist/disclosure.js +141 -0
- package/dist/env.d.ts +10 -0
- package/dist/env.js +33 -0
- package/dist/errors.d.ts +25 -0
- package/dist/errors.js +60 -0
- package/dist/exposure.d.ts +145 -0
- package/dist/exposure.js +244 -0
- package/dist/fn-tool.d.ts +14 -0
- package/dist/fn-tool.js +14 -0
- package/dist/fs/file-io.d.ts +78 -0
- package/dist/fs/file-io.js +239 -0
- package/dist/guard/path-guard.d.ts +144 -0
- package/dist/guard/path-guard.js +289 -0
- package/dist/guard/paths.d.ts +35 -0
- package/dist/guard/paths.js +100 -0
- package/dist/http/errors.d.ts +24 -0
- package/dist/http/errors.js +64 -0
- package/dist/http/headers.d.ts +19 -0
- package/dist/http/headers.js +62 -0
- package/dist/http/redirects.d.ts +31 -0
- package/dist/http/redirects.js +76 -0
- package/dist/http/ssrf.d.ts +105 -0
- package/dist/http/ssrf.js +272 -0
- package/dist/http/transport.d.ts +50 -0
- package/dist/http/transport.js +130 -0
- package/dist/index.d.ts +114 -0
- package/dist/index.js +129 -0
- package/dist/memory/log.d.ts +75 -0
- package/dist/memory/log.js +157 -0
- package/dist/memory/store.d.ts +47 -0
- package/dist/memory/store.js +61 -0
- package/dist/platform/exec.d.ts +79 -0
- package/dist/platform/exec.js +218 -0
- package/dist/platform/index.d.ts +12 -0
- package/dist/platform/index.js +12 -0
- package/dist/platform/paths.d.ts +51 -0
- package/dist/platform/paths.js +60 -0
- package/dist/platform/quote.d.ts +72 -0
- package/dist/platform/quote.js +102 -0
- package/dist/plugin.d.ts +96 -0
- package/dist/plugin.js +101 -0
- package/dist/process/buffers.d.ts +33 -0
- package/dist/process/buffers.js +86 -0
- package/dist/process/registry.d.ts +98 -0
- package/dist/process/registry.js +282 -0
- package/dist/registry.d.ts +52 -0
- package/dist/registry.js +161 -0
- package/dist/run-code/broker.d.ts +68 -0
- package/dist/run-code/broker.js +465 -0
- package/dist/run-code/limits.d.ts +69 -0
- package/dist/run-code/limits.js +88 -0
- package/dist/run-code/lines.d.ts +69 -0
- package/dist/run-code/lines.js +199 -0
- package/dist/run-code/sdk-ts.d.ts +34 -0
- package/dist/run-code/sdk-ts.js +276 -0
- package/dist/run-code/sdk.d.ts +39 -0
- package/dist/run-code/sdk.js +294 -0
- package/dist/sandbox/async.d.ts +10 -0
- package/dist/sandbox/async.js +26 -0
- package/dist/sandbox/bwrap-argv.d.ts +62 -0
- package/dist/sandbox/bwrap-argv.js +113 -0
- package/dist/sandbox/bwrap.d.ts +94 -0
- package/dist/sandbox/bwrap.js +159 -0
- package/dist/sandbox/child.d.ts +38 -0
- package/dist/sandbox/child.js +98 -0
- package/dist/sandbox/config.d.ts +89 -0
- package/dist/sandbox/config.js +149 -0
- package/dist/sandbox/fake-sandbox.d.ts +57 -0
- package/dist/sandbox/fake-sandbox.js +110 -0
- package/dist/sandbox/launch.d.ts +51 -0
- package/dist/sandbox/launch.js +134 -0
- package/dist/sandbox/limits.d.ts +63 -0
- package/dist/sandbox/limits.js +113 -0
- package/dist/sandbox/probe.d.ts +46 -0
- package/dist/sandbox/probe.js +102 -0
- package/dist/sandbox/provider.d.ts +83 -0
- package/dist/sandbox/provider.js +126 -0
- package/dist/sandbox/rlimit.d.ts +60 -0
- package/dist/sandbox/rlimit.js +76 -0
- package/dist/sandbox/seccomp.d.ts +48 -0
- package/dist/sandbox/seccomp.js +115 -0
- package/dist/sandbox/userspace.d.ts +65 -0
- package/dist/sandbox/userspace.js +107 -0
- package/dist/sandbox/workdir.d.ts +13 -0
- package/dist/sandbox/workdir.js +44 -0
- package/dist/schema.d.ts +20 -0
- package/dist/schema.js +135 -0
- package/dist/testing/platform-gates.d.ts +54 -0
- package/dist/testing/platform-gates.js +62 -0
- package/dist/tool-failure.d.ts +13 -0
- package/dist/tool-failure.js +19 -0
- package/dist/tools/ask-user.d.ts +32 -0
- package/dist/tools/ask-user.js +145 -0
- package/dist/tools/browser.d.ts +24 -0
- package/dist/tools/browser.js +132 -0
- package/dist/tools/http-request.d.ts +31 -0
- package/dist/tools/http-request.js +117 -0
- package/dist/tools/list-dir.d.ts +9 -0
- package/dist/tools/list-dir.js +45 -0
- package/dist/tools/load-skill.d.ts +37 -0
- package/dist/tools/load-skill.js +76 -0
- package/dist/tools/memory.d.ts +46 -0
- package/dist/tools/memory.js +131 -0
- package/dist/tools/process-control.d.ts +14 -0
- package/dist/tools/process-control.js +49 -0
- package/dist/tools/read-file.d.ts +11 -0
- package/dist/tools/read-file.js +81 -0
- package/dist/tools/read-image.d.ts +33 -0
- package/dist/tools/read-image.js +144 -0
- package/dist/tools/run-code.d.ts +48 -0
- package/dist/tools/run-code.js +115 -0
- package/dist/tools/run-shell.d.ts +22 -0
- package/dist/tools/run-shell.js +81 -0
- package/dist/tools/write-file.d.ts +8 -0
- package/dist/tools/write-file.js +31 -0
- package/package.json +28 -0
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSRF target policy for `http_request` (`crates/tools/src/http.rs`, W249 P0-3).
|
|
3
|
+
*
|
|
4
|
+
* `CELESTEA_HTTP_ALLOW` / `CELESTEA_HTTP_DENY` carry comma-separated IP/CIDR
|
|
5
|
+
* entries. Both unset ⇒ the policy is inactive and every target is allowed
|
|
6
|
+
* (status quo, surfaced as `active === false` so the caller can decide to log
|
|
7
|
+
* it). When a policy is active, EVERY resolved IP of the target — and of every
|
|
8
|
+
* redirect hop — must pass it (`allow` is a whitelist when non-empty, `deny`
|
|
9
|
+
* always wins).
|
|
10
|
+
*
|
|
11
|
+
* **Fail closed**: a malformed entry makes the policy deny everything until the
|
|
12
|
+
* operator fixes the configuration — a typo can never silently widen access.
|
|
13
|
+
*
|
|
14
|
+
* **No check-then-use (W738 P1)**: [HttpTargetPolicy.checkUrl] is a verdict only.
|
|
15
|
+
* The *authorizing* call is [HttpTargetPolicy.resolveChecked], which returns the
|
|
16
|
+
* exact addresses it approved so the transport can PIN them: between the check
|
|
17
|
+
* and the connect there is no second `getaddrinfo`, so a host name that answers
|
|
18
|
+
* with a public address during the check and with `127.0.0.1`/`169.254.169.254`
|
|
19
|
+
* at connect time (DNS rebinding) can no longer reach an address the policy
|
|
20
|
+
* refused. Never connect by name after a check.
|
|
21
|
+
*
|
|
22
|
+
* W516 (session grants): a host may pass an [SsrfGrantView] with the session's
|
|
23
|
+
* `net_hosts` entries. They are UNIONed into the allow side only, and only when
|
|
24
|
+
* the env policy is active: neither the deny list nor the fail-closed verdict
|
|
25
|
+
* can be reached by a grant, and an inactive policy (both env vars unset) stays
|
|
26
|
+
* inactive — a grant must never *tighten* a deployment into a whitelist
|
|
27
|
+
* (`netHostsIneffective` reports exactly that case to the host's audit).
|
|
28
|
+
*/
|
|
29
|
+
import { lookup } from "node:dns/promises";
|
|
30
|
+
import { isIP } from "node:net";
|
|
31
|
+
import { envString } from "../env.js";
|
|
32
|
+
export const ENV_HTTP_ALLOW = "CELESTEA_HTTP_ALLOW";
|
|
33
|
+
export const ENV_HTTP_DENY = "CELESTEA_HTTP_DENY";
|
|
34
|
+
/** Parse `1.2.3.4`, `10.0.0.0/8`, `::1` or `fd00::/8` (throws on nonsense). */
|
|
35
|
+
export function parseIpRange(entry) {
|
|
36
|
+
const [baseText = "", prefixText] = entry.trim().split("/");
|
|
37
|
+
const family = isIP(baseText);
|
|
38
|
+
if (family !== 4 && family !== 6)
|
|
39
|
+
throw new Error(`unparseable ip/cidr '${entry}'`);
|
|
40
|
+
const bits = family === 4 ? 32 : 128;
|
|
41
|
+
const prefix = prefixText === undefined ? bits : Number(prefixText.trim());
|
|
42
|
+
if (!Number.isInteger(prefix) || prefix < 0 || prefix > bits) {
|
|
43
|
+
throw new Error(`bad ipv${family} prefix '${prefixText}' in '${entry}'`);
|
|
44
|
+
}
|
|
45
|
+
return { family, base: ipToBigInt(baseText, family), prefix };
|
|
46
|
+
}
|
|
47
|
+
/** Containment test on the masked network prefix. */
|
|
48
|
+
export function ipInRange(range, ip) {
|
|
49
|
+
const family = isIP(ip);
|
|
50
|
+
if (family !== 4 && family !== 6)
|
|
51
|
+
return false;
|
|
52
|
+
// W824 (W812 P0-4 / W822 A1): an IPv4-mapped IPv6 address (::ffff:a.b.c.d)
|
|
53
|
+
// IS the IPv4 address the socket actually reaches on Linux. Normalize both
|
|
54
|
+
// sides into the RANGE's family before masking, so a v4 deny range catches
|
|
55
|
+
// the mapped form and a mapped v6 range catches the plain v4 form.
|
|
56
|
+
let value;
|
|
57
|
+
let bits;
|
|
58
|
+
if (family === range.family) {
|
|
59
|
+
value = ipToBigInt(ip, family);
|
|
60
|
+
bits = family === 4 ? 32 : 128;
|
|
61
|
+
}
|
|
62
|
+
else if (family === 6) {
|
|
63
|
+
const v4 = mappedV4(ipToBigInt(ip, 6));
|
|
64
|
+
if (v4 === null)
|
|
65
|
+
return false;
|
|
66
|
+
value = v4;
|
|
67
|
+
bits = 32;
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
value = (0xffffn << 32n) | ipToBigInt(ip, 4);
|
|
71
|
+
bits = 128;
|
|
72
|
+
}
|
|
73
|
+
const hostBits = BigInt(bits - range.prefix);
|
|
74
|
+
if (hostBits < 0n)
|
|
75
|
+
return false;
|
|
76
|
+
const mask = ((1n << BigInt(bits)) - 1n) ^ ((1n << hostBits) - 1n);
|
|
77
|
+
return (value & mask) === (range.base & mask);
|
|
78
|
+
}
|
|
79
|
+
/** The IPv4 value inside ::ffff:0:0/96, or null when not an IPv4-mapped v6. */
|
|
80
|
+
function mappedV4(value) {
|
|
81
|
+
return value >> 32n === 0xffffn ? value & 0xffffffffn : null;
|
|
82
|
+
}
|
|
83
|
+
function ipToBigInt(ip, family) {
|
|
84
|
+
if (family === 4)
|
|
85
|
+
return ip.split(".").reduce((acc, part) => (acc << 8n) | BigInt(Number(part)), 0n);
|
|
86
|
+
return expandV6(ip).reduce((acc, part) => (acc << 16n) | BigInt(parseInt(part, 16)), 0n);
|
|
87
|
+
}
|
|
88
|
+
/** 169.254.169.254 -> ["a9fe", "a9fe"] (two 16-bit groups). */
|
|
89
|
+
function v4Groups(quad) {
|
|
90
|
+
const [a = 0, b = 0, c = 0, d = 0] = quad.split(".").map((part) => Number(part));
|
|
91
|
+
return [(((a << 8) | b).toString(16)), (((c << 8) | d).toString(16))];
|
|
92
|
+
}
|
|
93
|
+
function expandV6(ip) {
|
|
94
|
+
// W822 A1: an embedded dotted quad (::ffff:169.254.169.254) must be split
|
|
95
|
+
// into two 16-bit groups; parseInt("169.254.169.254", 16) used to truncate it
|
|
96
|
+
// to 0x00a9 and corrupt the base.
|
|
97
|
+
const embedded = /^(.*:)(\d{1,3}(?:\.\d{1,3}){3})$/.exec(ip);
|
|
98
|
+
const prefix = embedded === null ? "" : (embedded[1] ?? "");
|
|
99
|
+
const quad = embedded === null ? "" : (embedded[2] ?? "");
|
|
100
|
+
const text = embedded === null ? ip : `${prefix}${v4Groups(quad).join(":")}`;
|
|
101
|
+
const [head = "", tail = ""] = text.split("::");
|
|
102
|
+
const headParts = head === "" ? [] : head.split(":");
|
|
103
|
+
if (!text.includes("::"))
|
|
104
|
+
return padV6(headParts);
|
|
105
|
+
const tailParts = tail === "" ? [] : tail.split(":");
|
|
106
|
+
const gap = 8 - headParts.length - tailParts.length;
|
|
107
|
+
return [...headParts, ...Array.from({ length: Math.max(gap, 0) }, () => "0"), ...tailParts];
|
|
108
|
+
}
|
|
109
|
+
function padV6(parts) {
|
|
110
|
+
const missing = 8 - parts.length;
|
|
111
|
+
return missing <= 0 ? parts : [...parts, ...Array.from({ length: missing }, () => "0")];
|
|
112
|
+
}
|
|
113
|
+
export class HttpTargetPolicy {
|
|
114
|
+
allow;
|
|
115
|
+
deny;
|
|
116
|
+
failClosed;
|
|
117
|
+
hostAllow;
|
|
118
|
+
hostsIneffective;
|
|
119
|
+
resolver;
|
|
120
|
+
constructor(state) {
|
|
121
|
+
this.allow = state.allow;
|
|
122
|
+
this.deny = state.deny;
|
|
123
|
+
this.failClosed = state.failClosed;
|
|
124
|
+
this.hostAllow = state.hostAllow ?? [];
|
|
125
|
+
this.hostsIneffective = state.hostsIneffective ?? false;
|
|
126
|
+
this.resolver = state.resolver ?? resolveTargets;
|
|
127
|
+
}
|
|
128
|
+
/** Parse the two lists; a malformed entry throws (see [fromEnv] for env use). */
|
|
129
|
+
static parse(allowSpec, denySpec, options = {}) {
|
|
130
|
+
return new HttpTargetPolicy({
|
|
131
|
+
allow: parseList(allowSpec),
|
|
132
|
+
deny: parseList(denySpec),
|
|
133
|
+
failClosed: false,
|
|
134
|
+
resolver: options.resolver,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Policy from the environment; malformed config ⇒ fail closed. Session grants
|
|
139
|
+
* are merged on the allow side ONLY (see the module docs): the env policy must
|
|
140
|
+
* be active for them to count at all.
|
|
141
|
+
*/
|
|
142
|
+
static fromEnv(env = process.env, grants = {}, options = {}) {
|
|
143
|
+
const allowSpec = envString(env, ENV_HTTP_ALLOW);
|
|
144
|
+
const denySpec = envString(env, ENV_HTTP_DENY);
|
|
145
|
+
const hosts = [...(grants.netHosts ?? [])].filter((h) => h.trim() !== "");
|
|
146
|
+
try {
|
|
147
|
+
const allow = parseList(allowSpec);
|
|
148
|
+
const deny = parseList(denySpec);
|
|
149
|
+
if (allowSpec === undefined && denySpec === undefined) {
|
|
150
|
+
return new HttpTargetPolicy({ ...options, allow, deny, failClosed: false, hostsIneffective: hosts.length > 0 });
|
|
151
|
+
}
|
|
152
|
+
const merged = splitGrantHosts(hosts);
|
|
153
|
+
return new HttpTargetPolicy({
|
|
154
|
+
...options,
|
|
155
|
+
allow: [...allow, ...merged.ranges],
|
|
156
|
+
deny,
|
|
157
|
+
failClosed: false,
|
|
158
|
+
hostAllow: merged.names,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
return new HttpTargetPolicy({ ...options, allow: [], deny: [], failClosed: true });
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/** Whether the policy constrains targets at all. */
|
|
166
|
+
get active() {
|
|
167
|
+
return this.failClosed || this.allow.length > 0 || this.deny.length > 0;
|
|
168
|
+
}
|
|
169
|
+
/** Allow/deny entry counts (diagnostics). */
|
|
170
|
+
get sizes() {
|
|
171
|
+
return { allow: this.allow.length, deny: this.deny.length, failClosed: this.failClosed };
|
|
172
|
+
}
|
|
173
|
+
/** true when `net_hosts` grants were dropped because the env policy is off. */
|
|
174
|
+
get netHostsIneffective() {
|
|
175
|
+
return this.hostsIneffective;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Verdict only (`null` = authorized). NEVER sufficient on its own: the caller
|
|
179
|
+
* must connect to the addresses returned by [resolveChecked] instead of
|
|
180
|
+
* letting the transport resolve the name again (W738 check-then-use).
|
|
181
|
+
*/
|
|
182
|
+
async checkUrl(url) {
|
|
183
|
+
return (await this.resolveChecked(url)).reason;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Authorize a target and return the addresses it approved. `reason !== null`
|
|
187
|
+
* ⇒ `ips` is empty and nothing may be connected; `reason === null` ⇒ `ips` is
|
|
188
|
+
* non-empty and is the COMPLETE set of addresses the caller may use (pin them
|
|
189
|
+
* on every hop).
|
|
190
|
+
*/
|
|
191
|
+
async resolveChecked(url) {
|
|
192
|
+
const parsed = new URL(url);
|
|
193
|
+
const host = parsed.hostname.replace(/^\[|\]$/g, "");
|
|
194
|
+
if (host === "")
|
|
195
|
+
return { reason: "target url has no host", ips: [] };
|
|
196
|
+
const granted = this.hostAllow.includes(host.toLowerCase());
|
|
197
|
+
const ips = await this.resolver(host, parsed.port === "" ? defaultPort(parsed.protocol) : Number(parsed.port));
|
|
198
|
+
if (typeof ips === "string")
|
|
199
|
+
return { reason: ips, ips: [] };
|
|
200
|
+
if (ips.length === 0)
|
|
201
|
+
return { reason: `host '${host}' resolves to no addresses`, ips: [] };
|
|
202
|
+
for (const ip of ips) {
|
|
203
|
+
const reason = this.ipAllowed(ip, granted);
|
|
204
|
+
if (reason !== null)
|
|
205
|
+
return { reason, ips: [] };
|
|
206
|
+
}
|
|
207
|
+
return { reason: null, ips: [...ips] };
|
|
208
|
+
}
|
|
209
|
+
ipAllowed(ip, hostGranted = false) {
|
|
210
|
+
if (this.failClosed) {
|
|
211
|
+
return `ssrf policy misconfigured (fail-closed): fix ${ENV_HTTP_ALLOW}/${ENV_HTTP_DENY}`;
|
|
212
|
+
}
|
|
213
|
+
if (!hostGranted && this.allow.length > 0 && !this.allow.some((range) => ipInRange(range, ip))) {
|
|
214
|
+
return `target ip ${ip} is not in the ${ENV_HTTP_ALLOW} allow list`;
|
|
215
|
+
}
|
|
216
|
+
// Deny always wins: a granted host still has to pass the deny list.
|
|
217
|
+
if (this.deny.some((range) => ipInRange(range, ip))) {
|
|
218
|
+
return `target ip ${ip} is in the ${ENV_HTTP_DENY} deny list`;
|
|
219
|
+
}
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
/** IP/CIDR entries -> extra allow ranges; host names -> host allow list. */
|
|
224
|
+
function splitGrantHosts(hosts) {
|
|
225
|
+
const ranges = [];
|
|
226
|
+
const names = [];
|
|
227
|
+
for (const raw of hosts) {
|
|
228
|
+
const entry = raw.trim();
|
|
229
|
+
const ipLike = isIP(entry) !== 0 || /^[0-9a-fA-F:.]+\/\d+$/.test(entry);
|
|
230
|
+
if (ipLike) {
|
|
231
|
+
try {
|
|
232
|
+
ranges.push(parseIpRange(entry));
|
|
233
|
+
}
|
|
234
|
+
catch {
|
|
235
|
+
// An unparseable grant entry is DROPPED (never fail-closed, §4.3.7).
|
|
236
|
+
}
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
const name = entry.toLowerCase();
|
|
240
|
+
if (HOST_NAME_RE.test(name))
|
|
241
|
+
names.push(name);
|
|
242
|
+
}
|
|
243
|
+
return { ranges, names };
|
|
244
|
+
}
|
|
245
|
+
/** RFC 1123 host name (no scheme, no port, no slash, no whitespace). */
|
|
246
|
+
const HOST_NAME_RE = /^(?=.{1,253}$)[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?(\.[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)*$/;
|
|
247
|
+
function parseList(spec) {
|
|
248
|
+
if (spec === undefined || spec === null || spec.trim() === "")
|
|
249
|
+
return [];
|
|
250
|
+
return spec
|
|
251
|
+
.split(",")
|
|
252
|
+
.map((entry) => entry.trim())
|
|
253
|
+
.filter((entry) => entry !== "")
|
|
254
|
+
.map((entry) => parseIpRange(entry));
|
|
255
|
+
}
|
|
256
|
+
/** Every address a target host resolves to, or a failure reason string. */
|
|
257
|
+
export async function resolveTargets(host, port) {
|
|
258
|
+
if (isIP(host) !== 0)
|
|
259
|
+
return [host];
|
|
260
|
+
try {
|
|
261
|
+
const addresses = await lookup(host, { all: true });
|
|
262
|
+
if (addresses.length === 0)
|
|
263
|
+
return `host '${host}' resolves to no addresses`;
|
|
264
|
+
return addresses.map((a) => a.address);
|
|
265
|
+
}
|
|
266
|
+
catch (e) {
|
|
267
|
+
return `cannot resolve host '${host}': ${e instanceof Error ? e.message : String(e)}`;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
function defaultPort(protocol) {
|
|
271
|
+
return protocol === "https:" ? 443 : 80;
|
|
272
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One HTTP(S) hop over `node:http` / `node:https` (`crates/tools/src/http.rs`).
|
|
3
|
+
*
|
|
4
|
+
* Deliberately low-level: no ambient proxy from the environment (`no_proxy`
|
|
5
|
+
* parity), no automatic redirect following (the caller caps hops at 5 and
|
|
6
|
+
* re-checks every hop against the SSRF policy), a total per-call deadline, and
|
|
7
|
+
* a hard cap on the buffered body — bytes past the cap are dropped, the socket
|
|
8
|
+
* is torn down, and `truncated: true` is reported instead of flooding the
|
|
9
|
+
* context.
|
|
10
|
+
*
|
|
11
|
+
* **Address pinning (W738 P1)**: the SSRF verdict is only worth something if the
|
|
12
|
+
* socket connects to the address that was checked. When `pinnedIps` is given,
|
|
13
|
+
* the request carries a custom `lookup` that answers from that list and never
|
|
14
|
+
* touches the resolver, so the connection cannot be re-bound to a different (for
|
|
15
|
+
* example internal) address between check and use. The URL, `Host` header and
|
|
16
|
+
* TLS `servername` stay exactly as before — only the address source changes.
|
|
17
|
+
*/
|
|
18
|
+
import type { LookupFunction } from "node:net";
|
|
19
|
+
import { type HeaderPairs } from "./headers.js";
|
|
20
|
+
export interface TransportRequest {
|
|
21
|
+
url: URL;
|
|
22
|
+
method: string;
|
|
23
|
+
headers: HeaderPairs;
|
|
24
|
+
body: string | null;
|
|
25
|
+
timeoutMs: number;
|
|
26
|
+
maxBodyBytes: number;
|
|
27
|
+
/**
|
|
28
|
+
* Addresses the SSRF policy approved for THIS hop (W738). When present the
|
|
29
|
+
* connection must use one of them — the transport's own DNS is bypassed — so a
|
|
30
|
+
* second, different resolution can never carry the request to another host.
|
|
31
|
+
*/
|
|
32
|
+
pinnedIps?: readonly string[];
|
|
33
|
+
}
|
|
34
|
+
export interface TransportResult {
|
|
35
|
+
status: number;
|
|
36
|
+
headers: Record<string, string>;
|
|
37
|
+
body: string;
|
|
38
|
+
truncated: boolean;
|
|
39
|
+
/** Raw `Location` value; redirect targets are resolved by the caller. */
|
|
40
|
+
location: string | null;
|
|
41
|
+
}
|
|
42
|
+
/** Perform exactly one request (no redirect following) and capture the body. */
|
|
43
|
+
export declare function requestOnce(req: TransportRequest): Promise<TransportResult>;
|
|
44
|
+
/**
|
|
45
|
+
* A `lookup` replacement that answers from an already-authorized address list.
|
|
46
|
+
* It never resolves anything: an empty list (or a family filter that matches
|
|
47
|
+
* none of the pinned addresses) is a hard `ENOTFOUND`, never a fallback to the
|
|
48
|
+
* platform resolver — a silent fallback would re-open the rebinding hole.
|
|
49
|
+
*/
|
|
50
|
+
export declare function pinnedLookup(pinned: readonly string[]): LookupFunction;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One HTTP(S) hop over `node:http` / `node:https` (`crates/tools/src/http.rs`).
|
|
3
|
+
*
|
|
4
|
+
* Deliberately low-level: no ambient proxy from the environment (`no_proxy`
|
|
5
|
+
* parity), no automatic redirect following (the caller caps hops at 5 and
|
|
6
|
+
* re-checks every hop against the SSRF policy), a total per-call deadline, and
|
|
7
|
+
* a hard cap on the buffered body — bytes past the cap are dropped, the socket
|
|
8
|
+
* is torn down, and `truncated: true` is reported instead of flooding the
|
|
9
|
+
* context.
|
|
10
|
+
*
|
|
11
|
+
* **Address pinning (W738 P1)**: the SSRF verdict is only worth something if the
|
|
12
|
+
* socket connects to the address that was checked. When `pinnedIps` is given,
|
|
13
|
+
* the request carries a custom `lookup` that answers from that list and never
|
|
14
|
+
* touches the resolver, so the connection cannot be re-bound to a different (for
|
|
15
|
+
* example internal) address between check and use. The URL, `Host` header and
|
|
16
|
+
* TLS `servername` stay exactly as before — only the address source changes.
|
|
17
|
+
*/
|
|
18
|
+
import { request as httpRequest } from "node:http";
|
|
19
|
+
import { request as httpsRequest } from "node:https";
|
|
20
|
+
import { isIP } from "node:net";
|
|
21
|
+
import { TransportError } from "./errors.js";
|
|
22
|
+
import { headerLookup, pickHeaders } from "./headers.js";
|
|
23
|
+
/** Perform exactly one request (no redirect following) and capture the body. */
|
|
24
|
+
export async function requestOnce(req) {
|
|
25
|
+
const send = req.url.protocol === "https:" ? httpsRequest : httpRequest;
|
|
26
|
+
return new Promise((resolve, reject) => {
|
|
27
|
+
let settled = false;
|
|
28
|
+
let timedOut = false;
|
|
29
|
+
// W824 (W812 P0-3): declared with let (not a TDZ const) and dereferenced
|
|
30
|
+
// with optional chaining, because the timeout callback may fire after a
|
|
31
|
+
// SYNCHRONOUS send() failure. Every settling path (finish/fail) - including
|
|
32
|
+
// the synchronous throw below - clears the timer, so a rejected request can
|
|
33
|
+
// no longer leave an armed, process-killing timer behind.
|
|
34
|
+
let request;
|
|
35
|
+
const timer = setTimeout(() => {
|
|
36
|
+
timedOut = true;
|
|
37
|
+
request?.destroy(new Error(`request timed out after ${req.timeoutMs}ms`));
|
|
38
|
+
}, req.timeoutMs);
|
|
39
|
+
const finish = (value) => {
|
|
40
|
+
if (settled)
|
|
41
|
+
return;
|
|
42
|
+
settled = true;
|
|
43
|
+
clearTimeout(timer);
|
|
44
|
+
resolve(value);
|
|
45
|
+
};
|
|
46
|
+
const fail = (error) => {
|
|
47
|
+
if (settled)
|
|
48
|
+
return;
|
|
49
|
+
settled = true;
|
|
50
|
+
clearTimeout(timer);
|
|
51
|
+
reject(new TransportError(error, timedOut));
|
|
52
|
+
};
|
|
53
|
+
try {
|
|
54
|
+
request = send(req.url, requestOptions(req), (response) => collectBody(response, req.maxBodyBytes, finish, fail));
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
fail(error);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
request?.on("error", fail);
|
|
61
|
+
if (req.body !== null && req.body !== "")
|
|
62
|
+
request?.write(req.body);
|
|
63
|
+
request?.end();
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
/** Wire options for one hop; `lookup` is only overridden when addresses are pinned. */
|
|
67
|
+
function requestOptions(req) {
|
|
68
|
+
const options = { method: req.method, headers: Object.fromEntries(req.headers), timeout: 0 };
|
|
69
|
+
if (req.pinnedIps !== undefined)
|
|
70
|
+
options.lookup = pinnedLookup(req.pinnedIps);
|
|
71
|
+
return options;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* A `lookup` replacement that answers from an already-authorized address list.
|
|
75
|
+
* It never resolves anything: an empty list (or a family filter that matches
|
|
76
|
+
* none of the pinned addresses) is a hard `ENOTFOUND`, never a fallback to the
|
|
77
|
+
* platform resolver — a silent fallback would re-open the rebinding hole.
|
|
78
|
+
*/
|
|
79
|
+
export function pinnedLookup(pinned) {
|
|
80
|
+
return (_hostname, options, callback) => {
|
|
81
|
+
const family = familyOf(options);
|
|
82
|
+
const usable = family === 0 ? [...pinned] : pinned.filter((ip) => isIP(ip) === family);
|
|
83
|
+
if (usable.length === 0) {
|
|
84
|
+
const error = new Error("no pinned address matches the requested family (ssrf pin)");
|
|
85
|
+
error.code = "ENOTFOUND";
|
|
86
|
+
callback(error, "", 0);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (options.all === true) {
|
|
90
|
+
callback(null, usable.map((address) => ({ address, family: isIP(address) })));
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const [first = ""] = usable;
|
|
94
|
+
callback(null, first, isIP(first));
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/** `4` / `6` when the caller demanded a family, else `0` (any). */
|
|
98
|
+
function familyOf(options) {
|
|
99
|
+
const raw = typeof options === "number" ? options : options?.family;
|
|
100
|
+
if (raw === 4 || raw === "IPv4")
|
|
101
|
+
return 4;
|
|
102
|
+
if (raw === 6 || raw === "IPv6")
|
|
103
|
+
return 6;
|
|
104
|
+
return 0;
|
|
105
|
+
}
|
|
106
|
+
function collectBody(response, cap, finish, fail) {
|
|
107
|
+
const status = response.statusCode ?? 0;
|
|
108
|
+
const headers = pickHeaders(response.headers);
|
|
109
|
+
const location = headerLookup(response.headers, "location");
|
|
110
|
+
const chunks = [];
|
|
111
|
+
let size = 0;
|
|
112
|
+
response.on("data", (chunk) => {
|
|
113
|
+
const room = cap - size;
|
|
114
|
+
// B3 / W812 P2-1: a body that is exactly `cap` bytes is complete; only a
|
|
115
|
+
// byte beyond the remaining capacity trips the truncated path.
|
|
116
|
+
if (chunk.length <= room) {
|
|
117
|
+
chunks.push(chunk);
|
|
118
|
+
size += chunk.length;
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
if (room > 0)
|
|
122
|
+
chunks.push(chunk.subarray(0, room));
|
|
123
|
+
response.destroy();
|
|
124
|
+
finish({ status, headers, body: Buffer.concat(chunks).toString("utf8"), truncated: true, location });
|
|
125
|
+
});
|
|
126
|
+
response.on("end", () => {
|
|
127
|
+
finish({ status, headers, body: Buffer.concat(chunks).toString("utf8"), truncated: false, location });
|
|
128
|
+
});
|
|
129
|
+
response.on("error", fail);
|
|
130
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@celestea/tools` — the tool pipeline: registry + guard chain + builtin tools.
|
|
3
|
+
*
|
|
4
|
+
* Parity target: `celestea_harness/crates/tools` (`registry.rs`, `guard.rs`,
|
|
5
|
+
* `builtin.rs`, `http.rs`, `process.rs`, `sandbox.rs`). The package implements
|
|
6
|
+
* the `Tool` / `ToolGuard` / `ToolRegistry` / `Sandbox` seams declared by
|
|
7
|
+
* `@celestea/core`; nothing here is reachable except through this barrel
|
|
8
|
+
* (ARCHITECTURE.md §2.2).
|
|
9
|
+
*
|
|
10
|
+
* Pipeline (one dispatch, `registry.ts` — the order is contract, not taste):
|
|
11
|
+
* schema validation → ToolGuard chain → execute → structured ToolOutput
|
|
12
|
+
*
|
|
13
|
+
* Module map:
|
|
14
|
+
* index.ts public surface (this file)
|
|
15
|
+
* registry.ts ToolRegistryImpl: the four-stage dispatch pipeline (registry.rs)
|
|
16
|
+
* exposure.ts exposedRegistry: the per-mode model-visible face (W791 §5.2)
|
|
17
|
+
* schema.ts JSON-Schema subset validator (pipeline stage 1)
|
|
18
|
+
* args.ts argument readers with parity error text
|
|
19
|
+
* desc.ts the shared `desc` UI-label parameter of every tool (W779)
|
|
20
|
+
* errors.ts structured contract errors (`toolargs:` / `toolguard:`)
|
|
21
|
+
* tool-failure.ts ToolFailure: the single rejection type of a Tool
|
|
22
|
+
* fn-tool.ts Tool over a plain async closure (builtin.rs)
|
|
23
|
+
* env.ts env readers (string / int / on-off flag)
|
|
24
|
+
* guard/paths.ts canonicalization: traversal + symlink containment (guard.rs)
|
|
25
|
+
* guard/path-guard.ts PathGuard: CELESTEA_TOOL_ROOTS whitelist, fail-closed (guard.rs)
|
|
26
|
+
* fs/file-io.ts capped reads, binary rejection, capped listings
|
|
27
|
+
* tools/read-file.ts read_file (builtin.rs)
|
|
28
|
+
* tools/write-file.ts write_file (builtin.rs)
|
|
29
|
+
* tools/list-dir.ts list_dir (builtin.rs)
|
|
30
|
+
* tools/load-skill.ts load_skill: one SKILL.md body on demand (W884)
|
|
31
|
+
* tools/memory.ts remember / forget: append-only workspace memory (F3 P1)
|
|
32
|
+
* tools/run_shell run_shell: orchestration over the Sandbox seam (builtin.rs, sandbox.rs)
|
|
33
|
+
* tools/process-control.ts process_control (poll / stdin / kill) (process.rs)
|
|
34
|
+
* tools/http-request.ts http_request (SSRF, timeout, truncation) (http.rs)
|
|
35
|
+
* tools/run-code.ts run_code: the broker tool + late-bound RegistryHandle (run_code.rs)
|
|
36
|
+
* run-code/sdk.ts the injected Python SDK preamble + runner + assembly (run_code.rs)
|
|
37
|
+
* run-code/limits.ts hard limits + env-tuned broker config (run_code.rs)
|
|
38
|
+
* run-code/lines.ts newline-framed line reader + UTF-8-safe byte budgets (run_code.rs)
|
|
39
|
+
* run-code/broker.ts the parent broker loop: dispatch, ledger, events (run_code.rs)
|
|
40
|
+
* http/ssrf.ts IP/CIDR allow+deny policy, fail-closed (http.rs)
|
|
41
|
+
* http/headers.ts request-header validation + response-header subset (http.rs)
|
|
42
|
+
* http/transport.ts one HTTP(S) hop over node:http/https (http.rs)
|
|
43
|
+
* http/redirects.ts policy-checked redirect following (<= 5 hops) (http.rs)
|
|
44
|
+
* http/errors.ts transport error classification (timeout|dns|connect|…)
|
|
45
|
+
* process/registry.ts background process registry + reaper + completion sink (process.rs)
|
|
46
|
+
* process/buffers.ts capped ring buffers / tails (process.rs)
|
|
47
|
+
* sandbox/config.ts sandbox knobs + shell invocation + env allowlist (sandbox.rs)
|
|
48
|
+
* sandbox/child.ts SandboxChild over node:child_process (sandbox.rs)
|
|
49
|
+
* sandbox/async.ts timeout race + bounded poll
|
|
50
|
+
* sandbox/userspace.ts userspace-lite Sandbox implementation (P2c: real isolation)
|
|
51
|
+
* sandbox/fake-sandbox.ts scripted FakeSandbox test double (seam replaceability)
|
|
52
|
+
* builtin.ts the six builtin tools, sharing one sandbox + registry (builtin.rs)
|
|
53
|
+
* plugin.ts toolsPlugin: provides the three tool services (plugin.rs)
|
|
54
|
+
*/
|
|
55
|
+
export { createToolRegistry, humanRender, ToolRegistryImpl } from "./registry.js";
|
|
56
|
+
export { EXECUTION_GUIDANCE, EXECUTION_TOOL_NAMES, executionExposure, exposedRegistry, exposedSpecs, faceForMode, TOOL_UNAVAILABLE_CODE, unavailableError, type ExposureOptions, } from "./exposure.js";
|
|
57
|
+
export type { ArgsValidationFailure } from "./schema.js";
|
|
58
|
+
export { DisclosurePolicy, disclosureExposure, type DisclosureGuidance, type DisclosurePolicyOptions, type DisclosureSnapshot, } from "./disclosure.js";
|
|
59
|
+
export { validateArgs } from "./schema.js";
|
|
60
|
+
export { contractError, contractFailure, errorCode, errorText, GUARD_ERROR_PREFIX, quoteMessage, TOOLARG_ERROR_PREFIX } from "./errors.js";
|
|
61
|
+
export { isToolFailure, ToolFailure } from "./tool-failure.js";
|
|
62
|
+
export { fnTool } from "./fn-tool.js";
|
|
63
|
+
export { ENV_TOOL_GUARD, ENV_TOOL_ROOTS, ENV_TOOL_WORKDIR, mountProductionGuards, parseToolRoots, PATH_ACCESS, PATH_ARG_KEYS, PathGuard, PathGuardPolicy, type PathAccess, type PathGuardGrants, type PathGuardPolicyInit, } from "./guard/path-guard.js";
|
|
64
|
+
export { absolutize, canonicalExisting, isDirectory, isInside, resolveExistingTarget, resolveWriteTarget } from "./guard/paths.js";
|
|
65
|
+
export { readFileTool, readFileSpec } from "./tools/read-file.js";
|
|
66
|
+
export { writeFileTool, writeFileSpec } from "./tools/write-file.js";
|
|
67
|
+
export { listDirTool, listDirSpec } from "./tools/list-dir.js";
|
|
68
|
+
export { runShellSpec, runShellTool, type RunShellToolOptions } from "./tools/run-shell.js";
|
|
69
|
+
export { processControlSpec, processControlTool } from "./tools/process-control.js";
|
|
70
|
+
export { RegistryHandle, runCodeSpec, runCodeTool, runCodeToolWithHandle, type RunCodeToolOptions } from "./tools/run-code.js";
|
|
71
|
+
export { httpRequestSpec, httpRequestTool, type HttpRequestToolOptions } from "./tools/http-request.js";
|
|
72
|
+
export { ASK_USER_DESCRIPTION, askUserSpec, askUserTool, type AskUserToolOptions } from "./tools/ask-user.js";
|
|
73
|
+
export { READ_IMAGE_DESCRIPTION, readImageSpec, readImageTool, type ReadImageToolOptions } from "./tools/read-image.js";
|
|
74
|
+
export { LOAD_SKILL_DESCRIPTION, LOAD_SKILL_ERROR_PREFIX, loadSkillSpec, loadSkillTool, type LoadSkillToolOptions } from "./tools/load-skill.js";
|
|
75
|
+
export { FORGET_DESCRIPTION, MEMORY_ERROR_PREFIX, REMEMBER_DESCRIPTION, forgetSpec, forgetTool, rememberSpec, rememberTool, type MemoryToolOptions, } from "./tools/memory.js";
|
|
76
|
+
export { MEMORY_ENTRIES_FILE_NAME, MEMORY_ENTRY_MAX_BYTES, MEMORY_LOG_VERSION, findEntryByText, foldMemoryLog, memoryEntryPaths, memoryLogHeader, memoryTextHash, nextMemoryId, parseMemoryLog, renderMemoryMarkdown, serializeMemoryLine, type MemoryEntryLine, type MemoryForgetLine, type MemoryLogLine, type MemoryLogState, } from "./memory/log.js";
|
|
77
|
+
export { appendMemoryLine, memoryStoreOf, nodeMemoryStoreIo, readMemoryLog, readMemoryState, type MemoryStore, type MemoryStoreIo } from "./memory/store.js";
|
|
78
|
+
export { builtinTools, type BuiltinToolsOptions } from "./builtin.js";
|
|
79
|
+
export { ATTACHMENTS_DIRNAME, ATTACHMENT_MAX_BYTES, ATTACHMENT_MAX_PIXELS, ATTACHMENT_MAX_SIDE, AttachmentError, createAttachmentStore, readImageDimensions, sniffImageMediaType, type AttachmentErrorCode, type AttachmentStore, type StoredAttachment, } from "./attachments/store.js";
|
|
80
|
+
export { ENV_HTTP_ALLOW, ENV_HTTP_DENY, HttpTargetPolicy, ipInRange, parseIpRange, resolveTargets, type CheckedTarget, type HostResolver, type HttpTargetPolicyOptions, type IpRange, type SsrfGrantView, } from "./http/ssrf.js";
|
|
81
|
+
export { HEADER_SUBSET, pickHeaders, validateHeaderPairs, type HeaderPairs } from "./http/headers.js";
|
|
82
|
+
export { MAX_REDIRECT_HOPS } from "./http/redirects.js";
|
|
83
|
+
export { pinnedLookup, requestOnce, type TransportRequest, type TransportResult } from "./http/transport.js";
|
|
84
|
+
export { HTTP_ERROR_PREFIX, classifyTransportError, httpFailure, TransportError } from "./http/errors.js";
|
|
85
|
+
export { MAX_BODY_BYTES, DEFAULT_TIMEOUT_MS, MAX_TIMEOUT_MS } from "./tools/http-request.js";
|
|
86
|
+
export { BINARY_SNIFF_BYTES, DEFAULT_READ_LIMIT, isProbablyBinary, listDirNames, MAX_DIR_ENTRIES, MAX_READ_BYTES, readTextFile, readTextLines, truncationNote, writeTextFile } from "./fs/file-io.js";
|
|
87
|
+
export { COMPLETION_TAIL_BYTES, foldNewlines, MAX_STREAM_BUFFER, RingBuffer, TAIL_BYTES, } from "./process/buffers.js";
|
|
88
|
+
export { KILL_GRACE_MS, KILL_WAIT_MS, PROCESS_REGISTRY_SERVICE, ProcessRegistry, STDIN_WRITE_TIMEOUT_MS, type CompletionSink, type ProcessCompletion, type ProcessHandle, type ProcessRegistryOptions, } from "./process/registry.js";
|
|
89
|
+
export { ENV_SHELL_PIN, ShellNotFoundError, execSuffixes, envValue, isWindows, kindOfExecutable, lookupFor, pathApi, pathDelimiter, resolveShell, resolveShellKind, shellArgv, whichInPath, WINDOWS_EXEC_SUFFIXES, type PlatformInput, type ResolvedShell, type ShellKind, type ShellLookup, type ShellResolveInput, } from "./platform/index.js";
|
|
90
|
+
export { PYTHON_CANDIDATES_POSIX, PYTHON_CANDIDATES_WINDOWS, pythonCandidates, quoteCmd, quoteForShell, quotePath, quoteWord, runCodeCommand, shellQuote, type RunCodeLanguageName, } from "./platform/quote.js";
|
|
91
|
+
export { taskkillTree } from "./sandbox/child.js";
|
|
92
|
+
export { FILE_MODES_MEANINGFUL, POSIX_PROCESS_GROUPS, POSIX_SHELL, platformGates, whichUsable, type PlatformGates, } from "./testing/platform-gates.js";
|
|
93
|
+
export { buildSandboxConfig, DEFAULT_MAX_OUTPUT_BYTES, DEFAULT_MAX_TIMEOUT_MS, DEFAULT_TIMEOUT_MS as DEFAULT_SANDBOX_TIMEOUT_MS, ENV_ALLOWLIST, ENV_SHELL_MAX_OUTPUT_BYTES, ENV_SHELL_MAX_TIMEOUT_MS, ENV_SHELL_ROOT, ENV_SHELL_TIMEOUT_MS, ENV_SHELL_WORKDIR, gitToplevelOr, sandboxConfigFromEnv, sanitizedEnv, sessionSandboxConfig, shellInvocation, type SandboxConfigOverrides, type SessionFsScope, } from "./sandbox/config.js";
|
|
94
|
+
export { USERSPACE_META, UserspaceSandbox, userspaceSandbox, userspaceSandboxWith } from "./sandbox/userspace.js";
|
|
95
|
+
export { readCapped, REAP_GRACE_MS, type CappedText } from "./sandbox/launch.js";
|
|
96
|
+
export { resolveWorkdir } from "./sandbox/workdir.js";
|
|
97
|
+
export { BWRAP_PROVIDER, buildBwrapArgv, buildBwrapCommand, bwrapMeta, DEFAULT_BWRAP_OPTIONS, SECCOMP_FD, type BwrapOptions, } from "./sandbox/bwrap-argv.js";
|
|
98
|
+
export { BwrapSandbox, bwrapSandbox, bwrapSandboxWith, rlimitVia, type BwrapMeta, type BwrapSandboxOptions } from "./sandbox/bwrap.js";
|
|
99
|
+
export { ENV_SANDBOX_FALLBACK, ENV_SANDBOX_MASK, ENV_SANDBOX_NET, ENV_SANDBOX_SECCOMP, ENV_SANDBOX_SHARE_TMP, bwrapOptionsFromEnv, fallbackMode, selectSandbox, selectSandboxDetailed, type SandboxFallbackMode, type SandboxGrantView, type SandboxSelection, type SelectOptions, } from "./sandbox/provider.js";
|
|
100
|
+
export { countUidThreads, DEFAULT_LIMITS, deriveNproc, ENV_SANDBOX_NPROC, ENV_SANDBOX_NPROC_HEADROOM, limitsFromEnv, NPROC_FLOOR, NPROC_HEADROOM, rlimitsEnabled, type SandboxLimits, } from "./sandbox/limits.js";
|
|
101
|
+
export { ENV_SANDBOX_BWRAP, probeHost, resetProbeCache, whichSync, type HostProbe } from "./sandbox/probe.js";
|
|
102
|
+
export { applyLimits, ulimitScript, type RlimitPlan, type RlimitVia } from "./sandbox/rlimit.js";
|
|
103
|
+
export { buildSeccompFilter, instructionCount, openSeccompBlob, toBlobBytes, type BpfInstruction } from "./sandbox/seccomp.js";
|
|
104
|
+
export { assembleTools, httpOptions, TOOLS_PLUGIN_NAME, toolsPlugin, type RunCodeMount, type ToolAssembly, type ToolAssemblyGrants, type ToolsPluginOptions, } from "./plugin.js";
|
|
105
|
+
export { assembleProgram, firstNonblankLineIndented, RUN_CODE_RUNNER, RUN_CODE_SDK } from "./run-code/sdk.js";
|
|
106
|
+
export { clampTimeoutMs, DEFAULT_TIMEOUT_MS as RUN_CODE_DEFAULT_TIMEOUT_MS, ENV_RUN_CODE_TIMEOUT_MS, EXIT_GRACE_MS, MAX_LINE_BYTES, MAX_LOG_BYTES, MAX_SUB_CALLS, MAX_SUB_OUTPUT_BYTES, MAX_TIMEOUT_MS as RUN_CODE_MAX_TIMEOUT_MS, resolveTimeoutMs, RUN_CODE_ERROR_PREFIX, runCodeConfig, runCodeConfigFromEnv, SDK_TOOLS, type RunCodeConfig, } from "./run-code/limits.js";
|
|
107
|
+
export { brokerRun, type BrokerContext, type RunCodeEventSink } from "./run-code/broker.js";
|
|
108
|
+
export { appendBounded, jsonByteLength, LineReader, safeUtf8, tail, truncateValue, utf8Prefix, type BoundedLine } from "./run-code/lines.js";
|
|
109
|
+
export { assertHttpUrl, BROWSER_ACT_DESCRIPTION, BROWSER_OPEN_DESCRIPTION, browserActSpec, browserActTool, browserOpenSpec, browserOpenTool, type BrowserToolOptions, } from "./tools/browser.js";
|
|
110
|
+
export { ADDRESS_SPACE_NOTE, BrowserManager, DEFAULT_BROWSER_MAX_BYTES, DEFAULT_BROWSER_MAX_NODES, DEFAULT_BROWSER_STARTUP_MS, type BrowserActRequest, type BrowserIsolation, type BrowserManagerOptions, type BrowserRef, type BrowserResult, type BrowserScreenshot, type BrowserSnapshotValue, type BrowserViewport, } from "./browser/session.js";
|
|
111
|
+
export { armMemoryGuard, DEFAULT_BROWSER_MEMORY_MB, readOwnCgroupPath, readTreeRssKb, type MemoryGuard, type MemoryGuardKind, type MemoryGuardStatus, } from "./browser/memory-guard.js";
|
|
112
|
+
export { buildAxSnapshot, collectBoxes, isInteractiveRole, quadToBox, type AxSnapshot, type SnapshotOptions, type SnapshotRef, } from "./browser/snapshot.js";
|
|
113
|
+
export { attachBrowser, BrowserNotFoundError, BrowserStartupError, findHeadlessShell, launchBrowser, parseDevToolsEndpoint, type AttachedBrowser, type BrowserProcess, type LaunchedBrowser, } from "./browser/launch.js";
|
|
114
|
+
export { CdpClient, CdpClosedError, CdpProtocolError, CdpTimeoutError, CdpTransportError, openWebSocketTransport, WebSocketTransport, type CdpTransport } from "./browser/cdp.js";
|