@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.js CHANGED
@@ -2089,6 +2089,181 @@ function analyzeShellCommand(command) {
2089
2089
  }
2090
2090
  return { actions, paths, allTokens };
2091
2091
  }
2092
+ function parseComponent(s) {
2093
+ if (!s) return null;
2094
+ if (/^0[xX][0-9a-fA-F]+$/.test(s)) return parseInt(s.slice(2), 16);
2095
+ if (s === "0") return 0;
2096
+ if (/^0[0-7]+$/.test(s)) return parseInt(s.slice(1), 8);
2097
+ if (/^[1-9][0-9]*$/.test(s)) return Number(s);
2098
+ return null;
2099
+ }
2100
+ function parseIpv4(input) {
2101
+ let s = input;
2102
+ if (s.endsWith(".")) s = s.slice(0, -1);
2103
+ if (!s) return null;
2104
+ const parts = s.split(".");
2105
+ if (parts.length > 4) return null;
2106
+ const vals = [];
2107
+ for (const p of parts) {
2108
+ const v = parseComponent(p);
2109
+ if (v === null || !Number.isFinite(v) || v < 0) return null;
2110
+ vals.push(v);
2111
+ }
2112
+ const n = vals.length;
2113
+ for (let i = 0; i < n - 1; i++) if (vals[i] > 255) return null;
2114
+ const last = vals[n - 1];
2115
+ const remainingBytes = 4 - (n - 1);
2116
+ const limit = Math.pow(256, remainingBytes);
2117
+ if (last >= limit) return null;
2118
+ let value = last;
2119
+ for (let i = 0; i < n - 1; i++) value += vals[i] * Math.pow(256, 3 - i);
2120
+ if (value > 4294967295) return null;
2121
+ return [value >>> 24 & 255, value >>> 16 & 255, value >>> 8 & 255, value & 255].join(".");
2122
+ }
2123
+ function expandIpv6(input) {
2124
+ const s = input.toLowerCase();
2125
+ if (!/^[0-9a-f:.]+$/.test(s)) return null;
2126
+ if ((s.match(/::/g) ?? []).length > 1) return null;
2127
+ let head = s;
2128
+ let tailV4 = null;
2129
+ const lastColon = s.lastIndexOf(":");
2130
+ const afterLast = s.slice(lastColon + 1);
2131
+ if (afterLast.includes(".")) {
2132
+ const dotted = parseIpv4(afterLast);
2133
+ if (!dotted) return null;
2134
+ const o = dotted.split(".").map(Number);
2135
+ tailV4 = [o[0] << 8 | o[1], o[2] << 8 | o[3]];
2136
+ head = s.slice(0, lastColon + 1) + "0";
2137
+ }
2138
+ const [lhs, rhs] = head.includes("::") ? head.split("::") : [head, null];
2139
+ const toGroups = (part) => {
2140
+ if (!part) return [];
2141
+ const out = [];
2142
+ for (const g of part.split(":")) {
2143
+ if (!/^[0-9a-f]{1,4}$/.test(g)) return null;
2144
+ out.push(parseInt(g, 16));
2145
+ }
2146
+ return out;
2147
+ };
2148
+ const left = toGroups(lhs);
2149
+ if (left === null) return null;
2150
+ let right = [];
2151
+ if (rhs !== null) {
2152
+ const r = toGroups(rhs);
2153
+ if (r === null) return null;
2154
+ right = r;
2155
+ }
2156
+ if (tailV4) {
2157
+ if (rhs !== null) right = right.slice(0, -1).concat(tailV4);
2158
+ else left.splice(left.length - 1, 1, ...tailV4);
2159
+ }
2160
+ const groups = rhs === null ? left : left.concat(new Array(8 - left.length - right.length).fill(0), right);
2161
+ if (rhs === null && groups.length !== 8) return null;
2162
+ if (rhs !== null && left.length + right.length > 8) return null;
2163
+ if (groups.length !== 8) return null;
2164
+ return groups;
2165
+ }
2166
+ function compressIpv6(g) {
2167
+ let bestStart = -1;
2168
+ let bestLen = 0;
2169
+ let i = 0;
2170
+ while (i < 8) {
2171
+ if (g[i] !== 0) {
2172
+ i++;
2173
+ continue;
2174
+ }
2175
+ let j = i;
2176
+ while (j < 8 && g[j] === 0) j++;
2177
+ if (j - i > bestLen) {
2178
+ bestLen = j - i;
2179
+ bestStart = i;
2180
+ }
2181
+ i = j;
2182
+ }
2183
+ const hex = g.map((x) => x.toString(16));
2184
+ if (bestLen < 2) return hex.join(":");
2185
+ return hex.slice(0, bestStart).join(":") + "::" + hex.slice(bestStart + bestLen).join(":");
2186
+ }
2187
+ function normalizeIpLiteral(host) {
2188
+ try {
2189
+ if (typeof host !== "string") return null;
2190
+ let s = host.trim();
2191
+ if (!s || s.length > SSRF_MAX_HOST) return null;
2192
+ if (s.startsWith("[") && s.endsWith("]")) s = s.slice(1, -1);
2193
+ const zone = s.indexOf("%");
2194
+ if (zone >= 0) s = s.slice(0, zone);
2195
+ if (!s) return null;
2196
+ if (s.includes(":")) {
2197
+ const g = expandIpv6(s);
2198
+ if (!g) return null;
2199
+ const mapped = g.slice(0, 5).every((x) => x === 0) && g[5] === 65535;
2200
+ if (mapped) {
2201
+ return [g[6] >> 8 & 255, g[6] & 255, g[7] >> 8 & 255, g[7] & 255].join(".");
2202
+ }
2203
+ return compressIpv6(g);
2204
+ }
2205
+ return parseIpv4(s);
2206
+ } catch {
2207
+ return null;
2208
+ }
2209
+ }
2210
+ function isStrictGatedTier(tier) {
2211
+ return STRICT_TIERS.has(tier);
2212
+ }
2213
+ function ssrfReason(m, asWritten) {
2214
+ return `Blocked: ${asWritten} is ${TIER_REASON[m.tier]}` + (m.normalized && m.normalized !== asWritten ? ` (${m.normalized})` : "") + (m.overridable ? "." : ". This address cannot be allowlisted.");
2215
+ }
2216
+ function classifySsrf(host) {
2217
+ try {
2218
+ if (typeof host !== "string" || !host) return null;
2219
+ const lower = host.trim().toLowerCase().replace(/\.$/, "");
2220
+ const ip = normalizeIpLiteral(host);
2221
+ if (ip === null) {
2222
+ return METADATA_HOSTNAMES.has(lower) ? { tier: "metadata", overridable: false, kind: "hostname" } : null;
2223
+ }
2224
+ const hit = (tier, overridable) => ({
2225
+ tier,
2226
+ overridable,
2227
+ kind: "address",
2228
+ normalized: ip
2229
+ });
2230
+ if (METADATA_ADDRESSES.has(ip)) return hit("metadata", false);
2231
+ const o = v4Octets(ip);
2232
+ if (o) {
2233
+ if (o[0] === 0 && o[1] === 0 && o[2] === 0 && o[3] === 0) return hit("unspecified", true);
2234
+ if (o[0] === 169 && o[1] === 254) return hit("link-local", false);
2235
+ if (o[0] >= 224 && o[0] <= 239) return hit("multicast", false);
2236
+ if (o[0] === 100 && o[1] >= 64 && o[1] <= 127) return hit("cgnat", true);
2237
+ if (o[0] === 127) return hit("private", true);
2238
+ if (o[0] === 10) return hit("private", true);
2239
+ if (o[0] === 192 && o[1] === 168) return hit("private", true);
2240
+ if (o[0] === 172 && o[1] >= 16 && o[1] <= 31) return hit("private", true);
2241
+ return null;
2242
+ }
2243
+ const g = expandIpv6(ip);
2244
+ if (!g) return null;
2245
+ if (g.every((x) => x === 0)) return hit("unspecified", true);
2246
+ if ((g[0] & 65472) === 65152) return hit("link-local", false);
2247
+ if ((g[0] & 65280) === 65280) return hit("multicast", false);
2248
+ if (g.slice(0, 7).every((x) => x === 0) && g[7] === 1) return hit("private", true);
2249
+ return null;
2250
+ } catch {
2251
+ return null;
2252
+ }
2253
+ }
2254
+ function ssrfFloor(tokens, opts = {}) {
2255
+ const exempt2 = new Set(
2256
+ (opts.ssrfAllow ?? []).map((e) => normalizeIpLiteral(e) ?? e.trim().toLowerCase())
2257
+ );
2258
+ for (const { token, binary } of tokens) {
2259
+ const m = classifySsrf(token);
2260
+ if (!m) continue;
2261
+ if (isStrictGatedTier(m.tier) && !opts.ssrfStrict) continue;
2262
+ if (m.overridable && m.normalized && exempt2.has(m.normalized)) continue;
2263
+ return { ...m, host: token, binary, reason: ssrfReason(m, token) };
2264
+ }
2265
+ return null;
2266
+ }
2092
2267
  function hostMatches(host, pattern) {
2093
2268
  const h = host.toLowerCase();
2094
2269
  const p = pattern.toLowerCase().trim();
@@ -2104,15 +2279,19 @@ function matchesAny(host, patterns) {
2104
2279
  for (const p of patterns) if (hostMatches(host, p)) return true;
2105
2280
  return false;
2106
2281
  }
2282
+ function isUniqueLocalV6(host) {
2283
+ const ip = normalizeIpLiteral(host);
2284
+ if (!ip || !ip.includes(":")) return false;
2285
+ const g = expandIpv6(ip);
2286
+ return g !== null && (g[0] & 65024) === 64512;
2287
+ }
2107
2288
  function isPrivateHost(host) {
2108
- const h = host.toLowerCase();
2109
- if (h === "localhost" || h === "0.0.0.0") return true;
2110
- if (h.endsWith(".local") || h.endsWith(".internal") || h.endsWith(".localhost")) return true;
2111
- if (/^127\./.test(h)) return true;
2112
- if (/^10\./.test(h)) return true;
2113
- if (/^192\.168\./.test(h)) return true;
2114
- if (/^172\.(1[6-9]|2\d|3[01])\./.test(h)) return true;
2115
- return false;
2289
+ const h = host.trim().toLowerCase();
2290
+ const m = classifySsrf(h);
2291
+ if (m) return m.kind === "address" && (m.tier === "private" || m.tier === "unspecified");
2292
+ if (h === "localhost") return true;
2293
+ if (PRIVATE_HOST_SUFFIXES.some((s) => h.endsWith(s))) return true;
2294
+ return isUniqueLocalV6(h);
2116
2295
  }
2117
2296
  function evaluateEgress(dests, policy) {
2118
2297
  if (!policy.enabled) return null;
@@ -2332,181 +2511,6 @@ function extractAllSshHosts(tokens) {
2332
2511
  }
2333
2512
  return [...hosts].filter(Boolean);
2334
2513
  }
2335
- function parseComponent(s) {
2336
- if (!s) return null;
2337
- if (/^0[xX][0-9a-fA-F]+$/.test(s)) return parseInt(s.slice(2), 16);
2338
- if (s === "0") return 0;
2339
- if (/^0[0-7]+$/.test(s)) return parseInt(s.slice(1), 8);
2340
- if (/^[1-9][0-9]*$/.test(s)) return Number(s);
2341
- return null;
2342
- }
2343
- function parseIpv4(input) {
2344
- let s = input;
2345
- if (s.endsWith(".")) s = s.slice(0, -1);
2346
- if (!s) return null;
2347
- const parts = s.split(".");
2348
- if (parts.length > 4) return null;
2349
- const vals = [];
2350
- for (const p of parts) {
2351
- const v = parseComponent(p);
2352
- if (v === null || !Number.isFinite(v) || v < 0) return null;
2353
- vals.push(v);
2354
- }
2355
- const n = vals.length;
2356
- for (let i = 0; i < n - 1; i++) if (vals[i] > 255) return null;
2357
- const last = vals[n - 1];
2358
- const remainingBytes = 4 - (n - 1);
2359
- const limit = Math.pow(256, remainingBytes);
2360
- if (last >= limit) return null;
2361
- let value = last;
2362
- for (let i = 0; i < n - 1; i++) value += vals[i] * Math.pow(256, 3 - i);
2363
- if (value > 4294967295) return null;
2364
- return [value >>> 24 & 255, value >>> 16 & 255, value >>> 8 & 255, value & 255].join(".");
2365
- }
2366
- function expandIpv6(input) {
2367
- const s = input.toLowerCase();
2368
- if (!/^[0-9a-f:.]+$/.test(s)) return null;
2369
- if ((s.match(/::/g) ?? []).length > 1) return null;
2370
- let head = s;
2371
- let tailV4 = null;
2372
- const lastColon = s.lastIndexOf(":");
2373
- const afterLast = s.slice(lastColon + 1);
2374
- if (afterLast.includes(".")) {
2375
- const dotted = parseIpv4(afterLast);
2376
- if (!dotted) return null;
2377
- const o = dotted.split(".").map(Number);
2378
- tailV4 = [o[0] << 8 | o[1], o[2] << 8 | o[3]];
2379
- head = s.slice(0, lastColon + 1) + "0";
2380
- }
2381
- const [lhs, rhs] = head.includes("::") ? head.split("::") : [head, null];
2382
- const toGroups = (part) => {
2383
- if (!part) return [];
2384
- const out = [];
2385
- for (const g of part.split(":")) {
2386
- if (!/^[0-9a-f]{1,4}$/.test(g)) return null;
2387
- out.push(parseInt(g, 16));
2388
- }
2389
- return out;
2390
- };
2391
- const left = toGroups(lhs);
2392
- if (left === null) return null;
2393
- let right = [];
2394
- if (rhs !== null) {
2395
- const r = toGroups(rhs);
2396
- if (r === null) return null;
2397
- right = r;
2398
- }
2399
- if (tailV4) {
2400
- if (rhs !== null) right = right.slice(0, -1).concat(tailV4);
2401
- else left.splice(left.length - 1, 1, ...tailV4);
2402
- }
2403
- const groups = rhs === null ? left : left.concat(new Array(8 - left.length - right.length).fill(0), right);
2404
- if (rhs === null && groups.length !== 8) return null;
2405
- if (rhs !== null && left.length + right.length > 8) return null;
2406
- if (groups.length !== 8) return null;
2407
- return groups;
2408
- }
2409
- function compressIpv6(g) {
2410
- let bestStart = -1;
2411
- let bestLen = 0;
2412
- let i = 0;
2413
- while (i < 8) {
2414
- if (g[i] !== 0) {
2415
- i++;
2416
- continue;
2417
- }
2418
- let j = i;
2419
- while (j < 8 && g[j] === 0) j++;
2420
- if (j - i > bestLen) {
2421
- bestLen = j - i;
2422
- bestStart = i;
2423
- }
2424
- i = j;
2425
- }
2426
- const hex = g.map((x) => x.toString(16));
2427
- if (bestLen < 2) return hex.join(":");
2428
- return hex.slice(0, bestStart).join(":") + "::" + hex.slice(bestStart + bestLen).join(":");
2429
- }
2430
- function normalizeIpLiteral(host) {
2431
- try {
2432
- if (typeof host !== "string") return null;
2433
- let s = host.trim();
2434
- if (!s || s.length > SSRF_MAX_HOST) return null;
2435
- if (s.startsWith("[") && s.endsWith("]")) s = s.slice(1, -1);
2436
- const zone = s.indexOf("%");
2437
- if (zone >= 0) s = s.slice(0, zone);
2438
- if (!s) return null;
2439
- if (s.includes(":")) {
2440
- const g = expandIpv6(s);
2441
- if (!g) return null;
2442
- const mapped = g.slice(0, 5).every((x) => x === 0) && g[5] === 65535;
2443
- if (mapped) {
2444
- return [g[6] >> 8 & 255, g[6] & 255, g[7] >> 8 & 255, g[7] & 255].join(".");
2445
- }
2446
- return compressIpv6(g);
2447
- }
2448
- return parseIpv4(s);
2449
- } catch {
2450
- return null;
2451
- }
2452
- }
2453
- function isStrictGatedTier(tier) {
2454
- return STRICT_TIERS.has(tier);
2455
- }
2456
- function ssrfReason(m, asWritten) {
2457
- return `Blocked: ${asWritten} is ${TIER_REASON[m.tier]}` + (m.normalized && m.normalized !== asWritten ? ` (${m.normalized})` : "") + (m.overridable ? "." : ". This address cannot be allowlisted.");
2458
- }
2459
- function classifySsrf(host) {
2460
- try {
2461
- if (typeof host !== "string" || !host) return null;
2462
- const lower = host.trim().toLowerCase().replace(/\.$/, "");
2463
- const ip = normalizeIpLiteral(host);
2464
- if (ip === null) {
2465
- return METADATA_HOSTNAMES.has(lower) ? { tier: "metadata", overridable: false, kind: "hostname" } : null;
2466
- }
2467
- const hit = (tier, overridable) => ({
2468
- tier,
2469
- overridable,
2470
- kind: "address",
2471
- normalized: ip
2472
- });
2473
- if (METADATA_ADDRESSES.has(ip)) return hit("metadata", false);
2474
- const o = v4Octets(ip);
2475
- if (o) {
2476
- if (o[0] === 0 && o[1] === 0 && o[2] === 0 && o[3] === 0) return hit("unspecified", true);
2477
- if (o[0] === 169 && o[1] === 254) return hit("link-local", false);
2478
- if (o[0] >= 224 && o[0] <= 239) return hit("multicast", false);
2479
- if (o[0] === 100 && o[1] >= 64 && o[1] <= 127) return hit("cgnat", true);
2480
- if (o[0] === 127) return hit("private", true);
2481
- if (o[0] === 10) return hit("private", true);
2482
- if (o[0] === 192 && o[1] === 168) return hit("private", true);
2483
- if (o[0] === 172 && o[1] >= 16 && o[1] <= 31) return hit("private", true);
2484
- return null;
2485
- }
2486
- const g = expandIpv6(ip);
2487
- if (!g) return null;
2488
- if (g.every((x) => x === 0)) return hit("unspecified", true);
2489
- if ((g[0] & 65472) === 65152) return hit("link-local", false);
2490
- if ((g[0] & 65280) === 65280) return hit("multicast", false);
2491
- if (g.slice(0, 7).every((x) => x === 0) && g[7] === 1) return hit("private", true);
2492
- return null;
2493
- } catch {
2494
- return null;
2495
- }
2496
- }
2497
- function ssrfFloor(tokens, opts = {}) {
2498
- const exempt2 = new Set(
2499
- (opts.ssrfAllow ?? []).map((e) => normalizeIpLiteral(e) ?? e.trim().toLowerCase())
2500
- );
2501
- for (const { token, binary } of tokens) {
2502
- const m = classifySsrf(token);
2503
- if (!m) continue;
2504
- if (isStrictGatedTier(m.tier) && !opts.ssrfStrict) continue;
2505
- if (m.overridable && m.normalized && exempt2.has(m.normalized)) continue;
2506
- return { ...m, host: token, binary, reason: ssrfReason(m, token) };
2507
- }
2508
- return null;
2509
- }
2510
2514
  function bareToolName(toolName) {
2511
2515
  const parts = toolName.split("__");
2512
2516
  return (parts.length >= 3 ? parts.slice(2).join("__") : toolName).toLowerCase();
@@ -3712,7 +3716,7 @@ function* stringValues(obj, depth = 0) {
3712
3716
  }
3713
3717
  for (const v of Object.values(obj)) yield* stringValues(v, depth + 1);
3714
3718
  }
3715
- var import_safe_regex2, import_crypto3, import_mvdan_sh, import_picomatch, import_safe_regex22, import_safe_regex23, import_crypto4, 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;
3719
+ var import_safe_regex2, import_crypto3, import_mvdan_sh, import_picomatch, import_safe_regex22, import_safe_regex23, import_crypto4, 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;
3716
3720
  var init_dist = __esm({
3717
3721
  "packages/policy-engine/dist/index.mjs"() {
3718
3722
  "use strict";
@@ -5157,6 +5161,36 @@ var init_dist = __esm({
5157
5161
  positionalAfter = (words, from, to = words.length) => positionedArgs(words, from, to).map((a) => a.value);
5158
5162
  NONE = { kind: "none" };
5159
5163
  UNKNOWN = { kind: "unknown" };
5164
+ SSRF_MAX_HOST = 253;
5165
+ METADATA_ADDRESSES = /* @__PURE__ */ new Set([
5166
+ "169.254.169.254",
5167
+ // AWS / Azure / DigitalOcean / OpenStack IMDS
5168
+ "169.254.170.2",
5169
+ // AWS ECS task role
5170
+ "168.63.129.16",
5171
+ // Azure WireServer
5172
+ "fd00:ec2::254",
5173
+ // AWS IMDS over IPv6 (inside fc00::/7, which is NOT a tier)
5174
+ // Alibaba Cloud IMDS. It sits INSIDE 100.64/10, so before this line it was
5175
+ // classified as cgnat and therefore exemptable: one ssrfAllow entry opened a
5176
+ // live credential endpoint. It has to be named here, above the range check,
5177
+ // and it is the reason relaxing cgnat is safe.
5178
+ "100.100.100.200"
5179
+ ]);
5180
+ STRICT_TIERS = /* @__PURE__ */ new Set(["private", "unspecified", "cgnat"]);
5181
+ METADATA_HOSTNAMES = /* @__PURE__ */ new Set(["metadata.google.internal", "metadata.goog", "metadata"]);
5182
+ v4Octets = (a) => {
5183
+ const p = a.split(".");
5184
+ return p.length === 4 ? p.map(Number) : null;
5185
+ };
5186
+ TIER_REASON = {
5187
+ metadata: "a cloud instance-metadata endpoint, the classic credential-theft target",
5188
+ "link-local": "a link-local address",
5189
+ multicast: "a multicast address",
5190
+ unspecified: "the unspecified address, which reaches this host",
5191
+ cgnat: "a carrier-grade NAT address",
5192
+ private: "a loopback or private address"
5193
+ };
5160
5194
  DEFAULT_EGRESS_ALLOWLIST = [
5161
5195
  // node9's own control plane (api, app, dev-api, staging and the apex).
5162
5196
  // Without it, turning egress on asks the user to approve node9 itself.
@@ -5180,6 +5214,7 @@ var init_dist = __esm({
5180
5214
  "deb.debian.org",
5181
5215
  "*.ubuntu.com"
5182
5216
  ];
5217
+ PRIVATE_HOST_SUFFIXES = [".local", ".internal", ".localhost"];
5183
5218
  SOURCE_COMMANDS = /* @__PURE__ */ new Set([...FS_READ_TOOLS, "tee"]);
5184
5219
  SINK_COMMANDS = /* @__PURE__ */ new Set([
5185
5220
  "curl",
@@ -5303,36 +5338,6 @@ var init_dist = __esm({
5303
5338
  socat: /* @__PURE__ */ new Set([])
5304
5339
  // socat uses address syntax, not flags — no value-flags
5305
5340
  };
5306
- SSRF_MAX_HOST = 253;
5307
- METADATA_ADDRESSES = /* @__PURE__ */ new Set([
5308
- "169.254.169.254",
5309
- // AWS / Azure / DigitalOcean / OpenStack IMDS
5310
- "169.254.170.2",
5311
- // AWS ECS task role
5312
- "168.63.129.16",
5313
- // Azure WireServer
5314
- "fd00:ec2::254",
5315
- // AWS IMDS over IPv6 (inside fc00::/7, which is NOT a tier)
5316
- // Alibaba Cloud IMDS. It sits INSIDE 100.64/10, so before this line it was
5317
- // classified as cgnat and therefore exemptable: one ssrfAllow entry opened a
5318
- // live credential endpoint. It has to be named here, above the range check,
5319
- // and it is the reason relaxing cgnat is safe.
5320
- "100.100.100.200"
5321
- ]);
5322
- STRICT_TIERS = /* @__PURE__ */ new Set(["private", "unspecified", "cgnat"]);
5323
- METADATA_HOSTNAMES = /* @__PURE__ */ new Set(["metadata.google.internal", "metadata.goog", "metadata"]);
5324
- v4Octets = (a) => {
5325
- const p = a.split(".");
5326
- return p.length === 4 ? p.map(Number) : null;
5327
- };
5328
- TIER_REASON = {
5329
- metadata: "a cloud instance-metadata endpoint, the classic credential-theft target",
5330
- "link-local": "a link-local address",
5331
- multicast: "a multicast address",
5332
- unspecified: "the unspecified address, which reaches this host",
5333
- cgnat: "a carrier-grade NAT address",
5334
- private: "a loopback or private address"
5335
- };
5336
5341
  DESTINATION_ARGS = /* @__PURE__ */ new Map([
5337
5342
  ["webfetch", ["url"]],
5338
5343
  ["fetch", ["url", "uri"]],