@node9/proxy 2.51.0 → 2.53.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/index.mjs CHANGED
@@ -423,15 +423,15 @@ function ssrfExemptMatches(entries, normalized) {
423
423
  continue;
424
424
  }
425
425
  if (!target) continue;
426
- const base2 = bitsOf(normalizeIpLiteral(entry.slice(0, slash)) ?? "");
426
+ const base3 = bitsOf(normalizeIpLiteral(entry.slice(0, slash)) ?? "");
427
427
  const prefixText = entry.slice(slash + 1);
428
428
  const prefix = /^\d+$/.test(prefixText) ? Number(prefixText) : NaN;
429
- if (!base2 || !Number.isInteger(prefix) || prefix < 0 || // A v4 range never matches a v6 address, and the reverse: the widths
429
+ if (!base3 || !Number.isInteger(prefix) || prefix < 0 || // A v4 range never matches a v6 address, and the reverse: the widths
430
430
  // differ, so `0.0.0.0/0` does not release `::1`.
431
- base2.length !== target.length || prefix > base2.length) {
431
+ base3.length !== target.length || prefix > base3.length) {
432
432
  continue;
433
433
  }
434
- if (base2.slice(0, prefix) === target.slice(0, prefix)) return true;
434
+ if (base3.slice(0, prefix) === target.slice(0, prefix)) return true;
435
435
  }
436
436
  return false;
437
437
  }
@@ -485,6 +485,10 @@ function reviewsEveryAgentFileWrite(mode) {
485
485
  const level = levelOf(mode);
486
486
  return level === "strict" || level === "block-all";
487
487
  }
488
+ function holdsSendsWithoutDaemon(mode) {
489
+ const level = levelOf(mode);
490
+ return level === "strict" || level === "block-all";
491
+ }
488
492
  function isBlockAll(mode) {
489
493
  return levelOf(mode) === "block-all";
490
494
  }
@@ -1689,6 +1693,39 @@ function protectedLiteral(code, isProtected) {
1689
1693
  }
1690
1694
  return null;
1691
1695
  }
