@node9/proxy 2.33.2 → 2.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.mjs CHANGED
@@ -1210,6 +1210,120 @@ function expandPlainParam(p) {
1210
1210
  if (recorded !== void 0) return recorded;
1211
1211
  return HOME_VARIABLES.has(p.Param?.Value) ? "~" : void 0;
1212
1212
  }
1213
+ function functionBodies(f) {
1214
+ const out = /* @__PURE__ */ new Map();
1215
+ syntax.Walk(f, (node) => {
1216
+ const n = node;
1217
+ if (n && syntax.NodeType(n) === "FuncDecl" && n.Name?.Value && n.Body) {
1218
+ const list = out.get(n.Name.Value) ?? [];
1219
+ list.push(n.Body);
1220
+ out.set(n.Name.Value, list);
1221
+ }
1222
+ return true;
1223
+ });
1224
+ return out;
1225
+ }
1226
+ function rebindsPositionals(body) {
1227
+ let found = false;
1228
+ syntax.Walk(body, (node) => {
1229
+ const n = node;
1230
+ if (found || !n) return false;
1231
+ if (syntax.NodeType(n) === "CallExpr") {
1232
+ const words = (n.Args || []).map((a) => resolveWordLiteral(a));
1233
+ if (words[0] === "shift") found = true;
1234
+ else if (words[0] === "set" && words.slice(1).some((w) => w === null || w === "--" || !/^[-+]/.test(w)))
1235
+ found = true;
1236
+ }
1237
+ return !found;
1238
+ });
1239
+ return found;
1240
+ }
1241
+ function tableBindings() {
1242
+ if (!assignmentTable) return "";
1243
+ const out = [];
1244
+ for (const [name, rec] of assignmentTable)
1245
+ if (rec.at < currentStmtOffset) out.push(`${name}=${rec.value ?? "\0"}`);
1246
+ return out.sort().join("");
1247
+ }
1248
+ function walkWithCalls(f, visit2) {
1249
+ const bodies = functionBodies(f);
1250
+ if (bodies.size === 0) {
1251
+ syntax.Walk(f, visit2);
1252
+ return;
1253
+ }
1254
+ const seenCalls = /* @__PURE__ */ new Set();
1255
+ const inline = (call) => {
1256
+ const words = (call.Args || []).map((a) => resolveWordLiteral(a));
1257
+ const list = words[0] ? bodies.get(words[0]) : void 0;
1258
+ if (!list || callDepth >= MAX_CALL_DEPTH) return;
1259
+ const args = words.slice(1);
1260
+ const key = `${words[0]}\0${JSON.stringify(args)}\0${tableBindings()}`;
1261
+ if (seenCalls.has(key)) return;
1262
+ seenCalls.add(key);
1263
+ const outerTable = assignmentTable;
1264
+ const outerOffset = currentStmtOffset;
1265
+ for (const body of list) {
1266
+ if (callWalks >= MAX_CALL_WALKS) return;
1267
+ callWalks++;
1268
+ const table = /* @__PURE__ */ new Map();
1269
+ for (const [name, rec] of outerTable ?? [])
1270
+ if (rec.at < outerOffset) table.set(name, { value: rec.value, at: -1 });
1271
+ for (const name of ["1", "2", "3", "4", "5", "6", "7", "8", "9", "@", "*", "#"])
1272
+ table.delete(name);
1273
+ if (!rebindsPositionals(body)) {
1274
+ args.slice(0, 9).forEach((value, i) => table.set(String(i + 1), { value, at: -1 }));
1275
+ if (args.length === 1) {
1276
+ table.set("@", { value: args[0], at: -1 });
1277
+ table.set("*", { value: args[0], at: -1 });
1278
+ }
1279
+ table.set("#", { value: String(args.length), at: -1 });
1280
+ }
1281
+ assignmentTable = table;
1282
+ currentStmtOffset = Number.MAX_SAFE_INTEGER;
1283
+ callDepth++;
1284
+ try {
1285
+ if (body.Cmd && syntax.NodeType(body.Cmd) === "Block") recordTopLevelAssignments(body.Cmd);
1286
+ syntax.Walk(body, step);
1287
+ } finally {
1288
+ callDepth--;
1289
+ assignmentTable = outerTable;
1290
+ currentStmtOffset = outerOffset;
1291
+ }
1292
+ }
1293
+ };
1294
+ const step = (node) => {
1295
+ if (!node) return false;
1296
+ const go = visit2(node);
1297
+ if (go !== false && syntax.NodeType(node) === "CallExpr") inline(node);
1298
+ return go;
1299
+ };
1300
+ syntax.Walk(f, step);
1301
+ }
1302
+ function withAssignments(f, body) {
1303
+ const outerTable = assignmentTable;
1304
+ const outerOffset = currentStmtOffset;
1305
+ const outerWalks = callWalks;
1306
+ callWalks = 0;
1307
+ assignmentTable = new Map(
1308
+ [...outerTable ?? []].map(([k, r]) => [k, { value: r.value, at: -1 }])
1309
+ );
1310
+ currentStmtOffset = Number.MAX_SAFE_INTEGER;
1311
+ try {
1312
+ recordTopLevelAssignments(f);
1313
+ return body();
1314
+ } finally {
1315
+ assignmentTable = outerTable;
1316
+ currentStmtOffset = outerOffset;
1317
+ callWalks = outerWalks;
1318
+ }
1319
+ }
1320
+ function trackStatement(n) {
1321
+ try {
1322
+ currentStmtOffset = n.Pos().Offset();
1323
+ } catch {
1324
+ currentStmtOffset = Number.MAX_SAFE_INTEGER;
1325
+ }
1326
+ }
1213
1327
  function resolveWordLiteral(w) {
1214
1328
  const parts = w?.Parts || [];
1215
1329
  let s = "";
@@ -1330,27 +1444,31 @@ function extractShellDestinations(command) {
1330
1444
  const out = [];
1331
1445
  const seen = /* @__PURE__ */ new Set();
1332
1446
  try {
1333
- syntax.Walk(f, (node) => {
1334
- if (!node) return false;
1335
- const n = node;
1336
- if (syntax.NodeType(n) !== "CallExpr") return true;
1337
- const callArgs = n.Args || [];
1338
- if (callArgs.length === 0) return true;
1339
- const words = callArgs.map((a) => resolveWordLiteral(a));
1340
- const at = unwrapCommandHead(words);
1341
- const name = (words[at] || "").toLowerCase().split("/").pop() ?? "";
1342
- if (!NET_BINARIES.has(name)) return true;
1343
- const rest = words.slice(at + 1);
1344
- for (const raw of destTokensForBinary(name, rest)) {
1345
- const host2 = parseDestHost(raw);
1346
- if (!host2) continue;
1347
- const key = `${name}:${host2}`;
1348
- if (seen.has(key)) continue;
1349
- seen.add(key);
1350
- out.push({ host: host2, binary: name, raw });
1351
- }
1352
- return true;
1353
- });
1447
+ withAssignments(
1448
+ f,
1449
+ () => walkWithCalls(f, (node) => {
1450
+ if (!node) return false;
1451
+ const n = node;
1452
+ if (syntax.NodeType(n) === "Stmt") trackStatement(n);
1453
+ if (syntax.NodeType(n) !== "CallExpr") return true;
1454
+ const callArgs = n.Args || [];
1455
+ if (callArgs.length === 0) return true;
1456
+ const words = callArgs.map((a) => resolveWordLiteral(a));
1457
+ const at = unwrapCommandHead(words);
1458
+ const name = (words[at] || "").toLowerCase().split("/").pop() ?? "";
1459
+ if (!NET_BINARIES.has(name)) return true;
1460
+ const rest = words.slice(at + 1);
1461
+ for (const raw of destTokensForBinary(name, rest)) {
1462
+ const host2 = parseDestHost(raw);
1463
+ if (!host2) continue;
1464
+ const key = `${name}:${host2}`;
1465
+ if (seen.has(key)) continue;
1466
+ seen.add(key);
1467
+ out.push({ host: host2, binary: name, raw });
1468
+ }
1469
+ return true;
1470
+ })
1471
+ );
1354
1472
  } catch {
1355
1473
  return out;
1356
1474
  }
@@ -1362,39 +1480,45 @@ function extractShellDestTokens(command) {
1362
1480
  const out = [];
1363
1481
  const seen = /* @__PURE__ */ new Set();
1364
1482
  try {
1365
- syntax.Walk(f, (node) => {
1366
- if (!node) return false;
1367
- const n = node;
1368
- if (syntax.NodeType(n) !== "CallExpr") return true;
1369
- const callArgs = n.Args || [];
1370
- if (callArgs.length === 0) return true;
1371
- const name = (resolveWordLiteral(callArgs[0]) || "").toLowerCase();
1372
- if (!NET_BINARIES.has(name)) return true;
1373
- const rest = callArgs.slice(1).map((a) => resolveWordLiteral(a));
1374
- for (const raw of destTokensForBinary(name, rest)) {
1375
- if (!raw) continue;
1376
- let tok = raw.trim();
1377
- const scheme = /^[a-z][a-z0-9+.-]*:\/\//i.exec(tok);
1378
- const hasScheme = scheme !== null;
1379
- if (hasScheme) tok = tok.slice(scheme[0].length);
1380
- tok = tok.split(/[/?#]/)[0];
1381
- const at = tok.lastIndexOf("@");
1382
- if (at >= 0) tok = tok.slice(at + 1);
1383
- if (tok.startsWith("[")) {
1384
- const close = tok.indexOf("]");
1385
- if (close > 0) tok = tok.slice(0, close + 1);
1386
- } else {
1387
- tok = tok.split(":")[0];
1483
+ withAssignments(
1484
+ f,
1485
+ () => walkWithCalls(f, (node) => {
1486
+ if (!node) return false;
1487
+ const n = node;
1488
+ if (syntax.NodeType(n) === "Stmt") trackStatement(n);
1489
+ if (syntax.NodeType(n) !== "CallExpr") return true;
1490
+ const callArgs = n.Args || [];
1491
+ if (callArgs.length === 0) return true;
1492
+ const words = callArgs.map((a) => resolveWordLiteral(a));
1493
+ const head = unwrapCommandHead(words);
1494
+ const name = (words[head] || "").toLowerCase().split("/").pop() ?? "";
1495
+ if (!NET_BINARIES.has(name)) return true;
1496
+ const rest = words.slice(head + 1);
1497
+ for (const raw of destTokensForBinary(name, rest)) {
1498
+ if (!raw) continue;
1499
+ let tok = raw.trim();
1500
+ const scheme = /^[a-z][a-z0-9+.-]*:\/\//i.exec(tok);
1501
+ const hasScheme = scheme !== null;
1502
+ if (hasScheme) tok = tok.slice(scheme[0].length);
1503
+ tok = tok.split(/[/?#]/)[0];
1504
+ const at = tok.lastIndexOf("@");
1505
+ if (at >= 0) tok = tok.slice(at + 1);
1506
+ if (tok.startsWith("[")) {
1507
+ const close = tok.indexOf("]");
1508
+ if (close > 0) tok = tok.slice(0, close + 1);
1509
+ } else {
1510
+ tok = tok.split(":")[0];
1511
+ }
1512
+ if (!tok) continue;
1513
+ if (!hasScheme && /^\d+$/.test(tok) && Number(tok) < 16777216) continue;
1514
+ const key = `${name}:${tok}`;
1515
+ if (seen.has(key)) continue;
1516
+ seen.add(key);
1517
+ out.push({ token: tok, binary: name });
1388
1518
  }
1389
- if (!tok) continue;
1390
- if (!hasScheme && /^\d+$/.test(tok) && Number(tok) < 16777216) continue;
1391
- const key = `${name}:${tok}`;
1392
- if (seen.has(key)) continue;
1393
- seen.add(key);
1394
- out.push({ token: tok, binary: name });
1395
- }
1396
- return true;
1397
- });
1519
+ return true;
1520
+ })
1521
+ );
1398
1522
  } catch {
1399
1523
  return out;
1400
1524
  }
@@ -1513,6 +1637,7 @@ function analyzeFsOperationImpl(command, depth = 0) {
1513
1637
  if (depth === 0) {
1514
1638
  seenPayloads = /* @__PURE__ */ new Set();
1515
1639
  payloadBudget = PAYLOAD_BUDGET;
1640
+ if (callDepth === 0) callWalks = 0;
1516
1641
  }
1517
1642
  assignmentTable = new Map(
1518
1643
  [...outerTable ?? []].map(([k, r]) => [k, { value: r.value, at: -1 }])
@@ -1520,7 +1645,7 @@ function analyzeFsOperationImpl(command, depth = 0) {
1520
1645
  currentStmtOffset = Number.MAX_SAFE_INTEGER;
1521
1646
  try {
1522
1647
  recordTopLevelAssignments(f);
1523
- syntax.Walk(f, (node) => {
1648
+ walkWithCalls(f, (node) => {
1524
1649
  if (!node || result?.verdict === "block") return false;
1525
1650
  const n = node;
1526
1651
  const nodeType = syntax.NodeType(n);
@@ -2309,6 +2434,19 @@ function runnablePrefix(command) {
2309
2434
  }
2310
2435
  return null;
2311
2436
  }
2437
+ function uploadedFile(tool, flag, value) {
2438
+ const v = value.value;
2439
+ const file2 = (f) => f && f !== "-" && f !== "." ? { value: f } : null;
2440
+ if (tool === "curl") {
2441
+ if (["-d", "--data", "--data-ascii", "--data-binary", "--json"].includes(flag))
2442
+ return v.startsWith("@") ? file2(v.slice(1)) : null;
2443
+ if (flag === "--data-urlencode") return file2(/^[^=@]*@(.+)$/.exec(v)?.[1]);
2444
+ if (flag === "-F" || flag === "--form") return file2(/^[^=]*=[@<]([^;]+)/.exec(v)?.[1]);
2445
+ if (flag === "-T" || flag === "--upload-file") return file2(v);
2446
+ }
2447
+ if (tool === "wget" && ["--post-file", "--body-file"].includes(flag)) return file2(v);
2448
+ return null;
2449
+ }
2312
2450
  function merge(a, b) {
2313
2451
  a.cwd = unique([...a.cwd, ...b.cwd]);
2314
2452
  for (const key of /* @__PURE__ */ new Set([...a.vars.keys(), ...b.vars.keys()])) {
@@ -2382,6 +2520,9 @@ function wordValues(word, state) {
2382
2520
  function collectShellFileAccesses(command, context = {}) {
2383
2521
  const out = [];
2384
2522
  let payloads = 256;
2523
+ const functions = /* @__PURE__ */ new Map();
2524
+ const seenCalls = /* @__PURE__ */ new Set();
2525
+ let callWalks2 = MAX_CALL_WALKS2;
2385
2526
  const initial = {
2386
2527
  cwd: [context.cwd ?? ""],
2387
2528
  vars: /* @__PURE__ */ new Map([["HOME", [context.homeDirs?.[0] ?? "~"]]])
@@ -2639,8 +2780,98 @@ function collectShellFileAccesses(command, context = {}) {
2639
2780
  }
2640
2781
  return;
2641
2782
  }
2783
+ if (name === "wget" || name === "curl" || name === "aria2c") {
2784
+ downloadOutputs(name, args, state);
2785
+ return;
2786
+ }
2787
+ if (["http", "https", "xh", "xhs"].includes(name)) {
2788
+ for (const token of args) {
2789
+ if (!token || token.dynamic || /^[a-z][a-z0-9+.-]*:\/\//i.test(token.value)) continue;
2790
+ const m = /^(?:@|[^=:@\s]+@|[^=:@\s]*=@)(.+)$/.exec(token.value);
2791
+ if (m && m[1] !== "-") add("read", { value: m[1] }, state, { upload: true });
2792
+ }
2793
+ return;
2794
+ }
2795
+ if (name === "scp" || name === "rsync") {
2796
+ const operands2 = strings.filter((s) => s && !s.startsWith("-"));
2797
+ const remote = operands2.slice(1).some((o) => /^[^/\s]+:/.test(o));
2798
+ for (const p of copySourcePaths(words))
2799
+ add("read", locate(p), state, remote ? { upload: true } : {});
2800
+ return;
2801
+ }
2642
2802
  for (const p of copySourcePaths(words)) add("read", locate(p), state);
2643
2803
  }
2804
+ function downloadOutputs(name, args, state) {
2805
+ const valueShort = DOWNLOAD_SHORT_VALUE[name];
2806
+ const outputs = [];
2807
+ const urls = [];
2808
+ let dir2 = null;
2809
+ let remoteName = false;
2810
+ let document = false;
2811
+ let serverNamed = false;
2812
+ const take = (flag, value) => {
2813
+ if (!value) return;
2814
+ const sent = uploadedFile(name, flag, value);
2815
+ if (sent) add("read", sent, state, { upload: true });
2816
+ if (DOWNLOAD_OUTPUT_FLAGS[name].includes(flag)) {
2817
+ if (["-O", "--output-document", "--out"].includes(flag) || name === "aria2c" && flag === "-o")
2818
+ document = true;
2819
+ if (value.value !== "-" || value.pattern || value.dynamic) outputs.push(value);
2820
+ } else if (DOWNLOAD_DIR_FLAGS[name].includes(flag)) dir2 = value;
2821
+ else if (name === "wget" && (flag === "-i" || flag === "--input-file")) serverNamed = true;
2822
+ };
2823
+ for (let i = 0; i < args.length; i++) {
2824
+ const token = args[i];
2825
+ if (!token) continue;
2826
+ const v = token.value;
2827
+ if (v.startsWith("--")) {
2828
+ const eq = v.indexOf("=");
2829
+ const flag = eq < 0 ? v : v.slice(0, eq);
2830
+ if (["--remote-name", "--remote-name-all"].includes(flag)) remoteName = true;
2831
+ if (["--remote-header-name", "--recursive", "--mirror", "--page-requisites"].includes(flag))
2832
+ serverNamed = true;
2833
+ if (DOWNLOAD_LONG_VALUE[name].includes(flag))
2834
+ take(flag, eq < 0 ? args[++i] ?? null : { ...token, value: v.slice(eq + 1) });
2835
+ continue;
2836
+ }
2837
+ if (v.startsWith("-") && v.length > 1 && !token.dynamic) {
2838
+ for (let j = 1; j < v.length; j++) {
2839
+ const c = v[j];
2840
+ if (name === "wget" && c === "n") {
2841
+ j++;
2842
+ continue;
2843
+ }
2844
+ if (name === "curl" && c === "O") remoteName = true;
2845
+ if (name === "curl" && c === "J") serverNamed = true;
2846
+ if (name === "wget" && (c === "r" || c === "m" || c === "p")) serverNamed = true;
2847
+ if (valueShort.includes(c)) {
2848
+ const rest = v.slice(j + 1);
2849
+ take(`-${c}`, rest ? { value: rest } : args[++i] ?? null);
2850
+ break;
2851
+ }
2852
+ }
2853
+ continue;
2854
+ }
2855
+ if (/^(?:https?|ftps?):\/\//i.test(v)) urls.push(v);
2856
+ }
2857
+ for (const out2 of outputs) {
2858
+ const spelled = name === "aria2c" && dir2?.value && !out2.value.startsWith("/") ? { ...out2, value: `${dir2.value.replace(/\/+$/, "")}/${out2.value}` } : out2;
2859
+ if (name === "curl" && /#\d/.test(spelled.value))
2860
+ add("write", { value: "", pattern: spelled.value.replace(/#\d+/g, "*"), dot: true }, state);
2861
+ else add("write", spelled, state);
2862
+ }
2863
+ const remote = name === "curl" ? remoteName || serverNamed : !document || serverNamed;
2864
+ if (!remote) return;
2865
+ const into = dir2?.value ? dir2.value.replace(/\/+$/, "") : ".";
2866
+ if (serverNamed || urls.length === 0) {
2867
+ add("write", { value: "", pattern: `${quoteGlob(into)}/*`, dot: true }, state);
2868
+ return;
2869
+ }
2870
+ for (const url of urls) {
2871
+ const file2 = url.replace(/[?#].*$/, "").replace(/^[a-z]+:\/\/[^/]*/i, "").split("/").pop();
2872
+ add("write", { value: `${into}/${file2 || "index.html"}` }, state);
2873
+ }
2874
+ }
2644
2875
  function findActions2(tokens, words, head, state, nested, depth) {
2645
2876
  const starts = findStartPoints(words, head).starts;
2646
2877
  const roots = (starts.length ? starts : ["."]).slice(0, MAX_ALTERNATIVES);
@@ -2712,7 +2943,35 @@ function collectShellFileAccesses(command, context = {}) {
2712
2943
  const words = wordValues(node, state);
2713
2944
  alternatives = alternatives.flatMap((prefix) => (words.length ? words : [null]).map((w) => [...prefix, w])).slice(0, MAX_ALTERNATIVES);
2714
2945
  }
2715
- for (const tokens of alternatives) fromWords(tokens, state, nested, depth);
2946
+ for (const tokens of alternatives) {
2947
+ fromWords(tokens, state, nested, depth);
2948
+ inlineCall(tokens, state, depth);
2949
+ }
2950
+ }
2951
+ function inlineCall(tokens, state, depth) {
2952
+ const head = tokens[0];
2953
+ if (!head || head.dynamic || head.pattern) return;
2954
+ const bodies = functions.get(head.value);
2955
+ if (!bodies || (state.callDepth ?? 0) >= MAX_CALL_DEPTH2) return;
2956
+ const args = tokens.slice(1).map((a) => a && !a.dynamic ? a.value : null);
2957
+ const vars = JSON.stringify([...state.vars].sort(([a], [b]) => a < b ? -1 : 1));
2958
+ const key = `${head.value}\0${JSON.stringify(args)}\0${state.cwd.join("")}\0${vars}`;
2959
+ if (seenCalls.has(key)) return;
2960
+ seenCalls.add(key);
2961
+ for (const body of bodies) {
2962
+ if (callWalks2-- <= 0) return;
2963
+ const local = clone(state);
2964
+ local.callDepth = (state.callDepth ?? 0) + 1;
2965
+ for (const name of POSITIONAL) local.vars.delete(name);
2966
+ if (!rebindsPositionals(body)) {
2967
+ args.slice(0, 9).forEach((v, i) => local.vars.set(String(i + 1), v === null ? null : [v]));
2968
+ const all = args.every((v) => v !== null) && args.length ? unique(args) : null;
2969
+ local.vars.set("@", all);
2970
+ local.vars.set("*", all);
2971
+ local.vars.set("#", [String(args.length)]);
2972
+ }
2973
+ statement(body, local, depth + 1);
2974
+ }
2716
2975
  }
2717
2976
  function readsScriptFromStdin(stmt) {
2718
2977
  const cmd = stmt?.Cmd;
@@ -2749,11 +3008,13 @@ function collectShellFileAccesses(command, context = {}) {
2749
3008
  if (!stmt || depth > MAX_DEPTH) return;
2750
3009
  state.onSuccess = void 0;
2751
3010
  if (stmt.Background) state = clone(state);
3011
+ const headWord = stmt.Cmd && syntax2.NodeType(stmt.Cmd) === "CallExpr" ? base(resolveWordLiteral(stmt.Cmd.Args?.[0])) : "";
3012
+ const sends = SENDING_STDIN.has(headWord);
2752
3013
  for (const redir of stmt.Redirs ?? []) {
2753
3014
  substitutions(redir.Word, state, depth);
2754
3015
  for (const word of wordValues(redir.Word, state)) {
2755
3016
  if (WRITES.has(redir.Op)) add("write", word, state);
2756
- if (READS.has(redir.Op)) add("read", word, state);
3017
+ if (READS.has(redir.Op)) add("read", word, state, sends ? { upload: true } : {});
2757
3018
  if (redir.Op === DUP_OUT && word.value && !/^\d+$/.test(word.value))
2758
3019
  add("write", word, state);
2759
3020
  }
@@ -2862,7 +3123,10 @@ function collectShellFileAccesses(command, context = {}) {
2862
3123
  }
2863
3124
  const parsed = parseShared(command);
2864
3125
  const program2 = typeof parsed !== "symbol" ? parsed : runnablePrefix(command);
2865
- if (program2) statements(program2.Stmts ?? [], initial, 0);
3126
+ if (program2) {
3127
+ for (const [name, bodies] of functionBodies(program2)) functions.set(name, bodies);
3128
+ statements(program2.Stmts ?? [], initial, 0);
3129
+ }
2866
3130
  return out;
2867
3131
  }
2868
3132
  function isAgentInstructionPath(absPath, root) {
@@ -2978,6 +3242,33 @@ function hits(access, paths, directories, context) {
2978
3242
  }
2979
3243
  return false;
2980
3244
  }
3245
+ function hitsSystemDir(access, context, dirs = SYSTEM_DIRS) {
3246
+ const home = context.homeDirs?.[0] ?? "~";
3247
+ const candidates2 = access.candidates ?? [
3248
+ normalizeFilePath(access.path, access.cwd ?? context.cwd, home)
3249
+ ];
3250
+ for (const p of candidates2) {
3251
+ if (dirs.some((d) => within(p, d)) && !exempt(p)) return true;
3252
+ if (access.recursive && dirs.some((d) => within(d, p))) return true;
3253
+ }
3254
+ if (!access.glob) return false;
3255
+ const pattern = normalizeFilePath(access.glob, access.cwd ?? context.cwd, home);
3256
+ const magic = pattern.search(/[*?[{]/);
3257
+ const literal = magic < 0 ? pattern : pattern.slice(0, pattern.lastIndexOf("/", magic) + 1);
3258
+ if (literal && exempt(literal.replace(/\/$/, "") || "/")) return false;
3259
+ const options = { dot: access.dotGlob === true, nonegate: true };
3260
+ const components = pattern.split("/");
3261
+ for (const root of dirs) {
3262
+ const count = root.split("/").length;
3263
+ if (components.length > count && picomatch(components.slice(0, count).join("/"), options)(root))
3264
+ return true;
3265
+ if (access.recursive && picomatch(pattern, options)(root)) return true;
3266
+ }
3267
+ return false;
3268
+ }
3269
+ function uploadDirs(context) {
3270
+ return context.homeDirs?.[0] === "/root" ? UPLOAD_SYSTEM_DIRS.filter((d) => d !== "/root") : UPLOAD_SYSTEM_DIRS;
3271
+ }
2981
3272
  function evaluateProtectedFileAccess(accesses, context, settings) {
2982
3273
  const homes = [.../* @__PURE__ */ new Set([...context.homeDirs ?? ["~"], "/root"])];
2983
3274
  const startup = homes.flatMap(
@@ -2999,6 +3290,14 @@ function evaluateProtectedFileAccess(accesses, context, settings) {
2999
3290
  "files.shell-startup",
3000
3291
  "review-shell-startup-mutation",
3001
3292
  "Changing shell startup files requires approval"
3293
+ ] : mutation && hitsSystemDir(access, context) ? [
3294
+ "files.system-dirs",
3295
+ "review-system-dir-mutation",
3296
+ "Changing files in a system directory requires approval"
3297
+ ] : !mutation && access.upload && hitsSystemDir(access, context, uploadDirs(context)) ? [
3298
+ "files.system-dirs",
3299
+ "review-system-file-upload",
3300
+ "Uploading a system file requires approval"
3002
3301
  ] : !mutation && hits(access, PASSWORD_HASH_FILES, [], context) ? [
3003
3302
  "files.password-hashes",
3004
3303
  "review-password-hash-read",
@@ -3326,16 +3625,21 @@ function looksGenerated(value, minLength, minEntropy, lettersOnlyAllowed = false
3326
3625
  if (EMAIL_VALUE_RE.test(value)) return false;
3327
3626
  if (looksLikeJoinedWords(value) || hasSequentialRun(value)) return false;
3328
3627
  if (PATH_VALUE_RE.test(value) || DOTTED_REFERENCE_RE.test(value)) return false;
3628
+ if (CONCATENATION_RE.test(value)) return false;
3329
3629
  if (rejectedByStructuredValidator(value)) return false;
3330
3630
  return shannonEntropy(value) >= minEntropy;
3331
3631
  }
3332
3632
  function nameHoldsCredential(name) {
3333
3633
  return !FIXTURE_NAME_RE.test(name) && !DERIVATIVE_NAME_RE.test(name);
3334
3634
  }
3635
+ function isOtpDocSecret(value) {
3636
+ return OTP_DOC_SECRETS.has(value.replace(/=+$/, "").toUpperCase());
3637
+ }
3335
3638
  function validateQueryCredential(raw) {
3336
3639
  const value = capturedValue(raw, QUERY_CREDENTIAL_VALUE_RE);
3337
3640
  if (value === null) return false;
3338
- if (OTP_SECRET_RE.test(value)) return looksGenerated(value, 16, 3, true);
3641
+ if (OTP_SECRET_RE.test(value))
3642
+ return !isOtpDocSecret(value) && looksGenerated(value, 16, 3, true);
3339
3643
  if (!/[0-9]/.test(value) || !/[A-Za-z]/.test(value)) return false;
3340
3644
  return looksGenerated(value, 12, 3);
3341
3645
  }
@@ -3349,13 +3653,40 @@ function keywordEndsAPart(name) {
3349
3653
  }
3350
3654
  function validateAssignedSecret(raw, before = "") {
3351
3655
  const value = capturedValue(raw, ASSIGNED_SECRET_VALUE_RE);
3352
- const name = capturedValue(raw, ASSIGNED_SECRET_NAME_RE);
3656
+ const name = capturedValue(raw, ASSIGNED_SECRET_NAME_RE)?.replace(/^_+/, "") ?? null;
3353
3657
  if (value === null || name === null) return false;
3354
- if (!keywordEndsAPart(name) || ARN_CONTEXT_RE.test(before)) return false;
3355
- const prefix = before.match(/[A-Za-z0-9_]*$/)?.[0] ?? "";
3658
+ if (!keywordEndsAPart(name)) return false;
3659
+ if (ARN_CONTEXT_RE.test(before)) return false;
3660
+ const prefix = (before.match(/[A-Za-z0-9_]*$/)?.[0] ?? "") + (raw.startsWith("_") ? "_" : "");
3356
3661
  if (!nameHoldsCredential(prefix + name)) return false;
3357
3662
  return looksGenerated(value, 12, 3.5);
3358
3663
  }
3664
+ function validateBasicCredential(raw) {
3665
+ const b64 = capturedValue(raw, BASIC_VALUE_RE);
3666
+ if (b64 === null) return false;
3667
+ const bare = b64.replace(/=+$/, "");
3668
+ if (bare.length % 4 === 1) return false;
3669
+ const bytes = Buffer.from(b64, "base64");
3670
+ if (bytes.toString("base64").replace(/=+$/, "") !== bare) return false;
3671
+ const text = bytes.toString("utf8");
3672
+ if (!Buffer.from(text, "utf8").equals(bytes)) return false;
3673
+ for (let i = 0; i < text.length; i++) {
3674
+ const c = text.charCodeAt(i);
3675
+ if (c < 32 || c === 127) return false;
3676
+ }
3677
+ const pair = BASIC_PAIR_RE.exec(text);
3678
+ if (!pair) return false;
3679
+ const password = pair[2];
3680
+ if (!/[0-9]/.test(password) || !/[A-Za-z]/.test(password)) return false;
3681
+ const lower2 = password.toLowerCase();
3682
+ if (DLP_STOPWORDS.some((sw) => lower2.includes(sw)) || hasSequentialRun(password)) return false;
3683
+ return shannonEntropy(password) >= 3;
3684
+ }
3685
+ function validateOtpSecret(raw) {
3686
+ const secret = capturedValue(raw, OTP_CAPTURE_RE);
3687
+ if (secret === null || isOtpDocSecret(secret)) return false;
3688
+ return looksGenerated(secret.replace(/=+$/, ""), 16, 3, true);
3689
+ }
3359
3690
  function matchSensitivePath2(resolvedPath, originalPath, options = {}) {
3360
3691
  const normalised = resolvedPath.replace(/\\/g, "/");
3361
3692
  if (!options.systemAccessHandled && isLegacySystemPath(normalised))
@@ -3664,6 +3995,156 @@ function parsePySpec(raw) {
3664
3995
  const exact = PY_EXACT_RE.exec(m[2].trim());
3665
3996
  return { name, version: exact ? exact[1] : void 0 };
3666
3997
  }
3998
+ function exactVersion(v) {
3999
+ if (!v) return void 0;
4000
+ const m = EXACT_VERSION_RE.exec(v.trim());
4001
+ return m ? m[1] : void 0;
4002
+ }
4003
+ function push(out, ecosystem, manager, name, version2, raw) {
4004
+ out.push({ ecosystem, manager, name, ...version2 ? { version: version2 } : {}, raw });
4005
+ }
4006
+ function gemInstall(words, from, out) {
4007
+ const dash = words.indexOf("--", from);
4008
+ const own = dash < 0 ? words : words.slice(0, dash);
4009
+ const pinned = exactVersion(flagValues(own, from, ["-v", "--version"])[0]);
4010
+ for (const raw of operands(own, from, GEM_VALUE_FLAGS)) {
4011
+ if (NON_REGISTRY_RE.test(raw) || /\.gem$/i.test(raw)) continue;
4012
+ const colon = raw.indexOf(":");
4013
+ const name = colon < 0 ? raw : raw.slice(0, colon);
4014
+ if (!GEM_NAME_RE.test(name)) continue;
4015
+ push(
4016
+ out,
4017
+ "RubyGems",
4018
+ "gem",
4019
+ name,
4020
+ colon < 0 ? pinned : exactVersion(raw.slice(colon + 1)),
4021
+ raw
4022
+ );
4023
+ }
4024
+ }
4025
+ function cargoCrates(words, from, manager, out) {
4026
+ const flags = manager === "cargo add" ? CARGO_ADD_VALUE_FLAGS : CARGO_INSTALL_VALUE_FLAGS;
4027
+ if (flagValues(words, from, ["--git", "--path", "--registry", "--index"]).length > 0) return;
4028
+ if (words.some((w) => w === "--git" || w === "--path")) return;
4029
+ const flagVersion = manager === "cargo install" ? exactVersion(flagValues(words, from, ["--version", "--vers"])[0]) : void 0;
4030
+ for (const raw of operands(words, from, flags)) {
4031
+ const at = raw.indexOf("@");
4032
+ const name = at < 0 ? raw : raw.slice(0, at);
4033
+ if (!CRATE_NAME_RE.test(name)) continue;
4034
+ push(
4035
+ out,
4036
+ "crates.io",
4037
+ manager,
4038
+ name,
4039
+ at < 0 ? flagVersion : exactVersion(raw.slice(at + 1)),
4040
+ raw
4041
+ );
4042
+ }
4043
+ }
4044
+ function goModules(words, from, manager, out) {
4045
+ let specs = operands(words, from, GO_VALUE_FLAGS);
4046
+ if (manager === "go run") specs = specs.slice(0, 1).filter((s) => s.includes("@"));
4047
+ for (const raw of specs) {
4048
+ const at = raw.indexOf("@");
4049
+ const pathPart = (at < 0 ? raw : raw.slice(0, at)).replace(/\/\.\.\.$/, "");
4050
+ const ver = at < 0 ? void 0 : raw.slice(at + 1);
4051
+ if (ver === "none") continue;
4052
+ if (!GO_PATH_RE.test(pathPart) || /\.go$/i.test(pathPart)) continue;
4053
+ const exact = ver && /^v\d/.test(ver) ? exactVersion(ver) : void 0;
4054
+ push(out, "Go", manager, pathPart, exact, raw);
4055
+ }
4056
+ }
4057
+ function dotnetAdd(words, from, out) {
4058
+ let k = -1;
4059
+ for (let i = from; i < Math.min(words.length, from + 2); i++) {
4060
+ if (lower(words[i]) === "package") {
4061
+ k = i;
4062
+ break;
4063
+ }
4064
+ }
4065
+ if (k < 0) return;
4066
+ const name = operands(words, k + 1, DOTNET_ADD_VALUE_FLAGS)[0];
4067
+ if (!name || !NUGET_NAME_RE.test(name)) return;
4068
+ const version2 = exactVersion(flagValues(words, k + 1, ["-v", "--version"])[0]);
4069
+ push(out, "NuGet", "dotnet add package", name, version2, name);
4070
+ }
4071
+ function dotnetTool(words, from, out) {
4072
+ const name = operands(words, from, DOTNET_TOOL_VALUE_FLAGS)[0];
4073
+ if (!name || !NUGET_NAME_RE.test(name)) return;
4074
+ const version2 = exactVersion(flagValues(words, from, ["--version"])[0]);
4075
+ push(out, "NuGet", "dotnet tool", name, version2, name);
4076
+ }
4077
+ function nugetInstall(words, from, out) {
4078
+ const lowered = words.map((w) => w !== null && w.startsWith("-") ? w.toLowerCase() : w);
4079
+ const name = operands(lowered, from, NUGET_VALUE_FLAGS)[0];
4080
+ if (!name || !NUGET_NAME_RE.test(name) || /\.config$/i.test(name)) return;
4081
+ const version2 = exactVersion(flagValues(lowered, from, ["-version"])[0]);
4082
+ push(out, "NuGet", "nuget install", name, version2, name);
4083
+ }
4084
+ function mavenGet(words, from, out) {
4085
+ const goal = words.slice(from).some(
4086
+ (w) => w !== null && /(?:^|:)dependency:get$|^org\.apache\.maven\.plugins:maven-dependency-plugin:(?:[\w.-]+:)?get$/.test(
4087
+ w
4088
+ )
4089
+ );
4090
+ if (!goal) return;
4091
+ const prop = (key) => {
4092
+ for (let i = from; i < words.length; i++) {
4093
+ const w = words[i];
4094
+ if (w === null) continue;
4095
+ if (w.startsWith(`-D${key}=`)) return w.slice(key.length + 3);
4096
+ if (w === "-D" && words[i + 1]?.startsWith(`${key}=`))
4097
+ return words[i + 1].slice(key.length + 1);
4098
+ }
4099
+ return void 0;
4100
+ };
4101
+ const artifact = prop("artifact");
4102
+ let coord;
4103
+ let ver;
4104
+ if (artifact) {
4105
+ const parts = artifact.split(":");
4106
+ if (parts.length < 2) return;
4107
+ coord = `${parts[0]}:${parts[1]}`;
4108
+ ver = parts[2];
4109
+ } else {
4110
+ const g = prop("groupId");
4111
+ const a = prop("artifactId");
4112
+ if (!g || !a) return;
4113
+ coord = `${g}:${a}`;
4114
+ ver = prop("version");
4115
+ }
4116
+ if (!MAVEN_COORD_RE.test(coord)) return;
4117
+ const exact = ver && !/^(?:LATEST|RELEASE)$/i.test(ver) ? exactVersion(ver) : void 0;
4118
+ push(out, "Maven", "mvn dependency:get", coord, exact, artifact ?? coord);
4119
+ }
4120
+ function composerPackages(words, from, manager, out) {
4121
+ const ops = operands(words, from, COMPOSER_VALUE_FLAGS);
4122
+ const specs = manager === "composer create-project" ? ops.slice(0, 1) : ops;
4123
+ for (const raw of specs) {
4124
+ const sep = raw.search(/[:=]/);
4125
+ const name = sep < 0 ? raw : raw.slice(0, sep);
4126
+ if (!COMPOSER_NAME_RE.test(name)) continue;
4127
+ let ver = sep < 0 ? void 0 : raw.slice(sep + 1);
4128
+ if (manager === "composer create-project" && ver === void 0) ver = ops[2] ?? void 0;
4129
+ push(out, "Packagist", manager, name.toLowerCase(), exactVersion(ver), raw);
4130
+ }
4131
+ }
4132
+ function editorExtensions(words, from, out) {
4133
+ for (const raw of flagValues(words, from, ["--install-extension"])) {
4134
+ if (/\.vsix$/i.test(raw) || NON_REGISTRY_RE.test(raw)) continue;
4135
+ const at = raw.lastIndexOf("@");
4136
+ const name = at <= 0 ? raw : raw.slice(0, at);
4137
+ if (!EXTENSION_ID_RE.test(name)) continue;
4138
+ push(
4139
+ out,
4140
+ "VSCode",
4141
+ "code --install-extension",
4142
+ name,
4143
+ at <= 0 ? void 0 : exactVersion(raw.slice(at + 1)),
4144
+ raw
4145
+ );
4146
+ }
4147
+ }
3667
4148
  function basename(w) {
3668
4149
  return (w ?? "").toLowerCase().split("/").pop() ?? "";
3669
4150
  }
@@ -3895,7 +4376,63 @@ function fromCall(words, out, depth) {
3895
4376
  pushPy(out, "pipenv", operands(words, v + 1, PIPENV_VALUE_FLAGS));
3896
4377
  return;
3897
4378
  }
4379
+ case "gem": {
4380
+ const v = verbIndex(words, start + 1, GEM_VALUE_FLAGS);
4381
+ if (v >= 0 && ["install", "i"].includes(lower(words[v]))) gemInstall(words, v + 1, out);
4382
+ return;
4383
+ }
4384
+ case "bundle": {
4385
+ const v = verbIndex(words, start + 1, BUNDLE_VALUE_FLAGS);
4386
+ if (v < 0 || lower(words[v]) !== "add") return;
4387
+ if (flagValues(words, v + 1, ["--git", "--github", "--path"]).length > 0) return;
4388
+ const version2 = exactVersion(flagValues(words, v + 1, ["-v", "--version"])[0]);
4389
+ for (const raw of operands(words, v + 1, BUNDLE_VALUE_FLAGS))
4390
+ if (GEM_NAME_RE.test(raw)) push(out, "RubyGems", "bundle add", raw, version2, raw);
4391
+ return;
4392
+ }
4393
+ case "cargo": {
4394
+ const from = words[start + 1]?.startsWith("+") ? start + 2 : start + 1;
4395
+ const v = verbIndex(words, from, CARGO_ADD_VALUE_FLAGS);
4396
+ const verb = lower(words[v]);
4397
+ if (verb === "add") cargoCrates(words, v + 1, "cargo add", out);
4398
+ else if (verb === "install") cargoCrates(words, v + 1, "cargo install", out);
4399
+ return;
4400
+ }
4401
+ case "go": {
4402
+ const v = verbIndex(words, start + 1, GO_VALUE_FLAGS);
4403
+ const verb = lower(words[v]);
4404
+ if (verb === "get" || verb === "install" || verb === "run")
4405
+ goModules(words, v + 1, `go ${verb}`, out);
4406
+ return;
4407
+ }
4408
+ case "dotnet": {
4409
+ const v = verbIndex(words, start + 1, DOTNET_ADD_VALUE_FLAGS);
4410
+ const verb = lower(words[v]);
4411
+ if (verb === "add") dotnetAdd(words, v + 1, out);
4412
+ else if (verb === "tool" && ["install", "update"].includes(lower(words[v + 1])))
4413
+ dotnetTool(words, v + 2, out);
4414
+ return;
4415
+ }
4416
+ case "nuget": {
4417
+ if (lower(words[start + 1]) === "install") nugetInstall(words, start + 2, out);
4418
+ return;
4419
+ }
4420
+ case "mvn":
4421
+ case "mvnw":
4422
+ mavenGet(words, start + 1, out);
4423
+ return;
4424
+ case "composer": {
4425
+ let v = verbIndex(words, start + 1, COMPOSER_VALUE_FLAGS);
4426
+ if (v >= 0 && lower(words[v]) === "global") v = verbIndex(words, v + 1, COMPOSER_VALUE_FLAGS);
4427
+ const verb = lower(words[v]);
4428
+ if (verb === "require" || verb === "req" || verb === "r")
4429
+ composerPackages(words, v + 1, "composer require", out);
4430
+ else if (verb === "create-project")
4431
+ composerPackages(words, v + 1, "composer create-project", out);
4432
+ return;
4433
+ }
3898
4434
  default:
4435
+ if (EDITOR_HEADS.includes(family)) editorExtensions(words, start + 1, out);
3899
4436
  return;
3900
4437
  }
3901
4438
  }
@@ -5459,11 +5996,11 @@ function isDecimalFraction(text, index, unseparated) {
5459
5996
  const a = text[index - 2];
5460
5997
  if (sep === ".") {
5461
5998
  if (a !== void 0 && a >= "0" && a <= "9") return true;
5462
- return a === void 0 || NUMBER_LEAD_RE.test(a);
5999
+ return a === void 0 || (a === "-" || a === "+") && index === 2;
5463
6000
  }
5464
6001
  if (sep === "," && a === "0") {
5465
6002
  const b = text[index - 3];
5466
- return b === void 0 || NUMBER_LEAD_RE.test(b);
6003
+ return b === void 0 || b === ";";
5467
6004
  }
5468
6005
  return false;
5469
6006
  }
@@ -6663,7 +7200,7 @@ function* stringValues(obj, depth = 0) {
6663
7200
  }
6664
7201
  for (const v of Object.values(obj)) yield* stringValues(v, depth + 1);
6665
7202
  }
6666
- var aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, BUILTIN_SHIELDS, SSRF_MAX_HOST, METADATA_ADDRESSES, STRICT_TIERS, METADATA_HOSTNAMES, v4Octets, TIER_REASON, CHECK_TEXT, VERDICTS, CHECK_VERDICT_RANK, CHECK_GROUPS, ALL, BUILTIN_DANGEROUS_WORDS, PRODUCT_CHECKS, SHIELD_RULE_NAME, SHIELD_RULES_FOLDED_INTO_PRODUCT, CHECKS, CHECK_BY_ID, LEGACY_TO_ID, PROJECT_JAIL_BLOCK_READ, OVERRIDE_PREFIX, LABEL_WRAPPERS, LEGACY_READERS, syntax, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, AST_CACHE_MAX, astCache, PARSE_FAIL, defaultExpansionOps, VARIABLE_READERS, 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, 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, FS_MUTATION_TOOLS, INTERP_LEADING_TARGET, NET_BINARIES, VALUE_FLAGS, HOME_VARIABLES, assignmentTable, currentStmtOffset, PAYLOAD_BUDGET, payloadBudget, seenPayloads, UNANALYSABLE_NESTING, ASSIGNMENT_HEADS, AND_OP, OR_OP, AND_OR_OPS, 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, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, FORBIDDEN_PATH_SEGMENTS, BASH_TOOL_NAMES, MCP_EXECUTORS, BUILTIN_FIELDS, ARGV_FIELDS, BARE_WORD, MAX_ARGV_CHARACTERS, syntax2, MAX_ALTERNATIVES, MAX_DEPTH, MAX_PAYLOAD_DEPTH, unique, clone, base, quoteGlob, asWord, WRITES, READS, DUP_OUT, HERE_STRING, AND, OR, DEFAULT_OPS, SYSTEM_AUTH_FILES, SYSTEM_AUTH_DIRS, PASSWORD_HASH_FILES, STARTUP_NAMES, AGENT_INSTRUCTION_FILES, AGENT_INSTRUCTION_DIRS, LEGACY_SYSTEM_PATH_RE, RANK, BIP39_ENGLISH, BIP39_PUBLIC_VECTORS, indices, lengths, hash, MAX_MNEMONIC_HASHES, IBAN_LENGTH, B58, B58_INDEX, XPRV_VERSIONS, B62, CRC32_TABLE, GITHUB_TOKEN_RE, B64URL, B64URL_INDEX, CASK_TWO_ZERO_SUFFIX, CASK_FOUR_ZERO_SUFFIX, INVISIBLE_RE, INVISIBLE_ONE, HOMOGLYPHS, HOMOGLYPH_RE, MAX_BLOBS, MIN_DECODED, BASE64_RUN_RE, HEX_RUN_RE, HEX_ESCAPE_RUN_RE, MAX, UNTRUSTED_TOOLS, SIGNALS, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, REFERENCE_VALUE_RE, IDENTIFIER_VALUE_RE, PATH_VALUE_RE, DOTTED_REFERENCE_RE, EMAIL_VALUE_RE, FIXTURE_NAME_RE, DERIVATIVE_NAME_RE, QUERY_CREDENTIAL_VALUE_RE, OTP_SECRET_RE, ASSIGNED_SECRET_VALUE_RE, ASSIGNED_SECRET_NAME_RE, NAME_KEYWORD_RE, ARN_CONTEXT_RE, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, SENSITIVE_PATH_REGEXES, MAX_DEPTH2, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, DLP_SCAN_LIMITS, BATCH_SEPARATOR, syntax3, NPM_INSTALL_VERBS, NPM_EXEC_VERBS, PNPM_INSTALL_VERBS, YARN_INSTALL_VERBS, BUN_INSTALL_VERBS, DLX_VERBS, NON_REGISTRY_RE, NPM_NAME_RE, EXACT_SEMVER_RE, NPM_VALUE_FLAGS, PNPM_VALUE_FLAGS, YARN_VALUE_FLAGS, BUN_VALUE_FLAGS, PIP_VALUE_FLAGS, UV_VALUE_FLAGS, POETRY_VALUE_FLAGS, PIPENV_VALUE_FLAGS, PIPX_VALUE_FLAGS, PY_SPEC_RE, PY_EXACT_RE, MANAGER_HEADS, SHELLS, MAX_NESTING, SHELL_VALUE_FLAGS, TUNING_FIELDS, MAP_GOVERNED, KNOB_GOVERNED, CONFIGURABLE_CHECK_IDS, DEFAULT_EGRESS_ALLOWLIST, PRIVATE_HOST_SUFFIXES, SOURCE_COMMANDS, SINK_COMMANDS, OBFUSCATORS, SENSITIVE_PATTERNS, FLAGS_WITH_VALUES, DESTINATION_ARGS, SECRET_VIEW_LIMITS, ARGUMENT_SCAN_LIMITS, RUN_CHAR, runChar, VERDICT_RANK, SQL_DML_KEYWORDS, MAX_DESTINATIONS, MAX_SCAN_CHARS, EMAIL_RE, URL_RE, IBAN_RE, RECIPIENT_KEYS, DESTRUCTIVE_VERB_RE, PII_EMAIL_RE, PII_SSN_RE, PII_PHONE_RE, PII_CC16_RE, PII_CC15_RE, PII_IBAN_RE, NUMBER_LEAD_RE, REALTIME_PII_PATTERNS, MAX_PII_SCAN_BYTES, MASKED_KINDS, ENTRY_RE, IBAN_TEXT_RE, EMAIL_TEXT_RE, ACCOUNT_KEY_RE, ACCOUNT_VALUE_RE, MAX_MASK_DEPTH, syntax4, EMPTY, CURL_SEND_FLAGS, WGET_SEND_FLAGS, IWR_SEND_FLAGS, WRITE_METHODS, GH_READ, GH_WRITE, GH_HOSTNAME_FLAGS, GH_REPO_VALUE_FLAGS, GH_API_VALUE_FLAGS, GIT_FETCH, PUBLISH, SHELLS2, OWN_HEADS, MAX_DEPTH3, CWD_CHANGERS, TERMINAL_ESCAPE_RE, CONTROL_CHAR_RE, LOOP_MAX_RECORDS, FINDING_TO_SIGNAL, SCAN_SIGNAL_WEIGHTS, LOOP_THRESHOLD_FOR_WASTE, MAX_BLAST_PATH, DESTRUCTIVE_OP_RE, SENSITIVE_PATH_RE, FILE_TOOLS, CANARY_MIN_LENGTH, MAX_TEXT, MAX_DEPTH4, 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;
7203
+ var aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, BUILTIN_SHIELDS, SSRF_MAX_HOST, METADATA_ADDRESSES, STRICT_TIERS, METADATA_HOSTNAMES, v4Octets, TIER_REASON, CHECK_TEXT, VERDICTS, CHECK_VERDICT_RANK, CHECK_GROUPS, ALL, BUILTIN_DANGEROUS_WORDS, PRODUCT_CHECKS, SHIELD_RULE_NAME, SHIELD_RULES_FOLDED_INTO_PRODUCT, CHECKS, CHECK_BY_ID, LEGACY_TO_ID, PROJECT_JAIL_BLOCK_READ, OVERRIDE_PREFIX, LABEL_WRAPPERS, LEGACY_READERS, syntax, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, AST_CACHE_MAX, astCache, PARSE_FAIL, defaultExpansionOps, VARIABLE_READERS, 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, 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, FS_MUTATION_TOOLS, INTERP_LEADING_TARGET, NET_BINARIES, VALUE_FLAGS, HOME_VARIABLES, assignmentTable, currentStmtOffset, PAYLOAD_BUDGET, payloadBudget, seenPayloads, UNANALYSABLE_NESTING, ASSIGNMENT_HEADS, AND_OP, OR_OP, AND_OR_OPS, MAX_CALL_DEPTH, MAX_CALL_WALKS, callDepth, callWalks, 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, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, FORBIDDEN_PATH_SEGMENTS, BASH_TOOL_NAMES, MCP_EXECUTORS, BUILTIN_FIELDS, ARGV_FIELDS, BARE_WORD, MAX_ARGV_CHARACTERS, syntax2, MAX_ALTERNATIVES, MAX_CALL_DEPTH2, MAX_CALL_WALKS2, POSITIONAL, MAX_DEPTH, MAX_PAYLOAD_DEPTH, unique, clone, SENDING_STDIN, DOWNLOAD_SHORT_VALUE, DOWNLOAD_LONG_VALUE, DOWNLOAD_OUTPUT_FLAGS, DOWNLOAD_DIR_FLAGS, base, quoteGlob, asWord, WRITES, READS, DUP_OUT, HERE_STRING, AND, OR, DEFAULT_OPS, SYSTEM_AUTH_FILES, SYSTEM_AUTH_DIRS, PASSWORD_HASH_FILES, STARTUP_NAMES, SYSTEM_DIRS, SYSTEM_DIRS_EXEMPT, UPLOAD_SYSTEM_DIRS, AGENT_INSTRUCTION_FILES, AGENT_INSTRUCTION_DIRS, LEGACY_SYSTEM_PATH_RE, exempt, RANK, BIP39_ENGLISH, BIP39_PUBLIC_VECTORS, indices, lengths, hash, MAX_MNEMONIC_HASHES, IBAN_LENGTH, B58, B58_INDEX, XPRV_VERSIONS, B62, CRC32_TABLE, GITHUB_TOKEN_RE, B64URL, B64URL_INDEX, CASK_TWO_ZERO_SUFFIX, CASK_FOUR_ZERO_SUFFIX, INVISIBLE_RE, INVISIBLE_ONE, HOMOGLYPHS, HOMOGLYPH_RE, MAX_BLOBS, MIN_DECODED, BASE64_RUN_RE, HEX_RUN_RE, HEX_ESCAPE_RUN_RE, MAX, UNTRUSTED_TOOLS, SIGNALS, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, REFERENCE_VALUE_RE, IDENTIFIER_VALUE_RE, PATH_VALUE_RE, DOTTED_REFERENCE_RE, CONCATENATION_RE, EMAIL_VALUE_RE, FIXTURE_NAME_RE, DERIVATIVE_NAME_RE, QUERY_CREDENTIAL_VALUE_RE, OTP_SECRET_RE, OTP_DOC_SECRETS, ASSIGNED_SECRET_VALUE_RE, ASSIGNED_SECRET_NAME_RE, NAME_KEYWORD_RE, ARN_CONTEXT_RE, BASIC_VALUE_RE, BASIC_PAIR_RE, OTP_CAPTURE_RE, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, SENSITIVE_PATH_REGEXES, MAX_DEPTH2, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, DLP_SCAN_LIMITS, BATCH_SEPARATOR, syntax3, NPM_INSTALL_VERBS, NPM_EXEC_VERBS, PNPM_INSTALL_VERBS, YARN_INSTALL_VERBS, BUN_INSTALL_VERBS, DLX_VERBS, NON_REGISTRY_RE, NPM_NAME_RE, EXACT_SEMVER_RE, NPM_VALUE_FLAGS, PNPM_VALUE_FLAGS, YARN_VALUE_FLAGS, BUN_VALUE_FLAGS, PIP_VALUE_FLAGS, UV_VALUE_FLAGS, POETRY_VALUE_FLAGS, PIPENV_VALUE_FLAGS, PIPX_VALUE_FLAGS, PY_SPEC_RE, PY_EXACT_RE, EXACT_VERSION_RE, GEM_NAME_RE, GEM_VALUE_FLAGS, BUNDLE_VALUE_FLAGS, CRATE_NAME_RE, CARGO_ADD_VALUE_FLAGS, CARGO_INSTALL_VALUE_FLAGS, GO_VALUE_FLAGS, GO_PATH_RE, NUGET_NAME_RE, DOTNET_ADD_VALUE_FLAGS, DOTNET_TOOL_VALUE_FLAGS, NUGET_VALUE_FLAGS, MAVEN_COORD_RE, COMPOSER_NAME_RE, COMPOSER_VALUE_FLAGS, EDITOR_HEADS, EXTENSION_ID_RE, MANAGER_HEADS, SHELLS, MAX_NESTING, SHELL_VALUE_FLAGS, TUNING_FIELDS, MAP_GOVERNED, KNOB_GOVERNED, CONFIGURABLE_CHECK_IDS, DEFAULT_EGRESS_ALLOWLIST, PRIVATE_HOST_SUFFIXES, SOURCE_COMMANDS, SINK_COMMANDS, OBFUSCATORS, SENSITIVE_PATTERNS, FLAGS_WITH_VALUES, DESTINATION_ARGS, SECRET_VIEW_LIMITS, ARGUMENT_SCAN_LIMITS, RUN_CHAR, runChar, VERDICT_RANK, SQL_DML_KEYWORDS, MAX_DESTINATIONS, MAX_SCAN_CHARS, EMAIL_RE, URL_RE, IBAN_RE, RECIPIENT_KEYS, DESTRUCTIVE_VERB_RE, 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, MASKED_KINDS, ENTRY_RE, IBAN_TEXT_RE, EMAIL_TEXT_RE, ACCOUNT_KEY_RE, ACCOUNT_VALUE_RE, MAX_MASK_DEPTH, syntax4, EMPTY, CURL_SEND_FLAGS, WGET_SEND_FLAGS, IWR_SEND_FLAGS, WRITE_METHODS, GH_READ, GH_WRITE, GH_HOSTNAME_FLAGS, GH_REPO_VALUE_FLAGS, GH_API_VALUE_FLAGS, GIT_FETCH, PUBLISH, SHELLS2, OWN_HEADS, MAX_DEPTH3, CWD_CHANGERS, TERMINAL_ESCAPE_RE, CONTROL_CHAR_RE, LOOP_MAX_RECORDS, FINDING_TO_SIGNAL, SCAN_SIGNAL_WEIGHTS, LOOP_THRESHOLD_FOR_WASTE, MAX_BLAST_PATH, DESTRUCTIVE_OP_RE, SENSITIVE_PATH_RE, FILE_TOOLS, CANARY_MIN_LENGTH, MAX_TEXT, MAX_DEPTH4, 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;
6667
7204
  var init_dist = __esm({
6668
7205
  "packages/policy-engine/dist/index.mjs"() {
6669
7206
  "use strict";
@@ -7464,6 +8001,11 @@ var init_dist = __esm({
7464
8001
  example: "cat /etc/shadow",
7465
8002
  advice: "Keep Review, or choose Block when agents must never read system password hashes."
7466
8003
  },
8004
+ "files.system-dirs": {
8005
+ plain: "The agent writes, creates or deletes a file in a system directory, or sends a system file in a request. /usr/local and a container working directory under /usr/src are ordinary project space and are not included.",
8006
+ example: "wget -O /usr/bin/tool https://example.com/tool",
8007
+ advice: "Keep Review on a workstation. A container image build that installs into /usr can set it to Log."
8008
+ },
7467
8009
  // ── Commands ──────────────────────────────────────────────────────────────
7468
8010
  "commands.inline-exec": {
7469
8011
  plain: "The agent runs a small program written inside the command itself.",
@@ -7660,7 +8202,7 @@ var init_dist = __esm({
7660
8202
  advice: "Fixed at Block. A person accepts the change on the machine (node9 mcp pin update)."
7661
8203
  },
7662
8204
  "loading.malicious-package": {
7663
- plain: "The agent installs a package that the public malicious-package database (OSV) lists, or one published minutes ago.",
8205
+ plain: "The agent installs a package that the public malicious-package database (OSV) lists, or one published minutes ago. Covers npm, PyPI, RubyGems, crates.io, Go, NuGet, Maven, Packagist and VS Code extensions; the publish-age and install-script signals are npm and PyPI only.",
7664
8206
  example: "npm install of a package flagged as malware",
7665
8207
  advice: "Keep Block for known-malicious packages; brand-new packages already stop for Review."
7666
8208
  }
@@ -7706,6 +8248,15 @@ var init_dist = __esm({
7706
8248
  defaultValue: "review",
7707
8249
  legacy: ["review-password-hash-read"]
7708
8250
  },
8251
+ {
8252
+ id: "files.system-dirs",
8253
+ group: "files",
8254
+ title: "System directories",
8255
+ catches: "Writes, new files and removals under /usr, /etc, /boot, /bin, /sbin, /lib and /var/log, and uploads of system files",
8256
+ values: ALL,
8257
+ defaultValue: "review",
8258
+ legacy: ["review-system-dir-mutation", "review-system-file-upload"]
8259
+ },
7709
8260
  // ── Commands ────────────────────────────────────────────────────────────
7710
8261
  {
7711
8262
  id: "commands.inline-exec",
@@ -9003,6 +9554,10 @@ var init_dist = __esm({
9003
9554
  AND_OP = probeBinOp("a && b");
9004
9555
  OR_OP = probeBinOp("a || b");
9005
9556
  AND_OR_OPS = new Set([AND_OP, OR_OP].filter((o) => o !== null));
9557
+ MAX_CALL_DEPTH = 4;
9558
+ MAX_CALL_WALKS = 256;
9559
+ callDepth = 0;
9560
+ callWalks = 0;
9006
9561
  FS_OP_CACHE_MAX = 5e3;
9007
9562
  fsOpCache = /* @__PURE__ */ new Map();
9008
9563
  stripDotSlash = (p) => p.replace(/^\.\//, "");
@@ -9055,10 +9610,79 @@ var init_dist = __esm({
9055
9610
  MAX_ARGV_CHARACTERS = 262144;
9056
9611
  ({ syntax: syntax2 } = mvdanSh2);
9057
9612
  MAX_ALTERNATIVES = 64;
9613
+ MAX_CALL_DEPTH2 = 4;
9614
+ MAX_CALL_WALKS2 = 256;
9615
+ POSITIONAL = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "@", "*", "#"];
9058
9616
  MAX_DEPTH = 64;
9059
9617
  MAX_PAYLOAD_DEPTH = 24;
9060
9618
  unique = (a) => [...new Set(a)].slice(0, MAX_ALTERNATIVES);
9061
- clone = (s) => ({ cwd: [...s.cwd], vars: new Map(s.vars) });
9619
+ clone = (s) => ({
9620
+ cwd: [...s.cwd],
9621
+ vars: new Map(s.vars),
9622
+ ...s.callDepth ? { callDepth: s.callDepth } : {}
9623
+ });
9624
+ SENDING_STDIN = /* @__PURE__ */ new Set(["nc", "ncat", "netcat", "socat", "telnet", "curl", "wget"]);
9625
+ DOWNLOAD_SHORT_VALUE = {
9626
+ curl: "odDeEFHKmPrtTuUwxXyYzAbcCQ",
9627
+ wget: "OoaPiBetTwQlARDXIUY",
9628
+ aria2c: "odisx"
9629
+ };
9630
+ DOWNLOAD_LONG_VALUE = {
9631
+ curl: [
9632
+ "--output",
9633
+ "--output-dir",
9634
+ "--dump-header",
9635
+ "--cookie-jar",
9636
+ "--data",
9637
+ "--data-binary",
9638
+ "--data-raw",
9639
+ "--data-ascii",
9640
+ "--data-urlencode",
9641
+ "--form",
9642
+ "--header",
9643
+ "--request",
9644
+ "--user",
9645
+ "--user-agent",
9646
+ "--referer",
9647
+ "--cookie",
9648
+ "--config",
9649
+ "--json",
9650
+ "--upload-file",
9651
+ "--max-time",
9652
+ "--proxy",
9653
+ "--write-out",
9654
+ "--url"
9655
+ ],
9656
+ wget: [
9657
+ "--output-document",
9658
+ "--output-file",
9659
+ "--append-output",
9660
+ "--directory-prefix",
9661
+ "--input-file",
9662
+ "--post-data",
9663
+ "--post-file",
9664
+ "--body-data",
9665
+ "--body-file",
9666
+ "--method",
9667
+ "--header",
9668
+ "--user-agent",
9669
+ "--user",
9670
+ "--password",
9671
+ "--tries",
9672
+ "--timeout"
9673
+ ],
9674
+ aria2c: ["--out", "--dir", "--input-file"]
9675
+ };
9676
+ DOWNLOAD_OUTPUT_FLAGS = {
9677
+ curl: ["-o", "--output", "-D", "--dump-header", "-c", "--cookie-jar"],
9678
+ wget: ["-O", "--output-document", "-o", "--output-file", "-a", "--append-output"],
9679
+ aria2c: ["-o", "--out"]
9680
+ };
9681
+ DOWNLOAD_DIR_FLAGS = {
9682
+ curl: ["--output-dir"],
9683
+ wget: ["-P", "--directory-prefix"],
9684
+ aria2c: ["-d", "--dir"]
9685
+ };
9062
9686
  base = (s) => (s ?? "").replace(/\/+$/, "").split("/").pop() ?? "";
9063
9687
  quoteGlob = (s) => s.replace(/[\\*?\[\]{}()!+@]/g, "\\$&");
9064
9688
  asWord = (p) => ({ value: p.value, ...p.glob ? { pattern: p.pattern } : {} });
@@ -9083,6 +9707,18 @@ var init_dist = __esm({
9083
9707
  SYSTEM_AUTH_DIRS = ["/etc/security", "/etc/pam.d", "/etc/sudoers.d"];
9084
9708
  PASSWORD_HASH_FILES = ["/etc/shadow", "/etc/gshadow"];
9085
9709
  STARTUP_NAMES = [".bashrc", ".zshrc", ".profile", ".bash_profile"];
9710
+ SYSTEM_DIRS = [
9711
+ "/usr",
9712
+ "/etc",
9713
+ "/boot",
9714
+ "/bin",
9715
+ "/sbin",
9716
+ "/lib",
9717
+ "/lib64",
9718
+ "/var/log"
9719
+ ];
9720
+ SYSTEM_DIRS_EXEMPT = ["/usr/local", "/usr/src"];
9721
+ UPLOAD_SYSTEM_DIRS = [...SYSTEM_DIRS, "/proc", "/sys", "/root"];
9086
9722
  AGENT_INSTRUCTION_FILES = [
9087
9723
  ".gitlab-ci.yml",
9088
9724
  "CLAUDE.md",
@@ -9100,6 +9736,7 @@ var init_dist = __esm({
9100
9736
  ".husky"
9101
9737
  ];
9102
9738
  LEGACY_SYSTEM_PATH_RE = /^(?:[a-zA-Z]:)?\/etc\/(?:passwd|shadow|sudoers)$/;
9739
+ exempt = (p) => SYSTEM_DIRS_EXEMPT.some((d) => within(p, d));
9103
9740
  RANK = { allow: 0, review: 1, block: 2 };
9104
9741
  BIP39_ENGLISH = `abandon
9105
9742
  ability
@@ -11426,15 +12063,20 @@ zoo`.split("\n");
11426
12063
  IDENTIFIER_VALUE_RE = /^[A-Za-z_.-]+$/;
11427
12064
  PATH_VALUE_RE = /^(?:\/|\.{1,2}\/|~\/)/;
11428
12065
  DOTTED_REFERENCE_RE = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)+$/;
12066
+ CONCATENATION_RE = /^\+[A-Za-z_$][\w$]*\./;
11429
12067
  EMAIL_VALUE_RE = /^[^@\s]+@[^@\s]+\.[A-Za-z]{2,}$/;
11430
12068
  FIXTURE_NAME_RE = /(?:fake|dummy|example|sample|mock|stub|placeholder|test)/i;
11431
- DERIVATIVE_NAME_RE = /(?:hash|digest|id|type|name|url|uri|path|file|len|length|count|size|ttl|expires?|expiry|age|date|time|format|field|header|param)$/i;
12069
+ DERIVATIVE_NAME_RE = /(?:hash|digest|fingerprint|thumbprint|checksum|id|type|name|url|uri|path|file|len|length|count|size|ttl|expires?|expiry|age|date|time|format|field|header|param)$/i;
11432
12070
  QUERY_CREDENTIAL_VALUE_RE = /=([A-Za-z0-9%+/_.~-]{8,512}={0,2})$/;
11433
12071
  OTP_SECRET_RE = /^[A-Z2-7]{16,}=*$/i;
11434
- ASSIGNED_SECRET_VALUE_RE = /(?::=|=>|[:=])[ \t]{0,8}["'`]?([A-Za-z0-9+/_.!@#$%^*~-]{12,512}={0,2})$/;
12072
+ OTP_DOC_SECRETS = /* @__PURE__ */ new Set(["JBSWY3DPEHPK3PXP", "GEZDGNBVGY3TQOJQ"]);
12073
+ ASSIGNED_SECRET_VALUE_RE = /(?::=|=>|[:=])[ \t]{0,8}\\?["'`]?([A-Za-z0-9+/_.!@#$%^*~-]{12,512}={0,2})$/;
11435
12074
  ASSIGNED_SECRET_NAME_RE = /^([A-Za-z0-9_]+)/;
11436
- NAME_KEYWORD_RE = /^(?:password|passphrase|passwd|pwd|secret|token|api[_-]?key|auth[_-]?key|private[_-]?key|access[_-]?key|client[_-]?secret)/i;
12075
+ NAME_KEYWORD_RE = /^(?:password|passphrase|passwd|pwd|secret|token|api[_-]?key|auth[_-]?key|private[_-]?key|access[_-]?key|client[_-]?secret|consumer[_-]?key|app[_-]?key|signing[_-]?key|encryption[_-]?key|master[_-]?key|jsapi[_-]?ticket|pass)/i;
11437
12076
  ARN_CONTEXT_RE = /\barn:[A-Za-z0-9-]*:[^\s"'`]*$/i;
12077
+ BASIC_VALUE_RE = /([A-Za-z0-9+/]{8,512}={0,2})$/;
12078
+ BASIC_PAIR_RE = /^([^:\s]{1,128}):(\S{8,256})$/;
12079
+ OTP_CAPTURE_RE = /["']([A-Z2-7]{16,128}=*)["']$/i;
11438
12080
  DLP_PATTERNS = [
11439
12081
  // ── AWS ───────────────────────────────────────────────────────────────────
11440
12082
  {
@@ -11772,6 +12414,22 @@ zoo`.split("\n");
11772
12414
  contextBoost: true,
11773
12415
  minEntropy: 3
11774
12416
  },
12417
+ // ── HTTP Basic credential ─────────────────────────────────────────────────
12418
+ // `Authorization: Basic <base64 of user:password>`, or the bare scheme in a
12419
+ // config list. The validator decodes the value and accepts only a
12420
+ // `user:password` whose password looks generated (letters and digits, 8+
12421
+ // characters, the query-credential rule), so prose ("Basic authentication")
12422
+ // and documentation pairs (`admin:changeme`, `user:pass1234`) stay allowed.
12423
+ // review: the same weight as a bare Bearer token.
12424
+ {
12425
+ name: "HTTP Basic Credential",
12426
+ regex: /\bBasic[ \t]{1,8}([A-Za-z0-9+/]{8,512}={0,2})/i,
12427
+ severity: "review",
12428
+ keywords: ["basic"],
12429
+ validate: validateBasicCredential,
12430
+ redactGroup: 1,
12431
+ argumentsOnly: true
12432
+ },
11775
12433
  // ── Resend ────────────────────────────────────────────────────────────────
11776
12434
  {
11777
12435
  name: "Resend API Key",
@@ -11913,6 +12571,19 @@ zoo`.split("\n");
11913
12571
  validate: validateQueryCredential,
11914
12572
  argumentsOnly: true
11915
12573
  },
12574
+ // ── OTP secret in a constructor ───────────────────────────────────────────
12575
+ // `ROTP::TOTP.new('<base32>')`, `pyotp.TOTP("<base32>")`, `new HOTP('...')`:
12576
+ // the shared secret of a one-time-password generator written as a literal.
12577
+ // The otpauth URI form is `Credential in URL Query`. review.
12578
+ {
12579
+ name: "OTP Secret",
12580
+ regex: /\b(?:TOTP|HOTP|OTP)(?:\.new|\.create)?[ \t]{0,8}\(?[ \t]{0,8}["']([A-Z2-7]{16,128}=*)["']/i,
12581
+ severity: "review",
12582
+ keywords: ["otp"],
12583
+ validate: validateOtpSecret,
12584
+ redactGroup: 1,
12585
+ argumentsOnly: true
12586
+ },
11916
12587
  // ── Secret assigned to a telling name ─────────────────────────────────────
11917
12588
  // `password: ...`, `JWT_SECRET=...`, `api_key = "..."`. The name alone is
11918
12589
  // not a finding: most such lines hold a reference (`process.env.X`), a
@@ -11920,12 +12591,14 @@ zoo`.split("\n");
11920
12591
  // generated: 12+ characters, not an identifier, Shannon entropy of 3.5
11921
12592
  // bits per character or more. review, never block (node9Firewall#348,
11922
12593
  // item 4). Tuned on CredData's public split; its look-alike lines are the
11923
- // negative control.
12594
+ // negative control. A quote may be escaped (`\"access_token\":\"...\"`):
12595
+ // JSON written inside a string literal. The two optional quotes after the
12596
+ // name are written out, not as a counted group, to keep star height 1.
11924
12597
  {
11925
12598
  name: "Assigned Secret",
11926
- regex: /(?:password|passphrase|passwd|pwd|secret|token|api[_-]?key|auth[_-]?key|private[_-]?key|access[_-]?key|client[_-]?secret)[A-Za-z0-9_]{0,40}["'\]]{0,2}[ \t]{0,8}(?::=|=>|[:=])[ \t]{0,8}["'`]?([A-Za-z0-9+/_.!@#$%^*~-]{12,512}={0,2})/i,
12599
+ regex: /(?:password|passphrase|passwd|pwd|secret|token|api[_-]?key|auth[_-]?key|private[_-]?key|access[_-]?key|client[_-]?secret|consumer[_-]?key|app[_-]?key|signing[_-]?key|encryption[_-]?key|master[_-]?key|jsapi[_-]?ticket|\bpass(?![A-Za-z0-9])|_pass(?![A-Za-z0-9]))[A-Za-z0-9_]{0,40}\\?["'\]]?\\?["'\]]?[ \t]{0,8}(?::=|=>|[:=])[ \t]{0,8}\\?["'`]?([A-Za-z0-9+/_.!@#$%^*~-]{12,512}={0,2})/i,
11927
12600
  severity: "review",
11928
- keywords: ["password", "passphrase", "passwd", "pwd", "secret", "token", "key"],
12601
+ keywords: ["pass", "pwd", "secret", "token", "key", "ticket"],
11929
12602
  validate: validateAssignedSecret,
11930
12603
  argumentsOnly: true
11931
12604
  }
@@ -12193,7 +12866,169 @@ zoo`.split("\n");
12193
12866
  ]);
12194
12867
  PY_SPEC_RE = /^([A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?)(?:\[[^\]]*\])?\s*(.*)$/;
12195
12868
  PY_EXACT_RE = /^===?\s*v?(\d+(?:\.\d+)*(?:[a-zA-Z0-9.+!-]*))$/;
12869
+ EXACT_VERSION_RE = /^=?v?(\d+(?:\.\d+)*(?:[-+][0-9A-Za-z][0-9A-Za-z.+-]*)?)$/;
12870
+ GEM_NAME_RE = /^[\p{L}\p{N}][\p{L}\p{N}_.-]*$/u;
12871
+ GEM_VALUE_FLAGS = /* @__PURE__ */ new Set([
12872
+ "-v",
12873
+ "--version",
12874
+ "-i",
12875
+ "--install-dir",
12876
+ "-n",
12877
+ "--bindir",
12878
+ "--document",
12879
+ "--build-root",
12880
+ "-P",
12881
+ "--trust-policy",
12882
+ "-s",
12883
+ "--source",
12884
+ "-p",
12885
+ "--http-proxy",
12886
+ "--platform",
12887
+ "-g",
12888
+ "--file",
12889
+ "--without"
12890
+ ]);
12891
+ BUNDLE_VALUE_FLAGS = /* @__PURE__ */ new Set([
12892
+ "-v",
12893
+ "--version",
12894
+ "-g",
12895
+ "--group",
12896
+ "-s",
12897
+ "--source",
12898
+ "-r",
12899
+ "--require",
12900
+ "--path",
12901
+ "--git",
12902
+ "--github",
12903
+ "--branch",
12904
+ "--ref",
12905
+ "--glob"
12906
+ ]);
12907
+ CRATE_NAME_RE = /^[\p{L}\p{N}][\p{L}\p{N}_-]*$/u;
12908
+ CARGO_ADD_VALUE_FLAGS = /* @__PURE__ */ new Set([
12909
+ "-F",
12910
+ "--features",
12911
+ "--rename",
12912
+ "--registry",
12913
+ "--manifest-path",
12914
+ "-p",
12915
+ "--package",
12916
+ "--git",
12917
+ "--branch",
12918
+ "--tag",
12919
+ "--rev",
12920
+ "--path",
12921
+ "--base",
12922
+ "--target",
12923
+ "--color",
12924
+ "--config",
12925
+ "-Z",
12926
+ "--lockfile-path"
12927
+ ]);
12928
+ CARGO_INSTALL_VALUE_FLAGS = /* @__PURE__ */ new Set([
12929
+ "--version",
12930
+ "--vers",
12931
+ "--git",
12932
+ "--branch",
12933
+ "--tag",
12934
+ "--rev",
12935
+ "--path",
12936
+ "--root",
12937
+ "--index",
12938
+ "--registry",
12939
+ "-F",
12940
+ "--features",
12941
+ "--profile",
12942
+ "--target",
12943
+ "--target-dir",
12944
+ "-j",
12945
+ "--jobs",
12946
+ "--bin",
12947
+ "--example",
12948
+ "--color",
12949
+ "--config",
12950
+ "-Z"
12951
+ ]);
12952
+ GO_VALUE_FLAGS = /* @__PURE__ */ new Set([
12953
+ "-C",
12954
+ "-tags",
12955
+ "-ldflags",
12956
+ "-gcflags",
12957
+ "-asmflags",
12958
+ "-mod",
12959
+ "-modfile",
12960
+ "-o",
12961
+ "-p",
12962
+ "-pkgdir",
12963
+ "-toolexec",
12964
+ "-exec",
12965
+ "-overlay",
12966
+ "-pgo",
12967
+ "-buildmode",
12968
+ "-compiler",
12969
+ "-gccgoflags",
12970
+ "-installsuffix",
12971
+ "-covermode",
12972
+ "-coverpkg"
12973
+ ]);
12974
+ GO_PATH_RE = /^[\p{L}\p{N}-]+(?:\.[\p{L}\p{N}-]+)+(?:\/[\p{L}\p{N}_.~+-]+)*$/u;
12975
+ NUGET_NAME_RE = /^[\p{L}\p{N}_][\p{L}\p{N}_.-]*$/u;
12976
+ DOTNET_ADD_VALUE_FLAGS = /* @__PURE__ */ new Set([
12977
+ "-v",
12978
+ "--version",
12979
+ "-s",
12980
+ "--source",
12981
+ "-f",
12982
+ "--framework",
12983
+ "--package-directory"
12984
+ ]);
12985
+ DOTNET_TOOL_VALUE_FLAGS = /* @__PURE__ */ new Set([
12986
+ "--version",
12987
+ "--add-source",
12988
+ "--configfile",
12989
+ "--framework",
12990
+ "--tool-path",
12991
+ "-a",
12992
+ "--arch",
12993
+ "--tool-manifest",
12994
+ "-v",
12995
+ "--verbosity"
12996
+ ]);
12997
+ NUGET_VALUE_FLAGS = /* @__PURE__ */ new Set([
12998
+ "-version",
12999
+ "-outputdirectory",
13000
+ "-o",
13001
+ "-source",
13002
+ "-configfile",
13003
+ "-fallbacksource",
13004
+ "-framework",
13005
+ "-packagesavemode",
13006
+ "-dependencyversion",
13007
+ "-solutiondirectory",
13008
+ "-verbosity"
13009
+ ]);
13010
+ MAVEN_COORD_RE = /^[\p{L}\p{N}_.-]+:[\p{L}\p{N}_.-]+$/u;
13011
+ COMPOSER_NAME_RE = /^[\p{L}\p{N}][\p{L}\p{N}_.-]*\/[\p{L}\p{N}][\p{L}\p{N}_.-]*$/u;
13012
+ COMPOSER_VALUE_FLAGS = /* @__PURE__ */ new Set([
13013
+ "-d",
13014
+ "--working-dir",
13015
+ "--stability",
13016
+ "--repository",
13017
+ "--add-repository"
13018
+ ]);
13019
+ EDITOR_HEADS = ["code", "code-insiders", "codium", "vscodium", "cursor"];
13020
+ EXTENSION_ID_RE = /^[\p{L}\p{N}][\p{L}\p{N}_-]*\.[\p{L}\p{N}][\p{L}\p{N}_.-]*$/u;
12196
13021
  MANAGER_HEADS = /* @__PURE__ */ new Set([
13022
+ "gem",
13023
+ "bundle",
13024
+ "cargo",
13025
+ "go",
13026
+ "dotnet",
13027
+ "nuget",
13028
+ "mvn",
13029
+ "mvnw",
13030
+ "composer",
13031
+ ...EDITOR_HEADS,
12197
13032
  "npm",
12198
13033
  "npx",
12199
13034
  "pnpm",
@@ -12272,6 +13107,7 @@ zoo`.split("\n");
12272
13107
  MAP_GOVERNED = /* @__PURE__ */ new Set([
12273
13108
  "files.shell-startup",
12274
13109
  "files.password-hashes",
13110
+ "files.system-dirs",
12275
13111
  "commands.inline-exec",
12276
13112
  "commands.eval-dynamic",
12277
13113
  "commands.curl-pipe-shell",
@@ -12540,7 +13376,6 @@ zoo`.split("\n");
12540
13376
  PII_CC16_RE = /\b(?:4\d{3}|5[1-5]\d{2}|6\d{3})[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}\b/;
12541
13377
  PII_CC15_RE = /\b3[47]\d{2}[-\s]?\d{6}[-\s]?\d{5}\b/;
12542
13378
  PII_IBAN_RE = /\b[A-Z]{2}\d{2}(?:[A-Z0-9]|[ -][A-Z0-9]){11,30}\b/;
12543
- NUMBER_LEAD_RE = /[\s:=;([{"'+-]/;
12544
13379
  REALTIME_PII_PATTERNS = ["SSN", "Credit Card", "IBAN"];
12545
13380
  MAX_PII_SCAN_BYTES = 1e5;
12546
13381
  MASKED_KINDS = /* @__PURE__ */ new Set(["email", "iban", "account"]);
@@ -12762,7 +13597,7 @@ zoo`.split("\n");
12762
13597
  { view: "separators-stripped", decoder: "separators", stripped: true }
12763
13598
  ];
12764
13599
  LONG_OUTPUT_THRESHOLD_BYTES = 100 * 1024;
12765
- CANONICAL_EXTRACTOR_VERSION = "canonical-v27";
13600
+ CANONICAL_EXTRACTOR_VERSION = "canonical-v28";
12766
13601
  DEDUPE_PREVIEW_LEN = 120;
12767
13602
  ENGINE_VERSION = "1.4.0";
12768
13603
  }
@@ -18122,7 +18957,20 @@ function ecoDir(eco) {
18122
18957
  return path24.join(osvRoot(), eco);
18123
18958
  }
18124
18959
  function indexKey(eco, name) {
18125
- return eco === "PyPI" ? normalizePyPiName(name) : name.toLowerCase();
18960
+ switch (eco) {
18961
+ case "PyPI":
18962
+ return normalizePyPiName(name);
18963
+ case "crates.io":
18964
+ return name.toLowerCase().replace(/_/g, "-");
18965
+ case "Go":
18966
+ case "Maven":
18967
+ return name;
18968
+ default:
18969
+ return name.toLowerCase();
18970
+ }
18971
+ }
18972
+ function recordEcosystemMatches(eco, recordEco) {
18973
+ return typeof recordEco === "string" && (recordEco === eco || recordEco.startsWith(`${eco}:`));
18126
18974
  }
18127
18975
  function shardOf(key) {
18128
18976
  return createHash5("sha1").update(key).digest("hex").slice(0, 2);
@@ -18168,7 +19016,8 @@ function entriesFromRecord(eco, record2) {
18168
19016
  const modifiedMs = typeof r.modified === "string" ? Date.parse(r.modified) || 0 : 0;
18169
19017
  const rows = /* @__PURE__ */ new Map();
18170
19018
  for (const a of r.affected ?? []) {
18171
- if (a?.package?.ecosystem !== eco || typeof a.package.name !== "string") continue;
19019
+ if (!recordEcosystemMatches(eco, a?.package?.ecosystem) || typeof a?.package?.name !== "string")
19020
+ continue;
18172
19021
  const key = indexKey(eco, a.package.name);
18173
19022
  const entry = rows.get(key) ?? { id: r.id };
18174
19023
  if (Array.isArray(a.versions)) {
@@ -18408,15 +19257,18 @@ async function pypiInfo(name, version2) {
18408
19257
  };
18409
19258
  }
18410
19259
  async function registryInfo(eco, name, version2, freshWindowMs, raw) {
18411
- return eco === "npm" ? npmInfo(name, version2, freshWindowMs, raw) : pypiInfo(name, version2);
19260
+ if (eco === "npm") return npmInfo(name, version2, freshWindowMs, raw);
19261
+ if (eco === "PyPI") return pypiInfo(name, version2);
19262
+ return null;
18412
19263
  }
18413
- var TIMEOUT_MS2, MAX_BYTES;
19264
+ var TIMEOUT_MS2, MAX_BYTES, HAS_REGISTRY_CLIENT;
18414
19265
  var init_registry2 = __esm({
18415
19266
  "src/supply-chain/registry.ts"() {
18416
19267
  "use strict";
18417
19268
  init_net();
18418
19269
  TIMEOUT_MS2 = 1500;
18419
19270
  MAX_BYTES = 2 * 1024 * 1024;
19271
+ HAS_REGISTRY_CLIENT = /* @__PURE__ */ new Set(["npm", "PyPI"]);
18420
19272
  }
18421
19273
  });
18422
19274
 
@@ -18463,25 +19315,39 @@ function commandOf(toolName, args) {
18463
19315
  return typeof a.command === "string" ? a.command : typeof a.cmd === "string" ? a.cmd : null;
18464
19316
  }
18465
19317
  function label(p, version2) {
18466
- return `${p.ecosystem === "npm" ? "npm" : "PyPI"} package "${p.name}${version2 ? `@${version2}` : ""}"`;
19318
+ return `${KIND[p.ecosystem]} "${p.name}${version2 ? `@${version2}` : ""}"`;
19319
+ }
19320
+ function osvNames(p) {
19321
+ if (p.ecosystem !== "Go") return [p.name];
19322
+ const parts = p.name.split("/");
19323
+ const out = [];
19324
+ for (let n = parts.length; n >= 2 && out.length < 6; n--) out.push(parts.slice(0, n).join("/"));
19325
+ return out;
18467
19326
  }
18468
19327
  async function maliciousFor(p, version2, cfg, misses, networkCapped) {
18469
- const local = lookupIndex(p.ecosystem, p.name);
18470
- const entries = local.status === "hit" ? local.entries : null;
18471
- if (!entries && !(local.status === "clean" && local.fresh)) {
19328
+ const names = osvNames(p);
19329
+ const local = names.map((n) => lookupIndex(p.ecosystem, n));
19330
+ const hits2 = local.flatMap((l) => l.status === "hit" ? l.entries : []);
19331
+ const entries = hits2.length > 0 ? hits2 : null;
19332
+ const freshClean = local.every((l) => l.status === "clean" && l.fresh);
19333
+ const status = local.some((l) => l.status === "unavailable") ? "unavailable" : local[0].status;
19334
+ if (!entries && !freshClean) {
18472
19335
  if (!cfg.onlineFallback || networkCapped) {
18473
19336
  misses.push(
18474
- `${label(p, version2)}: local index ${local.status}, no online lookup (${networkCapped ? "over the per-command network cap" : "online fallback off"})`
19337
+ `${label(p, version2)}: local index ${status}, no online lookup (${networkCapped ? "over the per-command network cap" : "online fallback off"})`
18475
19338
  );
18476
19339
  return null;
18477
19340
  }
18478
- let ids = null;
19341
+ let perName = null;
18479
19342
  try {
18480
- ids = await queryOsvMalicious([{ ecosystem: p.ecosystem, name: p.name, version: version2 }]);
19343
+ perName = await queryOsvMalicious(
19344
+ names.map((name) => ({ ecosystem: p.ecosystem, name, version: version2 }))
19345
+ );
18481
19346
  } catch (err2) {
18482
19347
  misses.push(`${label(p, version2)}: OSV online ${err2.message}`);
18483
19348
  return null;
18484
19349
  }
19350
+ const ids = perName ? [[...new Set(perName.flat())]] : null;
18485
19351
  if (!ids) {
18486
19352
  misses.push(`${label(p, version2)}: OSV online unavailable`);
18487
19353
  return null;
@@ -18551,7 +19417,7 @@ async function checkOne(p, cfg, misses, networkCapped, cwd) {
18551
19417
  }
18552
19418
  const findings = [];
18553
19419
  let info = null;
18554
- const wantsRegistry = cfg.newPackage !== "off" || cfg.installScript !== "off";
19420
+ const wantsRegistry = (cfg.newPackage !== "off" || cfg.installScript !== "off") && HAS_REGISTRY_CLIENT.has(p.ecosystem);
18555
19421
  if (wantsRegistry && !networkCapped) {
18556
19422
  try {
18557
19423
  const windowMs = cfg.newPackage === "off" ? 0 : cfg.maxAgeHours * 36e5;
@@ -18631,7 +19497,7 @@ async function runPackageCheck(toolName, args, cfg, cwd) {
18631
19497
  return { ...ALLOW, misses: [`package check error: ${err2?.message ?? err2}`] };
18632
19498
  }
18633
19499
  }
18634
- var MAX_NETWORK_PACKAGES, ALLOW;
19500
+ var MAX_NETWORK_PACKAGES, ALLOW, KIND;
18635
19501
  var init_check = __esm({
18636
19502
  "src/supply-chain/check.ts"() {
18637
19503
  "use strict";
@@ -18642,6 +19508,17 @@ var init_check = __esm({
18642
19508
  init_local_resolve();
18643
19509
  MAX_NETWORK_PACKAGES = 10;
18644
19510
  ALLOW = { verdict: "allow", findings: [], misses: [], checked: 0 };
19511
+ KIND = {
19512
+ npm: "npm package",
19513
+ PyPI: "PyPI package",
19514
+ RubyGems: "RubyGems package",
19515
+ "crates.io": "crates.io package",
19516
+ Go: "Go module",
19517
+ NuGet: "NuGet package",
19518
+ Maven: "Maven artifact",
19519
+ Packagist: "Packagist package",
19520
+ VSCode: "VS Code extension"
19521
+ };
18645
19522
  }
18646
19523
  });
18647
19524
 
@@ -33873,7 +34750,17 @@ var init_osv_sync = __esm({
33873
34750
  init_zip();
33874
34751
  init_osv_index();
33875
34752
  init_net();
33876
- ECOSYSTEMS2 = ["npm", "PyPI"];
34753
+ ECOSYSTEMS2 = [
34754
+ "npm",
34755
+ "PyPI",
34756
+ "RubyGems",
34757
+ "crates.io",
34758
+ "Go",
34759
+ "NuGet",
34760
+ "Maven",
34761
+ "Packagist",
34762
+ "VSCode"
34763
+ ];
33877
34764
  SYNC_INTERVAL_MS2 = 6 * 60 * 60 * 1e3;
33878
34765
  FIRST_SYNC_DELAY_MS = 2 * 60 * 1e3;
33879
34766
  FULL_REBUILD_MS = 7 * 24 * 60 * 60 * 1e3;
@@ -35864,14 +36751,14 @@ async function onboardMachine(apiKey, opts = {}) {
35864
36751
  detail: `${sync.rules} cloud rule${sync.rules === 1 ? "" : "s"} active`
35865
36752
  } : { name: "policy-sync", ok: false, detail: sync.reason }
35866
36753
  );
35867
- const push = await runPolicyPush();
36754
+ const push2 = await runPolicyPush();
35868
36755
  steps.push(
35869
- push.ok ? { name: "register", ok: true, detail: "machine visible in the dashboard" } : { name: "register", ok: false, detail: push.reason }
36756
+ push2.ok ? { name: "register", ok: true, detail: "machine visible in the dashboard" } : { name: "register", ok: false, detail: push2.reason }
35870
36757
  );
35871
36758
  const healed = ensureAutostartHealthy(!!getConfig().settings.autoStartDaemon);
35872
36759
  let detail = healed === "repaired" ? "autostart re-enabled (survives reboot)" : isDaemonRunning() ? "running" : healed === "unsupported" ? "no background service on this platform: starts on agent activity" : "starts on agent activity (make it survive reboots: node9 daemon install)";
35873
36760
  let daemonOk = true;
35874
- if (sync.ok && push.ok && isInteractive() && mayChangeService({ stdoutTTY: !!process.stdout.isTTY, ci: isCI() }) && autostartState() === "absent") {
36761
+ if (sync.ok && push2.ok && isInteractive() && mayChangeService({ stdoutTTY: !!process.stdout.isTTY, ci: isCI() }) && autostartState() === "absent") {
35875
36762
  try {
35876
36763
  if (await confirm2({
35877
36764
  message: "Run node9 in the background when this computer starts?",
@@ -65318,7 +66205,7 @@ function collectToolOutputText(toolResponse, opts = {}) {
65318
66205
  let size = 0;
65319
66206
  let fields = 0;
65320
66207
  let truncated = false;
65321
- const push = (s) => {
66208
+ const push2 = (s) => {
65322
66209
  if (s.length === 0) return true;
65323
66210
  const room = maxBytes - size;
65324
66211
  if (s.length > room) {
@@ -65336,7 +66223,7 @@ function collectToolOutputText(toolResponse, opts = {}) {
65336
66223
  return true;
65337
66224
  };
65338
66225
  const walk2 = (v, depth) => {
65339
- if (typeof v === "string") return push(v);
66226
+ if (typeof v === "string") return push2(v);
65340
66227
  if (depth >= MAX_DEPTH5 || v === null || typeof v !== "object") return true;
65341
66228
  const children = Array.isArray(v) ? v : Object.values(v);
65342
66229
  for (const child of children) if (!walk2(child, depth + 1)) return false;
@@ -75963,7 +76850,7 @@ function addHost(list, host2) {
75963
76850
  if (!cliGuardPolicyWrite(`egress ${list} ${host2}`)) return false;
75964
76851
  return guard(() => addEgressHost(list, host2));
75965
76852
  }
75966
- function exempt(address) {
76853
+ function exempt2(address) {
75967
76854
  if (!cliGuardPolicyWrite(`egress exempt ${address}`)) return false;
75968
76855
  return guard(() => addSsrfExemption(address));
75969
76856
  }
@@ -76146,7 +77033,7 @@ function registerEgressCommand(program2) {
76146
77033
  process.exitCode = 1;
76147
77034
  return;
76148
77035
  }
76149
- if (!exempt(a)) return;
77036
+ if (!exempt2(a)) return;
76150
77037
  console.log(chalk34.green(`
76151
77038
  \u2713 ${a} is exempt from the floor.`));
76152
77039
  if (!m)
@@ -77935,17 +78822,18 @@ function registerSessionExposureCommand(program2) {
77935
78822
  init_osv_index();
77936
78823
  init_osv_sync();
77937
78824
  import chalk42 from "chalk";
78825
+ var ECO_WIDTH = Math.max(...ECOSYSTEMS2.map((e) => e.length));
77938
78826
  function registerPackageIndexCommand(program2) {
77939
78827
  const cmd = program2.command("package-index").description("Local OSV malicious-package index used to check installs before they run");
77940
78828
  cmd.command("status").description("Show when each ecosystem was last synced and how many records it holds").action(() => {
77941
78829
  for (const eco of ECOSYSTEMS2) {
77942
78830
  const meta = readMeta(eco);
77943
78831
  if (!meta) {
77944
- console.log(`${eco.padEnd(5)} ${chalk42.yellow("not synced")}`);
78832
+ console.log(`${eco.padEnd(ECO_WIDTH)} ${chalk42.yellow("not synced")}`);
77945
78833
  continue;
77946
78834
  }
77947
78835
  console.log(
77948
- `${eco.padEnd(5)} ${meta.records} records synced ${meta.syncedAt} (${meta.mode})`
78836
+ `${eco.padEnd(ECO_WIDTH)} ${meta.records} records synced ${meta.syncedAt} (${meta.mode})`
77949
78837
  );
77950
78838
  }
77951
78839
  });
@@ -77955,9 +78843,9 @@ function registerPackageIndexCommand(program2) {
77955
78843
  for (const r of results) {
77956
78844
  if (r.error) {
77957
78845
  failed = true;
77958
- console.error(`${r.ecosystem.padEnd(5)} ${chalk42.red("failed")}: ${r.error}`);
78846
+ console.error(`${r.ecosystem.padEnd(ECO_WIDTH)} ${chalk42.red("failed")}: ${r.error}`);
77959
78847
  } else {
77960
- console.log(`${r.ecosystem.padEnd(5)} ${r.mode}: ${r.records} records`);
78848
+ console.log(`${r.ecosystem.padEnd(ECO_WIDTH)} ${r.mode}: ${r.records} records`);
77961
78849
  }
77962
78850
  }
77963
78851
  process.exitCode = failed ? 1 : 0;