@node9/proxy 2.16.1 → 2.16.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/dist/cli.mjs CHANGED
@@ -2100,6 +2100,181 @@ function analyzeShellCommand(command) {
2100
2100
  }
2101
2101
  return { actions, paths, allTokens };
2102
2102
  }
2103
+ function parseComponent(s) {
2104
+ if (!s) return null;
2105
+ if (/^0[xX][0-9a-fA-F]+$/.test(s)) return parseInt(s.slice(2), 16);
2106
+ if (s === "0") return 0;
2107
+ if (/^0[0-7]+$/.test(s)) return parseInt(s.slice(1), 8);
2108
+ if (/^[1-9][0-9]*$/.test(s)) return Number(s);
2109
+ return null;
2110
+ }
2111
+ function parseIpv4(input) {
2112
+ let s = input;
2113
+ if (s.endsWith(".")) s = s.slice(0, -1);
2114
+ if (!s) return null;
2115
+ const parts = s.split(".");
2116
+ if (parts.length > 4) return null;
2117
+ const vals = [];
2118
+ for (const p of parts) {
2119
+ const v = parseComponent(p);
2120
+ if (v === null || !Number.isFinite(v) || v < 0) return null;
2121
+ vals.push(v);
2122
+ }
2123
+ const n = vals.length;
2124
+ for (let i = 0; i < n - 1; i++) if (vals[i] > 255) return null;
2125
+ const last = vals[n - 1];
2126
+ const remainingBytes = 4 - (n - 1);
2127
+ const limit = Math.pow(256, remainingBytes);
2128
+ if (last >= limit) return null;
2129
+ let value = last;
2130
+ for (let i = 0; i < n - 1; i++) value += vals[i] * Math.pow(256, 3 - i);
2131
+ if (value > 4294967295) return null;
2132
+ return [value >>> 24 & 255, value >>> 16 & 255, value >>> 8 & 255, value & 255].join(".");
2133
+ }
2134
+ function expandIpv6(input) {
2135
+ const s = input.toLowerCase();
2136
+ if (!/^[0-9a-f:.]+$/.test(s)) return null;
2137
+ if ((s.match(/::/g) ?? []).length > 1) return null;
2138
+ let head = s;
2139
+ let tailV4 = null;
2140
+ const lastColon = s.lastIndexOf(":");
2141
+ const afterLast = s.slice(lastColon + 1);
2142
+ if (afterLast.includes(".")) {
2143
+ const dotted = parseIpv4(afterLast);
2144
+ if (!dotted) return null;
2145
+ const o = dotted.split(".").map(Number);
2146
+ tailV4 = [o[0] << 8 | o[1], o[2] << 8 | o[3]];
2147
+ head = s.slice(0, lastColon + 1) + "0";
2148
+ }
2149
+ const [lhs, rhs] = head.includes("::") ? head.split("::") : [head, null];
2150
+ const toGroups = (part) => {
2151
+ if (!part) return [];
2152
+ const out = [];
2153
+ for (const g of part.split(":")) {
2154
+ if (!/^[0-9a-f]{1,4}$/.test(g)) return null;
2155
+ out.push(parseInt(g, 16));
2156
+ }
2157
+ return out;
2158
+ };
2159
+ const left = toGroups(lhs);
2160
+ if (left === null) return null;
2161
+ let right = [];
2162
+ if (rhs !== null) {
2163
+ const r = toGroups(rhs);
2164
+ if (r === null) return null;
2165
+ right = r;
2166
+ }
2167
+ if (tailV4) {
2168
+ if (rhs !== null) right = right.slice(0, -1).concat(tailV4);
2169
+ else left.splice(left.length - 1, 1, ...tailV4);
2170
+ }
2171
+ const groups = rhs === null ? left : left.concat(new Array(8 - left.length - right.length).fill(0), right);
2172
+ if (rhs === null && groups.length !== 8) return null;
2173
+ if (rhs !== null && left.length + right.length > 8) return null;
2174
+ if (groups.length !== 8) return null;
2175
+ return groups;
2176
+ }
2177
+ function compressIpv6(g) {
2178
+ let bestStart = -1;
2179
+ let bestLen = 0;
2180
+ let i = 0;
2181
+ while (i < 8) {
2182
+ if (g[i] !== 0) {
2183
+ i++;
2184
+ continue;
2185
+ }
2186
+ let j = i;
2187
+ while (j < 8 && g[j] === 0) j++;
2188
+ if (j - i > bestLen) {
2189
+ bestLen = j - i;
2190
+ bestStart = i;
2191
+ }
2192
+ i = j;
2193
+ }
2194
+ const hex = g.map((x) => x.toString(16));
2195
+ if (bestLen < 2) return hex.join(":");
2196
+ return hex.slice(0, bestStart).join(":") + "::" + hex.slice(bestStart + bestLen).join(":");
2197
+ }
2198
+ function normalizeIpLiteral(host) {
2199
+ try {
2200
+ if (typeof host !== "string") return null;
2201
+ let s = host.trim();
2202
+ if (!s || s.length > SSRF_MAX_HOST) return null;
2203
+ if (s.startsWith("[") && s.endsWith("]")) s = s.slice(1, -1);
2204
+ const zone = s.indexOf("%");
2205
+ if (zone >= 0) s = s.slice(0, zone);
2206
+ if (!s) return null;
2207
+ if (s.includes(":")) {
2208
+ const g = expandIpv6(s);
2209
+ if (!g) return null;
2210
+ const mapped = g.slice(0, 5).every((x) => x === 0) && g[5] === 65535;
2211
+ if (mapped) {
2212
+ return [g[6] >> 8 & 255, g[6] & 255, g[7] >> 8 & 255, g[7] & 255].join(".");
2213
+ }
2214
+ return compressIpv6(g);
2215
+ }
2216
+ return parseIpv4(s);
2217
+ } catch {
2218
+ return null;
2219
+ }
2220
+ }
2221
+ function isStrictGatedTier(tier) {
2222
+ return STRICT_TIERS.has(tier);
2223
+ }
2224
+ function ssrfReason(m, asWritten) {
2225
+ return `Blocked: ${asWritten} is ${TIER_REASON[m.tier]}` + (m.normalized && m.normalized !== asWritten ? ` (${m.normalized})` : "") + (m.overridable ? "." : ". This address cannot be allowlisted.");
2226
+ }
2227
+ function classifySsrf(host) {
2228
+ try {
2229
+ if (typeof host !== "string" || !host) return null;
2230
+ const lower = host.trim().toLowerCase().replace(/\.$/, "");
2231
+ const ip = normalizeIpLiteral(host);
2232
+ if (ip === null) {
2233
+ return METADATA_HOSTNAMES.has(lower) ? { tier: "metadata", overridable: false, kind: "hostname" } : null;
2234
+ }
2235
+ const hit = (tier, overridable) => ({
2236
+ tier,
2237
+ overridable,
2238
+ kind: "address",
2239
+ normalized: ip
2240
+ });
2241
+ if (METADATA_ADDRESSES.has(ip)) return hit("metadata", false);
2242
+ const o = v4Octets(ip);
2243
+ if (o) {
2244
+ if (o[0] === 0 && o[1] === 0 && o[2] === 0 && o[3] === 0) return hit("unspecified", true);
2245
+ if (o[0] === 169 && o[1] === 254) return hit("link-local", false);
2246
+ if (o[0] >= 224 && o[0] <= 239) return hit("multicast", false);
2247
+ if (o[0] === 100 && o[1] >= 64 && o[1] <= 127) return hit("cgnat", true);
2248
+ if (o[0] === 127) return hit("private", true);
2249
+ if (o[0] === 10) return hit("private", true);
2250
+ if (o[0] === 192 && o[1] === 168) return hit("private", true);
2251
+ if (o[0] === 172 && o[1] >= 16 && o[1] <= 31) return hit("private", true);
2252
+ return null;
2253
+ }
2254
+ const g = expandIpv6(ip);
2255
+ if (!g) return null;
2256
+ if (g.every((x) => x === 0)) return hit("unspecified", true);
2257
+ if ((g[0] & 65472) === 65152) return hit("link-local", false);
2258
+ if ((g[0] & 65280) === 65280) return hit("multicast", false);
2259
+ if (g.slice(0, 7).every((x) => x === 0) && g[7] === 1) return hit("private", true);
2260
+ return null;
2261
+ } catch {
2262
+ return null;
2263
+ }
2264
+ }
2265
+ function ssrfFloor(tokens, opts = {}) {
2266
+ const exempt2 = new Set(
2267
+ (opts.ssrfAllow ?? []).map((e) => normalizeIpLiteral(e) ?? e.trim().toLowerCase())
2268
+ );
2269
+ for (const { token, binary } of tokens) {
2270
+ const m = classifySsrf(token);
2271
+ if (!m) continue;
2272
+ if (isStrictGatedTier(m.tier) && !opts.ssrfStrict) continue;
2273
+ if (m.overridable && m.normalized && exempt2.has(m.normalized)) continue;
2274
+ return { ...m, host: token, binary, reason: ssrfReason(m, token) };
2275
+ }
2276
+ return null;
2277
+ }
2103
2278
  function hostMatches(host, pattern) {
2104
2279
  const h = host.toLowerCase();
2105
2280
  const p = pattern.toLowerCase().trim();
@@ -2115,15 +2290,19 @@ function matchesAny(host, patterns) {
2115
2290
  for (const p of patterns) if (hostMatches(host, p)) return true;
2116
2291
  return false;
2117
2292
  }
2293
+ function isUniqueLocalV6(host) {
2294
+ const ip = normalizeIpLiteral(host);
2295
+ if (!ip || !ip.includes(":")) return false;
2296
+ const g = expandIpv6(ip);
2297
+ return g !== null && (g[0] & 65024) === 64512;
2298
+ }
2118
2299
  function isPrivateHost(host) {
2119
- const h = host.toLowerCase();
2120
- if (h === "localhost" || h === "0.0.0.0") return true;
2121
- if (h.endsWith(".local") || h.endsWith(".internal") || h.endsWith(".localhost")) return true;
2122
- if (/^127\./.test(h)) return true;
2123
- if (/^10\./.test(h)) return true;
2124
- if (/^192\.168\./.test(h)) return true;
2125
- if (/^172\.(1[6-9]|2\d|3[01])\./.test(h)) return true;
2126
- return false;
2300
+ const h = host.trim().toLowerCase();
2301
+ const m = classifySsrf(h);
2302
+ if (m) return m.kind === "address" && (m.tier === "private" || m.tier === "unspecified");
2303
+ if (h === "localhost") return true;
2304
+ if (PRIVATE_HOST_SUFFIXES.some((s) => h.endsWith(s))) return true;
2305
+ return isUniqueLocalV6(h);
2127
2306
  }
2128
2307
  function evaluateEgress(dests, policy) {
2129
2308
  if (!policy.enabled) return null;
@@ -2343,181 +2522,6 @@ function extractAllSshHosts(tokens) {
2343
2522
  }
2344
2523
  return [...hosts].filter(Boolean);
2345
2524
  }
2346
- function parseComponent(s) {
2347
- if (!s) return null;
2348
- if (/^0[xX][0-9a-fA-F]+$/.test(s)) return parseInt(s.slice(2), 16);
2349
- if (s === "0") return 0;
2350
- if (/^0[0-7]+$/.test(s)) return parseInt(s.slice(1), 8);
2351
- if (/^[1-9][0-9]*$/.test(s)) return Number(s);
2352
- return null;
2353
- }
2354
- function parseIpv4(input) {
2355
- let s = input;
2356
- if (s.endsWith(".")) s = s.slice(0, -1);
2357
- if (!s) return null;
2358
- const parts = s.split(".");
2359
- if (parts.length > 4) return null;
2360
- const vals = [];
2361
- for (const p of parts) {
2362
- const v = parseComponent(p);
2363
- if (v === null || !Number.isFinite(v) || v < 0) return null;
2364
- vals.push(v);
2365
- }
2366
- const n = vals.length;
2367
- for (let i = 0; i < n - 1; i++) if (vals[i] > 255) return null;
2368
- const last = vals[n - 1];
2369
- const remainingBytes = 4 - (n - 1);
2370
- const limit = Math.pow(256, remainingBytes);
2371
- if (last >= limit) return null;
2372
- let value = last;
2373
- for (let i = 0; i < n - 1; i++) value += vals[i] * Math.pow(256, 3 - i);
2374
- if (value > 4294967295) return null;
2375
- return [value >>> 24 & 255, value >>> 16 & 255, value >>> 8 & 255, value & 255].join(".");
2376
- }
2377
- function expandIpv6(input) {
2378
- const s = input.toLowerCase();
2379
- if (!/^[0-9a-f:.]+$/.test(s)) return null;
2380
- if ((s.match(/::/g) ?? []).length > 1) return null;
2381
- let head = s;
2382
- let tailV4 = null;
2383
- const lastColon = s.lastIndexOf(":");
2384
- const afterLast = s.slice(lastColon + 1);
2385
- if (afterLast.includes(".")) {
2386
- const dotted = parseIpv4(afterLast);
2387
- if (!dotted) return null;
2388
- const o = dotted.split(".").map(Number);
2389
- tailV4 = [o[0] << 8 | o[1], o[2] << 8 | o[3]];
2390
- head = s.slice(0, lastColon + 1) + "0";
2391
- }
2392
- const [lhs, rhs] = head.includes("::") ? head.split("::") : [head, null];
2393
- const toGroups = (part) => {
2394
- if (!part) return [];
2395
- const out = [];
2396
- for (const g of part.split(":")) {
2397
- if (!/^[0-9a-f]{1,4}$/.test(g)) return null;
2398
- out.push(parseInt(g, 16));
2399
- }
2400
- return out;
2401
- };
2402
- const left = toGroups(lhs);
2403
- if (left === null) return null;
2404
- let right = [];
2405
- if (rhs !== null) {
2406
- const r = toGroups(rhs);
2407
- if (r === null) return null;
2408
- right = r;
2409
- }
2410
- if (tailV4) {
2411
- if (rhs !== null) right = right.slice(0, -1).concat(tailV4);
2412
- else left.splice(left.length - 1, 1, ...tailV4);
2413
- }
2414
- const groups = rhs === null ? left : left.concat(new Array(8 - left.length - right.length).fill(0), right);
2415
- if (rhs === null && groups.length !== 8) return null;
2416
- if (rhs !== null && left.length + right.length > 8) return null;
2417
- if (groups.length !== 8) return null;
2418
- return groups;
2419
- }
2420
- function compressIpv6(g) {
2421
- let bestStart = -1;
2422
- let bestLen = 0;
2423
- let i = 0;
2424
- while (i < 8) {
2425
- if (g[i] !== 0) {
2426
- i++;
2427
- continue;
2428
- }
2429
- let j = i;
2430
- while (j < 8 && g[j] === 0) j++;
2431
- if (j - i > bestLen) {
2432
- bestLen = j - i;
2433
- bestStart = i;
2434
- }
2435
- i = j;
2436
- }
2437
- const hex = g.map((x) => x.toString(16));
2438
- if (bestLen < 2) return hex.join(":");
2439
- return hex.slice(0, bestStart).join(":") + "::" + hex.slice(bestStart + bestLen).join(":");
2440
- }
2441
- function normalizeIpLiteral(host) {
2442
- try {
2443
- if (typeof host !== "string") return null;
2444
- let s = host.trim();
2445
- if (!s || s.length > SSRF_MAX_HOST) return null;
2446
- if (s.startsWith("[") && s.endsWith("]")) s = s.slice(1, -1);
2447
- const zone = s.indexOf("%");
2448
- if (zone >= 0) s = s.slice(0, zone);
2449
- if (!s) return null;
2450
- if (s.includes(":")) {
2451
- const g = expandIpv6(s);
2452
- if (!g) return null;
2453
- const mapped = g.slice(0, 5).every((x) => x === 0) && g[5] === 65535;
2454
- if (mapped) {
2455
- return [g[6] >> 8 & 255, g[6] & 255, g[7] >> 8 & 255, g[7] & 255].join(".");
2456
- }
2457
- return compressIpv6(g);
2458
- }
2459
- return parseIpv4(s);
2460
- } catch {
2461
- return null;
2462
- }
2463
- }
2464
- function isStrictGatedTier(tier) {
2465
- return STRICT_TIERS.has(tier);
2466
- }
2467
- function ssrfReason(m, asWritten) {
2468
- return `Blocked: ${asWritten} is ${TIER_REASON[m.tier]}` + (m.normalized && m.normalized !== asWritten ? ` (${m.normalized})` : "") + (m.overridable ? "." : ". This address cannot be allowlisted.");
2469
- }
2470
- function classifySsrf(host) {
2471
- try {
2472
- if (typeof host !== "string" || !host) return null;
2473
- const lower = host.trim().toLowerCase().replace(/\.$/, "");
2474
- const ip = normalizeIpLiteral(host);
2475
- if (ip === null) {
2476
- return METADATA_HOSTNAMES.has(lower) ? { tier: "metadata", overridable: false, kind: "hostname" } : null;
2477
- }
2478
- const hit = (tier, overridable) => ({
2479
- tier,
2480
- overridable,
2481
- kind: "address",
2482
- normalized: ip
2483
- });
2484
- if (METADATA_ADDRESSES.has(ip)) return hit("metadata", false);
2485
- const o = v4Octets(ip);
2486
- if (o) {
2487
- if (o[0] === 0 && o[1] === 0 && o[2] === 0 && o[3] === 0) return hit("unspecified", true);
2488
- if (o[0] === 169 && o[1] === 254) return hit("link-local", false);
2489
- if (o[0] >= 224 && o[0] <= 239) return hit("multicast", false);
2490
- if (o[0] === 100 && o[1] >= 64 && o[1] <= 127) return hit("cgnat", true);
2491
- if (o[0] === 127) return hit("private", true);
2492
- if (o[0] === 10) return hit("private", true);
2493
- if (o[0] === 192 && o[1] === 168) return hit("private", true);
2494
- if (o[0] === 172 && o[1] >= 16 && o[1] <= 31) return hit("private", true);
2495
- return null;
2496
- }
2497
- const g = expandIpv6(ip);
2498
- if (!g) return null;
2499
- if (g.every((x) => x === 0)) return hit("unspecified", true);
2500
- if ((g[0] & 65472) === 65152) return hit("link-local", false);
2501
- if ((g[0] & 65280) === 65280) return hit("multicast", false);
2502
- if (g.slice(0, 7).every((x) => x === 0) && g[7] === 1) return hit("private", true);
2503
- return null;
2504
- } catch {
2505
- return null;
2506
- }
2507
- }
2508
- function ssrfFloor(tokens, opts = {}) {
2509
- const exempt2 = new Set(
2510
- (opts.ssrfAllow ?? []).map((e) => normalizeIpLiteral(e) ?? e.trim().toLowerCase())
2511
- );
2512
- for (const { token, binary } of tokens) {
2513
- const m = classifySsrf(token);
2514
- if (!m) continue;
2515
- if (isStrictGatedTier(m.tier) && !opts.ssrfStrict) continue;
2516
- if (m.overridable && m.normalized && exempt2.has(m.normalized)) continue;
2517
- return { ...m, host: token, binary, reason: ssrfReason(m, token) };
2518
- }
2519
- return null;
2520
- }
2521
2525
  function bareToolName(toolName) {
2522
2526
  const parts = toolName.split("__");
2523
2527
  return (parts.length >= 3 ? parts.slice(2).join("__") : toolName).toLowerCase();
@@ -3723,7 +3727,7 @@ function* stringValues(obj, depth = 0) {
3723
3727
  }
3724
3728
  for (const v of Object.values(obj)) yield* stringValues(v, depth + 1);
3725
3729
  }
3726
- var IBAN_LENGTH, B58, B58_INDEX, XPRV_VERSIONS, MAX, UNTRUSTED_TOOLS, SIGNALS, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, MAX_DEPTH, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, DLP_SCAN_LIMITS, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, FORBIDDEN_PATH_SEGMENTS, syntax, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, AST_CACHE_MAX, astCache, PARSE_FAIL, FS_READ_TOOLS, GREP_SHAPE, PATTERN_VERBS, PATTERN_VERB_NAMES, GREP_FILE_OPERANDS, READER_VALUE_LETTERS, FILE_OPERAND_FLAGS, SCP_VALUE_FLAGS, TAR_VALUE_LETTERS, ZIP_VALUE_LETTERS, RSYNC_VALUE_LETTERS, RSYNC_SKIP, CP_VALUE_LETTERS, INSTALL_VALUE_LETTERS, COPY_VERBS, TAR_MODE_WORD, COPY_VERB_HEADS, FS_OP_PRESCREEN_RE, HOME_CACHE_ALLOWLIST, SENSITIVE_PATH_RULES, BASH_TOOL_NAMES, AST_FS_REGEX_RULES, SQL_DB_CLIS, SQL_DDL_RE, CHMOD_OPEN_PERM_TOKENS, COMMAND_WRAPPERS, INLINE_INTERPRETER, RUNNER_WRAPPERS, _redirStdinOps, _listOps, WRAPPER_TAKES_TARGET, FIND_EXEC_FLAGS, INTERP_LEADING_TARGET, NET_BINARIES, VALUE_FLAGS, FS_OP_CACHE_MAX, fsOpCache, stripDotSlash, REDIR_TRUNCATE_OPS, REDIR_FILE_IN_OPS, REDIR_HEREDOC_OPS, FIND_OPTIONS, COPY_RULE_OF, isReaderWord, positionalAfter, NONE, UNKNOWN, DEFAULT_EGRESS_ALLOWLIST, SOURCE_COMMANDS, SINK_COMMANDS, OBFUSCATORS, SENSITIVE_PATTERNS, FLAGS_WITH_VALUES, SSRF_MAX_HOST, METADATA_ADDRESSES, STRICT_TIERS, METADATA_HOSTNAMES, v4Octets, TIER_REASON, DESTINATION_ARGS, VERDICT_RANK, SQL_DML_KEYWORDS, TERMINAL_ESCAPE_RE, CONTROL_CHAR_RE, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, BUILTIN_SHIELDS, LOOP_MAX_RECORDS, FINDING_TO_SIGNAL, SCAN_SIGNAL_WEIGHTS, LOOP_THRESHOLD_FOR_WASTE, MAX_BLAST_PATH, DESTRUCTIVE_OP_RE, SENSITIVE_PATH_RE, FILE_TOOLS, PII_EMAIL_RE, PII_SSN_RE, PII_PHONE_RE, PII_CC16_RE, PII_CC15_RE, PII_IBAN_RE, REALTIME_PII_PATTERNS, MAX_PII_SCAN_BYTES, CANARY_MIN_LENGTH, MAX_TEXT, MAX_DEPTH2, MAX_JSON_PARSE, URL_DEPTH, B64_DEPTH, MIN_SEGMENT, SEPARATORS, stripSeparators, looksText, CANARY_DECODERS, VIEWS, LONG_OUTPUT_THRESHOLD_BYTES, CANONICAL_EXTRACTOR_VERSION, DEDUPE_PREVIEW_LEN, ENGINE_VERSION;
3730
+ var IBAN_LENGTH, B58, B58_INDEX, XPRV_VERSIONS, MAX, UNTRUSTED_TOOLS, SIGNALS, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, MAX_DEPTH, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, DLP_SCAN_LIMITS, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, FORBIDDEN_PATH_SEGMENTS, syntax, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, AST_CACHE_MAX, astCache, PARSE_FAIL, FS_READ_TOOLS, GREP_SHAPE, PATTERN_VERBS, PATTERN_VERB_NAMES, GREP_FILE_OPERANDS, READER_VALUE_LETTERS, FILE_OPERAND_FLAGS, SCP_VALUE_FLAGS, TAR_VALUE_LETTERS, ZIP_VALUE_LETTERS, RSYNC_VALUE_LETTERS, RSYNC_SKIP, CP_VALUE_LETTERS, INSTALL_VALUE_LETTERS, COPY_VERBS, TAR_MODE_WORD, COPY_VERB_HEADS, FS_OP_PRESCREEN_RE, HOME_CACHE_ALLOWLIST, SENSITIVE_PATH_RULES, BASH_TOOL_NAMES, AST_FS_REGEX_RULES, SQL_DB_CLIS, SQL_DDL_RE, CHMOD_OPEN_PERM_TOKENS, COMMAND_WRAPPERS, INLINE_INTERPRETER, RUNNER_WRAPPERS, _redirStdinOps, _listOps, WRAPPER_TAKES_TARGET, FIND_EXEC_FLAGS, INTERP_LEADING_TARGET, NET_BINARIES, VALUE_FLAGS, FS_OP_CACHE_MAX, fsOpCache, stripDotSlash, REDIR_TRUNCATE_OPS, REDIR_FILE_IN_OPS, REDIR_HEREDOC_OPS, FIND_OPTIONS, COPY_RULE_OF, isReaderWord, positionalAfter, NONE, UNKNOWN, SSRF_MAX_HOST, METADATA_ADDRESSES, STRICT_TIERS, METADATA_HOSTNAMES, v4Octets, TIER_REASON, DEFAULT_EGRESS_ALLOWLIST, PRIVATE_HOST_SUFFIXES, SOURCE_COMMANDS, SINK_COMMANDS, OBFUSCATORS, SENSITIVE_PATTERNS, FLAGS_WITH_VALUES, DESTINATION_ARGS, VERDICT_RANK, SQL_DML_KEYWORDS, TERMINAL_ESCAPE_RE, CONTROL_CHAR_RE, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, BUILTIN_SHIELDS, LOOP_MAX_RECORDS, FINDING_TO_SIGNAL, SCAN_SIGNAL_WEIGHTS, LOOP_THRESHOLD_FOR_WASTE, MAX_BLAST_PATH, DESTRUCTIVE_OP_RE, SENSITIVE_PATH_RE, FILE_TOOLS, PII_EMAIL_RE, PII_SSN_RE, PII_PHONE_RE, PII_CC16_RE, PII_CC15_RE, PII_IBAN_RE, REALTIME_PII_PATTERNS, MAX_PII_SCAN_BYTES, CANARY_MIN_LENGTH, MAX_TEXT, MAX_DEPTH2, MAX_JSON_PARSE, URL_DEPTH, B64_DEPTH, MIN_SEGMENT, SEPARATORS, stripSeparators, looksText, CANARY_DECODERS, VIEWS, LONG_OUTPUT_THRESHOLD_BYTES, CANONICAL_EXTRACTOR_VERSION, DEDUPE_PREVIEW_LEN, ENGINE_VERSION;
3727
3731
  var init_dist = __esm({
3728
3732
  "packages/policy-engine/dist/index.mjs"() {
3729
3733
  "use strict";
@@ -5161,6 +5165,36 @@ var init_dist = __esm({
5161
5165
  positionalAfter = (words, from, to = words.length) => positionedArgs(words, from, to).map((a) => a.value);
5162
5166
  NONE = { kind: "none" };
5163
5167
  UNKNOWN = { kind: "unknown" };
5168
+ SSRF_MAX_HOST = 253;
5169
+ METADATA_ADDRESSES = /* @__PURE__ */ new Set([
5170
+ "169.254.169.254",
5171
+ // AWS / Azure / DigitalOcean / OpenStack IMDS
5172
+ "169.254.170.2",
5173
+ // AWS ECS task role
5174
+ "168.63.129.16",
5175
+ // Azure WireServer
5176
+ "fd00:ec2::254",
5177
+ // AWS IMDS over IPv6 (inside fc00::/7, which is NOT a tier)
5178
+ // Alibaba Cloud IMDS. It sits INSIDE 100.64/10, so before this line it was
5179
+ // classified as cgnat and therefore exemptable: one ssrfAllow entry opened a
5180
+ // live credential endpoint. It has to be named here, above the range check,
5181
+ // and it is the reason relaxing cgnat is safe.
5182
+ "100.100.100.200"
5183
+ ]);
5184
+ STRICT_TIERS = /* @__PURE__ */ new Set(["private", "unspecified", "cgnat"]);
5185
+ METADATA_HOSTNAMES = /* @__PURE__ */ new Set(["metadata.google.internal", "metadata.goog", "metadata"]);
5186
+ v4Octets = (a) => {
5187
+ const p = a.split(".");
5188
+ return p.length === 4 ? p.map(Number) : null;
5189
+ };
5190
+ TIER_REASON = {
5191
+ metadata: "a cloud instance-metadata endpoint, the classic credential-theft target",
5192
+ "link-local": "a link-local address",
5193
+ multicast: "a multicast address",
5194
+ unspecified: "the unspecified address, which reaches this host",
5195
+ cgnat: "a carrier-grade NAT address",
5196
+ private: "a loopback or private address"
5197
+ };
5164
5198
  DEFAULT_EGRESS_ALLOWLIST = [
5165
5199
  // node9's own control plane (api, app, dev-api, staging and the apex).
5166
5200
  // Without it, turning egress on asks the user to approve node9 itself.
@@ -5184,6 +5218,7 @@ var init_dist = __esm({
5184
5218
  "deb.debian.org",
5185
5219
  "*.ubuntu.com"
5186
5220
  ];
5221
+ PRIVATE_HOST_SUFFIXES = [".local", ".internal", ".localhost"];
5187
5222
  SOURCE_COMMANDS = /* @__PURE__ */ new Set([...FS_READ_TOOLS, "tee"]);
5188
5223
  SINK_COMMANDS = /* @__PURE__ */ new Set([
5189
5224
  "curl",
@@ -5307,36 +5342,6 @@ var init_dist = __esm({
5307
5342
  socat: /* @__PURE__ */ new Set([])
5308
5343
  // socat uses address syntax, not flags — no value-flags
5309
5344
  };
5310
- SSRF_MAX_HOST = 253;
5311
- METADATA_ADDRESSES = /* @__PURE__ */ new Set([
5312
- "169.254.169.254",
5313
- // AWS / Azure / DigitalOcean / OpenStack IMDS
5314
- "169.254.170.2",
5315
- // AWS ECS task role
5316
- "168.63.129.16",
5317
- // Azure WireServer
5318
- "fd00:ec2::254",
5319
- // AWS IMDS over IPv6 (inside fc00::/7, which is NOT a tier)
5320
- // Alibaba Cloud IMDS. It sits INSIDE 100.64/10, so before this line it was
5321
- // classified as cgnat and therefore exemptable: one ssrfAllow entry opened a
5322
- // live credential endpoint. It has to be named here, above the range check,
5323
- // and it is the reason relaxing cgnat is safe.
5324
- "100.100.100.200"
5325
- ]);
5326
- STRICT_TIERS = /* @__PURE__ */ new Set(["private", "unspecified", "cgnat"]);
5327
- METADATA_HOSTNAMES = /* @__PURE__ */ new Set(["metadata.google.internal", "metadata.goog", "metadata"]);
5328
- v4Octets = (a) => {
5329
- const p = a.split(".");
5330
- return p.length === 4 ? p.map(Number) : null;
5331
- };
5332
- TIER_REASON = {
5333
- metadata: "a cloud instance-metadata endpoint, the classic credential-theft target",
5334
- "link-local": "a link-local address",
5335
- multicast: "a multicast address",
5336
- unspecified: "the unspecified address, which reaches this host",
5337
- cgnat: "a carrier-grade NAT address",
5338
- private: "a loopback or private address"
5339
- };
5340
5345
  DESTINATION_ARGS = /* @__PURE__ */ new Map([
5341
5346
  ["webfetch", ["url"]],
5342
5347
  ["fetch", ["url", "uri"]],
@@ -1944,7 +1944,7 @@ function matchCanaryArgs(args, values) {
1944
1944
  return null;
1945
1945
  }
1946
1946
  }
1947
- var B58, B58_INDEX, XPRV_VERSIONS, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, MAX_DEPTH, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, FORBIDDEN_PATH_SEGMENTS, syntax, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, AST_CACHE_MAX, astCache, PARSE_FAIL, FS_READ_TOOLS, GREP_SHAPE, PATTERN_VERBS, PATTERN_VERB_NAMES, GREP_FILE_OPERANDS, READER_VALUE_LETTERS, FILE_OPERAND_FLAGS, SCP_VALUE_FLAGS, TAR_VALUE_LETTERS, ZIP_VALUE_LETTERS, RSYNC_VALUE_LETTERS, RSYNC_SKIP, CP_VALUE_LETTERS, INSTALL_VALUE_LETTERS, COPY_VERBS, TAR_MODE_WORD, COPY_VERB_HEADS, FS_OP_PRESCREEN_RE, HOME_CACHE_ALLOWLIST, SENSITIVE_PATH_RULES, BASH_TOOL_NAMES, AST_FS_REGEX_RULES, COMMAND_WRAPPERS, INLINE_INTERPRETER, RUNNER_WRAPPERS, WRAPPER_TAKES_TARGET, FIND_EXEC_FLAGS, FS_OP_CACHE_MAX, fsOpCache, REDIR_TRUNCATE_OPS, REDIR_FILE_IN_OPS, REDIR_HEREDOC_OPS, FIND_OPTIONS, COPY_RULE_OF, isReaderWord, positionalAfter, NONE, UNKNOWN, SOURCE_COMMANDS, SSRF_MAX_HOST, METADATA_ADDRESSES, METADATA_HOSTNAMES, v4Octets, TERMINAL_ESCAPE_RE, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, BUILTIN_SHIELDS, CANARY_MIN_LENGTH, MAX_TEXT, MAX_DEPTH2, MAX_JSON_PARSE, URL_DEPTH, B64_DEPTH, MIN_SEGMENT, SEPARATORS, stripSeparators, looksText, CANARY_DECODERS, VIEWS, LONG_OUTPUT_THRESHOLD_BYTES;
1947
+ var B58, B58_INDEX, XPRV_VERSIONS, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, MAX_DEPTH, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, FORBIDDEN_PATH_SEGMENTS, syntax, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, AST_CACHE_MAX, astCache, PARSE_FAIL, FS_READ_TOOLS, GREP_SHAPE, PATTERN_VERBS, PATTERN_VERB_NAMES, GREP_FILE_OPERANDS, READER_VALUE_LETTERS, FILE_OPERAND_FLAGS, SCP_VALUE_FLAGS, TAR_VALUE_LETTERS, ZIP_VALUE_LETTERS, RSYNC_VALUE_LETTERS, RSYNC_SKIP, CP_VALUE_LETTERS, INSTALL_VALUE_LETTERS, COPY_VERBS, TAR_MODE_WORD, COPY_VERB_HEADS, FS_OP_PRESCREEN_RE, HOME_CACHE_ALLOWLIST, SENSITIVE_PATH_RULES, BASH_TOOL_NAMES, AST_FS_REGEX_RULES, COMMAND_WRAPPERS, INLINE_INTERPRETER, RUNNER_WRAPPERS, WRAPPER_TAKES_TARGET, FIND_EXEC_FLAGS, FS_OP_CACHE_MAX, fsOpCache, REDIR_TRUNCATE_OPS, REDIR_FILE_IN_OPS, REDIR_HEREDOC_OPS, FIND_OPTIONS, COPY_RULE_OF, isReaderWord, positionalAfter, NONE, UNKNOWN, SSRF_MAX_HOST, METADATA_ADDRESSES, METADATA_HOSTNAMES, v4Octets, SOURCE_COMMANDS, TERMINAL_ESCAPE_RE, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, BUILTIN_SHIELDS, CANARY_MIN_LENGTH, MAX_TEXT, MAX_DEPTH2, MAX_JSON_PARSE, URL_DEPTH, B64_DEPTH, MIN_SEGMENT, SEPARATORS, stripSeparators, looksText, CANARY_DECODERS, VIEWS, LONG_OUTPUT_THRESHOLD_BYTES;
1948
1948
  var init_dist = __esm({
1949
1949
  "packages/policy-engine/dist/index.mjs"() {
1950
1950
  "use strict";
@@ -3136,7 +3136,6 @@ var init_dist = __esm({
3136
3136
  positionalAfter = (words, from, to = words.length) => positionedArgs(words, from, to).map((a) => a.value);
3137
3137
  NONE = { kind: "none" };
3138
3138
  UNKNOWN = { kind: "unknown" };
3139
- SOURCE_COMMANDS = /* @__PURE__ */ new Set([...FS_READ_TOOLS, "tee"]);
3140
3139
  SSRF_MAX_HOST = 253;
3141
3140
  METADATA_ADDRESSES = /* @__PURE__ */ new Set([
3142
3141
  "169.254.169.254",
@@ -3158,6 +3157,7 @@ var init_dist = __esm({
3158
3157
  const p = a.split(".");
3159
3158
  return p.length === 4 ? p.map(Number) : null;
3160
3159
  };
3160
+ SOURCE_COMMANDS = /* @__PURE__ */ new Set([...FS_READ_TOOLS, "tee"]);
3161
3161
  TERMINAL_ESCAPE_RE = /\x1b\[[0-9;?]*[A-Za-z]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b[@-_]|[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g;
3162
3162
  aws_default = {
3163
3163
  name: "aws",