1696
+ function inlineReadPaths(language, code, preload = {}) {
1697
+ const out = [];
1698
+ const before = READ_BEFORE[language];
1699
+ const after = READ_AFTER[language];
1700
+ const assigned = /* @__PURE__ */ new Map();
1701
+ for (const m of code.matchAll(STRING_LITERAL)) {
1702
+ const raw = m[2].trim();
1703
+ const value = raw.replace(/^[<+]\s*/, "");
1704
+ if (!value) continue;
1705
+ const head = code.slice(Math.max(0, m.index - 160), m.index);
1706
+ const tail = code.slice(m.index + m[0].length, m.index + m[0].length + 40);
1707
+ if (/^[>|]/.test(raw) || WRITE_MODE_AFTER.test(tail) || PERL_WRITE_BEFORE.test(head)) continue;
1708
+ if (before.test(head) || after?.test(tail) || language === "node" && DOTENV_PATH_OPTION.test(head)) {
1709
+ out.push(value);
1710
+ continue;
1711
+ }
1712
+ const name = ASSIGNED.exec(head)?.[1];
1713
+ if (name) assigned.set(name, [...assigned.get(name) ?? [], value]);
1714
+ }
1715
+ if (assigned.size > 0) {
1716
+ const opened = /* @__PURE__ */ new Set();
1717
+ for (const m of code.matchAll(OPENED_NAME[language])) opened.add(m[1] ?? m[2]);
1718
+ for (const [name, values] of assigned) if (opened.has(name)) out.push(...values);
1719
+ }
1720
+ if (language === "python" || language === "node") {
1721
+ const d = DOTENV[language];
1722
+ const readsEnv = d.env.test(code);
1723
+ const loads = /dotenv/.test(code) && d.load.test(code) && !d.literalPath.test(code);
1724
+ if ((loads || preload.dotenv) && (readsEnv || d.values.test(code))) out.push(".env");
1725
+ if (readsEnv) out.push(...preload.envFiles ?? []);
1726
+ }
1727
+ return out;
1728
+ }
1692
1729
  function amplifies(language, code) {
1693
1730
  const loop = language === "python" ? /\bwhile\s+(?:True|1)\s*:/ : language === "node" ? /\bwhile\s*\(\s*(?:true|1)\s*\)|\bfor\s*\(\s*;\s*;\s*\)|\bsetInterval\s*\(/ : /\bwhile\s*\(?\s*(?:1|true)\s*\)?|\bloop\s+do\b|\bfor\s*\(\s*;\s*;\s*\)/;
1694
1731
  const m = loop.exec(code);
@@ -2543,15 +2580,28 @@ function inlineExecCall(stmt, pipeFed) {
2543
2580
  const fromStdin = () => {
2544
2581
  if (stdinFed) {
2545
2582
  const { code, source } = stdinCodeOf(redirs);
2546
- return { interp, code: [code], source };
2583
+ return { interp, code: [code], source, preload };
2547
2584
  }
2548
2585
  return { interp, code: [null], source: "pipe" };
2549
2586
  };
2550
2587
  let positionals2 = 0;
2551
2588
  let selectedProgram = false;
2552
2589
  const codes = [];
2590
+ const preload = {};
2591
+ const nodeFamily = inlineLanguageOf(interp) === "node";
2553
2592
  for (let i = 0; i < args.length; i++) {
2554
2593
  const a = args[i];
2594
+ if (nodeFamily && !selectedProgram && codes.length === 0 && a != null) {
2595
+ const flag = NODE_VALUE_FLAG.exec(a);
2596
+ if (flag) {
2597
+ const value = flag[2] ?? args[++i];
2598
+ if (value != null) {
2599
+ if (/^--env-file/.test(flag[1])) (preload.envFiles ??= []).push(value);
2600
+ else if (/^dotenv\/config(?:\.js)?$/.test(value)) preload.dotenv = true;
2601
+ }
2602
+ continue;
2603
+ }
2604
+ }
2555
2605
  if (a == null) {
2556
2606
  if (codes.length > 0) break;
2557
2607
  positionals2++;
@@ -2588,7 +2638,12 @@ function inlineExecCall(stmt, pipeFed) {
2588
2638
  }
2589
2639
  }
2590
2640
  if (codes.length > 0) {
2591
- return { interp, code: codes, source: codes.includes(null) ? "variable" : void 0 };
2641
+ return {
2642
+ interp,
2643
+ code: codes,
2644
+ source: codes.includes(null) ? "variable" : void 0,
2645
+ preload
2646
+ };
2592
2647
  }
2593
2648
  if (positionals2 === 0 && (stdinFed || pipeFed)) {
2594
2649
  if (!/^(bash|sh|zsh)$/i.test(interp)) return fromStdin();
@@ -3411,7 +3466,7 @@ function extractShellDestTokens(command) {
3411
3466
  }
3412
3467
  function analyzeFsOperation(command) {
3413
3468
  const normalized = normalizeCommandForPolicy(command);
3414
- if (!FS_OP_PRESCREEN_RE.test(normalized) && !FS_OP_PRESCREEN_RE.test(command) && !SOURCES_FILE_RE.test(command))
3469
+ if (!FS_OP_PRESCREEN_RE.test(normalized) && !FS_OP_PRESCREEN_RE.test(command) && !SOURCES_FILE_RE.test(command) && !runsInlineInterpreter(command, normalized))
3415
3470
  return null;
3416
3471
  if (fsOpCache.has(command)) {
3417
3472
  const hit = fsOpCache.get(command) ?? null;
@@ -3419,7 +3474,8 @@ function analyzeFsOperation(command) {
3419
3474
  fsOpCache.set(command, hit);
3420
3475
  return hit;
3421
3476
  }
3422
- const computed = analyzeFsOperationImpl(command) ?? (INDIRECT_SHAPE_RE.test(command) ? structuralJailRead(command) : null);
3477
+ const direct = analyzeFsOperationImpl(command) ?? (INDIRECT_SHAPE_RE.test(command) ? structuralJailRead(command) : null);
3478
+ const computed = direct?.verdict === "block" || !runsInlineInterpreter(command, normalized) ? direct : stricter(direct, inlineCodeJailRead(command));
3423
3479
  if (fsOpCache.size >= FS_OP_CACHE_MAX) {
3424
3480
  const oldest = fsOpCache.keys().next().value;
3425
3481
  if (oldest !== void 0) fsOpCache.delete(oldest);
@@ -3428,8 +3484,8 @@ function analyzeFsOperation(command) {
3428
3484
  return computed;
3429
3485
  }
3430
3486
  function isSensitiveCleanupName(p) {
3431
- const base2 = p.replace(/^.*[\\/]/, "");
3432
- return /^\.env(\.|$)/i.test(base2) || /(?:^|[\\/])\.(?:ssh|aws|gnupg|git)(?:[\\/]|$)/i.test(p) || /\.(?:pem|key|p12|pfx|crt)$/i.test(base2) || /^\.?(?:netrc|npmrc|pgpass|htpasswd)$/i.test(base2) || /^id_(?:rsa|dsa|ecdsa|ed25519)/i.test(base2) || /credential/i.test(p) || /secret/i.test(base2);
3487
+ const base3 = p.replace(/^.*[\\/]/, "");
3488
+ return /^\.env(\.|$)/i.test(base3) || /(?:^|[\\/])\.(?:ssh|aws|gnupg|git)(?:[\\/]|$)/i.test(p) || /\.(?:pem|key|p12|pfx|crt)$/i.test(base3) || /^\.?(?:netrc|npmrc|pgpass|htpasswd)$/i.test(base3) || /^id_(?:rsa|dsa|ecdsa|ed25519)/i.test(base3) || /credential/i.test(p) || /secret/i.test(base3);
3433
3489
  }
3434
3490
  function isWaivableCleanupTarget(p) {
3435
3491
  if (/^[/~]/.test(p) || /^\$/.test(p)) return false;
@@ -4158,6 +4214,24 @@ function structuralJailRead(command) {
4158
4214
  }
4159
4215
  return review;
4160
4216
  }
4217
+ function inlineCodeJailRead(command) {
4218
+ const calls = inlineExecCalls(command);
4219
+ if (calls === PARSE_FAIL) return null;
4220
+ let review = null;
4221
+ for (const call of calls) {
4222
+ const language = inlineLanguageOf(call.interp);
4223
+ if (language === null) continue;
4224
+ for (const code of call.code) {
4225
+ if (code === null) continue;
4226
+ for (const p of inlineReadPaths(language, code, call.preload)) {
4227
+ const hit = matchSensitivePath(p);
4228
+ if (hit?.verdict === "block") return hit;
4229
+ review ??= hit;
4230
+ }
4231
+ }
4232
+ }
4233
+ return review;
4234
+ }
4161
4235
  function baseWord(w) {
4162
4236
  return (w ?? "").split("/").pop()?.toLowerCase() ?? "";
4163
4237
  }
@@ -4715,6 +4789,21 @@ function hits(access, paths, directories, context) {
4715
4789
  }
4716
4790
  return false;
4717
4791
  }
4792
+ function hitsNode9Dir(access, dirs, context) {
4793
+ const home = context.homeDirs?.[0] ?? "~";
4794
+ if (access.glob) {
4795
+ const pattern = normalizeFilePath(access.glob, access.cwd ?? context.cwd, home);
4796
+ const magic = pattern.search(/[*?[{]/);
4797
+ const literal = magic < 0 ? pattern : pattern.slice(0, pattern.lastIndexOf("/", magic));
4798
+ return dirs.some((d) => within(literal, d));
4799
+ }
4800
+ const candidates = access.candidates ?? [
4801
+ normalizeFilePath(access.path, access.cwd ?? context.cwd, home)
4802
+ ];
4803
+ return candidates.some(
4804
+ (p) => dirs.some((d) => within(p, d)) || access.recursive === true && dirs.some((d) => within(d, p))
4805
+ );
4806
+ }
4718
4807
  function hitsSystemDir(access, context, dirs = SYSTEM_DIRS) {
4719
4808
  const home = context.homeDirs?.[0] ?? "~";
4720
4809
  const candidates = access.candidates ?? [
@@ -4755,6 +4844,7 @@ function evaluateProtectedFileAccess(accesses, context, settings) {
4755
4844
  const consider = (result) => {
4756
4845
  if (!best || RANK[result.decision] > RANK[best.decision]) best = result;
4757
4846
  };
4847
+ const node9Dirs = homes.map((home) => `${home.replace(/\/$/, "")}/.node9`);
4758
4848
  for (const access of accesses) {
4759
4849
  const mutation = access.operation !== "read";
4760
4850
  let kind;
@@ -4783,6 +4873,10 @@ function evaluateProtectedFileAccess(accesses, context, settings) {
4783
4873
  "files.system-dirs",
4784
4874
  "review-root-home-read",
4785
4875
  "Reading the root account's home requires approval"
4876
+ ] : mutation && hitsNode9Dir(access, node9Dirs, context) ? [
4877
+ "commands.node9-disarm",
4878
+ "review-node9-disarm",
4879
+ "Changing node9's own files requires approval"
4786
4880
  ] : null;
4787
4881
  } catch {
4788
4882
  consider(incompleteFileVerdict(access.operation));
@@ -5388,9 +5482,9 @@ function validateBelgianNationalNumber(raw) {
5388
5482
  if (!/^\d{11}$/.test(d)) return false;
5389
5483
  const month = Number(d.slice(2, 4)) % 20;
5390
5484
  if (month !== 0 && !plausibleDate(String(month).padStart(2, "0"), d.slice(4, 6))) return false;
5391
- const base2 = Number(d.slice(0, 9));
5485
+ const base3 = Number(d.slice(0, 9));
5392
5486
  const check = Number(d.slice(9, 11));
5393
- return 97 - base2 % 97 === check || 97 - (2e9 + base2) % 97 === check;
5487
+ return 97 - base3 % 97 === check || 97 - (2e9 + base3) % 97 === check;
5394
5488
  }
5395
5489
  function validateCuit(raw) {
5396
5490
  const d = raw.replace(/-/g, "");
@@ -6492,9 +6586,9 @@ function evaluateGitHooksFileAccess(args, accesses, context, settings, shell) {
6492
6586
  }
6493
6587
  function agentFileKind(p) {
6494
6588
  const lower2 = p.toLowerCase();
6495
- const base2 = lower2.slice(lower2.lastIndexOf("/") + 1);
6496
- if (INSTRUCTION_NAMES.has(base2)) return "instructions";
6497
- if (base2 === "copilot-instructions.md" && lower2.endsWith("/.github/" + base2))
6589
+ const base3 = lower2.slice(lower2.lastIndexOf("/") + 1);
6590
+ if (INSTRUCTION_NAMES.has(base3)) return "instructions";
6591
+ if (base3 === "copilot-instructions.md" && lower2.endsWith("/.github/" + base3))
6498
6592
  return "instructions";
6499
6593
  if (/\/\.cursor\/rules(?:\/|$)/.test(lower2)) return "instructions";
6500
6594
  if (/\/\.claude\/projects\/[^/]+\/memory(?:\/|$)/.test(lower2)) return "memory";
@@ -6663,7 +6757,7 @@ function evaluateAgentFileWrite(args, accesses, context, settings, shell, hooks
6663
6757
  skills: "agent skill",
6664
6758
  settings: "agent settings"
6665
6759
  };
6666
- const base2 = `${kindLabel[kind]} written: ${access.path}`;
6760
+ const base3 = `${kindLabel[kind]} written: ${access.path}`;
6667
6761
  if (!signal && !every) {
6668
6762
  return {
6669
6763
  decision: "allow",
@@ -6673,14 +6767,14 @@ function evaluateAgentFileWrite(args, accesses, context, settings, shell, hooks
6673
6767
  path: access.path,
6674
6768
  operation: access.operation,
6675
6769
  kind,
6676
- reason: base2,
6770
+ reason: base3,
6677
6771
  ruleDescription: "The AI writes to a file every later agent session loads as instructions.",
6678
- blockedByLabel: `Node9 Files: ${base2}`,
6772
+ blockedByLabel: `Node9 Files: ${base3}`,
6679
6773
  tier: 2
6680
6774
  };
6681
6775
  }
6682
6776
  const ruleName = signal === "content" ? "review-agent-file-content" : signal === "hooks" ? "review-agent-hooks-change" : "review-agent-file-write";
6683
- const reason = signal ? `${base2} (${detail})` : base2;
6777
+ const reason = signal ? `${base3} (${detail})` : base3;
6684
6778
  const description = signal === "content" ? "The AI writes an instruction into a file every later agent session loads: to hide something from you, to skip approvals or the guard, or to read or send a secret. It would persist past this session; confirm it is yours." : signal === "hooks" ? "The AI changes the hooks in the agent's settings. Hooks are how node9 and your other checks run before each tool call; a change here can disarm them." : "The AI writes to a file every later agent session loads as instructions (CLAUDE.md, AGENTS.md, memory, agents, commands). Confirm the change is yours.";
6685
6779
  return {
6686
6780
  decision: value === "log" ? "allow" : value,
@@ -8288,6 +8382,139 @@ function scanArgumentSecrets(args) {
8288
8382
  severity: "review"
8289
8383
  } : null;
8290
8384
  }
8385
+ function patternSelects(pattern, targets) {
8386
+ const pieces = pattern.toLowerCase().split(/[\\^$.*+?()[\]{}|]+/).filter(Boolean);
8387
+ if (pieces.length === 0) return false;
8388
+ return targets.some((t) => pieces.every((p) => t.includes(p)));
8389
+ }
8390
+ function sourceText(bytes, pos, end) {
8391
+ if (typeof pos !== "number" || typeof end !== "number" || pos < 0 || end < pos) return "";
8392
+ return new TextDecoder().decode(bytes.subarray(pos, end));
8393
+ }
8394
+ function nextOperand(words, from) {
8395
+ for (let i = from; i < words.length; i++) {
8396
+ const w = words[i];
8397
+ if (w !== null && w.startsWith("-")) continue;
8398
+ return i;
8399
+ }
8400
+ return -1;
8401
+ }
8402
+ function node9Command(words, h) {
8403
+ const head = base2(words[h]);
8404
+ if (head !== "node9" && head !== "node9.cmd" && words[h] !== "@node9/proxy") return null;
8405
+ const s = nextOperand(words, h + 1);
8406
+ const sub = s < 0 ? "" : (words[s] ?? "").toLowerCase();
8407
+ if (sub === "uninstall") return "node9 uninstall";
8408
+ if (sub !== "daemon") return null;
8409
+ const a = nextOperand(words, s + 1);
8410
+ const action = a < 0 ? "" : (words[a] ?? "").toLowerCase();
8411
+ return action === "stop" || action === "uninstall" ? `node9 daemon ${action}` : null;
8412
+ }
8413
+ function killsDaemon(words, texts, h, daemonPid) {
8414
+ const head = base2(words[h]);
8415
+ const args = words.slice(h + 1);
8416
+ if (head === "kill") {
8417
+ for (let i = 0; i < args.length; i++) {
8418
+ const w = args[i];
8419
+ if (w === "-s" || w === "-n") {
8420
+ i++;
8421
+ continue;
8422
+ }
8423
+ if (w !== null && w.startsWith("-")) continue;
8424
+ if (w === null) {
8425
+ if (HOME_PID_FILE_RE.test(texts[h + 1 + i] ?? "")) return "kill of the pid in daemon.pid";
8426
+ continue;
8427
+ }
8428
+ if (daemonPid !== null && w === String(daemonPid)) return `kill ${w} (the node9 daemon)`;
8429
+ }
8430
+ return null;
8431
+ }
8432
+ if (head === "pkill") {
8433
+ let full = false;
8434
+ let pattern = null;
8435
+ for (let i = 0; i < args.length; i++) {
8436
+ const w = args[i];
8437
+ if (w === null) continue;
8438
+ if (PKILL_VALUE_FLAGS.has(w)) {
8439
+ i++;
8440
+ continue;
8441
+ }
8442
+ if (w === "--full") full = true;
8443
+ else if (/^-[a-zA-Z]+$/.test(w) && w.includes("f")) full = true;
8444
+ if (w.startsWith("-")) continue;
8445
+ pattern = w;
8446
+ }
8447
+ if (pattern === null) return null;
8448
+ return patternSelects(pattern, full ? DAEMON_CMDLINES : DAEMON_NAMES) ? `pkill ${full ? "-f " : ""}${pattern}` : null;
8449
+ }
8450
+ if (head === "killall") {
8451
+ const regex = args.some((w) => w === "-r" || w === "--regexp");
8452
+ for (const w of args) {
8453
+ if (w === null || w.startsWith("-")) continue;
8454
+ if (regex ? patternSelects(w, DAEMON_NAMES) : KILLALL_NAMES.has(w.toLowerCase()))
8455
+ return `killall ${w}`;
8456
+ }
8457
+ return null;
8458
+ }
8459
+ if (head === "taskkill" || head === "taskkill.exe") {
8460
+ for (let i = 0; i < args.length - 1; i++) {
8461
+ const flag = (args[i] ?? "").toLowerCase();
8462
+ const value = (args[i + 1] ?? "").toLowerCase();
8463
+ if ((flag === "/im" || flag === "-im") && KILLALL_NAMES.has(value))
8464
+ return `taskkill /IM ${value}`;
8465
+ if ((flag === "/pid" || flag === "-pid") && daemonPid !== null && value === String(daemonPid))
8466
+ return `taskkill /PID ${value} (the node9 daemon)`;
8467
+ }
8468
+ }
8469
+ return null;
8470
+ }
8471
+ function stopsService(words, h) {
8472
+ const head = base2(words[h]);
8473
+ const args = words.slice(h + 1).map((w) => (w ?? "").toLowerCase());
8474
+ if (head === "systemctl") {
8475
+ const action = args.find((w) => STOP_SYSTEMCTL.has(w));
8476
+ if (action && args.some((w) => /^node9-daemon(\.service)?$/.test(w)))
8477
+ return `systemctl ${action} node9-daemon`;
8478
+ }
8479
+ if (head === "launchctl") {
8480
+ const action = args.find((w) => STOP_LAUNCHCTL.has(w));
8481
+ if (action && args.some((w) => w.includes("ai.node9.daemon")))
8482
+ return `launchctl ${action} ai.node9.daemon`;
8483
+ }
8484
+ return null;
8485
+ }
8486
+ function analyzeNode9Disarm(command, daemonPid = null, depth = 0) {
8487
+ if (depth > 3 || !NAME_PRESCREEN_RE.test(command.replace(/[\\'"]/g, ""))) return null;
8488
+ let shown = null;
8489
+ const bytes = new TextEncoder().encode(command);
8490
+ try {
8491
+ walkCommandScope(command, (node) => {
8492
+ if (shown !== null) return false;
8493
+ const n = node;
8494
+ if (!n || n.kind !== "CallExpr") return true;
8495
+ const words = (n.Args || []).map((a) => resolveWordLiteral(a));
8496
+ if (words.length === 0) return true;
8497
+ const texts = (n.Args || []).map((a) => sourceText(bytes, a.pos, a.end));
8498
+ const h = unwrapCommandHead(words);
8499
+ shown = node9Command(words, h) ?? killsDaemon(words, texts, h, daemonPid) ?? stopsService(words, h);
8500
+ if (shown === null) {
8501
+ const payload = literalShellPayload(words, base2(words[0]));
8502
+ if (payload) {
8503
+ const inner = analyzeNode9Disarm(payload, daemonPid, depth + 1);
8504
+ if (inner) shown = inner.reason.replace(/^node9 switched off by the agent: /, "");
8505
+ }
8506
+ }
8507
+ return true;
8508
+ });
8509
+ } catch {
8510
+ }
8511
+ if (shown === null) return null;
8512
+ return {
8513
+ ruleName: RULE_NAME2,
8514
+ reason: `node9 switched off by the agent: ${shown}`,
8515
+ description: DESCRIPTION
8516
+ };
8517
+ }
8291
8518
  function catalogSettings(config) {
8292
8519
  return catalogSettingsFromConfig(config.settings, config.policy);
8293
8520
  }
@@ -8381,6 +8608,10 @@ function egressPolicyVerdict(eg) {
8381
8608
  };
8382
8609
  }
8383
8610
  async function evaluatePolicy(config, toolName, args, context = {}, hostHooks = {}) {
8611
+ const v = await evaluatePolicyVerdict(config, toolName, args, context, hostHooks);
8612
+ return v.decision === "block" && v.checkId !== void 0 && NOT_OVERRIDABLE_CHECKS.has(v.checkId) ? { ...v, overridable: false } : v;
8613
+ }
8614
+ async function evaluatePolicyVerdict(config, toolName, args, context = {}, hostHooks = {}) {
8384
8615
  let protectedVerdict = null;
8385
8616
  const layers = inspectionLayers(config.policy);
8386
8617
  if (context.agent !== "Terminal") {
@@ -8438,7 +8669,7 @@ async function evaluatePolicy(config, toolName, args, context = {}, hostHooks =
8438
8669
  }
8439
8670
  async function evaluatePolicyCore(config, toolName, args, context = {}, hooks = {}) {
8440
8671
  const { agent, cwd, activeEnvironment } = context;
8441
- const { checkProvenance: checkProvenance2, isTrustedHost: isTrustedHost2, isConfiguredRegistry, isGitWorkTreeTop: isGitWorkTreeTop2 } = hooks;
8672
+ const { checkProvenance: checkProvenance2, isTrustedHost: isTrustedHost2, isConfiguredRegistry, isGitWorkTreeTop: isGitWorkTreeTop2, daemonPid } = hooks;
8442
8673
  const settingsView = catalogSettings(config);
8443
8674
  const layers = inspectionLayers(config.policy);
8444
8675
  const cv = (id) => checkValue(settingsView, id);
@@ -8623,6 +8854,23 @@ async function evaluatePolicyCore(config, toolName, args, context = {}, hooks =
8623
8854
  else if (v && !pendingAstReview) pendingAstReview = v;
8624
8855
  }
8625
8856
  }
8857
+ if (cv("commands.node9-disarm") !== "off") {
8858
+ const finding = analyzeNode9Disarm(bashCommand, daemonPid?.() ?? null);
8859
+ if (finding) {
8860
+ const v = applyCheckValue(cv("commands.node9-disarm"), {
8861
+ decision: "review",
8862
+ blockedByLabel: `Node9 (AST): ${finding.ruleName}`,
8863
+ reason: finding.reason,
8864
+ tier: 2,
8865
+ ruleName: finding.ruleName,
8866
+ ruleDescription: finding.description,
8867
+ checkId: "commands.node9-disarm"
8868
+ });
8869
+ if (v?.logged) noteLogged(v);
8870
+ else if (v?.decision === "block") return v;
8871
+ else if (v && !pendingAstReview) pendingAstReview = v;
8872
+ }
8873
+ }
8626
8874
  }
8627
8875
  if (config.policy.smartRules.length > 0) {
8628
8876
  const rmCleanupWaiver = bashCommand !== null && isRmCreatedInCommandCleanup(bashCommand);
@@ -9070,8 +9318,8 @@ function hasRoutingNumber(text) {
9070
9318
  }
9071
9319
  return false;
9072
9320
  }
9073
- function* validCardRuns(text, base2, exemptFractions = true) {
9074
- const re = new RegExp(base2.source, "g");
9321
+ function* validCardRuns(text, base3, exemptFractions = true) {
9322
+ const re = new RegExp(base3.source, "g");
9075
9323
  let m;
9076
9324
  while ((m = re.exec(text)) !== null) {
9077
9325
  const digits = m[0].replace(/\D/g, "");
@@ -9084,17 +9332,17 @@ function* validCardRuns(text, base2, exemptFractions = true) {
9084
9332
  }
9085
9333
  }
9086
9334
  function hasValidCard(text) {
9087
- for (const base2 of CARD_PATTERNS) {
9088
- if (!validCardRuns(text, base2).next().done) return true;
9335
+ for (const base3 of CARD_PATTERNS) {
9336
+ if (!validCardRuns(text, base3).next().done) return true;
9089
9337
  }
9090
9338
  return false;
9091
9339
  }
9092
9340
  function replaceValidCards(text, replace, { exemptFractions = true } = {}) {
9093
9341
  let out = text;
9094
- for (const base2 of CARD_PATTERNS) {
9342
+ for (const base3 of CARD_PATTERNS) {
9095
9343
  let res = "";
9096
9344
  let last = 0;
9097
- for (const run of validCardRuns(out, base2, exemptFractions)) {
9345
+ for (const run of validCardRuns(out, base3, exemptFractions)) {
9098
9346
  res += out.slice(last, run.index) + replace(run.digits);
9099
9347
  last = run.index + run.length;
9100
9348
  }
@@ -9335,9 +9583,9 @@ function maskLeaf(value, key, depth) {
9335
9583
  const used = /* @__PURE__ */ new Set();
9336
9584
  return Object.fromEntries(
9337
9585
  Object.entries(value).map(([k, v]) => {
9338
- const base2 = maskPersonalData(k);
9339
- let name = base2;
9340
- for (let n = 2; used.has(name); n++) name = `${base2}#${n}`;
9586
+ const base3 = maskPersonalData(k);
9587
+ let name = base3;
9588
+ for (let n = 2; used.has(name); n++) name = `${base3}#${n}`;
9341
9589
  used.add(name);
9342
9590
  return [name, maskLeaf(v, k, depth + 1)];
9343
9591
  })
@@ -9831,7 +10079,7 @@ function matchCanaryArgs(args, values) {
9831
10079
  return null;
9832
10080
  }
9833
10081
  }
9834
- var getFilename, __filename2, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, finance_default, identity_default, publishing_default, settings_default, branches_default, ACTION_TEMPLATES, ACTION_CHECKS, GAP, prog, httpTo, httpWrite, HTTP_WRITE, HTTP_WRITE_ANY, NOT_STRIPE_TEST, postTo, STRIPE_OBJECTS, APP_SHIELD_SPECS, APP_SHIELDS, APP_SHIELD_NAMES, APP_SERVER_GLOB, GENERAL_ACTION_RULES, BUILTIN_SHIELDS, SSRF_MAX_HOST, METADATA_ADDRESSES, STRICT_TIERS, METADATA_HOSTNAMES, v4Octets, TIER_REASON, CHECK_TEXT, LEVELS, MODE_VALUES, SOFT, STRICT, BLOCK_ALL, LEVEL_CHECKS, ASI, ASI_IDS, PACK_SHIELD_OWASP, PACK_RULE_OWASP, VERDICTS, CHECK_VERDICT_RANK, 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, KINDS, STRUCTS, unknownKinds, INVISIBLE_RE, INVISIBLE_ONE, HOMOGLYPHS, HOMOGLYPH_RE, MAX_BLOBS, MIN_DECODED, BASE64_RUN_RE, HEX_RUN_RE, HEX_ESCAPE_RUN_RE, 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, MAX_ALTERNATIVES, MAX_CALL_DEPTH, MAX_CALL_WALKS, MAX_XARGS_PAYLOAD, 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, ops, MAX_INLINE_CODE, WHAT, OUTSIDE, pyImport, either, PYTHON, NODE, PERL, RUBY, PHP, TABLES, STRING_LITERAL, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, codeBodiesCache, AST_CACHE_MAX, astCache, PARSE_FAIL, INTERPRETER_LINE, PROGRAM_DIR, ACCESS_VIEW_CACHE_MAX, accessViewCache, MAX_READING_PAYLOADS, STDIN_CODE_INTERPRETERS, RUNS_TEXT_ELSEWHERE, defaultExpansionOps, EMPTY_NAMES, VARIABLE_READERS, PLAIN_PROGRAM_NAME, FS_READ_TOOLS, GREP_SHAPE, PATTERN_VERBS, PATTERN_VERB_NAMES, GREP_FILE_OPERANDS, READER_VALUE_LETTERS, FILE_OPERAND_FLAGS, SCP_VALUE_FLAGS, CURL_SOURCE_FLAGS, CURL_VALUE_LETTERS, WGET_VALUE_LETTERS, 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, MAX_PAYLOAD_NESTING, payloadNesting, MAX_INLINE_CALLS, INLINE_EXEC_FALLBACK, UNREADABLE_SOURCE, SCRIPT_RUNNERS, SCRIPT_RUNNER_VALUE_FLAGS, MAX_SCRIPT_OPERANDS, NET_BINARIES, VALUE_FLAGS, HOME_VARIABLES, assignmentTable, currentStmtOffset, PAYLOAD_BUDGET, payloadBudget, seenPayloads, UNANALYSABLE_NESTING, ASSIGNMENT_HEADS, andOrOps, ARRAY_MARK, ARRAY_SEP, MAX_CALL_DEPTH2, MAX_CALL_WALKS2, callDepth, callWalks, FS_OP_CACHE_MAX, fsOpCache, stripDotSlash, REDIR_TRUNCATE_OPS, REDIR_FILE_IN_OPS, REDIR_HEREDOC_OPS, FIND_OPTIONS, COPY_RULE_OF, INDIRECT_SHAPE_RE, SOURCES_FILE_RE, SHELL_SET_VARS, pipeOps, callWords, JAILED_GLOB_NAMES, JAILED_DIR_NAMES, shellGlob, GLOB_META, isReaderWord, positionalAfter, NONE, UNKNOWN, FORBIDDEN_PATH_SEGMENTS, BASH_TOOL_NAMES, MCP_EXECUTORS, BUILTIN_FIELDS, ARGV_FIELDS, BARE_WORD, MAX_ARGV_CHARACTERS, exempt, RANK, SEQUENCE_OPS, BIP39_ENGLISH, BIP39_PUBLIC_VECTORS, indices, lengths, hash, MAX_MNEMONIC_HASHES, SECRET_VIEW_LIMITS, IBAN_LENGTH, IBAN_BBAN_SHAPE, B58, B58_INDEX, XPRV_VERSIONS, B62, CRC32_TABLE, GITHUB_TOKEN_RE, B64URL, B64URL_INDEX, CASK_TWO_ZERO_SUFFIX, CASK_FOUR_ZERO_SUFFIX, CF_ODD, CF_EVEN, SPANISH_LETTERS, HETU_CHARS, VERHOEFF_D, VERHOEFF_P, MAX, UNTRUSTED_TOOLS, INJECTION_SIGNALS, MENTION_QUOTE_RE, MENTION_WORD_RE, 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, CIPHER_KEY_NAME_RE, CIPHER_NAME_PARTS, CIPHER_KEY_VALUE_RE, LARAVEL_KEY_VALUE_RE, SQL_PASSWORD_VALUE_RE, GRAFANA_KEY_VALUE_RE, BASIC_VALUE_RE, BASIC_PAIR_RE, BASIC_HEADER_RE, BASIC_HEADER_PAIR_RE, BASIC_PLACEHOLDER_RE, BASIC_TEMPLATE_MARKERS, DROPBOX_SECRET_RE, OTP_CAPTURE_RE, DB_URL_PASSWORD_RE, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, SENSITIVE_PATH_REGEXES, MAX_DEPTH2, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, BATCH_SEPARATOR, IMPERATIVE, SECRET_TARGET, GUARD, SIGNALS, DESTINATION_RE, REUSED_SIGNALS, NEVER_NEGATED, NEGATION_BEFORE, RDR_IN, COPY_VERBS2, LITERAL_VERBS, GIT_PRESCREEN_RE, MAX_PAYLOAD_DEPTH2, MAX_ALIAS_DEPTH, CACHE_MAX, cache, PAYLOAD_SHELLS, CONFIG_SCOPE_FLAGS, CONFIG_VALUE_FLAGS, GLOBAL_VALUE_OPTIONS, GLOBAL_FLAG_OPTIONS, MAX_LOOP_ITEMS, GitWalk, POSITIONAL2, NO_VARS, NO_APPENDS, FORCE_PUSH, DESTRUCTIVE, UNREADABLE, PUSH_LONG_VALUE, BRANCH_LONG_VALUE, TAG_LONG_VALUE, CLEAN_LONG_VALUE, RESTORE_LONG_VALUE, CHECKOUT_LONG_VALUE, HOOKS_BYPASS, NO_VERIFY_SUBS, HOOK_KILL_SWITCHES, COMMIT_LONG_VALUE, CHMOD_OPTION_RE, _appendOp, STAGE_ALL, ADD_LONG_VALUE, CHECK_ID, RULE_NAME, INERT_LINE_RE, CHECK_ID2, INSTRUCTION_NAMES, SETTINGS_RE, HOOK_WORDS_RE, SHELLS, NETCATS, NET_CLIENTS, SENDERS, NETCAT_EXEC_LONG, DECL_DUMPS, PROC_ENVIRON_RE, DEV_NET_RE, SOCAT_PROGRAM_RE, SOCAT_NET_RE, PRESCREEN_RE, MAX_PAYLOAD_DEPTH3, CACHE_MAX2, cache2, dupInOp, REVERSE_SHELL_DESCRIPTION, ENV_EXFIL_DESCRIPTION, DYNAMIC_WORD, MULTICALL, SHELL_VALUE_OPTIONS, INTERPRETERS, OutboundWalk, 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, SHELLS2, MAX_NESTING, SHELL_VALUE_FLAGS, NPM_FAMILY, PIP_FAMILY, NPM_FETCH_VERBS, PIP_FETCH_VERBS, NPM_FLAGS, PIP_FLAGS, NPM_VARS, PIP_VARS, PRESCREEN_RE2, PIP_ATTACHED_RE, MAX_PAYLOAD_DEPTH4, PUBLIC_REGISTRY_HOSTS, 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, MCP_DESTINATION_KEYS, 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_CC14_RE, PII_CC13_RE, PII_CC19_RE, CARD_PATTERNS, PII_ROUTING_RE, ROUTING_LABEL_RE, ROUTING_LABEL_WINDOW, NATIONAL_ID_SHAPES, NATIONAL_ID_LABEL_RE, NATIONAL_ID_LABEL_CS_RE, idLabelIn, NATIONAL_ID_LABEL_WINDOW, NATIONAL_ID_GUARD_RE, PII_IBAN_RE, REALTIME_PII_PATTERNS, MAX_PII_SCAN_BYTES, IBAN_DESTINATION_KEYS, WHOLE_IBAN_RE, JSON_ESCAPE_RE, JSON_ESCAPES, MASKED_KINDS, ENTRY_RE, IBAN_TEXT_RE, EMAIL_TEXT_RE, ACCOUNT_KEY_RE, ACCOUNT_VALUE_RE, MAX_MASK_DEPTH, 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, SHELLS3, OWN_HEADS, MAX_DEPTH3, CWD_CHANGERS, TERMINAL_ESCAPE_RE, LOOP_MAX_RECORDS, 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;
10082
+ var getFilename, __filename2, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, finance_default, identity_default, publishing_default, settings_default, branches_default, ACTION_TEMPLATES, ACTION_CHECKS, GAP, prog, httpTo, httpWrite, HTTP_WRITE, HTTP_WRITE_ANY, NOT_STRIPE_TEST, postTo, STRIPE_OBJECTS, APP_SHIELD_SPECS, APP_SHIELDS, APP_SHIELD_NAMES, APP_SERVER_GLOB, GENERAL_ACTION_RULES, BUILTIN_SHIELDS, SSRF_MAX_HOST, METADATA_ADDRESSES, STRICT_TIERS, METADATA_HOSTNAMES, v4Octets, TIER_REASON, CHECK_TEXT, LEVELS, MODE_VALUES, SOFT, STRICT, BLOCK_ALL, LEVEL_CHECKS, ASI, ASI_IDS, PACK_SHIELD_OWASP, PACK_RULE_OWASP, VERDICTS, CHECK_VERDICT_RANK, 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, KINDS, STRUCTS, unknownKinds, INVISIBLE_RE, INVISIBLE_ONE, HOMOGLYPHS, HOMOGLYPH_RE, MAX_BLOBS, MIN_DECODED, BASE64_RUN_RE, HEX_RUN_RE, HEX_ESCAPE_RUN_RE, 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, MAX_ALTERNATIVES, MAX_CALL_DEPTH, MAX_CALL_WALKS, MAX_XARGS_PAYLOAD, 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, ops, MAX_INLINE_CODE, WHAT, OUTSIDE, pyImport, either, PYTHON, NODE, PERL, RUBY, PHP, TABLES, STRING_LITERAL, READ_CALL, ARG, WRAP, STR_PREFIX, WRITE_MODE_LITERAL, WRITE_MODE, WRITE_MODE_AFTER, PERL_WRITE_BEFORE, readCallBefore, READ_BEFORE, READ_AFTER, ASSIGNED, openedName, OPENED_NAME, DOTENV, DOTENV_PATH_OPTION, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, codeBodiesCache, AST_CACHE_MAX, astCache, PARSE_FAIL, INTERPRETER_LINE, PROGRAM_DIR, ACCESS_VIEW_CACHE_MAX, accessViewCache, MAX_READING_PAYLOADS, STDIN_CODE_INTERPRETERS, RUNS_TEXT_ELSEWHERE, defaultExpansionOps, EMPTY_NAMES, VARIABLE_READERS, PLAIN_PROGRAM_NAME, FS_READ_TOOLS, GREP_SHAPE, PATTERN_VERBS, PATTERN_VERB_NAMES, GREP_FILE_OPERANDS, READER_VALUE_LETTERS, FILE_OPERAND_FLAGS, SCP_VALUE_FLAGS, CURL_SOURCE_FLAGS, CURL_VALUE_LETTERS, WGET_VALUE_LETTERS, 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, NODE_VALUE_FLAG, MAX_PAYLOAD_NESTING, payloadNesting, MAX_INLINE_CALLS, INLINE_EXEC_FALLBACK, UNREADABLE_SOURCE, SCRIPT_RUNNERS, SCRIPT_RUNNER_VALUE_FLAGS, MAX_SCRIPT_OPERANDS, NET_BINARIES, VALUE_FLAGS, HOME_VARIABLES, assignmentTable, currentStmtOffset, PAYLOAD_BUDGET, payloadBudget, seenPayloads, UNANALYSABLE_NESTING, ASSIGNMENT_HEADS, andOrOps, ARRAY_MARK, ARRAY_SEP, MAX_CALL_DEPTH2, MAX_CALL_WALKS2, callDepth, callWalks, FS_OP_CACHE_MAX, fsOpCache, stripDotSlash, REDIR_TRUNCATE_OPS, REDIR_FILE_IN_OPS, REDIR_HEREDOC_OPS, FIND_OPTIONS, COPY_RULE_OF, INDIRECT_SHAPE_RE, SOURCES_FILE_RE, SHELL_SET_VARS, pipeOps, callWords, JAILED_GLOB_NAMES, JAILED_DIR_NAMES, shellGlob, GLOB_META, INLINE_INTERP_PRESCREEN_RE, runsInlineInterpreter, isReaderWord, positionalAfter, NONE, UNKNOWN, FORBIDDEN_PATH_SEGMENTS, BASH_TOOL_NAMES, MCP_EXECUTORS, BUILTIN_FIELDS, ARGV_FIELDS, BARE_WORD, MAX_ARGV_CHARACTERS, exempt, RANK, SEQUENCE_OPS, BIP39_ENGLISH, BIP39_PUBLIC_VECTORS, indices, lengths, hash, MAX_MNEMONIC_HASHES, SECRET_VIEW_LIMITS, IBAN_LENGTH, IBAN_BBAN_SHAPE, B58, B58_INDEX, XPRV_VERSIONS, B62, CRC32_TABLE, GITHUB_TOKEN_RE, B64URL, B64URL_INDEX, CASK_TWO_ZERO_SUFFIX, CASK_FOUR_ZERO_SUFFIX, CF_ODD, CF_EVEN, SPANISH_LETTERS, HETU_CHARS, VERHOEFF_D, VERHOEFF_P, MAX, UNTRUSTED_TOOLS, INJECTION_SIGNALS, MENTION_QUOTE_RE, MENTION_WORD_RE, 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, CIPHER_KEY_NAME_RE, CIPHER_NAME_PARTS, CIPHER_KEY_VALUE_RE, LARAVEL_KEY_VALUE_RE, SQL_PASSWORD_VALUE_RE, GRAFANA_KEY_VALUE_RE, BASIC_VALUE_RE, BASIC_PAIR_RE, BASIC_HEADER_RE, BASIC_HEADER_PAIR_RE, BASIC_PLACEHOLDER_RE, BASIC_TEMPLATE_MARKERS, DROPBOX_SECRET_RE, OTP_CAPTURE_RE, DB_URL_PASSWORD_RE, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, SENSITIVE_PATH_REGEXES, MAX_DEPTH2, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, BATCH_SEPARATOR, IMPERATIVE, SECRET_TARGET, GUARD, SIGNALS, DESTINATION_RE, REUSED_SIGNALS, NEVER_NEGATED, NEGATION_BEFORE, RDR_IN, COPY_VERBS2, LITERAL_VERBS, GIT_PRESCREEN_RE, MAX_PAYLOAD_DEPTH2, MAX_ALIAS_DEPTH, CACHE_MAX, cache, PAYLOAD_SHELLS, CONFIG_SCOPE_FLAGS, CONFIG_VALUE_FLAGS, GLOBAL_VALUE_OPTIONS, GLOBAL_FLAG_OPTIONS, MAX_LOOP_ITEMS, GitWalk, POSITIONAL2, NO_VARS, NO_APPENDS, FORCE_PUSH, DESTRUCTIVE, UNREADABLE, PUSH_LONG_VALUE, BRANCH_LONG_VALUE, TAG_LONG_VALUE, CLEAN_LONG_VALUE, RESTORE_LONG_VALUE, CHECKOUT_LONG_VALUE, HOOKS_BYPASS, NO_VERIFY_SUBS, HOOK_KILL_SWITCHES, COMMIT_LONG_VALUE, CHMOD_OPTION_RE, _appendOp, STAGE_ALL, ADD_LONG_VALUE, CHECK_ID, RULE_NAME, INERT_LINE_RE, CHECK_ID2, INSTRUCTION_NAMES, SETTINGS_RE, HOOK_WORDS_RE, SHELLS, NETCATS, NET_CLIENTS, SENDERS, NETCAT_EXEC_LONG, DECL_DUMPS, PROC_ENVIRON_RE, DEV_NET_RE, SOCAT_PROGRAM_RE, SOCAT_NET_RE, PRESCREEN_RE, MAX_PAYLOAD_DEPTH3, CACHE_MAX2, cache2, dupInOp, REVERSE_SHELL_DESCRIPTION, ENV_EXFIL_DESCRIPTION, DYNAMIC_WORD, MULTICALL, SHELL_VALUE_OPTIONS, INTERPRETERS, OutboundWalk, 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, SHELLS2, MAX_NESTING, SHELL_VALUE_FLAGS, NPM_FAMILY, PIP_FAMILY, NPM_FETCH_VERBS, PIP_FETCH_VERBS, NPM_FLAGS, PIP_FLAGS, NPM_VARS, PIP_VARS, PRESCREEN_RE2, PIP_ATTACHED_RE, MAX_PAYLOAD_DEPTH4, PUBLIC_REGISTRY_HOSTS, 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, MCP_DESTINATION_KEYS, ARGUMENT_SCAN_LIMITS, RUN_CHAR, runChar, RULE_NAME2, DESCRIPTION, DAEMON_CMDLINES, DAEMON_NAMES, KILLALL_NAMES, STOP_SYSTEMCTL, STOP_LAUNCHCTL, PKILL_VALUE_FLAGS, NAME_PRESCREEN_RE, base2, HOME_PID_FILE_RE, VERDICT_RANK, SQL_DML_KEYWORDS, NOT_OVERRIDABLE_CHECKS, 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_CC14_RE, PII_CC13_RE, PII_CC19_RE, CARD_PATTERNS, PII_ROUTING_RE, ROUTING_LABEL_RE, ROUTING_LABEL_WINDOW, NATIONAL_ID_SHAPES, NATIONAL_ID_LABEL_RE, NATIONAL_ID_LABEL_CS_RE, idLabelIn, NATIONAL_ID_LABEL_WINDOW, NATIONAL_ID_GUARD_RE, PII_IBAN_RE, REALTIME_PII_PATTERNS, MAX_PII_SCAN_BYTES, IBAN_DESTINATION_KEYS, WHOLE_IBAN_RE, JSON_ESCAPE_RE, JSON_ESCAPES, MASKED_KINDS, ENTRY_RE, IBAN_TEXT_RE, EMAIL_TEXT_RE, ACCOUNT_KEY_RE, ACCOUNT_VALUE_RE, MAX_MASK_DEPTH, 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, SHELLS3, OWN_HEADS, MAX_DEPTH3, CWD_CHANGERS, TERMINAL_ESCAPE_RE, LOOP_MAX_RECORDS, 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;
9835
10083
  var init_dist = __esm({
9836
10084
  "packages/policy-engine/dist/index.mjs"() {
9837
10085
  "use strict";
@@ -11830,7 +12078,7 @@ var init_dist = __esm({
11830
12078
  advice: "Fixed at Review for now."
11831
12079
  },
11832
12080
  "commands.node9-disarm": {
11833
- plain: "The agent runs a node9 command that would switch off a protection: clear a session flag, trust an address, pause the gate, weaken a shield.",
12081
+ plain: "The agent switches node9 off or weakens it: stops or kills the daemon, stops its service, uninstalls node9, changes files in ~/.node9, clears a session flag, trusts an address, pauses the gate, weakens a shield.",
11834
12082
  example: "After reading a web page, the agent runs node9 session-exposure clear --all",
11835
12083
  advice: "Keep Review: these are your commands to run. Block if the agent should never touch node9."
11836
12084
  },
@@ -12143,7 +12391,7 @@ var init_dist = __esm({
12143
12391
  group: "commands",
12144
12392
  owasp: "ASI10",
12145
12393
  title: "node9 disarmed from the shell",
12146
- catches: "a node9 command that clears a session flag, trusts a destination, pauses the gate or weakens a shield, run by the agent",
12394
+ catches: "the agent stopping or killing the node9 daemon, stopping its service, uninstalling node9, changing files in ~/.node9, or running a node9 command that clears a session flag, trusts a destination, pauses the gate or weakens a shield",
12147
12395
  values: ALL,
12148
12396
  defaultValue: "review",
12149
12397
  legacy: ["review-node9-disarm"]
@@ -13024,6 +13272,62 @@ var init_dist = __esm({
13024
13272
  php: PHP
13025
13273
  };
13026
13274
  STRING_LITERAL = /(['"`])((?:\\.|(?!\1)[^\\\n]){1,4096})\1/g;
13275
+ READ_CALL = {
13276
+ python: String.raw`(?:\b(?:io|codecs)\.)?\bopen|\b(?:dotenv_values|load_dotenv|get_key|read_file|loadtxt|read_csv)`,
13277
+ node: String.raw`\b(?:readFileSync|readFile|createReadStream|readTextFile(?:Sync)?|readFileStr|openSync|file)`,
13278
+ perl: String.raw`\b(?:open|sysopen|read_file|slurp)`,
13279
+ ruby: String.raw`\b(?:File|IO)\.(?:read|binread|readlines|foreach|open)|\bDotenv\.(?:load|parse|overload)`,
13280
+ php: String.raw`\b(?:file_get_contents|fopen|file|readfile|parse_ini_file|highlight_file|show_source)`
13281
+ };
13282
+ ARG = String.raw`(?:(?:(?:my|our|local)\s+)?[\w.$]+(?:\s*\(\s*\))?|'[^'\n]{0,8}'|"[^"\n]{0,8}")\s*,\s*`;
13283
+ WRAP = String.raw`(?:(?:new\s+)?[\w.$]+\s*\(\s*(?:${ARG}){0,3}){0,3}`;
13284
+ STR_PREFIX = String.raw`[rRbBuUfF]{0,2}`;
13285
+ WRITE_MODE_LITERAL = String.raw`['"](?=[rbtU+]*w|(?![^'"]*\+)[rbtU]*[ax])[rwaxbtU+]{1,4}['"]`;
13286
+ WRITE_MODE = String.raw`\s*,\s*(?:mode\s*=\s*)?${WRITE_MODE_LITERAL}`;
13287
+ WRITE_MODE_AFTER = new RegExp(`^(?:\\s*\\)){0,3}${WRITE_MODE}`);
13288
+ PERL_WRITE_BEFORE = /['"]\s*(?:\+?[>|]|>>)\s*['"]\s*,\s*$/;
13289
+ readCallBefore = (language) => new RegExp(
13290
+ String.raw`(?:${READ_CALL[language]})(?![\w$])\s*\(?\s*(?:${ARG}){0,3}${WRAP}${STR_PREFIX}$`
13291
+ );
13292
+ READ_BEFORE = {
13293
+ python: readCallBefore("python"),
13294
+ node: readCallBefore("node"),
13295
+ perl: readCallBefore("perl"),
13296
+ ruby: readCallBefore("ruby"),
13297
+ php: readCallBefore("php")
13298
+ };
13299
+ READ_AFTER = {
13300
+ python: new RegExp(
13301
+ String.raw`^\s*\)\s*\.\s*(?:read_text|read_bytes|open)\s*\((?!\s*(?:mode\s*=\s*)?${WRITE_MODE_LITERAL})`
13302
+ )
13303
+ };
13304
+ ASSIGNED = new RegExp(String.raw`([$\w]+)\s*=\s*${WRAP}${STR_PREFIX}$`);
13305
+ openedName = (language) => new RegExp(
13306
+ String.raw`(?:${READ_CALL[language]})(?![\w$])\s*\(?\s*(?:${ARG}){0,3}${WRAP}([$\w]+)(?!${WRITE_MODE})\s*[),]` + (language === "python" ? String.raw`|\bPath\s*\(\s*([$\w]+)\s*\)\s*\.\s*(?:read_text|read_bytes|open)\s*\(` : ""),
13307
+ "g"
13308
+ );
13309
+ OPENED_NAME = {
13310
+ python: openedName("python"),
13311
+ node: openedName("node"),
13312
+ perl: openedName("perl"),
13313
+ ruby: openedName("ruby"),
13314
+ php: openedName("php")
13315
+ };
13316
+ DOTENV = {
13317
+ node: {
13318
+ load: /\brequire\s*\(\s*['"`]dotenv(?:\/config)?['"`]\s*\)|\bimport\s*(?:[\w${}\s,*]+\s+from\s*)?['"`]dotenv(?:\/config)?['"`]|\bimport\s*\(\s*['"`]dotenv(?:\/config)?['"`]\s*\)/,
13319
+ values: /\.parsed\b|[(,]\s*(?:require\s*\(\s*['"`]dotenv['"`]\s*\)|dotenv)\s*\.\s*config\s*\(/,
13320
+ env: /\b(?:process|Deno|Bun)\s*\.\s*env\b/,
13321
+ literalPath: /\bconfig\s*\(\s*\{[^)]*\bpath\s*:\s*['"`]/
13322
+ },
13323
+ python: {
13324
+ load: /\b(?:load_dotenv|dotenv_values|get_key)\s*\(/,
13325
+ values: /\b(?:dotenv_values|get_key)\s*\(/,
13326
+ env: /\b(?:environ|getenv)\b/,
13327
+ literalPath: /\b(?:load_dotenv|dotenv_values)\s*\(\s*(?:dotenv_path\s*=\s*)?[rRbBuU]?['"]/
13328
+ }
13329
+ };
13330
+ DOTENV_PATH_OPTION = /\bconfig\s*\(\s*\{[^)]*\bpath\s*:\s*$/;
13027
13331
  sharedParser = lazy(() => getSyntax().NewParser());
13028
13332
  MESSAGE_FLAGS = /* @__PURE__ */ new Set([
13029
13333
  "-m",
@@ -13818,6 +14122,7 @@ var init_dist = __esm({
13818
14122
  "rmdir"
13819
14123
  ]);
13820
14124
  INTERP_LEADING_TARGET = /* @__PURE__ */ new Set(["su"]);
14125
+ NODE_VALUE_FLAG = /^(-r|--require|--import|--loader|--experimental-loader|--env-file|--env-file-if-exists|--input-type|-C|--conditions)(?:=(.*))?$/;
13821
14126
  MAX_PAYLOAD_NESTING = 8;
13822
14127
  payloadNesting = 0;
13823
14128
  MAX_INLINE_CALLS = 16;
@@ -14066,6 +14371,8 @@ var init_dist = __esm({
14066
14371
  JAILED_DIR_NAMES = [".ssh", ".aws"];
14067
14372
  shellGlob = (p) => p.replace(/\[!/g, "[^");
14068
14373
  GLOB_META = /(?<!\\)[*?[]/;
14374
+ INLINE_INTERP_PRESCREEN_RE = /(?:^|[^\w-])(?:python|node|tsx|ts-node|deno|bun|perl|ruby|irb|php)(?![A-Za-z_])/i;
14375
+ runsInlineInterpreter = (command, normalized) => INLINE_INTERP_PRESCREEN_RE.test(command) || INLINE_INTERP_PRESCREEN_RE.test(normalized);
14069
14376
  isReaderWord = (w) => w !== null && FS_READ_TOOLS.has(baseWord(w));
14070
14377
  positionalAfter = (words, from, to = words.length) => positionedArgs(words, from, to).map((a) => a.value);
14071
14378
  NONE = { kind: "none" };
@@ -17381,9 +17688,9 @@ zoo`.split("\n");
17381
17688
  const name = n.Name.Value;
17382
17689
  if (n.Append) {
17383
17690
  const prev = this.appended.has(name) ? this.appended.get(name) : resolveWordLiteral(n.Value) === null ? null : void 0;
17384
- const base2 = prev === void 0 ? recordedExpansion(name) : prev;
17691
+ const base3 = prev === void 0 ? recordedExpansion(name) : prev;
17385
17692
  const piece = resolveWordLiteral(n.Value);
17386
- this.appended.set(name, typeof base2 === "string" && piece !== null ? base2 + piece : null);
17693
+ this.appended.set(name, typeof base3 === "string" && piece !== null ? base3 + piece : null);
17387
17694
  this.treeTopVars.delete(name);
17388
17695
  return true;
17389
17696
  }
@@ -18899,12 +19206,50 @@ ${text}` : text);
18899
19206
  const char = value[index] ?? "";
18900
19207
  return RUN_CHAR.test(char) || char !== "" && isInvisibleChar(char);
18901
19208
  };
19209
+ RULE_NAME2 = "review-node9-disarm";
19210
+ DESCRIPTION = "The AI wants to stop node9, remove it, or change its own files. These are your commands to run, not the agent's: with the daemon stopped the outside-content and taint checks are off.";
19211
+ DAEMON_CMDLINES = [
19212
+ "node /usr/lib/node_modules/@node9/proxy/dist/cli.js daemon start",
19213
+ "node9 daemon start"
19214
+ ];
19215
+ DAEMON_NAMES = ["node", "node9"];
19216
+ KILLALL_NAMES = /* @__PURE__ */ new Set(["node", "node9", "node.exe", "node9.exe"]);
19217
+ STOP_SYSTEMCTL = /* @__PURE__ */ new Set(["stop", "disable", "mask", "kill"]);
19218
+ STOP_LAUNCHCTL = /* @__PURE__ */ new Set(["unload", "bootout", "remove", "stop", "kill", "disable"]);
19219
+ PKILL_VALUE_FLAGS = /* @__PURE__ */ new Set([
19220
+ "-u",
19221
+ "-U",
19222
+ "-g",
19223
+ "-G",
19224
+ "-P",
19225
+ "-s",
19226
+ "-t",
19227
+ "-F",
19228
+ "--signal",
19229
+ "--euid",
19230
+ "--uid",
19231
+ "--pgroup",
19232
+ "--group",
19233
+ "--parent",
19234
+ "--session",
19235
+ "--terminal",
19236
+ "--pidfile",
19237
+ "--ns",
19238
+ "--nslist"
19239
+ ]);
19240
+ NAME_PRESCREEN_RE = /node9|kill|systemctl|launchctl|daemon/i;
19241
+ base2 = (w) => (w ?? "").split(/[\\/]/).pop()?.toLowerCase() ?? "";
19242
+ HOME_PID_FILE_RE = /(?:^|[\s"'(=`])(?:~|\$HOME|\$\{HOME\}|\/home\/[^/\s]+|\/Users\/[^/\s]+|\/root)\/\.node9\/daemon\.pid/;
18902
19243
  VERDICT_RANK = {
18903
19244
  allow: 0,
18904
19245
  review: 1,
18905
19246
  block: 2
18906
19247
  };
18907
19248
  SQL_DML_KEYWORDS = /* @__PURE__ */ new Set(["select", "insert", "update", "delete", "merge", "upsert"]);
19249
+ NOT_OVERRIDABLE_CHECKS = /* @__PURE__ */ new Set([
19250
+ "data.credential-files",
19251
+ "data.credential-files-other"
19252
+ ]);
18908
19253
  MAX_DESTINATIONS = 200;
18909
19254
  MAX_SCAN_CHARS = 1e5;
18910
19255
  EMAIL_RE = /[\p{L}\p{N}._%+-]+@[\p{L}\p{N}](?:[\p{L}\p{N}-]*[\p{L}\p{N}])?(?:\.[\p{L}\p{N}](?:[\p{L}\p{N}-]*[\p{L}\p{N}])?)*\.\p{L}{2,}/gu;
@@ -20318,8 +20663,8 @@ function safeApiUrl(raw, onReject) {
20318
20663
  function sanitizeSsrfAllow(entries, source) {
20319
20664
  const kept = [];
20320
20665
  for (const entry of entries) {
20321
- const base2 = entry.includes("/") ? entry.slice(0, entry.indexOf("/")).trim() : entry;
20322
- const m = classifySsrf(base2);
20666
+ const base3 = entry.includes("/") ? entry.slice(0, entry.indexOf("/")).trim() : entry;
20667
+ const m = classifySsrf(base3);
20323
20668
  if (m && !m.overridable) {
20324
20669
  const what = entry.includes("/") ? "covers only protected addresses" : "is a protected address";
20325
20670
  process.emitWarning(
@@ -21419,12 +21764,12 @@ function getConfig(cwd) {
21419
21764
  continue;
21420
21765
  explicit[id] = v;
21421
21766
  }
21422
- const base2 = resolveCheckMap({
21767
+ const base3 = resolveCheckMap({
21423
21768
  ...catalogSettingsFrom(mergedSettings, mergedPolicy),
21424
21769
  checks: explicit
21425
21770
  });
21426
21771
  for (const [id, v] of Object.entries(v2ChecksByPath.get(projectPath)?.checks ?? {})) {
21427
- if (CHECK_VERDICT_RANK[v] > CHECK_VERDICT_RANK[base2[id] ?? "off"]) {
21772
+ if (CHECK_VERDICT_RANK[v] > CHECK_VERDICT_RANK[base3[id] ?? "off"]) {
21428
21773
  explicit[id] = v;
21429
21774
  checkSources[id] = "project";
21430
21775
  }
@@ -21576,7 +21921,7 @@ init_dist();
21576
21921
  // src/policy/index.ts
21577
21922
  init_esm_shims();
21578
21923
  init_dist();
21579
- import fs14 from "fs";
21924
+ import fs15 from "fs";
21580
21925
 
21581
21926
  // src/utils/read-capped.ts
21582
21927
  init_esm_shims();
@@ -21620,19 +21965,51 @@ function readCappedText(file2, maxBytes, openFlags = "r") {
21620
21965
 
21621
21966
  // src/policy/index.ts
21622
21967
  import path15 from "path";
21968
+ import os10 from "os";
21969
+
21970
+ // src/utils/read-small-file.ts
21971
+ init_esm_shims();
21972
+ import fs7 from "fs";
21973
+ function readSmallFile(file2, maxBytes) {
21974
+ let fd;
21975
+ try {
21976
+ fd = fs7.openSync(file2, fs7.constants.O_RDONLY | (fs7.constants.O_NONBLOCK ?? 0));
21977
+ } catch {
21978
+ return null;
21979
+ }
21980
+ try {
21981
+ const st = fs7.fstatSync(fd);
21982
+ if (!st.isFile() || st.size > maxBytes) return null;
21983
+ const buf = Buffer.alloc(Math.min(st.size, maxBytes));
21984
+ let read2 = 0;
21985
+ while (read2 < buf.length) {
21986
+ const n = fs7.readSync(fd, buf, read2, buf.length - read2, read2);
21987
+ if (n === 0) break;
21988
+ read2 += n;
21989
+ }
21990
+ return buf.toString("utf8", 0, read2);
21991
+ } catch {
21992
+ return null;
21993
+ } finally {
21994
+ fs7.closeSync(fd);
21995
+ }
21996
+ }
21997
+
21998
+ // src/policy/index.ts
21999
+ init_forecast_context();
21623
22000
 
21624
22001
  // src/dlp.ts
21625
22002
  init_esm_shims();
21626
22003
  init_dist();
21627
22004
  init_dist();
21628
- import fs7 from "fs";
22005
+ import fs8 from "fs";
21629
22006
  import path7 from "path";
21630
22007
  function scanFilePath(filePath, cwd = process.cwd(), options = {}) {
21631
22008
  if (!filePath) return null;
21632
22009
  let resolved;
21633
22010
  try {
21634
22011
  const absolute2 = path7.resolve(cwd, filePath);
21635
- resolved = fs7.realpathSync.native(absolute2);
22012
+ resolved = fs8.realpathSync.native(absolute2);
21636
22013
  } catch (err) {
21637
22014
  const code = err.code;
21638
22015
  if (code === "ENOENT" || code === "ENOTDIR") {
@@ -21859,7 +22236,7 @@ function combineWithEngine(gates, engine, opts = { panicMode: false }) {
21859
22236
  // src/policy/file-access.ts
21860
22237
  init_esm_shims();
21861
22238
  init_dist();
21862
- import fs8 from "fs";
22239
+ import fs9 from "fs";
21863
22240
  import os6 from "os";
21864
22241
  import path8 from "path";
21865
22242
  function absoluteSpelling(p, cwd, home) {
@@ -21873,13 +22250,13 @@ function physical(p, followFinal, links = 0) {
21873
22250
  return { path: `${parent.path}/${path8.basename(p)}`, missing: parent.missing };
21874
22251
  }
21875
22252
  try {
21876
- return { path: fs8.realpathSync.native(p), missing: false };
22253
+ return { path: fs9.realpathSync.native(p), missing: false };
21877
22254
  } catch (error) {
21878
22255
  const code = error.code;
21879
22256
  if (!["ENOENT", "ENOTDIR"].includes(code ?? "")) throw error;
21880
22257
  try {
21881
- if (fs8.lstatSync(p).isSymbolicLink()) {
21882
- const target = fs8.readlinkSync(p);
22258
+ if (fs9.lstatSync(p).isSymbolicLink()) {
22259
+ const target = fs9.readlinkSync(p);
21883
22260
  return physical(
21884
22261
  path8.isAbsolute(target) ? target : `${path8.dirname(p)}/${target}`,
21885
22262
  true,
@@ -21919,14 +22296,14 @@ function prepareFileAccesses(tool, args, cwd = process.cwd(), inspection = {}, h
21919
22296
  const raw = absoluteSpelling(access.path, access.cwd ?? cwd, home);
21920
22297
  if (access.directoryTarget) {
21921
22298
  try {
21922
- if (!fs8.statSync(absoluteSpelling(access.directoryTarget, access.cwd ?? cwd, home)).isDirectory())
22299
+ if (!fs9.statSync(absoluteSpelling(access.directoryTarget, access.cwd ?? cwd, home)).isDirectory())
21923
22300
  continue;
21924
22301
  } catch {
21925
22302
  }
21926
22303
  }
21927
22304
  if (access.operation === "create") {
21928
22305
  try {
21929
- fs8.statSync(raw);
22306
+ fs9.statSync(raw);
21930
22307
  continue;
21931
22308
  } catch {
21932
22309
  }
@@ -21983,34 +22360,34 @@ import path12 from "path";
21983
22360
  // src/supply-chain/osv-index.ts
21984
22361
  init_esm_shims();
21985
22362
  init_dist();
21986
- import fs10 from "fs";
22363
+ import fs11 from "fs";
21987
22364
  import os7 from "os";
21988
22365
  import path10 from "path";
21989
22366
  import { createHash as createHash4 } from "crypto";
21990
22367
 
21991
22368
  // src/utils/atomic-write.ts
21992
22369
  init_esm_shims();
21993
- import fs9 from "fs";
22370
+ import fs10 from "fs";
21994
22371
  import path9 from "path";
21995
22372
  import { randomUUID } from "crypto";
21996
22373
  function atomicWriteSync(filePath, data, options) {
21997
22374
  const dir2 = path9.dirname(filePath);
21998
- if (!fs9.existsSync(dir2)) fs9.mkdirSync(dir2, { recursive: true });
22375
+ if (!fs10.existsSync(dir2)) fs10.mkdirSync(dir2, { recursive: true });
21999
22376
  const tmpPath = `${filePath}.${randomUUID()}.tmp`;
22000
22377
  try {
22001
- fs9.writeFileSync(tmpPath, data, options);
22378
+ fs10.writeFileSync(tmpPath, data, options);
22002
22379
  } catch (err) {
22003
22380
  try {
22004
- fs9.unlinkSync(tmpPath);
22381
+ fs10.unlinkSync(tmpPath);
22005
22382
  } catch {
22006
22383
  }
22007
22384
  throw err;
22008
22385
  }
22009
22386
  try {
22010
- fs9.renameSync(tmpPath, filePath);
22387
+ fs10.renameSync(tmpPath, filePath);
22011
22388
  } catch (err) {
22012
22389
  try {
22013
- fs9.unlinkSync(tmpPath);
22390
+ fs10.unlinkSync(tmpPath);
22014
22391
  } catch {
22015
22392
  }
22016
22393
  throw err;
@@ -22046,7 +22423,7 @@ function shardPath(eco, shard) {
22046
22423
  }
22047
22424
  function readMeta(eco) {
22048
22425
  try {
22049
- const m = JSON.parse(fs10.readFileSync(path10.join(ecoDir(eco), "meta.json"), "utf8"));
22426
+ const m = JSON.parse(fs11.readFileSync(path10.join(ecoDir(eco), "meta.json"), "utf8"));
22050
22427
  if (typeof m?.syncedAt !== "string" || typeof m?.lastModifiedMs !== "number") return null;
22051
22428
  return m;
22052
22429
  } catch {
@@ -22055,7 +22432,7 @@ function readMeta(eco) {
22055
22432
  }
22056
22433
  function readShard(eco, shard) {
22057
22434
  try {
22058
- const s = JSON.parse(fs10.readFileSync(shardPath(eco, shard), "utf8"));
22435
+ const s = JSON.parse(fs11.readFileSync(shardPath(eco, shard), "utf8"));
22059
22436
  return s && typeof s === "object" && !Array.isArray(s) ? s : {};
22060
22437
  } catch {
22061
22438
  return {};
@@ -22165,15 +22542,15 @@ async function fetchJson(url, opts) {
22165
22542
  // src/supply-chain/osv-online.ts
22166
22543
  var TIMEOUT_MS = 2500;
22167
22544
  async function queryOsvMalicious(queries) {
22168
- const base2 = endpoints().osvApi;
22169
- if (!base2 || queries.length === 0) return null;
22545
+ const base3 = endpoints().osvApi;
22546
+ if (!base3 || queries.length === 0) return null;
22170
22547
  const body = JSON.stringify({
22171
22548
  queries: queries.map((q) => ({
22172
22549
  package: { name: q.name, ecosystem: q.ecosystem },
22173
22550
  ...q.version ? { version: q.version } : {}
22174
22551
  }))
22175
22552
  });
22176
- const res = await fetchJson(`${base2}/v1/querybatch`, {
22553
+ const res = await fetchJson(`${base3}/v1/querybatch`, {
22177
22554
  timeoutMs: TIMEOUT_MS,
22178
22555
  maxBytes: 1024 * 1024,
22179
22556
  body
@@ -22234,9 +22611,9 @@ function pypiRepoOf(doc) {
22234
22611
  return null;
22235
22612
  }
22236
22613
  async function npmSourceRepo(name, version) {
22237
- const base2 = endpoints().npmRegistry;
22238
- if (!base2) return "unknown";
22239
- const url = `${base2}/-/npm/v1/attestations/${name.replaceAll("/", "%2F")}@${encodeURIComponent(version)}`;
22614
+ const base3 = endpoints().npmRegistry;
22615
+ if (!base3) return "unknown";
22616
+ const url = `${base3}/-/npm/v1/attestations/${name.replaceAll("/", "%2F")}@${encodeURIComponent(version)}`;
22240
22617
  return read(url, npmRepoOf);
22241
22618
  }
22242
22619
  async function read(url, repoOf) {
@@ -22249,9 +22626,9 @@ async function read(url, repoOf) {
22249
22626
  }
22250
22627
  }
22251
22628
  async function pypiSourceRepo(name, version, filename) {
22252
- const base2 = endpoints().pypi;
22253
- if (!base2) return "unknown";
22254
- const url = `${base2}/integrity/${encodeURIComponent(name)}/${encodeURIComponent(version)}/${encodeURIComponent(filename)}/provenance`;
22629
+ const base3 = endpoints().pypi;
22630
+ if (!base3) return "unknown";
22631
+ const url = `${base3}/integrity/${encodeURIComponent(name)}/${encodeURIComponent(version)}/${encodeURIComponent(filename)}/provenance`;
22255
22632
  return read(url, pypiRepoOf);
22256
22633
  }
22257
22634
  var PYPI_FINAL = /^\d+(?:\.\d+)*$/;
@@ -22296,9 +22673,9 @@ async function npmVersionDocument(url, selector) {
22296
22673
  };
22297
22674
  }
22298
22675
  async function npmInfo(name, version, freshWindowMs, raw) {
22299
- const base2 = endpoints().npmRegistry;
22300
- if (!base2) return null;
22301
- const url = `${base2}/${name.replaceAll("/", "%2F")}`;
22676
+ const base3 = endpoints().npmRegistry;
22677
+ if (!base3) return null;
22678
+ const url = `${base3}/${name.replaceAll("/", "%2F")}`;
22302
22679
  const aliasStart = raw?.indexOf("@npm:") ?? -1;
22303
22680
  const target = aliasStart > 0 ? raw.slice(aliasStart + 5) : raw?.replace(/^npm:/, "");
22304
22681
  const specStart = target?.indexOf("@", 1) ?? -1;
@@ -22366,9 +22743,9 @@ async function npmInfo(name, version, freshWindowMs, raw) {
22366
22743
  return info;
22367
22744
  }
22368
22745
  async function pypiInfo(name, version) {
22369
- const base2 = endpoints().pypi;
22370
- if (!base2) return null;
22371
- const url = version ? `${base2}/pypi/${encodeURIComponent(name)}/${encodeURIComponent(version)}/json` : `${base2}/pypi/${encodeURIComponent(name)}/json`;
22746
+ const base3 = endpoints().pypi;
22747
+ if (!base3) return null;
22748
+ const url = version ? `${base3}/pypi/${encodeURIComponent(name)}/${encodeURIComponent(version)}/json` : `${base3}/pypi/${encodeURIComponent(name)}/json`;
22372
22749
  const doc = await fetchJson(url, { timeoutMs: TIMEOUT_MS3, maxBytes: MAX_BYTES2 });
22373
22750
  if (!doc?.info?.version) return null;
22374
22751
  const times = (doc.urls ?? []).map((u) => u.upload_time_iso_8601 ? Date.parse(u.upload_time_iso_8601) : NaN).filter((t) => Number.isFinite(t));
@@ -22392,9 +22769,9 @@ function pypiHistoryOf(releases, version) {
22392
22769
  };
22393
22770
  }
22394
22771
  async function pypiHistory(name, version) {
22395
- const base2 = endpoints().pypi;
22396
- if (!base2) return null;
22397
- const doc = await fetchJson(`${base2}/pypi/${encodeURIComponent(name)}/json`, {
22772
+ const base3 = endpoints().pypi;
22773
+ if (!base3) return null;
22774
+ const doc = await fetchJson(`${base3}/pypi/${encodeURIComponent(name)}/json`, {
22398
22775
  timeoutMs: TIMEOUT_MS3,
22399
22776
  maxBytes: MAX_BYTES2
22400
22777
  });
@@ -22409,7 +22786,7 @@ async function registryInfo(eco, name, version, freshWindowMs, raw) {
22409
22786
 
22410
22787
  // src/supply-chain/local-resolve.ts
22411
22788
  init_esm_shims();
22412
- import fs11 from "fs";
22789
+ import fs12 from "fs";
22413
22790
  import path11 from "path";
22414
22791
  var MAX_LEVELS = 32;
22415
22792
  var LOCAL_FIRST_MANAGERS = /* @__PURE__ */ new Set(["npx", "bunx", "npm exec"]);
@@ -22421,7 +22798,7 @@ function resolveInstalled(name, cwd) {
22421
22798
  const pkgDir = path11.join(dir2, "node_modules", ...name.split("/"));
22422
22799
  const manifest = path11.join(pkgDir, "package.json");
22423
22800
  try {
22424
- const raw = fs11.readFileSync(manifest, "utf8");
22801
+ const raw = fs12.readFileSync(manifest, "utf8");
22425
22802
  const parsed = JSON.parse(raw);
22426
22803
  if (typeof parsed.version === "string" && parsed.version.length > 0) {
22427
22804
  return { version: parsed.version, dir: pkgDir };
@@ -22699,7 +23076,7 @@ init_esm_shims();
22699
23076
  init_dist();
22700
23077
  import os8 from "os";
22701
23078
  import path13 from "path";
22702
- import fs12 from "fs";
23079
+ import fs13 from "fs";
22703
23080
  var MAX_CONFIG_BYTES = 256 * 1024;
22704
23081
  var MAX_PARENT_DIRS = 12;
22705
23082
  var TTL_MS = 5e3;
@@ -22824,7 +23201,7 @@ function configuredRegistryHosts(cwd, env = process.env, home = os8.homedir()) {
22824
23201
  const r = readCappedText(
22825
23202
  file2,
22826
23203
  MAX_CONFIG_BYTES,
22827
- fs12.constants.O_RDONLY | (fs12.constants.O_NONBLOCK ?? 0)
23204
+ fs13.constants.O_RDONLY | (fs13.constants.O_NONBLOCK ?? 0)
22828
23205
  );
22829
23206
  if (r) hostsInText(r.text, hosts);
22830
23207
  }
@@ -22834,7 +23211,7 @@ function configuredRegistryHosts(cwd, env = process.env, home = os8.homedir()) {
22834
23211
 
22835
23212
  // src/utils/provenance.ts
22836
23213
  init_esm_shims();
22837
- import fs13 from "fs";
23214
+ import fs14 from "fs";
22838
23215
  import path14 from "path";
22839
23216
  import os9 from "os";
22840
23217
  var SYSTEM_PREFIXES = ["/usr/bin", "/usr/sbin", "/bin", "/sbin"];
@@ -22854,7 +23231,7 @@ function findInPath(cmd) {
22854
23231
  if (!dir2) continue;
22855
23232
  const full = path14.join(dir2, cmd);
22856
23233
  try {
22857
- fs13.accessSync(full, fs13.constants.X_OK);
23234
+ fs14.accessSync(full, fs14.constants.X_OK);
22858
23235
  return full;
22859
23236
  } catch {
22860
23237
  }
@@ -22899,7 +23276,7 @@ function checkProvenance(cmd, cwd) {
22899
23276
  reason: "binary not found in PATH"
22900
23277
  };
22901
23278
  }
22902
- resolved = fs13.realpathSync(found);
23279
+ resolved = fs14.realpathSync(found);
22903
23280
  } catch {
22904
23281
  return {
22905
23282
  resolvedPath: cmd,
@@ -22908,7 +23285,7 @@ function checkProvenance(cmd, cwd) {
22908
23285
  };
22909
23286
  }
22910
23287
  try {
22911
- const stat = fs13.statSync(resolved);
23288
+ const stat = fs14.statSync(resolved);
22912
23289
  if (stat.mode & 2) {
22913
23290
  return {
22914
23291
  resolvedPath: resolved,
@@ -22957,14 +23334,26 @@ async function evaluatePolicy2(toolName, args, agent, cwd, opts) {
22957
23334
  isTrustedHost: config.policy.trustedHostsManaged ? (host2) => matchesTrustedHost(host2, config.policy.trustedHosts) : isTrustedHost,
22958
23335
  readAgentSettings,
22959
23336
  isConfiguredRegistry: (host2, at) => configuredRegistryHosts(at).has(host2),
22960
- isGitWorkTreeTop
23337
+ isGitWorkTreeTop,
23338
+ daemonPid: daemonPidFromFile
22961
23339
  }
22962
23340
  );
22963
23341
  }
23342
+ function daemonPidFromFile() {
23343
+ if (forecastRun()) return null;
23344
+ const text = readSmallFile(path15.join(os10.homedir(), ".node9", "daemon.pid"), 4096);
23345
+ if (text === null) return null;
23346
+ try {
23347
+ const pid = JSON.parse(text).pid;
23348
+ return typeof pid === "number" && Number.isInteger(pid) && pid > 0 && pid <= 4194304 ? pid : null;
23349
+ } catch {
23350
+ return null;
23351
+ }
23352
+ }
22964
23353
  function isGitWorkTreeTop(dir2, cwd) {
22965
23354
  if (!path15.isAbsolute(cwd)) return true;
22966
23355
  try {
22967
- return fs14.existsSync(path15.join(path15.resolve(cwd, dir2), ".git"));
23356
+ return fs15.existsSync(path15.join(path15.resolve(cwd, dir2), ".git"));
22968
23357
  } catch {
22969
23358
  return true;
22970
23359
  }
@@ -22974,7 +23363,7 @@ function readAgentSettings(absPath) {
22974
23363
  const r = readCappedText(
22975
23364
  absPath,
22976
23365
  MAX_AGENT_SETTINGS_BYTES,
22977
- fs14.constants.O_RDONLY | (fs14.constants.O_NONBLOCK ?? 0)
23366
+ fs15.constants.O_RDONLY | (fs15.constants.O_NONBLOCK ?? 0)
22978
23367
  );
22979
23368
  return r && !r.truncated ? r.text : null;
22980
23369
  }
@@ -22984,12 +23373,12 @@ function isIgnoredTool2(toolName) {
22984
23373
 
22985
23374
  // src/auth/state.ts
22986
23375
  init_esm_shims();
22987
- import fs15 from "fs";
23376
+ import fs16 from "fs";
22988
23377
  import path16 from "path";
22989
- import os10 from "os";
23378
+ import os11 from "os";
22990
23379
  init_forecast_context();
22991
- var PAUSED_FILE = path16.join(os10.homedir(), ".node9", "PAUSED");
22992
- var TRUST_FILE = path16.join(os10.homedir(), ".node9", "trust.json");
23380
+ var PAUSED_FILE = path16.join(os11.homedir(), ".node9", "PAUSED");
23381
+ var TRUST_FILE = path16.join(os11.homedir(), ".node9", "trust.json");
22993
23382
  function extractCommandPattern(toolName, args) {
22994
23383
  const lower2 = toolName.toLowerCase();
22995
23384
  if (lower2 !== "bash" && lower2 !== "execute_bash" && lower2 !== "shell") return void 0;
@@ -23002,11 +23391,11 @@ function extractCommandPattern(toolName, args) {
23002
23391
  function checkPause() {
23003
23392
  if (forecastRun()) return { paused: false };
23004
23393
  try {
23005
- if (!fs15.existsSync(PAUSED_FILE)) return { paused: false };
23006
- const state = JSON.parse(fs15.readFileSync(PAUSED_FILE, "utf-8"));
23394
+ if (!fs16.existsSync(PAUSED_FILE)) return { paused: false };
23395
+ const state = JSON.parse(fs16.readFileSync(PAUSED_FILE, "utf-8"));
23007
23396
  if (state.expiry > 0 && Date.now() >= state.expiry) {
23008
23397
  try {
23009
- fs15.unlinkSync(PAUSED_FILE);
23398
+ fs16.unlinkSync(PAUSED_FILE);
23010
23399
  } catch {
23011
23400
  }
23012
23401
  return { paused: false };
@@ -23019,8 +23408,8 @@ function checkPause() {
23019
23408
  function getActiveTrustSession(toolName, args) {
23020
23409
  if (forecastRun()) return false;
23021
23410
  try {
23022
- if (!fs15.existsSync(TRUST_FILE)) return false;
23023
- const trust = JSON.parse(fs15.readFileSync(TRUST_FILE, "utf-8"));
23411
+ if (!fs16.existsSync(TRUST_FILE)) return false;
23412
+ const trust = JSON.parse(fs16.readFileSync(TRUST_FILE, "utf-8"));
23024
23413
  const now = Date.now();
23025
23414
  const active = trust.entries.filter((e) => e.expiry > now);
23026
23415
  if (active.length !== trust.entries.length) {
@@ -23043,8 +23432,8 @@ function writeTrustSession(toolName, durationMs, args) {
23043
23432
  try {
23044
23433
  let trust = { entries: [] };
23045
23434
  try {
23046
- if (fs15.existsSync(TRUST_FILE)) {
23047
- trust = JSON.parse(fs15.readFileSync(TRUST_FILE, "utf-8"));
23435
+ if (fs16.existsSync(TRUST_FILE)) {
23436
+ trust = JSON.parse(fs16.readFileSync(TRUST_FILE, "utf-8"));
23048
23437
  }
23049
23438
  } catch {
23050
23439
  }
@@ -23066,9 +23455,9 @@ function writeTrustSession(toolName, durationMs, args) {
23066
23455
  }
23067
23456
  function getPersistentDecision(toolName) {
23068
23457
  try {
23069
- const file2 = path16.join(os10.homedir(), ".node9", "decisions.json");
23070
- if (!fs15.existsSync(file2)) return null;
23071
- const decisions = JSON.parse(fs15.readFileSync(file2, "utf-8"));
23458
+ const file2 = path16.join(os11.homedir(), ".node9", "decisions.json");
23459
+ if (!fs16.existsSync(file2)) return null;
23460
+ const decisions = JSON.parse(fs16.readFileSync(file2, "utf-8"));
23072
23461
  const d = decisions[toolName];
23073
23462
  if (d === "allow" || d === "deny") return d;
23074
23463
  } catch {
@@ -23079,15 +23468,15 @@ function getPersistentDecision(toolName) {
23079
23468
  // src/auth/daemon.ts
23080
23469
  init_esm_shims();
23081
23470
  init_forecast_context();
23082
- import fs17 from "fs";
23471
+ import fs18 from "fs";
23083
23472
  import http from "http";
23084
23473
  import net from "net";
23085
23474
  import path18 from "path";
23086
- import os11 from "os";
23475
+ import os12 from "os";
23087
23476
 
23088
23477
  // src/utils/activity-socket.ts
23089
23478
  init_esm_shims();
23090
- import fs16 from "fs";
23479
+ import fs17 from "fs";
23091
23480
  import path17 from "path";
23092
23481
 
23093
23482
  // src/utils/own-dir.ts
@@ -23097,7 +23486,7 @@ init_esm_shims();
23097
23486
  var currentUid = () => typeof process.getuid === "function" ? process.getuid() : void 0;
23098
23487
  function isPrivateDir(dir2, uid = currentUid()) {
23099
23488
  try {
23100
- const st = fs16.lstatSync(dir2);
23489
+ const st = fs17.lstatSync(dir2);
23101
23490
  if (st.isSymbolicLink() || !st.isDirectory()) return false;
23102
23491
  if (uid !== void 0 && st.uid !== uid) return false;
23103
23492
  return (st.mode & 18) === 0;
@@ -23109,7 +23498,7 @@ var WINDOWS_PIPE = /^\\\\\.\\pipe\\node9-activity-[0-9a-f]{32}$/;
23109
23498
  function isTrustedActivitySocket(socketPath, uid = currentUid()) {
23110
23499
  if (process.platform === "win32") return WINDOWS_PIPE.test(socketPath);
23111
23500
  try {
23112
- const st = fs16.lstatSync(socketPath);
23501
+ const st = fs17.lstatSync(socketPath);
23113
23502
  if (!st.isSocket()) return false;
23114
23503
  if (uid !== void 0 && st.uid !== uid) return false;
23115
23504
  } catch {
@@ -23267,9 +23656,9 @@ function recordFromLegacyWait(body, now = Date.now()) {
23267
23656
  }
23268
23657
 
23269
23658
  // src/auth/daemon.ts
23270
- function daemonActivitySocket(home = os11.homedir()) {
23659
+ function daemonActivitySocket(home = os12.homedir()) {
23271
23660
  try {
23272
- const raw = fs17.readFileSync(path18.join(home, ".node9", "daemon.pid"), "utf-8");
23661
+ const raw = fs18.readFileSync(path18.join(home, ".node9", "daemon.pid"), "utf-8");
23273
23662
  const p = JSON.parse(raw).activitySocket;
23274
23663
  return typeof p === "string" && isTrustedActivitySocket(p) ? p : null;
23275
23664
  } catch {
@@ -23333,9 +23722,9 @@ var DAEMON_HOST = "127.0.0.1";
23333
23722
  function getInternalToken() {
23334
23723
  if (forecastRun()) return null;
23335
23724
  try {
23336
- const pidFile = path18.join(os11.homedir(), ".node9", "daemon.pid");
23337
- if (!fs17.existsSync(pidFile)) return null;
23338
- const data = JSON.parse(fs17.readFileSync(pidFile, "utf-8"));
23725
+ const pidFile = path18.join(os12.homedir(), ".node9", "daemon.pid");
23726
+ if (!fs18.existsSync(pidFile)) return null;
23727
+ const data = JSON.parse(fs18.readFileSync(pidFile, "utf-8"));
23339
23728
  process.kill(data.pid, 0);
23340
23729
  return data.internalToken ?? null;
23341
23730
  } catch {
@@ -23344,12 +23733,12 @@ function getInternalToken() {
23344
23733
  }
23345
23734
  function isDaemonRunning() {
23346
23735
  if (forecastRun()) return false;
23347
- const pidFile = path18.join(os11.homedir(), ".node9", "daemon.pid");
23348
- if (fs17.existsSync(pidFile)) {
23736
+ const pidFile = path18.join(os12.homedir(), ".node9", "daemon.pid");
23737
+ if (fs18.existsSync(pidFile)) {
23349
23738
  let pid;
23350
23739
  let port;
23351
23740
  try {
23352
- const data = JSON.parse(fs17.readFileSync(pidFile, "utf-8"));
23741
+ const data = JSON.parse(fs18.readFileSync(pidFile, "utf-8"));
23353
23742
  pid = data.pid;
23354
23743
  port = data.port;
23355
23744
  } catch {
@@ -23367,7 +23756,7 @@ function isDaemonRunning() {
23367
23756
  } catch (err) {
23368
23757
  if (err instanceof Error && "code" in err && err.code === "ESRCH") {
23369
23758
  try {
23370
- fs17.unlinkSync(pidFile);
23759
+ fs18.unlinkSync(pidFile);
23371
23760
  } catch {
23372
23761
  }
23373
23762
  }
@@ -23391,11 +23780,11 @@ async function daemonHasInteractiveApprover(timeoutMs = 400) {
23391
23780
  }
23392
23781
  }
23393
23782
  async function registerDaemonEntry(toolName, args, meta, riskMetadata, activityId, cwd, recoveryCommand, skipBackgroundAuth, viewOnly, localSmartRuleMatched, socketActivitySent, sessionId, card) {
23394
- const base2 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
23783
+ const base3 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
23395
23784
  const ctrl = new AbortController();
23396
23785
  const timer = setTimeout(() => ctrl.abort(), 5e3);
23397
23786
  try {
23398
- const res = await fetch(`${base2}/check`, {
23787
+ const res = await fetch(`${base3}/check`, {
23399
23788
  method: "POST",
23400
23789
  headers: { "Content-Type": "application/json" },
23401
23790
  body: JSON.stringify({
@@ -23505,8 +23894,8 @@ function daemonGet(urlPath, signal, token = getInternalToken()) {
23505
23894
  });
23506
23895
  }
23507
23896
  async function notifyDaemonViewer(toolName, args, meta, riskMetadata, activityId, socketActivitySent, extra) {
23508
- const base2 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
23509
- const res = await fetch(`${base2}/check`, {
23897
+ const base3 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
23898
+ const res = await fetch(`${base3}/check`, {
23510
23899
  method: "POST",
23511
23900
  headers: { "Content-Type": "application/json" },
23512
23901
  body: JSON.stringify({
@@ -23538,9 +23927,9 @@ async function notifyTaint(filePath, source, fromEid) {
23538
23927
  return;
23539
23928
  }
23540
23929
  if (!isDaemonRunning()) return;
23541
- const base2 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
23930
+ const base3 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
23542
23931
  try {
23543
- await fetch(`${base2}/taint`, {
23932
+ await fetch(`${base3}/taint`, {
23544
23933
  method: "POST",
23545
23934
  headers: { "Content-Type": "application/json" },
23546
23935
  // Phase D2 — fromEid is the audit eid that created this taint (edge source).
@@ -23558,9 +23947,9 @@ async function checkTaint(paths) {
23558
23947
  return record ? { tainted: true, record } : { tainted: false };
23559
23948
  }
23560
23949
  if (!isDaemonRunning()) return { tainted: false, daemonUnavailable: true };
23561
- const base2 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
23950
+ const base3 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
23562
23951
  try {
23563
- const res = await fetch(`${base2}/taint/check`, {
23952
+ const res = await fetch(`${base3}/taint/check`, {
23564
23953
  method: "POST",
23565
23954
  headers: { "Content-Type": "application/json" },
23566
23955
  body: JSON.stringify({ paths }),
@@ -23588,9 +23977,9 @@ async function notifyWritten(paths) {
23588
23977
  return;
23589
23978
  }
23590
23979
  if (paths.length === 0 || !isDaemonRunning()) return;
23591
- const base2 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
23980
+ const base3 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
23592
23981
  try {
23593
- await fetch(`${base2}/written`, {
23982
+ await fetch(`${base3}/written`, {
23594
23983
  method: "POST",
23595
23984
  headers: exposureHeaders(),
23596
23985
  body: JSON.stringify({ paths: paths.slice(0, 64) }),
@@ -23604,9 +23993,9 @@ async function checkWritten(paths) {
23604
23993
  const forecast = forecastRun();
23605
23994
  if (forecast) return { written: forecast.session.written(paths) };
23606
23995
  if (!isDaemonRunning()) return { written: [], daemonUnavailable: true };
23607
- const base2 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
23996
+ const base3 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
23608
23997
  try {
23609
- const res = await fetch(`${base2}/written/check`, {
23998
+ const res = await fetch(`${base3}/written/check`, {
23610
23999
  method: "POST",
23611
24000
  headers: exposureHeaders(),
23612
24001
  body: JSON.stringify({ paths: paths.slice(0, 64) }),
@@ -23628,9 +24017,9 @@ async function checkSessionTaint(sessionId) {
23628
24017
  return record ? { tainted: true, record, exposed } : { tainted: false, exposed };
23629
24018
  }
23630
24019
  if (!sessionId || !isDaemonRunning()) return { tainted: false };
23631
- const base2 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
24020
+ const base3 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
23632
24021
  try {
23633
- const res = await fetch(`${base2}/session-taint/check`, {
24022
+ const res = await fetch(`${base3}/session-taint/check`, {
23634
24023
  method: "POST",
23635
24024
  headers: exposureHeaders(),
23636
24025
  body: JSON.stringify({ sessionId }),
@@ -23648,8 +24037,8 @@ function exposureHeaders() {
23648
24037
  };
23649
24038
  }
23650
24039
  async function exposurePost(route, body, timeoutMs) {
23651
- const base2 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
23652
- const res = await fetch(`${base2}${route}`, {
24040
+ const base3 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
24041
+ const res = await fetch(`${base3}${route}`, {
23653
24042
  method: "POST",
23654
24043
  headers: exposureHeaders(),
23655
24044
  body: JSON.stringify(body),
@@ -23664,7 +24053,8 @@ async function checkSessionExposure(sessionId) {
23664
24053
  const record = forecast.session.checkExposure(forecast.now);
23665
24054
  return record ? { exposed: true, record } : { exposed: false };
23666
24055
  }
23667
- if (!sessionId || !isDaemonRunning()) return { exposed: false };
24056
+ if (!sessionId) return { exposed: false };
24057
+ if (!isDaemonRunning()) return { exposed: false, daemonUnavailable: true };
23668
24058
  try {
23669
24059
  return await exposurePost(
23670
24060
  "/session-exposure/check",
@@ -23688,8 +24078,8 @@ async function approveSessionExposure(sessionId, keys) {
23688
24078
  }
23689
24079
  }
23690
24080
  async function resolveViaDaemon(id, decision, internalToken, source, record) {
23691
- const base2 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
23692
- const res = await fetch(`${base2}/resolve/${id}`, {
24081
+ const base3 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
24082
+ const res = await fetch(`${base3}/resolve/${id}`, {
23693
24083
  method: "POST",
23694
24084
  headers: { "Content-Type": "application/json", "X-Node9-Internal": internalToken },
23695
24085
  body: JSON.stringify({ decision, ...source && { source }, ...record && { record } }),
@@ -23707,8 +24097,8 @@ import { randomUUID as randomUUID2 } from "crypto";
23707
24097
 
23708
24098
  // src/apps/state.ts
23709
24099
  init_esm_shims();
23710
- import fs19 from "fs";
23711
- import os13 from "os";
24100
+ import fs20 from "fs";
24101
+ import os14 from "os";
23712
24102
  import path20 from "path";
23713
24103
 
23714
24104
  // src/apps/catalog.ts
@@ -23843,22 +24233,22 @@ init_audit();
23843
24233
 
23844
24234
  // src/daemon/mcp-tools.ts
23845
24235
  init_esm_shims();
23846
- import fs18 from "fs";
24236
+ import fs19 from "fs";
23847
24237
  import path19 from "path";
23848
- import os12 from "os";
24238
+ import os13 from "os";
23849
24239
 
23850
24240
  // src/mcp-cmd.ts
23851
24241
  init_esm_shims();
23852
24242
 
23853
24243
  // src/daemon/mcp-tools.ts
23854
24244
  function getMcpToolsFile() {
23855
- return path19.join(os12.homedir(), ".node9", "mcp-tools.json");
24245
+ return path19.join(os13.homedir(), ".node9", "mcp-tools.json");
23856
24246
  }
23857
24247
  function readMcpToolsConfig() {
23858
24248
  try {
23859
24249
  const file2 = getMcpToolsFile();
23860
- if (!fs18.existsSync(file2)) return {};
23861
- const raw = fs18.readFileSync(file2, "utf-8");
24250
+ if (!fs19.existsSync(file2)) return {};
24251
+ const raw = fs19.readFileSync(file2, "utf-8");
23862
24252
  return JSON.parse(raw);
23863
24253
  } catch {
23864
24254
  return {};
@@ -23866,7 +24256,7 @@ function readMcpToolsConfig() {
23866
24256
  }
23867
24257
 
23868
24258
  // src/apps/state.ts
23869
- function appsFilePath(home = os13.homedir()) {
24259
+ function appsFilePath(home = os14.homedir()) {
23870
24260
  return path20.join(home, ".node9", "apps.json");
23871
24261
  }
23872
24262
  var SIGNAL_KINDS = /* @__PURE__ */ new Set(["mcp-server", "command", "package", "env-name"]);
@@ -23915,7 +24305,7 @@ function readAppsFile(home) {
23915
24305
  function readAppsFileStrict(home) {
23916
24306
  let text;
23917
24307
  try {
23918
- text = fs19.readFileSync(appsFilePath(home), "utf8");
24308
+ text = fs20.readFileSync(appsFilePath(home), "utf8");
23919
24309
  } catch (e) {
23920
24310
  return e.code === "ENOENT" ? emptyFile() : null;
23921
24311
  }
@@ -23930,7 +24320,7 @@ var NO_LINKS = /* @__PURE__ */ Object.create(null);
23930
24320
  function localLinks() {
23931
24321
  const file2 = appsFilePath();
23932
24322
  try {
23933
- const mtimeMs = fs19.statSync(file2).mtimeMs;
24323
+ const mtimeMs = fs20.statSync(file2).mtimeMs;
23934
24324
  if (!linkCache || linkCache.file !== file2 || linkCache.mtimeMs !== mtimeMs) {
23935
24325
  linkCache = { mtimeMs, file: file2, links: readAppsFile().links };
23936
24326
  }
@@ -23970,7 +24360,7 @@ function resolveLinkedServer(label2, opts = {}) {
23970
24360
  }
23971
24361
 
23972
24362
  // src/auth/orchestrator.ts
23973
- import os21 from "os";
24363
+ import os22 from "os";
23974
24364
  import path32 from "path";
23975
24365
 
23976
24366
  // src/ui/native.ts
@@ -24321,12 +24711,12 @@ end run`;
24321
24711
 
24322
24712
  // src/canary/registry.ts
24323
24713
  init_esm_shims();
24324
- import fs20 from "fs";
24325
- import os14 from "os";
24714
+ import fs21 from "fs";
24715
+ import os15 from "os";
24326
24716
  import path23 from "path";
24327
24717
  init_dist();
24328
24718
  function canaryStorePath() {
24329
- return path23.join(os14.homedir(), ".node9", "canaries.json");
24719
+ return path23.join(os15.homedir(), ".node9", "canaries.json");
24330
24720
  }
24331
24721
  function isRecord(x) {
24332
24722
  if (!x || typeof x !== "object") return false;
@@ -24337,7 +24727,7 @@ function loadCanaries(opts) {
24337
24727
  const p = canaryStorePath();
24338
24728
  let raw;
24339
24729
  try {
24340
- raw = fs20.readFileSync(p, "utf-8");
24730
+ raw = fs21.readFileSync(p, "utf-8");
24341
24731
  } catch (e) {
24342
24732
  if (e.code === "ENOENT") return [];
24343
24733
  throw e;
@@ -24365,21 +24755,21 @@ init_dist();
24365
24755
  // src/auth/session-script.ts
24366
24756
  init_esm_shims();
24367
24757
  init_dist();
24368
- import fs21 from "fs";
24369
- import os15 from "os";
24758
+ import fs22 from "fs";
24759
+ import os16 from "os";
24370
24760
  import path24 from "path";
24371
24761
  var MAX_SCRIPT_BYTES = 64 * 1024;
24372
24762
  var MAX_RECORDED_WRITES = 32;
24373
24763
  function readScriptFile(resolved) {
24374
24764
  let fd;
24375
24765
  try {
24376
- fd = fs21.openSync(resolved, fs21.constants.O_RDONLY | (fs21.constants.O_NONBLOCK ?? 0));
24377
- const st = fs21.fstatSync(fd);
24766
+ fd = fs22.openSync(resolved, fs22.constants.O_RDONLY | (fs22.constants.O_NONBLOCK ?? 0));
24767
+ const st = fs22.fstatSync(fd);
24378
24768
  if (!st.isFile() || st.size > MAX_SCRIPT_BYTES) return null;
24379
24769
  const buf = Buffer.alloc(MAX_SCRIPT_BYTES + 1);
24380
24770
  let n = 0;
24381
24771
  while (n < buf.length) {
24382
- const got = fs21.readSync(fd, buf, n, buf.length - n, n);
24772
+ const got = fs22.readSync(fd, buf, n, buf.length - n, n);
24383
24773
  if (got === 0) break;
24384
24774
  n += got;
24385
24775
  }
@@ -24390,7 +24780,7 @@ function readScriptFile(resolved) {
24390
24780
  } finally {
24391
24781
  if (fd !== void 0) {
24392
24782
  try {
24393
- fs21.closeSync(fd);
24783
+ fs22.closeSync(fd);
24394
24784
  } catch {
24395
24785
  }
24396
24786
  }
@@ -24400,16 +24790,16 @@ function absolute(p, cwd, home) {
24400
24790
  const spelled = p === "~" || p.startsWith("~/") ? home + p.slice(1) : p;
24401
24791
  return path24.resolve(cwd, spelled);
24402
24792
  }
24403
- async function resolveSessionScript(toolName, args, cwd, layers, deps, home = os15.homedir()) {
24793
+ async function resolveSessionScript(toolName, args, cwd, layers, deps, home = os16.homedir()) {
24404
24794
  try {
24405
24795
  const d = deps ?? { checkWritten, readScript: readScriptFile };
24406
24796
  const shell = resolveShellInput(toolName, args, layers);
24407
24797
  if (shell.kind !== "shell") return null;
24408
24798
  const operands2 = shellScriptOperands(shell.command);
24409
24799
  if (operands2.length === 0) return null;
24410
- const base2 = cwd ?? process.cwd();
24800
+ const base3 = cwd ?? process.cwd();
24411
24801
  const byResolved = /* @__PURE__ */ new Map();
24412
- for (const op of operands2) byResolved.set(absolute(op, base2, home), op);
24802
+ for (const op of operands2) byResolved.set(absolute(op, base3, home), op);
24413
24803
  const { written } = await d.checkWritten([...byResolved.keys()]);
24414
24804
  for (const resolved of written) {
24415
24805
  const spelled = byResolved.get(resolved);
@@ -24421,7 +24811,7 @@ async function resolveSessionScript(toolName, args, cwd, layers, deps, home = os
24421
24811
  path: spelled,
24422
24812
  resolved,
24423
24813
  args: scriptArgs,
24424
- fileAccess: prepareFileAccesses("Bash", scriptArgs, base2, layers, home)
24814
+ fileAccess: prepareFileAccesses("Bash", scriptArgs, base3, layers, home)
24425
24815
  };
24426
24816
  }
24427
24817
  return null;
@@ -24429,17 +24819,17 @@ async function resolveSessionScript(toolName, args, cwd, layers, deps, home = os
24429
24819
  return null;
24430
24820
  }
24431
24821
  }
24432
- function sessionWriteTargets(toolName, args, fileAccess, cwd, home = os15.homedir()) {
24433
- const base2 = cwd ?? process.cwd();
24822
+ function sessionWriteTargets(toolName, args, fileAccess, cwd, home = os16.homedir()) {
24823
+ const base3 = cwd ?? process.cwd();
24434
24824
  const out = /* @__PURE__ */ new Set();
24435
24825
  const argsObj = args && typeof args === "object" ? args : {};
24436
24826
  const named = argsObj.file_path ?? argsObj.path ?? argsObj.filename;
24437
24827
  if (typeof named === "string" && named && /write|edit|create|save|append/i.test(toolName))
24438
- out.add(absolute(named, base2, home));
24828
+ out.add(absolute(named, base3, home));
24439
24829
  for (const a of fileAccess?.accesses ?? []) {
24440
24830
  if (a.operation !== "write" && a.operation !== "create") continue;
24441
24831
  if (a.glob || a.recursive) continue;
24442
- out.add(absolute(a.path, a.cwd ?? base2, home));
24832
+ out.add(absolute(a.path, a.cwd ?? base3, home));
24443
24833
  if (out.size >= MAX_RECORDED_WRITES) break;
24444
24834
  }
24445
24835
  return [...out];
@@ -24460,27 +24850,27 @@ init_dist();
24460
24850
 
24461
24851
  // src/exposure/known-hosts.ts
24462
24852
  init_esm_shims();
24463
- import fs23 from "fs";
24853
+ import fs24 from "fs";
24464
24854
  import path26 from "path";
24465
24855
 
24466
24856
  // src/utils/git-remotes.ts
24467
24857
  init_esm_shims();
24468
24858
  init_dist();
24469
- import fs22 from "fs";
24859
+ import fs23 from "fs";
24470
24860
  import path25 from "path";
24471
- function readSmallFile(p, max) {
24861
+ function readSmallFile2(p, max) {
24472
24862
  let fd;
24473
24863
  try {
24474
- fd = fs22.openSync(p, fs22.constants.O_RDONLY | (fs22.constants.O_NOFOLLOW ?? 0));
24475
- const st = fs22.fstatSync(fd);
24864
+ fd = fs23.openSync(p, fs23.constants.O_RDONLY | (fs23.constants.O_NOFOLLOW ?? 0));
24865
+ const st = fs23.fstatSync(fd);
24476
24866
  if (!st.isFile() || st.size > max) return null;
24477
24867
  const buf = Buffer.alloc(st.size);
24478
- const n = fs22.readSync(fd, buf, 0, st.size, 0);
24868
+ const n = fs23.readSync(fd, buf, 0, st.size, 0);
24479
24869
  return buf.subarray(0, n).toString("utf8");
24480
24870
  } catch {
24481
24871
  return null;
24482
24872
  } finally {
24483
- if (fd !== void 0) fs22.closeSync(fd);
24873
+ if (fd !== void 0) fs23.closeSync(fd);
24484
24874
  }
24485
24875
  }
24486
24876
  function gitRemoteHost(url) {
@@ -24502,15 +24892,15 @@ function findGitConfig(dir2, walkUp = true) {
24502
24892
  let cur = path25.resolve(dir2);
24503
24893
  for (let i = 0; i < (walkUp ? 64 : 1); i++) {
24504
24894
  const dotGit = path25.join(cur, ".git");
24505
- const direct = readSmallFile(path25.join(dotGit, "config"), 1e6);
24895
+ const direct = readSmallFile2(path25.join(dotGit, "config"), 1e6);
24506
24896
  if (direct !== null) return direct;
24507
- const pointer = readSmallFile(dotGit, 4096);
24897
+ const pointer = readSmallFile2(dotGit, 4096);
24508
24898
  const m = pointer && /^gitdir:\s*(.+?)\s*$/m.exec(pointer);
24509
24899
  if (m) {
24510
24900
  let gitDir = path25.resolve(cur, m[1]);
24511
- const common = readSmallFile(path25.join(gitDir, "commondir"), 4096);
24901
+ const common = readSmallFile2(path25.join(gitDir, "commondir"), 4096);
24512
24902
  if (common !== null) gitDir = path25.resolve(gitDir, common.trim());
24513
- return readSmallFile(path25.join(gitDir, "config"), 1e6);
24903
+ return readSmallFile2(path25.join(gitDir, "config"), 1e6);
24514
24904
  }
24515
24905
  const parent = path25.dirname(cur);
24516
24906
  if (parent === cur) return null;
@@ -24567,7 +24957,7 @@ function manifestRoot(dir2) {
24567
24957
  for (let i = 0; i < 64; i++) {
24568
24958
  for (const [m] of ECOSYSTEMS) {
24569
24959
  try {
24570
- if (fs23.existsSync(path26.join(cur, m))) return cur;
24960
+ if (fs24.existsSync(path26.join(cur, m))) return cur;
24571
24961
  } catch {
24572
24962
  }
24573
24963
  }
@@ -24593,10 +24983,10 @@ function knownProjectHosts(dir2) {
24593
24983
  const root = manifestRoot(key);
24594
24984
  if (root !== null) {
24595
24985
  for (const [m, registry] of ECOSYSTEMS) {
24596
- if (fs23.existsSync(path26.join(root, m))) out.hosts.add(registry);
24986
+ if (fs24.existsSync(path26.join(root, m))) out.hosts.add(registry);
24597
24987
  }
24598
24988
  try {
24599
- if (fs23.readdirSync(root).some((f) => f.endsWith(".gemspec")))
24989
+ if (fs24.readdirSync(root).some((f) => f.endsWith(".gemspec")))
24600
24990
  out.hosts.add("host:rubygems.org");
24601
24991
  } catch {
24602
24992
  }
@@ -24610,8 +25000,8 @@ function knownProjectHosts(dir2) {
24610
25000
  // src/exposure/writes.ts
24611
25001
  init_esm_shims();
24612
25002
  init_dist();
24613
- import fs24 from "fs";
24614
- import os16 from "os";
25003
+ import fs25 from "fs";
25004
+ import os17 from "os";
24615
25005
  import path27 from "path";
24616
25006
  var WRITE_OPS = /* @__PURE__ */ new Set(["write", "create", "remove"]);
24617
25007
  var LIFECYCLE_RE = /"(scripts|preinstall|install|postinstall|prepare|prepublish|prepublishOnly|prepack|postpack|publish|postpublish)"\s*:/;
@@ -24641,7 +25031,7 @@ function packageScriptsTouched(args, absPath, shellCommand) {
24641
25031
  if (typeof a.content === "string") {
24642
25032
  try {
24643
25033
  const next = JSON.parse(a.content);
24644
- const prev = JSON.parse(fs24.readFileSync(absPath, "utf8"));
25034
+ const prev = JSON.parse(fs25.readFileSync(absPath, "utf8"));
24645
25035
  return JSON.stringify(next.scripts ?? null) !== JSON.stringify(prev.scripts ?? null);
24646
25036
  } catch {
24647
25037
  return LIFECYCLE_RE.test(a.content);
@@ -24651,8 +25041,8 @@ function packageScriptsTouched(args, absPath, shellCommand) {
24651
25041
  }
24652
25042
  function judgeWritesAfterExposure(opts) {
24653
25043
  const holds = [];
24654
- const home = opts.home ?? os16.homedir();
24655
- const tmp = normalizeFilePath(os16.tmpdir());
25044
+ const home = opts.home ?? os17.homedir();
25045
+ const tmp = normalizeFilePath(os17.tmpdir());
24656
25046
  const root = opts.cwd ? normalizeFilePath(opts.cwd, "", home) : null;
24657
25047
  const seen = /* @__PURE__ */ new Set();
24658
25048
  for (const access of opts.accesses) {
@@ -24685,8 +25075,8 @@ function judgeWritesAfterExposure(opts) {
24685
25075
  // src/exposure/marker.ts
24686
25076
  init_esm_shims();
24687
25077
  import crypto3 from "crypto";
24688
- import fs25 from "fs";
24689
- import os17 from "os";
25078
+ import fs26 from "fs";
25079
+ import os18 from "os";
24690
25080
  import path28 from "path";
24691
25081
 
24692
25082
  // src/daemon/exposure-store.ts
@@ -24696,7 +25086,7 @@ var SESSION_EXPOSURE_TTL_MS = 4 * 60 * 60 * 1e3;
24696
25086
  // src/exposure/marker.ts
24697
25087
  init_forecast_context();
24698
25088
  function dir() {
24699
- return path28.join(os17.homedir(), ".node9", "exposure-sessions");
25089
+ return path28.join(os18.homedir(), ".node9", "exposure-sessions");
24700
25090
  }
24701
25091
  function file(sessionId) {
24702
25092
  const id = crypto3.createHash("sha256").update(sessionId).digest("hex").slice(0, 32);
@@ -24707,7 +25097,7 @@ function maybeSessionExposed(sessionId) {
24707
25097
  const forecast = forecastRun();
24708
25098
  if (forecast) return forecast.session.checkExposure(forecast.now) !== null;
24709
25099
  try {
24710
- const st = fs25.statSync(file(sessionId));
25100
+ const st = fs26.statSync(file(sessionId));
24711
25101
  return Date.now() - st.mtimeMs < SESSION_EXPOSURE_TTL_MS;
24712
25102
  } catch (e) {
24713
25103
  return e.code !== "ENOENT";
@@ -24716,7 +25106,7 @@ function maybeSessionExposed(sessionId) {
24716
25106
 
24717
25107
  // src/exposure/roles.ts
24718
25108
  init_esm_shims();
24719
- import fs26 from "fs";
25109
+ import fs27 from "fs";
24720
25110
  var EXPOSURE_LABEL = "\u{1F6E1}\uFE0F Node9 Exposure";
24721
25111
  var AGENT_INTERNAL_PREFIXES = ["ccd_", "visualize"];
24722
25112
  function foldServerName(name) {
@@ -24726,7 +25116,7 @@ var inventoryCache = null;
24726
25116
  var inventoryStamp = null;
24727
25117
  function inventoryFileStamp() {
24728
25118
  try {
24729
- const st = fs26.statSync(getMcpToolsFile());
25119
+ const st = fs27.statSync(getMcpToolsFile());
24730
25120
  return `${st.mtimeMs}:${st.size}`;
24731
25121
  } catch {
24732
25122
  return "";
@@ -24812,15 +25202,15 @@ function sourceWord(toolName) {
24812
25202
  // src/auth/trusted-recipients.ts
24813
25203
  init_esm_shims();
24814
25204
  init_dist();
24815
- import fs27 from "fs";
25205
+ import fs28 from "fs";
24816
25206
  import path29 from "path";
24817
- import os18 from "os";
25207
+ import os19 from "os";
24818
25208
  function getTrustedRecipientsPath() {
24819
- return path29.join(os18.homedir(), ".node9", "trusted-recipients.json");
25209
+ return path29.join(os19.homedir(), ".node9", "trusted-recipients.json");
24820
25210
  }
24821
25211
  function readTrustedRecipients() {
24822
25212
  try {
24823
- const raw = fs27.readFileSync(getTrustedRecipientsPath(), "utf8");
25213
+ const raw = fs28.readFileSync(getTrustedRecipientsPath(), "utf8");
24824
25214
  const parsed = JSON.parse(raw);
24825
25215
  return Array.isArray(parsed.entries) ? parsed.entries.filter((e) => e && typeof e.value === "string") : [];
24826
25216
  } catch {
@@ -24829,10 +25219,10 @@ function readTrustedRecipients() {
24829
25219
  }
24830
25220
  function writeTrustedRecipients(entries) {
24831
25221
  const filePath = getTrustedRecipientsPath();
24832
- fs27.mkdirSync(path29.dirname(filePath), { recursive: true });
25222
+ fs28.mkdirSync(path29.dirname(filePath), { recursive: true });
24833
25223
  const tmp = filePath + ".node9-tmp";
24834
- fs27.writeFileSync(tmp, JSON.stringify({ entries }, null, 2), { mode: 384 });
24835
- fs27.renameSync(tmp, filePath);
25224
+ fs28.writeFileSync(tmp, JSON.stringify({ entries }, null, 2), { mode: 384 });
25225
+ fs28.renameSync(tmp, filePath);
24836
25226
  }
24837
25227
  function addTrustedRecipient(raw, addedBy = "user") {
24838
25228
  const value = normalizeRecipient(raw);
@@ -24847,7 +25237,7 @@ function addTrustedRecipient(raw, addedBy = "user") {
24847
25237
  var cache6 = null;
24848
25238
  function mtimeOf() {
24849
25239
  try {
24850
- return fs27.statSync(getTrustedRecipientsPath()).mtimeMs;
25240
+ return fs28.statSync(getTrustedRecipientsPath()).mtimeMs;
24851
25241
  } catch {
24852
25242
  return 0;
24853
25243
  }
@@ -24866,8 +25256,8 @@ init_forecast_context();
24866
25256
  // src/auth/cloud.ts
24867
25257
  init_esm_shims();
24868
25258
  init_audit();
24869
- import fs28 from "fs";
24870
- import os19 from "os";
25259
+ import fs29 from "fs";
25260
+ import os20 from "os";
24871
25261
  import path30 from "path";
24872
25262
  init_dist();
24873
25263
  var CLOUD_ARRAY_KEYS = /* @__PURE__ */ new Set(["exposureDestinations", "exposureDestinationHashes"]);
@@ -24903,7 +25293,7 @@ async function initNode9SaaS(toolName, args, creds, meta, riskMetadata, agentPol
24903
25293
  let ciContext;
24904
25294
  if (process.env.CI) {
24905
25295
  try {
24906
- const ciContextPath = path30.join(os19.homedir(), ".node9", "ci-context.json");
25296
+ const ciContextPath = path30.join(os20.homedir(), ".node9", "ci-context.json");
24907
25297
  const ci = readCappedText(ciContextPath, 1e4);
24908
25298
  if (!ci) throw new Error("ci-context.json unreadable");
24909
25299
  if (ci.truncated) throw new Error("ci-context.json exceeds 10 KB");
@@ -24945,9 +25335,9 @@ async function initNode9SaaS(toolName, args, creds, meta, riskMetadata, agentPol
24945
25335
  context: {
24946
25336
  agent: meta?.agent,
24947
25337
  mcpServer: meta?.mcpServer,
24948
- hostname: os19.hostname(),
25338
+ hostname: os20.hostname(),
24949
25339
  cwd: process.cwd(),
24950
- platform: os19.platform()
25340
+ platform: os20.platform()
24951
25341
  },
24952
25342
  // The SaaS validates riskMetadata strictly and would drop it whole on
24953
25343
  // an unknown key, so only fields its schema declares go on the wire.
@@ -25021,14 +25411,14 @@ async function resolveNode9SaaS(requestId, creds, approved, decidedBy) {
25021
25411
  });
25022
25412
  clearTimeout(timer);
25023
25413
  if (!res.ok) {
25024
- fs28.appendFileSync(
25414
+ fs29.appendFileSync(
25025
25415
  HOOK_DEBUG_LOG,
25026
25416
  `[resolve-cloud] PATCH ${safeMessage(resolveUrl, 200)} \u2192 HTTP ${res.status}
25027
25417
  `
25028
25418
  );
25029
25419
  }
25030
25420
  } catch (err) {
25031
- fs28.appendFileSync(
25421
+ fs29.appendFileSync(
25032
25422
  HOOK_DEBUG_LOG,
25033
25423
  `[resolve-cloud] PATCH failed for ${safeMessage(requestId, 64)}: ${safeMessage(err)}
25034
25424
  `
@@ -25041,16 +25431,16 @@ init_esm_shims();
25041
25431
  init_dist();
25042
25432
  init_forecast_context();
25043
25433
  init_dist();
25044
- import fs29 from "fs";
25434
+ import fs30 from "fs";
25045
25435
  import path31 from "path";
25046
- import os20 from "os";
25436
+ import os21 from "os";
25047
25437
  function loopStateFile() {
25048
- return path31.join(os20.homedir(), ".node9", "loop-state.json");
25438
+ return path31.join(os21.homedir(), ".node9", "loop-state.json");
25049
25439
  }
25050
25440
  function readState() {
25051
25441
  try {
25052
- if (!fs29.existsSync(loopStateFile())) return [];
25053
- const raw = fs29.readFileSync(loopStateFile(), "utf-8");
25442
+ if (!fs30.existsSync(loopStateFile())) return [];
25443
+ const raw = fs30.readFileSync(loopStateFile(), "utf-8");
25054
25444
  const parsed = JSON.parse(raw);
25055
25445
  if (!Array.isArray(parsed)) return [];
25056
25446
  return parsed;
@@ -25060,10 +25450,10 @@ function readState() {
25060
25450
  }
25061
25451
  function writeState(records) {
25062
25452
  const dir2 = path31.dirname(loopStateFile());
25063
- if (!fs29.existsSync(dir2)) fs29.mkdirSync(dir2, { recursive: true });
25064
- const tmpPath = `${loopStateFile()}.${os20.hostname()}.${process.pid}.tmp`;
25065
- fs29.writeFileSync(tmpPath, JSON.stringify(records));
25066
- fs29.renameSync(tmpPath, loopStateFile());
25453
+ if (!fs30.existsSync(dir2)) fs30.mkdirSync(dir2, { recursive: true });
25454
+ const tmpPath = `${loopStateFile()}.${os21.hostname()}.${process.pid}.tmp`;
25455
+ fs30.writeFileSync(tmpPath, JSON.stringify(records));
25456
+ fs30.renameSync(tmpPath, loopStateFile());
25067
25457
  }
25068
25458
  function recordAndCheck(tool, args, threshold = 3, windowMs = 12e4) {
25069
25459
  try {
@@ -25262,7 +25652,7 @@ function isMcpToolName(toolName) {
25262
25652
  return /^mcp__[^_](?:[^_]|_(?!_))*?__/i.test(toolName);
25263
25653
  }
25264
25654
  function absoluteUploadPath(spelled, cwd) {
25265
- const expanded = spelled === "~" || spelled.startsWith("~/") ? path32.join(os21.homedir(), spelled.slice(1)) : spelled;
25655
+ const expanded = spelled === "~" || spelled.startsWith("~/") ? path32.join(os22.homedir(), spelled.slice(1)) : spelled;
25266
25656
  if (path32.isAbsolute(expanded)) return expanded;
25267
25657
  return cwd && path32.isAbsolute(cwd) ? path32.resolve(cwd, expanded) : expanded;
25268
25658
  }
@@ -25588,8 +25978,17 @@ async function _authorizeHeadlessCore(toolName, args, metaArg, options) {
25588
25978
  const judged = sendsSomething || (!!shellExp?.fetches || writes.length > 0) && mayBeExposed;
25589
25979
  if (judged) {
25590
25980
  const exposure = await checkSessionExposure(meta.sessionId);
25591
- if (exposure.exposed && exposure.record) {
25592
- const rec = exposure.record;
25981
+ const unknownRead = exposure.daemonUnavailable === true && holdsSendsWithoutDaemon(config.settings.mode);
25982
+ const rec = exposure.exposed && exposure.record ? exposure.record : unknownRead ? {
25983
+ sessionId: meta.sessionId,
25984
+ sources: [],
25985
+ destinations: [],
25986
+ userMentioned: [],
25987
+ approved: [],
25988
+ createdAt: 0,
25989
+ expiresAt: 0
25990
+ } : null;
25991
+ if (rec) {
25593
25992
  const dests = new Set(
25594
25993
  acts && !shellExp && !mcpFileWrite ? extractArgDestinations(args) : []
25595
25994
  );
@@ -25648,7 +26047,7 @@ async function _authorizeHeadlessCore(toolName, args, metaArg, options) {
25648
26047
  if (dests.size === 0 && acts && !mcpFileWrite && (role.destructive === true || isDestructiveToolName(bare))) {
25649
26048
  held.push({ dest: "", why: "destructive" });
25650
26049
  }
25651
- const writeHolds = writes.length > 0 ? judgeWritesAfterExposure({
26050
+ const writeHolds = writes.length > 0 && !(unknownRead && !exposure.exposed) ? judgeWritesAfterExposure({
25652
26051
  accesses: writes,
25653
26052
  args,
25654
26053
  shellCommand: shellInput.kind === "shell" ? shellInput.command : null,
@@ -25675,14 +26074,14 @@ async function _authorizeHeadlessCore(toolName, args, metaArg, options) {
25675
26074
  const parts = held.map(
25676
26075
  (h) => h.dest ? `sends to ${destinationValue(h.dest)}, ${h.why === "appeared in what was read" ? `${noun(h.dest)} that appeared in that ${last ? readWord : "content"}` : `${noun(h.dest)} this session did not know`}` : h.why === "repository" ? h.repo.endsWith("/*") ? `sends to a repository on ${h.repo.slice(0, -2)} it does not name (a gist, a new repository or an API call)` : `sends to ${h.repo}, a repository that is not one of this project's remotes` : h.why === "unresolved" ? "sends to a destination this session cannot see (built at run time, or a remote of another repository)" : `is ${bare}, a destructive call, with no known destination`
25677
26076
  );
25678
- const home = os21.homedir();
26077
+ const home = os22.homedir();
25679
26078
  const short = (p) => p.startsWith(home + "/") ? "~" + p.slice(home.length) : p;
25680
26079
  for (const w of writeHolds) {
25681
26080
  parts.push(
25682
26081
  w.why === "outside-project" ? `writes outside the project, to ${short(w.path)}` : w.why === "agent-instruction" ? `writes to ${short(w.path)}, a file that runs code or instructs the agent` : `writes content carrying ${destinationValue(w.destination ?? "")}, ${noun(w.destination ?? "host:x")} that appeared in that ${readWord}`
25683
26082
  );
25684
26083
  }
25685
- exposureReview = `\u{1F6E1}\uFE0F Node9: this session read ${read2}. This call ${parts.join("; and ")}.`;
26084
+ exposureReview = unknownRead && !exposure.exposed ? `\u{1F6E1}\uFE0F Node9: the node9 daemon is not running, so node9 cannot tell what this session read. This call ${parts.join("; and ")}. Start it with: node9 daemon start.` : `\u{1F6E1}\uFE0F Node9: this session read ${read2}. This call ${parts.join("; and ")}.`;
25686
26085
  exposureFromEid = last?.eid;
25687
26086
  if (exposureFromEid) exposureMeta = { exposureFromEid };
25688
26087
  exposureKeys = [