@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.
@@ -470,15 +470,15 @@ function ssrfExemptMatches(entries, normalized) {
470
470
  continue;
471
471
  }
472
472
  if (!target) continue;
473
- const base22 = bitsOf(normalizeIpLiteral(entry.slice(0, slash)) ?? "");
473
+ const base32 = bitsOf(normalizeIpLiteral(entry.slice(0, slash)) ?? "");
474
474
  const prefixText = entry.slice(slash + 1);
475
475
  const prefix = /^\d+$/.test(prefixText) ? Number(prefixText) : NaN;
476
- if (!base22 || !Number.isInteger(prefix) || prefix < 0 || // A v4 range never matches a v6 address, and the reverse: the widths
476
+ if (!base32 || !Number.isInteger(prefix) || prefix < 0 || // A v4 range never matches a v6 address, and the reverse: the widths
477
477
  // differ, so `0.0.0.0/0` does not release `::1`.
478
- base22.length !== target.length || prefix > base22.length) {
478
+ base32.length !== target.length || prefix > base32.length) {
479
479
  continue;
480
480
  }
481
- if (base22.slice(0, prefix) === target.slice(0, prefix)) return true;
481
+ if (base32.slice(0, prefix) === target.slice(0, prefix)) return true;
482
482
  }
483
483
  return false;
484
484
  }
@@ -532,6 +532,10 @@ function reviewsEveryAgentFileWrite(mode) {
532
532
  const level = levelOf(mode);
533
533
  return level === "strict" || level === "block-all";
534
534
  }
535
+ function holdsSendsWithoutDaemon(mode) {
536
+ const level = levelOf(mode);
537
+ return level === "strict" || level === "block-all";
538
+ }
535
539
  function isBlockAll(mode) {
536
540
  return levelOf(mode) === "block-all";
537
541
  }
@@ -1739,6 +1743,39 @@ function protectedLiteral(code2, isProtected) {
1739
1743
  }
1740
1744
  return null;
1741
1745
  }
1746
+ function inlineReadPaths(language, code2, preload = {}) {
1747
+ const out = [];
1748
+ const before = READ_BEFORE[language];
1749
+ const after = READ_AFTER[language];
1750
+ const assigned = /* @__PURE__ */ new Map();
1751
+ for (const m of code2.matchAll(STRING_LITERAL)) {
1752
+ const raw = m[2].trim();
1753
+ const value = raw.replace(/^[<+]\s*/, "");
1754
+ if (!value) continue;
1755
+ const head = code2.slice(Math.max(0, m.index - 160), m.index);
1756
+ const tail = code2.slice(m.index + m[0].length, m.index + m[0].length + 40);
1757
+ if (/^[>|]/.test(raw) || WRITE_MODE_AFTER.test(tail) || PERL_WRITE_BEFORE.test(head)) continue;
1758
+ if (before.test(head) || after?.test(tail) || language === "node" && DOTENV_PATH_OPTION.test(head)) {
1759
+ out.push(value);
1760
+ continue;
1761
+ }
1762
+ const name = ASSIGNED.exec(head)?.[1];
1763
+ if (name) assigned.set(name, [...assigned.get(name) ?? [], value]);
1764
+ }
1765
+ if (assigned.size > 0) {
1766
+ const opened = /* @__PURE__ */ new Set();
1767
+ for (const m of code2.matchAll(OPENED_NAME[language])) opened.add(m[1] ?? m[2]);
1768
+ for (const [name, values] of assigned) if (opened.has(name)) out.push(...values);
1769
+ }
1770
+ if (language === "python" || language === "node") {
1771
+ const d = DOTENV[language];
1772
+ const readsEnv = d.env.test(code2);
1773
+ const loads = /dotenv/.test(code2) && d.load.test(code2) && !d.literalPath.test(code2);
1774
+ if ((loads || preload.dotenv) && (readsEnv || d.values.test(code2))) out.push(".env");
1775
+ if (readsEnv) out.push(...preload.envFiles ?? []);
1776
+ }
1777
+ return out;
1778
+ }
1742
1779
  function amplifies(language, code2) {
1743
1780
  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*\)/;
1744
1781
  const m = loop.exec(code2);
@@ -2593,15 +2630,28 @@ function inlineExecCall(stmt, pipeFed) {
2593
2630
  const fromStdin = () => {
2594
2631
  if (stdinFed) {
2595
2632
  const { code: code2, source } = stdinCodeOf(redirs);
2596
- return { interp, code: [code2], source };
2633
+ return { interp, code: [code2], source, preload };
2597
2634
  }
2598
2635
  return { interp, code: [null], source: "pipe" };
2599
2636
  };
2600
2637
  let positionals2 = 0;
2601
2638
  let selectedProgram = false;
2602
2639
  const codes2 = [];
2640
+ const preload = {};
2641
+ const nodeFamily = inlineLanguageOf(interp) === "node";
2603
2642
  for (let i = 0; i < args.length; i++) {
2604
2643
  const a = args[i];
2644
+ if (nodeFamily && !selectedProgram && codes2.length === 0 && a != null) {
2645
+ const flag = NODE_VALUE_FLAG.exec(a);
2646
+ if (flag) {
2647
+ const value = flag[2] ?? args[++i];
2648
+ if (value != null) {
2649
+ if (/^--env-file/.test(flag[1])) (preload.envFiles ??= []).push(value);
2650
+ else if (/^dotenv\/config(?:\.js)?$/.test(value)) preload.dotenv = true;
2651
+ }
2652
+ continue;
2653
+ }
2654
+ }
2605
2655
  if (a == null) {
2606
2656
  if (codes2.length > 0) break;
2607
2657
  positionals2++;
@@ -2638,7 +2688,12 @@ function inlineExecCall(stmt, pipeFed) {
2638
2688
  }
2639
2689
  }
2640
2690
  if (codes2.length > 0) {
2641
- return { interp, code: codes2, source: codes2.includes(null) ? "variable" : void 0 };
2691
+ return {
2692
+ interp,
2693
+ code: codes2,
2694
+ source: codes2.includes(null) ? "variable" : void 0,
2695
+ preload
2696
+ };
2642
2697
  }
2643
2698
  if (positionals2 === 0 && (stdinFed || pipeFed)) {
2644
2699
  if (!/^(bash|sh|zsh)$/i.test(interp)) return fromStdin();
@@ -3461,7 +3516,7 @@ function extractShellDestTokens(command) {
3461
3516
  }
3462
3517
  function analyzeFsOperation(command) {
3463
3518
  const normalized = normalizeCommandForPolicy(command);
3464
- if (!FS_OP_PRESCREEN_RE.test(normalized) && !FS_OP_PRESCREEN_RE.test(command) && !SOURCES_FILE_RE.test(command))
3519
+ if (!FS_OP_PRESCREEN_RE.test(normalized) && !FS_OP_PRESCREEN_RE.test(command) && !SOURCES_FILE_RE.test(command) && !runsInlineInterpreter(command, normalized))
3465
3520
  return null;
3466
3521
  if (fsOpCache.has(command)) {
3467
3522
  const hit = fsOpCache.get(command) ?? null;
@@ -3469,7 +3524,8 @@ function analyzeFsOperation(command) {
3469
3524
  fsOpCache.set(command, hit);
3470
3525
  return hit;
3471
3526
  }
3472
- const computed = analyzeFsOperationImpl(command) ?? (INDIRECT_SHAPE_RE.test(command) ? structuralJailRead(command) : null);
3527
+ const direct = analyzeFsOperationImpl(command) ?? (INDIRECT_SHAPE_RE.test(command) ? structuralJailRead(command) : null);
3528
+ const computed = direct?.verdict === "block" || !runsInlineInterpreter(command, normalized) ? direct : stricter(direct, inlineCodeJailRead(command));
3473
3529
  if (fsOpCache.size >= FS_OP_CACHE_MAX) {
3474
3530
  const oldest = fsOpCache.keys().next().value;
3475
3531
  if (oldest !== void 0) fsOpCache.delete(oldest);
@@ -3478,8 +3534,8 @@ function analyzeFsOperation(command) {
3478
3534
  return computed;
3479
3535
  }
3480
3536
  function isSensitiveCleanupName(p) {
3481
- const base22 = p.replace(/^.*[\\/]/, "");
3482
- return /^\.env(\.|$)/i.test(base22) || /(?:^|[\\/])\.(?:ssh|aws|gnupg|git)(?:[\\/]|$)/i.test(p) || /\.(?:pem|key|p12|pfx|crt)$/i.test(base22) || /^\.?(?:netrc|npmrc|pgpass|htpasswd)$/i.test(base22) || /^id_(?:rsa|dsa|ecdsa|ed25519)/i.test(base22) || /credential/i.test(p) || /secret/i.test(base22);
3537
+ const base32 = p.replace(/^.*[\\/]/, "");
3538
+ return /^\.env(\.|$)/i.test(base32) || /(?:^|[\\/])\.(?:ssh|aws|gnupg|git)(?:[\\/]|$)/i.test(p) || /\.(?:pem|key|p12|pfx|crt)$/i.test(base32) || /^\.?(?:netrc|npmrc|pgpass|htpasswd)$/i.test(base32) || /^id_(?:rsa|dsa|ecdsa|ed25519)/i.test(base32) || /credential/i.test(p) || /secret/i.test(base32);
3483
3539
  }
3484
3540
  function isWaivableCleanupTarget(p) {
3485
3541
  if (/^[/~]/.test(p) || /^\$/.test(p)) return false;
@@ -4208,6 +4264,24 @@ function structuralJailRead(command) {
4208
4264
  }
4209
4265
  return review;
4210
4266
  }
4267
+ function inlineCodeJailRead(command) {
4268
+ const calls = inlineExecCalls(command);
4269
+ if (calls === PARSE_FAIL) return null;
4270
+ let review = null;
4271
+ for (const call of calls) {
4272
+ const language = inlineLanguageOf(call.interp);
4273
+ if (language === null) continue;
4274
+ for (const code2 of call.code) {
4275
+ if (code2 === null) continue;
4276
+ for (const p of inlineReadPaths(language, code2, call.preload)) {
4277
+ const hit = matchSensitivePath(p);
4278
+ if (hit?.verdict === "block") return hit;
4279
+ review ??= hit;
4280
+ }
4281
+ }
4282
+ }
4283
+ return review;
4284
+ }
4211
4285
  function baseWord(w) {
4212
4286
  return (w ?? "").split("/").pop()?.toLowerCase() ?? "";
4213
4287
  }
@@ -4765,6 +4839,21 @@ function hits(access, paths, directories, context) {
4765
4839
  }
4766
4840
  return false;
4767
4841
  }
4842
+ function hitsNode9Dir(access, dirs, context) {
4843
+ const home = context.homeDirs?.[0] ?? "~";
4844
+ if (access.glob) {
4845
+ const pattern = normalizeFilePath(access.glob, access.cwd ?? context.cwd, home);
4846
+ const magic = pattern.search(/[*?[{]/);
4847
+ const literal = magic < 0 ? pattern : pattern.slice(0, pattern.lastIndexOf("/", magic));
4848
+ return dirs.some((d) => within(literal, d));
4849
+ }
4850
+ const candidates = access.candidates ?? [
4851
+ normalizeFilePath(access.path, access.cwd ?? context.cwd, home)
4852
+ ];
4853
+ return candidates.some(
4854
+ (p) => dirs.some((d) => within(p, d)) || access.recursive === true && dirs.some((d) => within(d, p))
4855
+ );
4856
+ }
4768
4857
  function hitsSystemDir(access, context, dirs = SYSTEM_DIRS) {
4769
4858
  const home = context.homeDirs?.[0] ?? "~";
4770
4859
  const candidates = access.candidates ?? [
@@ -4805,6 +4894,7 @@ function evaluateProtectedFileAccess(accesses, context, settings) {
4805
4894
  const consider = (result) => {
4806
4895
  if (!best || RANK[result.decision] > RANK[best.decision]) best = result;
4807
4896
  };
4897
+ const node9Dirs = homes.map((home) => `${home.replace(/\/$/, "")}/.node9`);
4808
4898
  for (const access of accesses) {
4809
4899
  const mutation = access.operation !== "read";
4810
4900
  let kind;
@@ -4833,6 +4923,10 @@ function evaluateProtectedFileAccess(accesses, context, settings) {
4833
4923
  "files.system-dirs",
4834
4924
  "review-root-home-read",
4835
4925
  "Reading the root account's home requires approval"
4926
+ ] : mutation && hitsNode9Dir(access, node9Dirs, context) ? [
4927
+ "commands.node9-disarm",
4928
+ "review-node9-disarm",
4929
+ "Changing node9's own files requires approval"
4836
4930
  ] : null;
4837
4931
  } catch {
4838
4932
  consider(incompleteFileVerdict(access.operation));
@@ -5438,9 +5532,9 @@ function validateBelgianNationalNumber(raw) {
5438
5532
  if (!/^\d{11}$/.test(d)) return false;
5439
5533
  const month = Number(d.slice(2, 4)) % 20;
5440
5534
  if (month !== 0 && !plausibleDate(String(month).padStart(2, "0"), d.slice(4, 6))) return false;
5441
- const base22 = Number(d.slice(0, 9));
5535
+ const base32 = Number(d.slice(0, 9));
5442
5536
  const check = Number(d.slice(9, 11));
5443
- return 97 - base22 % 97 === check || 97 - (2e9 + base22) % 97 === check;
5537
+ return 97 - base32 % 97 === check || 97 - (2e9 + base32) % 97 === check;
5444
5538
  }
5445
5539
  function validateCuit(raw) {
5446
5540
  const d = raw.replace(/-/g, "");
@@ -6574,9 +6668,9 @@ function evaluateGitHooksFileAccess(args, accesses, context, settings, shell) {
6574
6668
  }
6575
6669
  function agentFileKind(p) {
6576
6670
  const lower2 = p.toLowerCase();
6577
- const base22 = lower2.slice(lower2.lastIndexOf("/") + 1);
6578
- if (INSTRUCTION_NAMES.has(base22)) return "instructions";
6579
- if (base22 === "copilot-instructions.md" && lower2.endsWith("/.github/" + base22))
6671
+ const base32 = lower2.slice(lower2.lastIndexOf("/") + 1);
6672
+ if (INSTRUCTION_NAMES.has(base32)) return "instructions";
6673
+ if (base32 === "copilot-instructions.md" && lower2.endsWith("/.github/" + base32))
6580
6674
  return "instructions";
6581
6675
  if (/\/\.cursor\/rules(?:\/|$)/.test(lower2)) return "instructions";
6582
6676
  if (/\/\.claude\/projects\/[^/]+\/memory(?:\/|$)/.test(lower2)) return "memory";
@@ -6745,7 +6839,7 @@ function evaluateAgentFileWrite(args, accesses, context, settings, shell, hooks
6745
6839
  skills: "agent skill",
6746
6840
  settings: "agent settings"
6747
6841
  };
6748
- const base22 = `${kindLabel[kind]} written: ${access.path}`;
6842
+ const base32 = `${kindLabel[kind]} written: ${access.path}`;
6749
6843
  if (!signal && !every) {
6750
6844
  return {
6751
6845
  decision: "allow",
@@ -6755,14 +6849,14 @@ function evaluateAgentFileWrite(args, accesses, context, settings, shell, hooks
6755
6849
  path: access.path,
6756
6850
  operation: access.operation,
6757
6851
  kind,
6758
- reason: base22,
6852
+ reason: base32,
6759
6853
  ruleDescription: "The AI writes to a file every later agent session loads as instructions.",
6760
- blockedByLabel: `Node9 Files: ${base22}`,
6854
+ blockedByLabel: `Node9 Files: ${base32}`,
6761
6855
  tier: 2
6762
6856
  };
6763
6857
  }
6764
6858
  const ruleName = signal === "content" ? "review-agent-file-content" : signal === "hooks" ? "review-agent-hooks-change" : "review-agent-file-write";
6765
- const reason = signal ? `${base22} (${detail})` : base22;
6859
+ const reason = signal ? `${base32} (${detail})` : base32;
6766
6860
  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.";
6767
6861
  return {
6768
6862
  decision: value === "log" ? "allow" : value,
@@ -8370,6 +8464,139 @@ function scanArgumentSecrets(args) {
8370
8464
  severity: "review"
8371
8465
  } : null;
8372
8466
  }
8467
+ function patternSelects(pattern, targets) {
8468
+ const pieces = pattern.toLowerCase().split(/[\\^$.*+?()[\]{}|]+/).filter(Boolean);
8469
+ if (pieces.length === 0) return false;
8470
+ return targets.some((t) => pieces.every((p) => t.includes(p)));
8471
+ }
8472
+ function sourceText(bytes, pos, end) {
8473
+ if (typeof pos !== "number" || typeof end !== "number" || pos < 0 || end < pos) return "";
8474
+ return new TextDecoder().decode(bytes.subarray(pos, end));
8475
+ }
8476
+ function nextOperand(words, from) {
8477
+ for (let i = from; i < words.length; i++) {
8478
+ const w = words[i];
8479
+ if (w !== null && w.startsWith("-")) continue;
8480
+ return i;
8481
+ }
8482
+ return -1;
8483
+ }
8484
+ function node9Command(words, h) {
8485
+ const head = base2(words[h]);
8486
+ if (head !== "node9" && head !== "node9.cmd" && words[h] !== "@node9/proxy") return null;
8487
+ const s = nextOperand(words, h + 1);
8488
+ const sub = s < 0 ? "" : (words[s] ?? "").toLowerCase();
8489
+ if (sub === "uninstall") return "node9 uninstall";
8490
+ if (sub !== "daemon") return null;
8491
+ const a = nextOperand(words, s + 1);
8492
+ const action = a < 0 ? "" : (words[a] ?? "").toLowerCase();
8493
+ return action === "stop" || action === "uninstall" ? `node9 daemon ${action}` : null;
8494
+ }
8495
+ function killsDaemon(words, texts, h, daemonPid) {
8496
+ const head = base2(words[h]);
8497
+ const args = words.slice(h + 1);
8498
+ if (head === "kill") {
8499
+ for (let i = 0; i < args.length; i++) {
8500
+ const w = args[i];
8501
+ if (w === "-s" || w === "-n") {
8502
+ i++;
8503
+ continue;
8504
+ }
8505
+ if (w !== null && w.startsWith("-")) continue;
8506
+ if (w === null) {
8507
+ if (HOME_PID_FILE_RE.test(texts[h + 1 + i] ?? "")) return "kill of the pid in daemon.pid";
8508
+ continue;
8509
+ }
8510
+ if (daemonPid !== null && w === String(daemonPid)) return `kill ${w} (the node9 daemon)`;
8511
+ }
8512
+ return null;
8513
+ }
8514
+ if (head === "pkill") {
8515
+ let full = false;
8516
+ let pattern = null;
8517
+ for (let i = 0; i < args.length; i++) {
8518
+ const w = args[i];
8519
+ if (w === null) continue;
8520
+ if (PKILL_VALUE_FLAGS.has(w)) {
8521
+ i++;
8522
+ continue;
8523
+ }
8524
+ if (w === "--full") full = true;
8525
+ else if (/^-[a-zA-Z]+$/.test(w) && w.includes("f")) full = true;
8526
+ if (w.startsWith("-")) continue;
8527
+ pattern = w;
8528
+ }
8529
+ if (pattern === null) return null;
8530
+ return patternSelects(pattern, full ? DAEMON_CMDLINES : DAEMON_NAMES) ? `pkill ${full ? "-f " : ""}${pattern}` : null;
8531
+ }
8532
+ if (head === "killall") {
8533
+ const regex = args.some((w) => w === "-r" || w === "--regexp");
8534
+ for (const w of args) {
8535
+ if (w === null || w.startsWith("-")) continue;
8536
+ if (regex ? patternSelects(w, DAEMON_NAMES) : KILLALL_NAMES.has(w.toLowerCase()))
8537
+ return `killall ${w}`;
8538
+ }
8539
+ return null;
8540
+ }
8541
+ if (head === "taskkill" || head === "taskkill.exe") {
8542
+ for (let i = 0; i < args.length - 1; i++) {
8543
+ const flag = (args[i] ?? "").toLowerCase();
8544
+ const value = (args[i + 1] ?? "").toLowerCase();
8545
+ if ((flag === "/im" || flag === "-im") && KILLALL_NAMES.has(value))
8546
+ return `taskkill /IM ${value}`;
8547
+ if ((flag === "/pid" || flag === "-pid") && daemonPid !== null && value === String(daemonPid))
8548
+ return `taskkill /PID ${value} (the node9 daemon)`;
8549
+ }
8550
+ }
8551
+ return null;
8552
+ }
8553
+ function stopsService(words, h) {
8554
+ const head = base2(words[h]);
8555
+ const args = words.slice(h + 1).map((w) => (w ?? "").toLowerCase());
8556
+ if (head === "systemctl") {
8557
+ const action = args.find((w) => STOP_SYSTEMCTL.has(w));
8558
+ if (action && args.some((w) => /^node9-daemon(\.service)?$/.test(w)))
8559
+ return `systemctl ${action} node9-daemon`;
8560
+ }
8561
+ if (head === "launchctl") {
8562
+ const action = args.find((w) => STOP_LAUNCHCTL.has(w));
8563
+ if (action && args.some((w) => w.includes("ai.node9.daemon")))
8564
+ return `launchctl ${action} ai.node9.daemon`;
8565
+ }
8566
+ return null;
8567
+ }
8568
+ function analyzeNode9Disarm(command, daemonPid = null, depth = 0) {
8569
+ if (depth > 3 || !NAME_PRESCREEN_RE.test(command.replace(/[\\'"]/g, ""))) return null;
8570
+ let shown = null;
8571
+ const bytes = new TextEncoder().encode(command);
8572
+ try {
8573
+ walkCommandScope(command, (node) => {
8574
+ if (shown !== null) return false;
8575
+ const n = node;
8576
+ if (!n || n.kind !== "CallExpr") return true;
8577
+ const words = (n.Args || []).map((a) => resolveWordLiteral(a));
8578
+ if (words.length === 0) return true;
8579
+ const texts = (n.Args || []).map((a) => sourceText(bytes, a.pos, a.end));
8580
+ const h = unwrapCommandHead(words);
8581
+ shown = node9Command(words, h) ?? killsDaemon(words, texts, h, daemonPid) ?? stopsService(words, h);
8582
+ if (shown === null) {
8583
+ const payload = literalShellPayload(words, base2(words[0]));
8584
+ if (payload) {
8585
+ const inner = analyzeNode9Disarm(payload, daemonPid, depth + 1);
8586
+ if (inner) shown = inner.reason.replace(/^node9 switched off by the agent: /, "");
8587
+ }
8588
+ }
8589
+ return true;
8590
+ });
8591
+ } catch {
8592
+ }
8593
+ if (shown === null) return null;
8594
+ return {
8595
+ ruleName: RULE_NAME2,
8596
+ reason: `node9 switched off by the agent: ${shown}`,
8597
+ description: DESCRIPTION
8598
+ };
8599
+ }
8373
8600
  function catalogSettings(config) {
8374
8601
  return catalogSettingsFromConfig(config.settings, config.policy);
8375
8602
  }
@@ -8463,6 +8690,10 @@ function egressPolicyVerdict(eg) {
8463
8690
  };
8464
8691
  }
8465
8692
  async function evaluatePolicy(config, toolName, args, context = {}, hostHooks = {}) {
8693
+ const v = await evaluatePolicyVerdict(config, toolName, args, context, hostHooks);
8694
+ return v.decision === "block" && v.checkId !== void 0 && NOT_OVERRIDABLE_CHECKS.has(v.checkId) ? { ...v, overridable: false } : v;
8695
+ }
8696
+ async function evaluatePolicyVerdict(config, toolName, args, context = {}, hostHooks = {}) {
8466
8697
  let protectedVerdict = null;
8467
8698
  const layers = inspectionLayers(config.policy);
8468
8699
  if (context.agent !== "Terminal") {
@@ -8520,7 +8751,7 @@ async function evaluatePolicy(config, toolName, args, context = {}, hostHooks =
8520
8751
  }
8521
8752
  async function evaluatePolicyCore(config, toolName, args, context = {}, hooks = {}) {
8522
8753
  const { agent, cwd, activeEnvironment } = context;
8523
- const { checkProvenance: checkProvenance2, isTrustedHost: isTrustedHost2, isConfiguredRegistry, isGitWorkTreeTop: isGitWorkTreeTop2 } = hooks;
8754
+ const { checkProvenance: checkProvenance2, isTrustedHost: isTrustedHost2, isConfiguredRegistry, isGitWorkTreeTop: isGitWorkTreeTop2, daemonPid } = hooks;
8524
8755
  const settingsView = catalogSettings(config);
8525
8756
  const layers = inspectionLayers(config.policy);
8526
8757
  const cv = (id) => checkValue(settingsView, id);
@@ -8705,6 +8936,23 @@ async function evaluatePolicyCore(config, toolName, args, context = {}, hooks =
8705
8936
  else if (v && !pendingAstReview) pendingAstReview = v;
8706
8937
  }
8707
8938
  }
8939
+ if (cv("commands.node9-disarm") !== "off") {
8940
+ const finding = analyzeNode9Disarm(bashCommand, daemonPid?.() ?? null);
8941
+ if (finding) {
8942
+ const v = applyCheckValue(cv("commands.node9-disarm"), {
8943
+ decision: "review",
8944
+ blockedByLabel: `Node9 (AST): ${finding.ruleName}`,
8945
+ reason: finding.reason,
8946
+ tier: 2,
8947
+ ruleName: finding.ruleName,
8948
+ ruleDescription: finding.description,
8949
+ checkId: "commands.node9-disarm"
8950
+ });
8951
+ if (v?.logged) noteLogged(v);
8952
+ else if (v?.decision === "block") return v;
8953
+ else if (v && !pendingAstReview) pendingAstReview = v;
8954
+ }
8955
+ }
8708
8956
  }
8709
8957
  if (config.policy.smartRules.length > 0) {
8710
8958
  const rmCleanupWaiver = bashCommand !== null && isRmCreatedInCommandCleanup(bashCommand);
@@ -9152,8 +9400,8 @@ function hasRoutingNumber(text) {
9152
9400
  }
9153
9401
  return false;
9154
9402
  }
9155
- function* validCardRuns(text, base22, exemptFractions = true) {
9156
- const re = new RegExp(base22.source, "g");
9403
+ function* validCardRuns(text, base32, exemptFractions = true) {
9404
+ const re = new RegExp(base32.source, "g");
9157
9405
  let m;
9158
9406
  while ((m = re.exec(text)) !== null) {
9159
9407
  const digits = m[0].replace(/\D/g, "");
@@ -9166,17 +9414,17 @@ function* validCardRuns(text, base22, exemptFractions = true) {
9166
9414
  }
9167
9415
  }
9168
9416
  function hasValidCard(text) {
9169
- for (const base22 of CARD_PATTERNS) {
9170
- if (!validCardRuns(text, base22).next().done) return true;
9417
+ for (const base32 of CARD_PATTERNS) {
9418
+ if (!validCardRuns(text, base32).next().done) return true;
9171
9419
  }
9172
9420
  return false;
9173
9421
  }
9174
9422
  function replaceValidCards(text, replace, { exemptFractions = true } = {}) {
9175
9423
  let out = text;
9176
- for (const base22 of CARD_PATTERNS) {
9424
+ for (const base32 of CARD_PATTERNS) {
9177
9425
  let res = "";
9178
9426
  let last = 0;
9179
- for (const run of validCardRuns(out, base22, exemptFractions)) {
9427
+ for (const run of validCardRuns(out, base32, exemptFractions)) {
9180
9428
  res += out.slice(last, run.index) + replace(run.digits);
9181
9429
  last = run.index + run.length;
9182
9430
  }
@@ -9417,9 +9665,9 @@ function maskLeaf(value, key, depth) {
9417
9665
  const used = /* @__PURE__ */ new Set();
9418
9666
  return Object.fromEntries(
9419
9667
  Object.entries(value).map(([k, v]) => {
9420
- const base22 = maskPersonalData(k);
9421
- let name = base22;
9422
- for (let n = 2; used.has(name); n++) name = `${base22}#${n}`;
9668
+ const base32 = maskPersonalData(k);
9669
+ let name = base32;
9670
+ for (let n = 2; used.has(name); n++) name = `${base32}#${n}`;
9423
9671
  used.add(name);
9424
9672
  return [name, maskLeaf(v, k, depth + 1)];
9425
9673
  })
@@ -9924,7 +10172,7 @@ function matchCanaryArgs(args, values) {
9924
10172
  return null;
9925
10173
  }
9926
10174
  }
9927
- var getFilename2, __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, POSTURE_CATEGORY_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, ENCODED_RUN_RE, 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, CANONICAL_EXTRACTOR_VERSION;
10175
+ var getFilename2, __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, POSTURE_CATEGORY_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, ENCODED_RUN_RE, 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, CANONICAL_EXTRACTOR_VERSION;
9928
10176
  var init_dist = __esm({
9929
10177
  "packages/policy-engine/dist/index.mjs"() {
9930
10178
  "use strict";
@@ -11923,7 +12171,7 @@ var init_dist = __esm({
11923
12171
  advice: "Fixed at Review for now."
11924
12172
  },
11925
12173
  "commands.node9-disarm": {
11926
- 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.",
12174
+ 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.",
11927
12175
  example: "After reading a web page, the agent runs node9 session-exposure clear --all",
11928
12176
  advice: "Keep Review: these are your commands to run. Block if the agent should never touch node9."
11929
12177
  },
@@ -12256,7 +12504,7 @@ var init_dist = __esm({
12256
12504
  group: "commands",
12257
12505
  owasp: "ASI10",
12258
12506
  title: "node9 disarmed from the shell",
12259
- catches: "a node9 command that clears a session flag, trusts a destination, pauses the gate or weakens a shield, run by the agent",
12507
+ 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",
12260
12508
  values: ALL,
12261
12509
  defaultValue: "review",
12262
12510
  legacy: ["review-node9-disarm"]
@@ -13137,6 +13385,62 @@ var init_dist = __esm({
13137
13385
  php: PHP
13138
13386
  };
13139
13387
  STRING_LITERAL = /(['"`])((?:\\.|(?!\1)[^\\\n]){1,4096})\1/g;
13388
+ READ_CALL = {
13389
+ python: String.raw`(?:\b(?:io|codecs)\.)?\bopen|\b(?:dotenv_values|load_dotenv|get_key|read_file|loadtxt|read_csv)`,
13390
+ node: String.raw`\b(?:readFileSync|readFile|createReadStream|readTextFile(?:Sync)?|readFileStr|openSync|file)`,
13391
+ perl: String.raw`\b(?:open|sysopen|read_file|slurp)`,
13392
+ ruby: String.raw`\b(?:File|IO)\.(?:read|binread|readlines|foreach|open)|\bDotenv\.(?:load|parse|overload)`,
13393
+ php: String.raw`\b(?:file_get_contents|fopen|file|readfile|parse_ini_file|highlight_file|show_source)`
13394
+ };
13395
+ ARG = String.raw`(?:(?:(?:my|our|local)\s+)?[\w.$]+(?:\s*\(\s*\))?|'[^'\n]{0,8}'|"[^"\n]{0,8}")\s*,\s*`;
13396
+ WRAP = String.raw`(?:(?:new\s+)?[\w.$]+\s*\(\s*(?:${ARG}){0,3}){0,3}`;
13397
+ STR_PREFIX = String.raw`[rRbBuUfF]{0,2}`;
13398
+ WRITE_MODE_LITERAL = String.raw`['"](?=[rbtU+]*w|(?![^'"]*\+)[rbtU]*[ax])[rwaxbtU+]{1,4}['"]`;
13399
+ WRITE_MODE = String.raw`\s*,\s*(?:mode\s*=\s*)?${WRITE_MODE_LITERAL}`;
13400
+ WRITE_MODE_AFTER = new RegExp(`^(?:\\s*\\)){0,3}${WRITE_MODE}`);
13401
+ PERL_WRITE_BEFORE = /['"]\s*(?:\+?[>|]|>>)\s*['"]\s*,\s*$/;
13402
+ readCallBefore = (language) => new RegExp(
13403
+ String.raw`(?:${READ_CALL[language]})(?![\w$])\s*\(?\s*(?:${ARG}){0,3}${WRAP}${STR_PREFIX}$`
13404
+ );
13405
+ READ_BEFORE = {
13406
+ python: readCallBefore("python"),
13407
+ node: readCallBefore("node"),
13408
+ perl: readCallBefore("perl"),
13409
+ ruby: readCallBefore("ruby"),
13410
+ php: readCallBefore("php")
13411
+ };
13412
+ READ_AFTER = {
13413
+ python: new RegExp(
13414
+ String.raw`^\s*\)\s*\.\s*(?:read_text|read_bytes|open)\s*\((?!\s*(?:mode\s*=\s*)?${WRITE_MODE_LITERAL})`
13415
+ )
13416
+ };
13417
+ ASSIGNED = new RegExp(String.raw`([$\w]+)\s*=\s*${WRAP}${STR_PREFIX}$`);
13418
+ openedName = (language) => new RegExp(
13419
+ 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*\(` : ""),
13420
+ "g"
13421
+ );
13422
+ OPENED_NAME = {
13423
+ python: openedName("python"),
13424
+ node: openedName("node"),
13425
+ perl: openedName("perl"),
13426
+ ruby: openedName("ruby"),
13427
+ php: openedName("php")
13428
+ };
13429
+ DOTENV = {
13430
+ node: {
13431
+ load: /\brequire\s*\(\s*['"`]dotenv(?:\/config)?['"`]\s*\)|\bimport\s*(?:[\w${}\s,*]+\s+from\s*)?['"`]dotenv(?:\/config)?['"`]|\bimport\s*\(\s*['"`]dotenv(?:\/config)?['"`]\s*\)/,
13432
+ values: /\.parsed\b|[(,]\s*(?:require\s*\(\s*['"`]dotenv['"`]\s*\)|dotenv)\s*\.\s*config\s*\(/,
13433
+ env: /\b(?:process|Deno|Bun)\s*\.\s*env\b/,
13434
+ literalPath: /\bconfig\s*\(\s*\{[^)]*\bpath\s*:\s*['"`]/
13435
+ },
13436
+ python: {
13437
+ load: /\b(?:load_dotenv|dotenv_values|get_key)\s*\(/,
13438
+ values: /\b(?:dotenv_values|get_key)\s*\(/,
13439
+ env: /\b(?:environ|getenv)\b/,
13440
+ literalPath: /\b(?:load_dotenv|dotenv_values)\s*\(\s*(?:dotenv_path\s*=\s*)?[rRbBuU]?['"]/
13441
+ }
13442
+ };
13443
+ DOTENV_PATH_OPTION = /\bconfig\s*\(\s*\{[^)]*\bpath\s*:\s*$/;
13140
13444
  sharedParser = lazy(() => getSyntax().NewParser());
13141
13445
  MESSAGE_FLAGS = /* @__PURE__ */ new Set([
13142
13446
  "-m",
@@ -13931,6 +14235,7 @@ var init_dist = __esm({
13931
14235
  "rmdir"
13932
14236
  ]);
13933
14237
  INTERP_LEADING_TARGET = /* @__PURE__ */ new Set(["su"]);
14238
+ NODE_VALUE_FLAG = /^(-r|--require|--import|--loader|--experimental-loader|--env-file|--env-file-if-exists|--input-type|-C|--conditions)(?:=(.*))?$/;
13934
14239
  MAX_PAYLOAD_NESTING = 8;
13935
14240
  payloadNesting = 0;
13936
14241
  MAX_INLINE_CALLS = 16;
@@ -14179,6 +14484,8 @@ var init_dist = __esm({
14179
14484
  JAILED_DIR_NAMES = [".ssh", ".aws"];
14180
14485
  shellGlob = (p) => p.replace(/\[!/g, "[^");
14181
14486
  GLOB_META = /(?<!\\)[*?[]/;
14487
+ INLINE_INTERP_PRESCREEN_RE = /(?:^|[^\w-])(?:python|node|tsx|ts-node|deno|bun|perl|ruby|irb|php)(?![A-Za-z_])/i;
14488
+ runsInlineInterpreter = (command, normalized) => INLINE_INTERP_PRESCREEN_RE.test(command) || INLINE_INTERP_PRESCREEN_RE.test(normalized);
14182
14489
  isReaderWord = (w) => w !== null && FS_READ_TOOLS.has(baseWord(w));
14183
14490
  positionalAfter = (words, from, to = words.length) => positionedArgs(words, from, to).map((a) => a.value);
14184
14491
  NONE = { kind: "none" };
@@ -17495,9 +17802,9 @@ zoo`.split("\n");
17495
17802
  const name = n.Name.Value;
17496
17803
  if (n.Append) {
17497
17804
  const prev = this.appended.has(name) ? this.appended.get(name) : resolveWordLiteral(n.Value) === null ? null : void 0;
17498
- const base22 = prev === void 0 ? recordedExpansion(name) : prev;
17805
+ const base32 = prev === void 0 ? recordedExpansion(name) : prev;
17499
17806
  const piece = resolveWordLiteral(n.Value);
17500
- this.appended.set(name, typeof base22 === "string" && piece !== null ? base22 + piece : null);
17807
+ this.appended.set(name, typeof base32 === "string" && piece !== null ? base32 + piece : null);
17501
17808
  this.treeTopVars.delete(name);
17502
17809
  return true;
17503
17810
  }
@@ -19013,12 +19320,50 @@ ${text}` : text);
19013
19320
  const char = value[index] ?? "";
19014
19321
  return RUN_CHAR.test(char) || char !== "" && isInvisibleChar(char);
19015
19322
  };
19323
+ RULE_NAME2 = "review-node9-disarm";
19324
+ 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.";
19325
+ DAEMON_CMDLINES = [
19326
+ "node /usr/lib/node_modules/@node9/proxy/dist/cli.js daemon start",
19327
+ "node9 daemon start"
19328
+ ];
19329
+ DAEMON_NAMES = ["node", "node9"];
19330
+ KILLALL_NAMES = /* @__PURE__ */ new Set(["node", "node9", "node.exe", "node9.exe"]);
19331
+ STOP_SYSTEMCTL = /* @__PURE__ */ new Set(["stop", "disable", "mask", "kill"]);
19332
+ STOP_LAUNCHCTL = /* @__PURE__ */ new Set(["unload", "bootout", "remove", "stop", "kill", "disable"]);
19333
+ PKILL_VALUE_FLAGS = /* @__PURE__ */ new Set([
19334
+ "-u",
19335
+ "-U",
19336
+ "-g",
19337
+ "-G",
19338
+ "-P",
19339
+ "-s",
19340
+ "-t",
19341
+ "-F",
19342
+ "--signal",
19343
+ "--euid",
19344
+ "--uid",
19345
+ "--pgroup",
19346
+ "--group",
19347
+ "--parent",
19348
+ "--session",
19349
+ "--terminal",
19350
+ "--pidfile",
19351
+ "--ns",
19352
+ "--nslist"
19353
+ ]);
19354
+ NAME_PRESCREEN_RE = /node9|kill|systemctl|launchctl|daemon/i;
19355
+ base2 = (w) => (w ?? "").split(/[\\/]/).pop()?.toLowerCase() ?? "";
19356
+ HOME_PID_FILE_RE = /(?:^|[\s"'(=`])(?:~|\$HOME|\$\{HOME\}|\/home\/[^/\s]+|\/Users\/[^/\s]+|\/root)\/\.node9\/daemon\.pid/;
19016
19357
  VERDICT_RANK = {
19017
19358
  allow: 0,
19018
19359
  review: 1,
19019
19360
  block: 2
19020
19361
  };
19021
19362
  SQL_DML_KEYWORDS = /* @__PURE__ */ new Set(["select", "insert", "update", "delete", "merge", "upsert"]);
19363
+ NOT_OVERRIDABLE_CHECKS = /* @__PURE__ */ new Set([
19364
+ "data.credential-files",
19365
+ "data.credential-files-other"
19366
+ ]);
19022
19367
  MAX_DESTINATIONS = 200;
19023
19368
  MAX_SCAN_CHARS = 1e5;
19024
19369
  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;
@@ -19362,7 +19707,7 @@ ${text}` : text);
19362
19707
  { view: "separators-stripped", decoder: "separators", stripped: true }
19363
19708
  ];
19364
19709
  LONG_OUTPUT_THRESHOLD_BYTES = 100 * 1024;
19365
- CANONICAL_EXTRACTOR_VERSION = "canonical-v37";
19710
+ CANONICAL_EXTRACTOR_VERSION = "canonical-v39";
19366
19711
  }
19367
19712
  });
19368
19713
 
@@ -19839,7 +20184,7 @@ function statAndFirstLine(file2) {
19839
20184
  fs3.closeSync(fd);
19840
20185
  }
19841
20186
  }
19842
- function listCodexSessionFiles(base3 = codexSessionsDir()) {
20187
+ function listCodexSessionFiles(base4 = codexSessionsDir()) {
19843
20188
  const files = [];
19844
20189
  const walk3 = (dir2) => {
19845
20190
  try {
@@ -19851,8 +20196,8 @@ function listCodexSessionFiles(base3 = codexSessionsDir()) {
19851
20196
  } catch {
19852
20197
  }
19853
20198
  };
19854
- walk3(base3);
19855
- if (path4.basename(base3) === "sessions") walk3(path4.join(path4.dirname(base3), "archived_sessions"));
20199
+ walk3(base4);
20200
+ if (path4.basename(base4) === "sessions") walk3(path4.join(path4.dirname(base4), "archived_sessions"));
19856
20201
  const sessions = /* @__PURE__ */ new Map();
19857
20202
  for (const file2 of files.sort()) {
19858
20203
  try {
@@ -20263,8 +20608,8 @@ function claudeDesktopConfigPath(homeDir = os6.homedir()) {
20263
20608
  }
20264
20609
  function opencodeConfigDir(home = os6.homedir()) {
20265
20610
  const xdg = process.env.XDG_CONFIG_HOME;
20266
- const base3 = xdg && path9.isAbsolute(xdg) ? xdg : path9.join(home, ".config");
20267
- return path9.join(base3, "opencode");
20611
+ const base4 = xdg && path9.isAbsolute(xdg) ? xdg : path9.join(home, ".config");
20612
+ return path9.join(base4, "opencode");
20268
20613
  }
20269
20614
  function commonBinDirs(home) {
20270
20615
  return [
@@ -21779,8 +22124,8 @@ import os13 from "os";
21779
22124
  function sanitizeSsrfAllow(entries, source) {
21780
22125
  const kept = [];
21781
22126
  for (const entry of entries) {
21782
- const base3 = entry.includes("/") ? entry.slice(0, entry.indexOf("/")).trim() : entry;
21783
- const m = classifySsrf(base3);
22127
+ const base4 = entry.includes("/") ? entry.slice(0, entry.indexOf("/")).trim() : entry;
22128
+ const m = classifySsrf(base4);
21784
22129
  if (m && !m.overridable) {
21785
22130
  const what = entry.includes("/") ? "covers only protected addresses" : "is a protected address";
21786
22131
  process.emitWarning(
@@ -22542,12 +22887,12 @@ function getConfig(cwd) {
22542
22887
  continue;
22543
22888
  explicit[id] = v;
22544
22889
  }
22545
- const base3 = resolveCheckMap({
22890
+ const base4 = resolveCheckMap({
22546
22891
  ...catalogSettingsFrom(mergedSettings, mergedPolicy),
22547
22892
  checks: explicit
22548
22893
  });
22549
22894
  for (const [id, v] of Object.entries(v2ChecksByPath.get(projectPath)?.checks ?? {})) {
22550
- if (CHECK_VERDICT_RANK[v] > CHECK_VERDICT_RANK[base3[id] ?? "off"]) {
22895
+ if (CHECK_VERDICT_RANK[v] > CHECK_VERDICT_RANK[base4[id] ?? "off"]) {
22551
22896
  explicit[id] = v;
22552
22897
  checkSources[id] = "project";
22553
22898
  }
@@ -23193,6 +23538,39 @@ var init_egress = __esm({
23193
23538
  }
23194
23539
  });
23195
23540
 
23541
+ // src/utils/read-small-file.ts
23542
+ import fs16 from "fs";
23543
+ function readSmallFile(file2, maxBytes) {
23544
+ let fd;
23545
+ try {
23546
+ fd = fs16.openSync(file2, fs16.constants.O_RDONLY | (fs16.constants.O_NONBLOCK ?? 0));
23547
+ } catch {
23548
+ return null;
23549
+ }
23550
+ try {
23551
+ const st = fs16.fstatSync(fd);
23552
+ if (!st.isFile() || st.size > maxBytes) return null;
23553
+ const buf = Buffer.alloc(Math.min(st.size, maxBytes));
23554
+ let read2 = 0;
23555
+ while (read2 < buf.length) {
23556
+ const n = fs16.readSync(fd, buf, read2, buf.length - read2, read2);
23557
+ if (n === 0) break;
23558
+ read2 += n;
23559
+ }
23560
+ return buf.toString("utf8", 0, read2);
23561
+ } catch {
23562
+ return null;
23563
+ } finally {
23564
+ fs16.closeSync(fd);
23565
+ }
23566
+ }
23567
+ var init_read_small_file = __esm({
23568
+ "src/utils/read-small-file.ts"() {
23569
+ "use strict";
23570
+ init_esm_shims();
23571
+ }
23572
+ });
23573
+
23196
23574
  // src/policy/gate-order.ts
23197
23575
  function fileGateOutcome(file2, panicMode) {
23198
23576
  if (!file2) return null;
@@ -23412,7 +23790,7 @@ var init_gate_order = __esm({
23412
23790
  });
23413
23791
 
23414
23792
  // src/policy/file-access.ts
23415
- import fs16 from "fs";
23793
+ import fs17 from "fs";
23416
23794
  import os14 from "os";
23417
23795
  import path17 from "path";
23418
23796
  function absoluteSpelling(p, cwd, home) {
@@ -23426,13 +23804,13 @@ function physical(p, followFinal, links = 0) {
23426
23804
  return { path: `${parent.path}/${path17.basename(p)}`, missing: parent.missing };
23427
23805
  }
23428
23806
  try {
23429
- return { path: fs16.realpathSync.native(p), missing: false };
23807
+ return { path: fs17.realpathSync.native(p), missing: false };
23430
23808
  } catch (error) {
23431
23809
  const code2 = error.code;
23432
23810
  if (!["ENOENT", "ENOTDIR"].includes(code2 ?? "")) throw error;
23433
23811
  try {
23434
- if (fs16.lstatSync(p).isSymbolicLink()) {
23435
- const target = fs16.readlinkSync(p);
23812
+ if (fs17.lstatSync(p).isSymbolicLink()) {
23813
+ const target = fs17.readlinkSync(p);
23436
23814
  return physical(
23437
23815
  path17.isAbsolute(target) ? target : `${path17.dirname(p)}/${target}`,
23438
23816
  true,
@@ -23472,14 +23850,14 @@ function prepareFileAccesses(tool, args, cwd = process.cwd(), inspection = {}, h
23472
23850
  const raw = absoluteSpelling(access.path, access.cwd ?? cwd, home);
23473
23851
  if (access.directoryTarget) {
23474
23852
  try {
23475
- if (!fs16.statSync(absoluteSpelling(access.directoryTarget, access.cwd ?? cwd, home)).isDirectory())
23853
+ if (!fs17.statSync(absoluteSpelling(access.directoryTarget, access.cwd ?? cwd, home)).isDirectory())
23476
23854
  continue;
23477
23855
  } catch {
23478
23856
  }
23479
23857
  }
23480
23858
  if (access.operation === "create") {
23481
23859
  try {
23482
- fs16.statSync(raw);
23860
+ fs17.statSync(raw);
23483
23861
  continue;
23484
23862
  } catch {
23485
23863
  }
@@ -23535,7 +23913,7 @@ var init_file_access = __esm({
23535
23913
  });
23536
23914
 
23537
23915
  // src/supply-chain/osv-index.ts
23538
- import fs17 from "fs";
23916
+ import fs18 from "fs";
23539
23917
  import os15 from "os";
23540
23918
  import path18 from "path";
23541
23919
  import { createHash as createHash4 } from "crypto";
@@ -23566,7 +23944,7 @@ function shardPath(eco, shard) {
23566
23944
  }
23567
23945
  function readMeta(eco) {
23568
23946
  try {
23569
- const m = JSON.parse(fs17.readFileSync(path18.join(ecoDir(eco), "meta.json"), "utf8"));
23947
+ const m = JSON.parse(fs18.readFileSync(path18.join(ecoDir(eco), "meta.json"), "utf8"));
23570
23948
  if (typeof m?.syncedAt !== "string" || typeof m?.lastModifiedMs !== "number") return null;
23571
23949
  return m;
23572
23950
  } catch {
@@ -23575,7 +23953,7 @@ function readMeta(eco) {
23575
23953
  }
23576
23954
  function readShard(eco, shard) {
23577
23955
  try {
23578
- const s = JSON.parse(fs17.readFileSync(shardPath(eco, shard), "utf8"));
23956
+ const s = JSON.parse(fs18.readFileSync(shardPath(eco, shard), "utf8"));
23579
23957
  return s && typeof s === "object" && !Array.isArray(s) ? s : {};
23580
23958
  } catch {
23581
23959
  return {};
@@ -23697,15 +24075,15 @@ var init_net = __esm({
23697
24075
 
23698
24076
  // src/supply-chain/osv-online.ts
23699
24077
  async function queryOsvMalicious(queries) {
23700
- const base3 = endpoints().osvApi;
23701
- if (!base3 || queries.length === 0) return null;
24078
+ const base4 = endpoints().osvApi;
24079
+ if (!base4 || queries.length === 0) return null;
23702
24080
  const body = JSON.stringify({
23703
24081
  queries: queries.map((q) => ({
23704
24082
  package: { name: q.name, ecosystem: q.ecosystem },
23705
24083
  ...q.version ? { version: q.version } : {}
23706
24084
  }))
23707
24085
  });
23708
- const res = await fetchJson(`${base3}/v1/querybatch`, {
24086
+ const res = await fetchJson(`${base4}/v1/querybatch`, {
23709
24087
  timeoutMs: TIMEOUT_MS,
23710
24088
  maxBytes: 1024 * 1024,
23711
24089
  body
@@ -23768,9 +24146,9 @@ function pypiRepoOf(doc) {
23768
24146
  return null;
23769
24147
  }
23770
24148
  async function npmSourceRepo(name, version) {
23771
- const base3 = endpoints().npmRegistry;
23772
- if (!base3) return "unknown";
23773
- const url = `${base3}/-/npm/v1/attestations/${name.replaceAll("/", "%2F")}@${encodeURIComponent(version)}`;
24149
+ const base4 = endpoints().npmRegistry;
24150
+ if (!base4) return "unknown";
24151
+ const url = `${base4}/-/npm/v1/attestations/${name.replaceAll("/", "%2F")}@${encodeURIComponent(version)}`;
23774
24152
  return read(url, npmRepoOf);
23775
24153
  }
23776
24154
  async function read(url, repoOf) {
@@ -23783,9 +24161,9 @@ async function read(url, repoOf) {
23783
24161
  }
23784
24162
  }
23785
24163
  async function pypiSourceRepo(name, version, filename) {
23786
- const base3 = endpoints().pypi;
23787
- if (!base3) return "unknown";
23788
- const url = `${base3}/integrity/${encodeURIComponent(name)}/${encodeURIComponent(version)}/${encodeURIComponent(filename)}/provenance`;
24164
+ const base4 = endpoints().pypi;
24165
+ if (!base4) return "unknown";
24166
+ const url = `${base4}/integrity/${encodeURIComponent(name)}/${encodeURIComponent(version)}/${encodeURIComponent(filename)}/provenance`;
23789
24167
  return read(url, pypiRepoOf);
23790
24168
  }
23791
24169
  function comparePypi(a, b) {
@@ -23837,9 +24215,9 @@ async function npmVersionDocument(url, selector) {
23837
24215
  };
23838
24216
  }
23839
24217
  async function npmInfo(name, version, freshWindowMs, raw) {
23840
- const base3 = endpoints().npmRegistry;
23841
- if (!base3) return null;
23842
- const url = `${base3}/${name.replaceAll("/", "%2F")}`;
24218
+ const base4 = endpoints().npmRegistry;
24219
+ if (!base4) return null;
24220
+ const url = `${base4}/${name.replaceAll("/", "%2F")}`;
23843
24221
  const aliasStart = raw?.indexOf("@npm:") ?? -1;
23844
24222
  const target = aliasStart > 0 ? raw.slice(aliasStart + 5) : raw?.replace(/^npm:/, "");
23845
24223
  const specStart = target?.indexOf("@", 1) ?? -1;
@@ -23907,9 +24285,9 @@ async function npmInfo(name, version, freshWindowMs, raw) {
23907
24285
  return info;
23908
24286
  }
23909
24287
  async function pypiInfo(name, version) {
23910
- const base3 = endpoints().pypi;
23911
- if (!base3) return null;
23912
- const url = version ? `${base3}/pypi/${encodeURIComponent(name)}/${encodeURIComponent(version)}/json` : `${base3}/pypi/${encodeURIComponent(name)}/json`;
24288
+ const base4 = endpoints().pypi;
24289
+ if (!base4) return null;
24290
+ const url = version ? `${base4}/pypi/${encodeURIComponent(name)}/${encodeURIComponent(version)}/json` : `${base4}/pypi/${encodeURIComponent(name)}/json`;
23913
24291
  const doc = await fetchJson(url, { timeoutMs: TIMEOUT_MS3, maxBytes: MAX_BYTES2 });
23914
24292
  if (!doc?.info?.version) return null;
23915
24293
  const times = (doc.urls ?? []).map((u) => u.upload_time_iso_8601 ? Date.parse(u.upload_time_iso_8601) : NaN).filter((t) => Number.isFinite(t));
@@ -23933,9 +24311,9 @@ function pypiHistoryOf(releases, version) {
23933
24311
  };
23934
24312
  }
23935
24313
  async function pypiHistory(name, version) {
23936
- const base3 = endpoints().pypi;
23937
- if (!base3) return null;
23938
- const doc = await fetchJson(`${base3}/pypi/${encodeURIComponent(name)}/json`, {
24314
+ const base4 = endpoints().pypi;
24315
+ if (!base4) return null;
24316
+ const doc = await fetchJson(`${base4}/pypi/${encodeURIComponent(name)}/json`, {
23939
24317
  timeoutMs: TIMEOUT_MS3,
23940
24318
  maxBytes: MAX_BYTES2
23941
24319
  });
@@ -23962,7 +24340,7 @@ var init_registry2 = __esm({
23962
24340
  });
23963
24341
 
23964
24342
  // src/supply-chain/local-resolve.ts
23965
- import fs18 from "fs";
24343
+ import fs19 from "fs";
23966
24344
  import path19 from "path";
23967
24345
  function resolveInstalled(name, cwd) {
23968
24346
  if (!cwd || !path19.isAbsolute(cwd)) return null;
@@ -23972,7 +24350,7 @@ function resolveInstalled(name, cwd) {
23972
24350
  const pkgDir = path19.join(dir2, "node_modules", ...name.split("/"));
23973
24351
  const manifest = path19.join(pkgDir, "package.json");
23974
24352
  try {
23975
- const raw = fs18.readFileSync(manifest, "utf8");
24353
+ const raw = fs19.readFileSync(manifest, "utf8");
23976
24354
  const parsed = JSON.parse(raw);
23977
24355
  if (typeof parsed.version === "string" && parsed.version.length > 0) {
23978
24356
  return { version: parsed.version, dir: pkgDir };
@@ -24272,7 +24650,7 @@ var init_check = __esm({
24272
24650
  // src/supply-chain/registries.ts
24273
24651
  import os16 from "os";
24274
24652
  import path21 from "path";
24275
- import fs19 from "fs";
24653
+ import fs20 from "fs";
24276
24654
  function userFiles(home, env) {
24277
24655
  const xdg = env.XDG_CONFIG_HOME || path21.join(home, ".config");
24278
24656
  return [
@@ -24357,7 +24735,7 @@ function configuredRegistryHosts(cwd, env = process.env, home = os16.homedir())
24357
24735
  const r = readCappedText(
24358
24736
  file2,
24359
24737
  MAX_CONFIG_BYTES,
24360
- fs19.constants.O_RDONLY | (fs19.constants.O_NONBLOCK ?? 0)
24738
+ fs20.constants.O_RDONLY | (fs20.constants.O_NONBLOCK ?? 0)
24361
24739
  );
24362
24740
  if (r) hostsInText(r.text, hosts);
24363
24741
  }
@@ -24415,7 +24793,7 @@ var init_registries = __esm({
24415
24793
  });
24416
24794
 
24417
24795
  // src/utils/provenance.ts
24418
- import fs20 from "fs";
24796
+ import fs21 from "fs";
24419
24797
  import path22 from "path";
24420
24798
  import os17 from "os";
24421
24799
  function findInPath(cmd) {
@@ -24425,7 +24803,7 @@ function findInPath(cmd) {
24425
24803
  if (!dir2) continue;
24426
24804
  const full = path22.join(dir2, cmd);
24427
24805
  try {
24428
- fs20.accessSync(full, fs20.constants.X_OK);
24806
+ fs21.accessSync(full, fs21.constants.X_OK);
24429
24807
  return full;
24430
24808
  } catch {
24431
24809
  }
@@ -24470,7 +24848,7 @@ function checkProvenance(cmd, cwd) {
24470
24848
  reason: "binary not found in PATH"
24471
24849
  };
24472
24850
  }
24473
- resolved = fs20.realpathSync(found);
24851
+ resolved = fs21.realpathSync(found);
24474
24852
  } catch {
24475
24853
  return {
24476
24854
  resolvedPath: cmd,
@@ -24479,7 +24857,7 @@ function checkProvenance(cmd, cwd) {
24479
24857
  };
24480
24858
  }
24481
24859
  try {
24482
- const stat = fs20.statSync(resolved);
24860
+ const stat = fs21.statSync(resolved);
24483
24861
  if (stat.mode & 2) {
24484
24862
  return {
24485
24863
  resolvedPath: resolved,
@@ -24516,8 +24894,9 @@ var init_provenance2 = __esm({
24516
24894
  });
24517
24895
 
24518
24896
  // src/policy/index.ts
24519
- import fs21 from "fs";
24897
+ import fs22 from "fs";
24520
24898
  import path23 from "path";
24899
+ import os18 from "os";
24521
24900
  async function evaluatePolicy2(toolName, args, agent, cwd, opts) {
24522
24901
  const config = opts?.config ?? getConfig(cwd);
24523
24902
  const prepared = opts?.fileAccess ?? prepareFileAccesses(toolName, args, cwd, inspectionLayers(config.policy));
@@ -24544,14 +24923,26 @@ async function evaluatePolicy2(toolName, args, agent, cwd, opts) {
24544
24923
  isTrustedHost: config.policy.trustedHostsManaged ? (host2) => matchesTrustedHost(host2, config.policy.trustedHosts) : isTrustedHost,
24545
24924
  readAgentSettings,
24546
24925
  isConfiguredRegistry: (host2, at) => configuredRegistryHosts(at).has(host2),
24547
- isGitWorkTreeTop
24926
+ isGitWorkTreeTop,
24927
+ daemonPid: daemonPidFromFile
24548
24928
  }
24549
24929
  );
24550
24930
  }
24931
+ function daemonPidFromFile() {
24932
+ if (forecastRun()) return null;
24933
+ const text = readSmallFile(path23.join(os18.homedir(), ".node9", "daemon.pid"), 4096);
24934
+ if (text === null) return null;
24935
+ try {
24936
+ const pid = JSON.parse(text).pid;
24937
+ return typeof pid === "number" && Number.isInteger(pid) && pid > 0 && pid <= 4194304 ? pid : null;
24938
+ } catch {
24939
+ return null;
24940
+ }
24941
+ }
24551
24942
  function isGitWorkTreeTop(dir2, cwd) {
24552
24943
  if (!path23.isAbsolute(cwd)) return true;
24553
24944
  try {
24554
- return fs21.existsSync(path23.join(path23.resolve(cwd, dir2), ".git"));
24945
+ return fs22.existsSync(path23.join(path23.resolve(cwd, dir2), ".git"));
24555
24946
  } catch {
24556
24947
  return true;
24557
24948
  }
@@ -24560,7 +24951,7 @@ function readAgentSettings(absPath) {
24560
24951
  const r = readCappedText(
24561
24952
  absPath,
24562
24953
  MAX_AGENT_SETTINGS_BYTES,
24563
- fs21.constants.O_RDONLY | (fs21.constants.O_NONBLOCK ?? 0)
24954
+ fs22.constants.O_RDONLY | (fs22.constants.O_NONBLOCK ?? 0)
24564
24955
  );
24565
24956
  return r && !r.truncated ? r.text : null;
24566
24957
  }
@@ -24574,6 +24965,8 @@ var init_policy = __esm({
24574
24965
  init_esm_shims();
24575
24966
  init_dist();
24576
24967
  init_read_capped();
24968
+ init_read_small_file();
24969
+ init_forecast_context();
24577
24970
  init_dlp();
24578
24971
  init_gate_order();
24579
24972
  init_file_access();
@@ -25743,46 +26136,13 @@ var init_codex = __esm({
25743
26136
  }
25744
26137
  });
25745
26138
 
25746
- // src/utils/read-small-file.ts
25747
- import fs22 from "fs";
25748
- function readSmallFile(file2, maxBytes) {
25749
- let fd;
25750
- try {
25751
- fd = fs22.openSync(file2, fs22.constants.O_RDONLY | (fs22.constants.O_NONBLOCK ?? 0));
25752
- } catch {
25753
- return null;
25754
- }
25755
- try {
25756
- const st = fs22.fstatSync(fd);
25757
- if (!st.isFile() || st.size > maxBytes) return null;
25758
- const buf = Buffer.alloc(Math.min(st.size, maxBytes));
25759
- let read2 = 0;
25760
- while (read2 < buf.length) {
25761
- const n = fs22.readSync(fd, buf, read2, buf.length - read2, read2);
25762
- if (n === 0) break;
25763
- read2 += n;
25764
- }
25765
- return buf.toString("utf8", 0, read2);
25766
- } catch {
25767
- return null;
25768
- } finally {
25769
- fs22.closeSync(fd);
25770
- }
25771
- }
25772
- var init_read_small_file = __esm({
25773
- "src/utils/read-small-file.ts"() {
25774
- "use strict";
25775
- init_esm_shims();
25776
- }
25777
- });
25778
-
25779
26139
  // src/posture/agent-permissions.ts
25780
- import os18 from "os";
26140
+ import os19 from "os";
25781
26141
  import path25 from "path";
25782
26142
  function readSmall(file2) {
25783
26143
  return readSmallFile(file2, MAX_FILE_BYTES2);
25784
26144
  }
25785
- function base2(agent, severity, title) {
26145
+ function base3(agent, severity, title) {
25786
26146
  return {
25787
26147
  category: CATEGORY,
25788
26148
  severity,
@@ -25809,7 +26169,7 @@ function claudeCode(home) {
25809
26169
  const findings = [];
25810
26170
  const mode = typeof cfg.permissions?.defaultMode === "string" ? cfg.permissions.defaultMode : "";
25811
26171
  if (CLAUDE_NO_PROMPT_MODES.has(mode) || cfg.dangerouslySkipPermissions === true) {
25812
- const f = base2("Claude Code", "high", "Claude Code runs without asking");
26172
+ const f = base3("Claude Code", "high", "Claude Code runs without asking");
25813
26173
  f.what = "Claude Code's own permission prompt is switched off for every session.";
25814
26174
  f.why = CLAUDE_NO_PROMPT_MODES.has(mode) ? `~/.claude/settings.json sets permissions.defaultMode to ${mode}.` : "~/.claude/settings.json sets dangerouslySkipPermissions.";
25815
26175
  f.detail = [
@@ -25820,7 +26180,7 @@ function claudeCode(home) {
25820
26180
  }
25821
26181
  for (const ci of analyzeAgentConfig(".claude/settings.json", text)) {
25822
26182
  if (ci.rule !== "CI-1.broad-allow" && ci.rule !== "CI-1.exec-escape-grant") continue;
25823
- const f = base2(
26183
+ const f = base3(
25824
26184
  "Claude Code",
25825
26185
  CI_SEVERITY[ci.severity],
25826
26186
  ci.rule === "CI-1.broad-allow" ? "Claude Code pre-approves broad tools for every session" : "Claude Code pre-approves a command that can run other programs"
@@ -25840,7 +26200,7 @@ function codex(home) {
25840
26200
  const findings = [];
25841
26201
  for (const ci of analyzeCodexConfig(".codex/config.toml", text)) {
25842
26202
  if (ci.rule !== "CI-1.codex-unsafe-defaults") continue;
25843
- const f = base2("Codex", "high", "Codex runs without approval or without a sandbox");
26203
+ const f = base3("Codex", "high", "Codex runs without approval or without a sandbox");
25844
26204
  f.what = "Codex's own approval or sandbox is switched off for every session.";
25845
26205
  f.why = "~/.codex/config.toml sets it for every project.";
25846
26206
  f.detail = ci.signals.slice(0, 6);
@@ -25873,7 +26233,7 @@ function gemini(home) {
25873
26233
  };
25874
26234
  visit2(cfg, "", 0);
25875
26235
  if (hits2.length === 0) return [];
25876
- const f = base2("Gemini CLI", "high", "Gemini CLI accepts every tool call on its own");
26236
+ const f = base3("Gemini CLI", "high", "Gemini CLI accepts every tool call on its own");
25877
26237
  f.what = "Gemini CLI's own confirmation is switched off for every session.";
25878
26238
  f.why = "~/.gemini/settings.json sets it.";
25879
26239
  f.detail = hits2.slice(0, 4);
@@ -25881,7 +26241,7 @@ function gemini(home) {
25881
26241
  return [f];
25882
26242
  }
25883
26243
  function checkAgentPermissions(ctx) {
25884
- const home = ctx.home || os18.homedir();
26244
+ const home = ctx.home || os19.homedir();
25885
26245
  return [...claudeCode(home), ...codex(home), ...gemini(home)];
25886
26246
  }
25887
26247
  var CATEGORY, CLAUDE_NO_PROMPT_MODES, MAX_FILE_BYTES2, CI_SEVERITY;
@@ -25905,7 +26265,7 @@ var init_agent_permissions = __esm({
25905
26265
  });
25906
26266
 
25907
26267
  // src/posture/outside-content.ts
25908
- import os19 from "os";
26268
+ import os20 from "os";
25909
26269
  function outsideSources(home, roles) {
25910
26270
  const out = [];
25911
26271
  const seen = /* @__PURE__ */ new Set();
@@ -25924,7 +26284,7 @@ function outsideSources(home, roles) {
25924
26284
  return out;
25925
26285
  }
25926
26286
  function checkOutsideContent(ctx) {
25927
- const home = ctx.home || os19.homedir();
26287
+ const home = ctx.home || os20.homedir();
25928
26288
  const config = getConfig(ctx.cwd);
25929
26289
  const roles = config.policy.untrustedContent?.roles ?? {};
25930
26290
  const sources = outsideSources(home, roles);
@@ -25982,7 +26342,7 @@ var init_outside_content = __esm({
25982
26342
 
25983
26343
  // src/posture/instruction-files.ts
25984
26344
  import fs23 from "fs";
25985
- import os20 from "os";
26345
+ import os21 from "os";
25986
26346
  import path26 from "path";
25987
26347
  function listMd(dir2, depth, out) {
25988
26348
  if (out.length >= MAX_FILES || depth < 0) return;
@@ -26041,7 +26401,7 @@ function counts(ci, memory) {
26041
26401
  return memory ? ci.severity === "high" || ci.severity === "critical" : true;
26042
26402
  }
26043
26403
  function checkInstructionFiles(ctx) {
26044
- const home = ctx.home || os20.homedir();
26404
+ const home = ctx.home || os21.homedir();
26045
26405
  const findings = [];
26046
26406
  const detail = [];
26047
26407
  let worst = "medium";
@@ -26109,9 +26469,9 @@ var init_instruction_files = __esm({
26109
26469
  // src/skill-pin.ts
26110
26470
  import fs24 from "fs";
26111
26471
  import path27 from "path";
26112
- import os21 from "os";
26472
+ import os22 from "os";
26113
26473
  import crypto3 from "crypto";
26114
- function getPinsFilePath(home = os21.homedir()) {
26474
+ function getPinsFilePath(home = os22.homedir()) {
26115
26475
  return path27.join(home, ".node9", "skill-pins.json");
26116
26476
  }
26117
26477
  function getRootKey(absPath) {
@@ -26132,7 +26492,7 @@ function readSkillPinsSafe(home) {
26132
26492
  return { ok: false, reason: "corrupt", detail: String(err) };
26133
26493
  }
26134
26494
  }
26135
- function defaultSkillRoots(_cwd, home = os21.homedir()) {
26495
+ function defaultSkillRoots(_cwd, home = os22.homedir()) {
26136
26496
  const marketplaces = path27.join(home, ".claude", "plugins", "marketplaces");
26137
26497
  const roots = [];
26138
26498
  let registries;
@@ -26168,7 +26528,7 @@ var init_skill_pin = __esm({
26168
26528
  });
26169
26529
 
26170
26530
  // src/posture/supply-chain.ts
26171
- import os22 from "os";
26531
+ import os23 from "os";
26172
26532
  import path28 from "path";
26173
26533
  import { parse as parseToml7 } from "smol-toml";
26174
26534
  function isNode9Managed(command, args = []) {
@@ -26197,7 +26557,7 @@ function readServers(file2, format, agent) {
26197
26557
  }
26198
26558
  }
26199
26559
  function checkSupplyChain(ctx) {
26200
- const home = ctx.home || os22.homedir();
26560
+ const home = ctx.home || os23.homedir();
26201
26561
  const servers = [];
26202
26562
  for (const spec of AGENT_SPECS) {
26203
26563
  if (!spec.mcpFile || spec.mcpInventory === false) continue;
@@ -26622,7 +26982,7 @@ var init_startup_log = __esm({
26622
26982
  // src/daemon/service.ts
26623
26983
  import fs27 from "fs";
26624
26984
  import path29 from "path";
26625
- import os23 from "os";
26985
+ import os24 from "os";
26626
26986
  import { spawnSync, execFileSync } from "child_process";
26627
26987
  function isLaunchdInstalled() {
26628
26988
  return fs27.existsSync(LAUNCHD_PLIST);
@@ -26630,7 +26990,7 @@ function isLaunchdInstalled() {
26630
26990
  function isSystemdInstalled() {
26631
26991
  return fs27.existsSync(SYSTEMD_UNIT);
26632
26992
  }
26633
- function windowsStartupDir(homeDir = os23.homedir()) {
26993
+ function windowsStartupDir(homeDir = os24.homedir()) {
26634
26994
  const appData = process.env.APPDATA || path29.join(homeDir, "AppData", "Roaming");
26635
26995
  return path29.join(appData, "Microsoft", "Windows", "Start Menu", "Programs", "Startup");
26636
26996
  }
@@ -26679,8 +27039,8 @@ var init_service = __esm({
26679
27039
  init_startup_log();
26680
27040
  init_api_url();
26681
27041
  LAUNCHD_LABEL = "ai.node9.daemon";
26682
- LAUNCHD_PLIST = path29.join(os23.homedir(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
26683
- SYSTEMD_UNIT_DIR = path29.join(os23.homedir(), ".config", "systemd", "user");
27042
+ LAUNCHD_PLIST = path29.join(os24.homedir(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
27043
+ SYSTEMD_UNIT_DIR = path29.join(os24.homedir(), ".config", "systemd", "user");
26684
27044
  SYSTEMD_UNIT = path29.join(SYSTEMD_UNIT_DIR, "node9-daemon.service");
26685
27045
  STARTUP_FILE = "node9-daemon.vbs";
26686
27046
  WIN_LAUNCHER = () => path29.join(windowsStartupDir(), STARTUP_FILE);
@@ -26690,9 +27050,9 @@ var init_service = __esm({
26690
27050
  });
26691
27051
 
26692
27052
  // src/posture/coverage.ts
26693
- import os24 from "os";
27053
+ import os25 from "os";
26694
27054
  function checkCoverage(ctx) {
26695
- const home = ctx.home || os24.homedir();
27055
+ const home = ctx.home || os25.homedir();
26696
27056
  const findings = [];
26697
27057
  const protectedAgents = getAgentWiring(home).filter((r) => r.isProtected);
26698
27058
  if (protectedAgents.length === 0) {
@@ -26910,14 +27270,14 @@ function scorePosture(findings, checksRun) {
26910
27270
  (f) => f.coverage?.state !== "covered" && f.coverage?.state !== "cant-fix"
26911
27271
  );
26912
27272
  const count = (sev) => open.filter((f) => f.severity === sev && !f.scoreWeight).length;
26913
- const base3 = computeSecurityScore({
27273
+ const base4 = computeSecurityScore({
26914
27274
  critical: count("critical"),
26915
27275
  high: count("high"),
26916
27276
  medium: count("medium"),
26917
27277
  total: Math.max(checksRun, 1)
26918
27278
  });
26919
27279
  const headroom = open.reduce((sum, f) => sum + (f.scoreWeight ?? 0), 0);
26920
- const score = Math.max(0, base3.score - headroom);
27280
+ const score = Math.max(0, base4.score - headroom);
26921
27281
  const tier = score >= 80 ? "good" : score >= 50 ? "at-risk" : "critical";
26922
27282
  return { score, tier };
26923
27283
  }
@@ -27091,7 +27451,7 @@ var init_enforcement = __esm({
27091
27451
  });
27092
27452
 
27093
27453
  // src/posture/index.ts
27094
- import os25 from "os";
27454
+ import os26 from "os";
27095
27455
  function dropEnforcementRedundant(findings) {
27096
27456
  const coveragePresent = findings.some((f) => f.category === "Coverage");
27097
27457
  if (!coveragePresent) return findings;
@@ -27117,7 +27477,7 @@ async function runChecks(checks, ctx) {
27117
27477
  }
27118
27478
  async function runPosture(opts = {}) {
27119
27479
  const ctx = {
27120
- home: opts.home ?? os25.homedir(),
27480
+ home: opts.home ?? os26.homedir(),
27121
27481
  cwd: opts.cwd ?? process.cwd(),
27122
27482
  agent: opts.agent,
27123
27483
  env: opts.env
@@ -27215,7 +27575,7 @@ var init_render = __esm({
27215
27575
  import chalk3 from "chalk";
27216
27576
  import fs28 from "fs";
27217
27577
  import path31 from "path";
27218
- import os26 from "os";
27578
+ import os27 from "os";
27219
27579
  function buildSensitivePaths(home, cwd) {
27220
27580
  const homeFiles = homeCredentialFiles(home).map(({ full, label: label2, description, score }) => ({
27221
27581
  full,
@@ -27254,7 +27614,7 @@ function isReadable(filePath) {
27254
27614
  }
27255
27615
  }
27256
27616
  function runBlast() {
27257
- const home = os26.homedir();
27617
+ const home = os27.homedir();
27258
27618
  const cwd = process.cwd();
27259
27619
  const paths = buildSensitivePaths(home, cwd);
27260
27620
  let scoreDeduction = 0;
@@ -27462,7 +27822,7 @@ var init_decision_record = __esm({
27462
27822
  import fs29 from "fs";
27463
27823
  import http from "http";
27464
27824
  import path32 from "path";
27465
- import os27 from "os";
27825
+ import os28 from "os";
27466
27826
  async function checkStatePredicates(predicates) {
27467
27827
  if (predicates.length === 0) return {};
27468
27828
  if (forecastRun()) return null;
@@ -27488,7 +27848,7 @@ function resolveDaemonPort(env = process.env) {
27488
27848
  function getInternalToken() {
27489
27849
  if (forecastRun()) return null;
27490
27850
  try {
27491
- const pidFile = path32.join(os27.homedir(), ".node9", "daemon.pid");
27851
+ const pidFile = path32.join(os28.homedir(), ".node9", "daemon.pid");
27492
27852
  if (!fs29.existsSync(pidFile)) return null;
27493
27853
  const data = JSON.parse(fs29.readFileSync(pidFile, "utf-8"));
27494
27854
  process.kill(data.pid, 0);
@@ -27499,7 +27859,7 @@ function getInternalToken() {
27499
27859
  }
27500
27860
  function isDaemonRunning() {
27501
27861
  if (forecastRun()) return false;
27502
- const pidFile = path32.join(os27.homedir(), ".node9", "daemon.pid");
27862
+ const pidFile = path32.join(os28.homedir(), ".node9", "daemon.pid");
27503
27863
  if (fs29.existsSync(pidFile)) {
27504
27864
  let pid;
27505
27865
  let port;
@@ -27546,11 +27906,11 @@ async function daemonHasInteractiveApprover(timeoutMs = 400) {
27546
27906
  }
27547
27907
  }
27548
27908
  async function registerDaemonEntry(toolName, args, meta, riskMetadata, activityId, cwd, recoveryCommand, skipBackgroundAuth, viewOnly, localSmartRuleMatched, socketActivitySent, sessionId, card) {
27549
- const base3 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
27909
+ const base4 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
27550
27910
  const ctrl = new AbortController();
27551
27911
  const timer = setTimeout(() => ctrl.abort(), 5e3);
27552
27912
  try {
27553
- const res = await fetch(`${base3}/check`, {
27913
+ const res = await fetch(`${base4}/check`, {
27554
27914
  method: "POST",
27555
27915
  headers: { "Content-Type": "application/json" },
27556
27916
  body: JSON.stringify({
@@ -27660,8 +28020,8 @@ function daemonGet(urlPath, signal, token = getInternalToken()) {
27660
28020
  });
27661
28021
  }
27662
28022
  async function notifyDaemonViewer(toolName, args, meta, riskMetadata, activityId, socketActivitySent, extra) {
27663
- const base3 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
27664
- const res = await fetch(`${base3}/check`, {
28023
+ const base4 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
28024
+ const res = await fetch(`${base4}/check`, {
27665
28025
  method: "POST",
27666
28026
  headers: { "Content-Type": "application/json" },
27667
28027
  body: JSON.stringify({
@@ -27693,9 +28053,9 @@ async function notifyTaint(filePath, source, fromEid) {
27693
28053
  return;
27694
28054
  }
27695
28055
  if (!isDaemonRunning()) return;
27696
- const base3 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
28056
+ const base4 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
27697
28057
  try {
27698
- await fetch(`${base3}/taint`, {
28058
+ await fetch(`${base4}/taint`, {
27699
28059
  method: "POST",
27700
28060
  headers: { "Content-Type": "application/json" },
27701
28061
  // Phase D2 — fromEid is the audit eid that created this taint (edge source).
@@ -27713,9 +28073,9 @@ async function checkTaint(paths) {
27713
28073
  return record2 ? { tainted: true, record: record2 } : { tainted: false };
27714
28074
  }
27715
28075
  if (!isDaemonRunning()) return { tainted: false, daemonUnavailable: true };
27716
- const base3 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
28076
+ const base4 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
27717
28077
  try {
27718
- const res = await fetch(`${base3}/taint/check`, {
28078
+ const res = await fetch(`${base4}/taint/check`, {
27719
28079
  method: "POST",
27720
28080
  headers: { "Content-Type": "application/json" },
27721
28081
  body: JSON.stringify({ paths }),
@@ -27743,9 +28103,9 @@ async function notifyWritten(paths) {
27743
28103
  return;
27744
28104
  }
27745
28105
  if (paths.length === 0 || !isDaemonRunning()) return;
27746
- const base3 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
28106
+ const base4 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
27747
28107
  try {
27748
- await fetch(`${base3}/written`, {
28108
+ await fetch(`${base4}/written`, {
27749
28109
  method: "POST",
27750
28110
  headers: exposureHeaders(),
27751
28111
  body: JSON.stringify({ paths: paths.slice(0, 64) }),
@@ -27759,9 +28119,9 @@ async function checkWritten(paths) {
27759
28119
  const forecast = forecastRun();
27760
28120
  if (forecast) return { written: forecast.session.written(paths) };
27761
28121
  if (!isDaemonRunning()) return { written: [], daemonUnavailable: true };
27762
- const base3 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
28122
+ const base4 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
27763
28123
  try {
27764
- const res = await fetch(`${base3}/written/check`, {
28124
+ const res = await fetch(`${base4}/written/check`, {
27765
28125
  method: "POST",
27766
28126
  headers: exposureHeaders(),
27767
28127
  body: JSON.stringify({ paths: paths.slice(0, 64) }),
@@ -27783,9 +28143,9 @@ async function checkSessionTaint(sessionId) {
27783
28143
  return record2 ? { tainted: true, record: record2, exposed } : { tainted: false, exposed };
27784
28144
  }
27785
28145
  if (!sessionId || !isDaemonRunning()) return { tainted: false };
27786
- const base3 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
28146
+ const base4 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
27787
28147
  try {
27788
- const res = await fetch(`${base3}/session-taint/check`, {
28148
+ const res = await fetch(`${base4}/session-taint/check`, {
27789
28149
  method: "POST",
27790
28150
  headers: exposureHeaders(),
27791
28151
  body: JSON.stringify({ sessionId }),
@@ -27803,8 +28163,8 @@ function exposureHeaders() {
27803
28163
  };
27804
28164
  }
27805
28165
  async function exposurePost(route, body, timeoutMs) {
27806
- const base3 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
27807
- const res = await fetch(`${base3}${route}`, {
28166
+ const base4 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
28167
+ const res = await fetch(`${base4}${route}`, {
27808
28168
  method: "POST",
27809
28169
  headers: exposureHeaders(),
27810
28170
  body: JSON.stringify(body),
@@ -27819,7 +28179,8 @@ async function checkSessionExposure(sessionId) {
27819
28179
  const record2 = forecast.session.checkExposure(forecast.now);
27820
28180
  return record2 ? { exposed: true, record: record2 } : { exposed: false };
27821
28181
  }
27822
- if (!sessionId || !isDaemonRunning()) return { exposed: false };
28182
+ if (!sessionId) return { exposed: false };
28183
+ if (!isDaemonRunning()) return { exposed: false, daemonUnavailable: true };
27823
28184
  try {
27824
28185
  return await exposurePost(
27825
28186
  "/session-exposure/check",
@@ -27843,8 +28204,8 @@ async function approveSessionExposure(sessionId, keys) {
27843
28204
  }
27844
28205
  }
27845
28206
  async function resolveViaDaemon(id, decision, internalToken, source, record2) {
27846
- const base3 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
27847
- const res = await fetch(`${base3}/resolve/${id}`, {
28207
+ const base4 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
28208
+ const res = await fetch(`${base4}/resolve/${id}`, {
27848
28209
  method: "POST",
27849
28210
  headers: { "Content-Type": "application/json", "X-Node9-Internal": internalToken },
27850
28211
  body: JSON.stringify({ decision, ...source && { source }, ...record2 && { record: record2 } }),
@@ -28108,7 +28469,7 @@ var init_scan_watermark = __esm({
28108
28469
 
28109
28470
  // src/cli/aggregate/report-audit.ts
28110
28471
  import fs31 from "fs";
28111
- import os28 from "os";
28472
+ import os29 from "os";
28112
28473
  import path34 from "path";
28113
28474
  function buildTestTimestamps(allEntries) {
28114
28475
  const testTs = /* @__PURE__ */ new Set();
@@ -28546,10 +28907,10 @@ function dimensionOfBlock(checkedBy, ruleName) {
28546
28907
  }
28547
28908
  function aggregateReportFromAudit(period, opts = {}) {
28548
28909
  const now = opts.now ?? /* @__PURE__ */ new Date();
28549
- const auditLogPath2 = opts.auditLogPath ?? path34.join(os28.homedir(), ".node9", "audit.log");
28550
- const claudeProjectsDir = opts.claudeProjectsDir ?? path34.join(os28.homedir(), ".claude", "projects");
28910
+ const auditLogPath2 = opts.auditLogPath ?? path34.join(os29.homedir(), ".node9", "audit.log");
28911
+ const claudeProjectsDir = opts.claudeProjectsDir ?? path34.join(os29.homedir(), ".claude", "projects");
28551
28912
  const codexSessionsDir2 = opts.codexSessionsDir ?? codexSessionsDir();
28552
- const geminiTmpDir = opts.geminiTmpDir ?? path34.join(os28.homedir(), ".gemini", "tmp");
28913
+ const geminiTmpDir = opts.geminiTmpDir ?? path34.join(os29.homedir(), ".gemini", "tmp");
28553
28914
  const hasAuditFile = fs31.existsSync(auditLogPath2);
28554
28915
  const allEntries = opts.preloadedAuditEntries ?? parseAuditLog(auditLogPath2);
28555
28916
  const unackedDlp = allEntries.filter((e) => e.source === "response-dlp");
@@ -28946,9 +29307,9 @@ var init_catalog = __esm({
28946
29307
  // src/daemon/mcp-tools.ts
28947
29308
  import fs32 from "fs";
28948
29309
  import path35 from "path";
28949
- import os29 from "os";
29310
+ import os30 from "os";
28950
29311
  function getMcpToolsFile() {
28951
- return path35.join(os29.homedir(), ".node9", "mcp-tools.json");
29312
+ return path35.join(os30.homedir(), ".node9", "mcp-tools.json");
28952
29313
  }
28953
29314
  function readMcpToolsConfig() {
28954
29315
  try {
@@ -28970,9 +29331,9 @@ var init_mcp_tools = __esm({
28970
29331
 
28971
29332
  // src/apps/state.ts
28972
29333
  import fs33 from "fs";
28973
- import os30 from "os";
29334
+ import os31 from "os";
28974
29335
  import path36 from "path";
28975
- function appsFilePath(home = os30.homedir()) {
29336
+ function appsFilePath(home = os31.homedir()) {
28976
29337
  return path36.join(home, ".node9", "apps.json");
28977
29338
  }
28978
29339
  function emptyFile() {
@@ -29442,7 +29803,7 @@ var init_native = __esm({
29442
29803
 
29443
29804
  // src/auth/session-script.ts
29444
29805
  import fs34 from "fs";
29445
- import os31 from "os";
29806
+ import os32 from "os";
29446
29807
  import path39 from "path";
29447
29808
  function readScriptFile(resolved) {
29448
29809
  let fd;
@@ -29474,16 +29835,16 @@ function absolute(p, cwd, home) {
29474
29835
  const spelled = p === "~" || p.startsWith("~/") ? home + p.slice(1) : p;
29475
29836
  return path39.resolve(cwd, spelled);
29476
29837
  }
29477
- async function resolveSessionScript(toolName, args, cwd, layers, deps, home = os31.homedir()) {
29838
+ async function resolveSessionScript(toolName, args, cwd, layers, deps, home = os32.homedir()) {
29478
29839
  try {
29479
29840
  const d = deps ?? { checkWritten, readScript: readScriptFile };
29480
29841
  const shell = resolveShellInput(toolName, args, layers);
29481
29842
  if (shell.kind !== "shell") return null;
29482
29843
  const operands2 = shellScriptOperands(shell.command);
29483
29844
  if (operands2.length === 0) return null;
29484
- const base3 = cwd ?? process.cwd();
29845
+ const base4 = cwd ?? process.cwd();
29485
29846
  const byResolved = /* @__PURE__ */ new Map();
29486
- for (const op of operands2) byResolved.set(absolute(op, base3, home), op);
29847
+ for (const op of operands2) byResolved.set(absolute(op, base4, home), op);
29487
29848
  const { written } = await d.checkWritten([...byResolved.keys()]);
29488
29849
  for (const resolved of written) {
29489
29850
  const spelled = byResolved.get(resolved);
@@ -29495,7 +29856,7 @@ async function resolveSessionScript(toolName, args, cwd, layers, deps, home = os
29495
29856
  path: spelled,
29496
29857
  resolved,
29497
29858
  args: scriptArgs,
29498
- fileAccess: prepareFileAccesses("Bash", scriptArgs, base3, layers, home)
29859
+ fileAccess: prepareFileAccesses("Bash", scriptArgs, base4, layers, home)
29499
29860
  };
29500
29861
  }
29501
29862
  return null;
@@ -29503,17 +29864,17 @@ async function resolveSessionScript(toolName, args, cwd, layers, deps, home = os
29503
29864
  return null;
29504
29865
  }
29505
29866
  }
29506
- function sessionWriteTargets(toolName, args, fileAccess, cwd, home = os31.homedir()) {
29507
- const base3 = cwd ?? process.cwd();
29867
+ function sessionWriteTargets(toolName, args, fileAccess, cwd, home = os32.homedir()) {
29868
+ const base4 = cwd ?? process.cwd();
29508
29869
  const out = /* @__PURE__ */ new Set();
29509
29870
  const argsObj = args && typeof args === "object" ? args : {};
29510
29871
  const named = argsObj.file_path ?? argsObj.path ?? argsObj.filename;
29511
29872
  if (typeof named === "string" && named && /write|edit|create|save|append/i.test(toolName))
29512
- out.add(absolute(named, base3, home));
29873
+ out.add(absolute(named, base4, home));
29513
29874
  for (const a of fileAccess?.accesses ?? []) {
29514
29875
  if (a.operation !== "write" && a.operation !== "create") continue;
29515
29876
  if (a.glob || a.recursive) continue;
29516
- out.add(absolute(a.path, a.cwd ?? base3, home));
29877
+ out.add(absolute(a.path, a.cwd ?? base4, home));
29517
29878
  if (out.size >= MAX_RECORDED_WRITES) break;
29518
29879
  }
29519
29880
  return [...out];
@@ -29700,7 +30061,7 @@ var init_known_hosts = __esm({
29700
30061
 
29701
30062
  // src/exposure/writes.ts
29702
30063
  import fs37 from "fs";
29703
- import os32 from "os";
30064
+ import os33 from "os";
29704
30065
  import path42 from "path";
29705
30066
  function writtenContent(args) {
29706
30067
  if (!args || typeof args !== "object" || Array.isArray(args)) return [];
@@ -29738,8 +30099,8 @@ function packageScriptsTouched(args, absPath, shellCommand) {
29738
30099
  }
29739
30100
  function judgeWritesAfterExposure(opts) {
29740
30101
  const holds = [];
29741
- const home = opts.home ?? os32.homedir();
29742
- const tmp = normalizeFilePath(os32.tmpdir());
30102
+ const home = opts.home ?? os33.homedir();
30103
+ const tmp = normalizeFilePath(os33.tmpdir());
29743
30104
  const root = opts.cwd ? normalizeFilePath(opts.cwd, "", home) : null;
29744
30105
  const seen = /* @__PURE__ */ new Set();
29745
30106
  for (const access of opts.accesses) {
@@ -29792,10 +30153,10 @@ var init_exposure_store = __esm({
29792
30153
  // src/exposure/marker.ts
29793
30154
  import crypto4 from "crypto";
29794
30155
  import fs38 from "fs";
29795
- import os33 from "os";
30156
+ import os34 from "os";
29796
30157
  import path43 from "path";
29797
30158
  function dir() {
29798
- return path43.join(os33.homedir(), ".node9", "exposure-sessions");
30159
+ return path43.join(os34.homedir(), ".node9", "exposure-sessions");
29799
30160
  }
29800
30161
  function file(sessionId) {
29801
30162
  const id = crypto4.createHash("sha256").update(sessionId).digest("hex").slice(0, 32);
@@ -29926,9 +30287,9 @@ var init_roles = __esm({
29926
30287
  // src/auth/trusted-recipients.ts
29927
30288
  import fs40 from "fs";
29928
30289
  import path44 from "path";
29929
- import os34 from "os";
30290
+ import os35 from "os";
29930
30291
  function getTrustedRecipientsPath() {
29931
- return path44.join(os34.homedir(), ".node9", "trusted-recipients.json");
30292
+ return path44.join(os35.homedir(), ".node9", "trusted-recipients.json");
29932
30293
  }
29933
30294
  function readTrustedRecipients() {
29934
30295
  try {
@@ -29983,7 +30344,7 @@ var init_trusted_recipients = __esm({
29983
30344
  // src/auth/state.ts
29984
30345
  import fs41 from "fs";
29985
30346
  import path45 from "path";
29986
- import os35 from "os";
30347
+ import os36 from "os";
29987
30348
  function extractCommandPattern(toolName, args) {
29988
30349
  const lower2 = toolName.toLowerCase();
29989
30350
  if (lower2 !== "bash" && lower2 !== "execute_bash" && lower2 !== "shell") return void 0;
@@ -30060,7 +30421,7 @@ function writeTrustSession(toolName, durationMs, args) {
30060
30421
  }
30061
30422
  function getPersistentDecision(toolName) {
30062
30423
  try {
30063
- const file2 = path45.join(os35.homedir(), ".node9", "decisions.json");
30424
+ const file2 = path45.join(os36.homedir(), ".node9", "decisions.json");
30064
30425
  if (!fs41.existsSync(file2)) return null;
30065
30426
  const decisions = JSON.parse(fs41.readFileSync(file2, "utf-8"));
30066
30427
  const d = decisions[toolName];
@@ -30077,14 +30438,14 @@ var init_state2 = __esm({
30077
30438
  init_policy();
30078
30439
  init_atomic_write();
30079
30440
  init_forecast_context();
30080
- PAUSED_FILE = path45.join(os35.homedir(), ".node9", "PAUSED");
30081
- TRUST_FILE = path45.join(os35.homedir(), ".node9", "trust.json");
30441
+ PAUSED_FILE = path45.join(os36.homedir(), ".node9", "PAUSED");
30442
+ TRUST_FILE = path45.join(os36.homedir(), ".node9", "trust.json");
30082
30443
  }
30083
30444
  });
30084
30445
 
30085
30446
  // src/auth/cloud.ts
30086
30447
  import fs42 from "fs";
30087
- import os36 from "os";
30448
+ import os37 from "os";
30088
30449
  import path46 from "path";
30089
30450
  function maskArgsForCloud(args) {
30090
30451
  if (args === void 0 || args === null) return args;
@@ -30118,7 +30479,7 @@ async function initNode9SaaS(toolName, args, creds, meta, riskMetadata, agentPol
30118
30479
  let ciContext;
30119
30480
  if (process.env.CI) {
30120
30481
  try {
30121
- const ciContextPath = path46.join(os36.homedir(), ".node9", "ci-context.json");
30482
+ const ciContextPath = path46.join(os37.homedir(), ".node9", "ci-context.json");
30122
30483
  const ci = readCappedText(ciContextPath, 1e4);
30123
30484
  if (!ci) throw new Error("ci-context.json unreadable");
30124
30485
  if (ci.truncated) throw new Error("ci-context.json exceeds 10 KB");
@@ -30160,9 +30521,9 @@ async function initNode9SaaS(toolName, args, creds, meta, riskMetadata, agentPol
30160
30521
  context: {
30161
30522
  agent: meta?.agent,
30162
30523
  mcpServer: meta?.mcpServer,
30163
- hostname: os36.hostname(),
30524
+ hostname: os37.hostname(),
30164
30525
  cwd: process.cwd(),
30165
- platform: os36.platform()
30526
+ platform: os37.platform()
30166
30527
  },
30167
30528
  // The SaaS validates riskMetadata strictly and would drop it whole on
30168
30529
  // an unknown key, so only fields its schema declares go on the wire.
@@ -30267,9 +30628,9 @@ var init_cloud = __esm({
30267
30628
  // src/loop-detector.ts
30268
30629
  import fs43 from "fs";
30269
30630
  import path47 from "path";
30270
- import os37 from "os";
30631
+ import os38 from "os";
30271
30632
  function loopStateFile() {
30272
- return path47.join(os37.homedir(), ".node9", "loop-state.json");
30633
+ return path47.join(os38.homedir(), ".node9", "loop-state.json");
30273
30634
  }
30274
30635
  function readState() {
30275
30636
  try {
@@ -30285,7 +30646,7 @@ function readState() {
30285
30646
  function writeState(records) {
30286
30647
  const dir2 = path47.dirname(loopStateFile());
30287
30648
  if (!fs43.existsSync(dir2)) fs43.mkdirSync(dir2, { recursive: true });
30288
- const tmpPath = `${loopStateFile()}.${os37.hostname()}.${process.pid}.tmp`;
30649
+ const tmpPath = `${loopStateFile()}.${os38.hostname()}.${process.pid}.tmp`;
30289
30650
  fs43.writeFileSync(tmpPath, JSON.stringify(records));
30290
30651
  fs43.renameSync(tmpPath, loopStateFile());
30291
30652
  }
@@ -30449,7 +30810,7 @@ var init_card_result = __esm({
30449
30810
  });
30450
30811
 
30451
30812
  // src/auth/orchestrator.ts
30452
- import os38 from "os";
30813
+ import os39 from "os";
30453
30814
  import path48 from "path";
30454
30815
  function isProtectedPathRule(ruleName) {
30455
30816
  if (!ruleName) return false;
@@ -30497,7 +30858,7 @@ function isMcpToolName(toolName) {
30497
30858
  return /^mcp__[^_](?:[^_]|_(?!_))*?__/i.test(toolName);
30498
30859
  }
30499
30860
  function absoluteUploadPath(spelled, cwd) {
30500
- const expanded = spelled === "~" || spelled.startsWith("~/") ? path48.join(os38.homedir(), spelled.slice(1)) : spelled;
30861
+ const expanded = spelled === "~" || spelled.startsWith("~/") ? path48.join(os39.homedir(), spelled.slice(1)) : spelled;
30501
30862
  if (path48.isAbsolute(expanded)) return expanded;
30502
30863
  return cwd && path48.isAbsolute(cwd) ? path48.resolve(cwd, expanded) : expanded;
30503
30864
  }
@@ -30777,8 +31138,17 @@ async function _authorizeHeadlessCore(toolName, args, metaArg, options) {
30777
31138
  const judged = sendsSomething || (!!shellExp?.fetches || writes.length > 0) && mayBeExposed;
30778
31139
  if (judged) {
30779
31140
  const exposure = await checkSessionExposure(meta.sessionId);
30780
- if (exposure.exposed && exposure.record) {
30781
- const rec = exposure.record;
31141
+ const unknownRead = exposure.daemonUnavailable === true && holdsSendsWithoutDaemon(config.settings.mode);
31142
+ const rec = exposure.exposed && exposure.record ? exposure.record : unknownRead ? {
31143
+ sessionId: meta.sessionId,
31144
+ sources: [],
31145
+ destinations: [],
31146
+ userMentioned: [],
31147
+ approved: [],
31148
+ createdAt: 0,
31149
+ expiresAt: 0
31150
+ } : null;
31151
+ if (rec) {
30782
31152
  const dests = new Set(
30783
31153
  acts && !shellExp && !mcpFileWrite ? extractArgDestinations(args) : []
30784
31154
  );
@@ -30837,7 +31207,7 @@ async function _authorizeHeadlessCore(toolName, args, metaArg, options) {
30837
31207
  if (dests.size === 0 && acts && !mcpFileWrite && (role.destructive === true || isDestructiveToolName(bare))) {
30838
31208
  held.push({ dest: "", why: "destructive" });
30839
31209
  }
30840
- const writeHolds = writes.length > 0 ? judgeWritesAfterExposure({
31210
+ const writeHolds = writes.length > 0 && !(unknownRead && !exposure.exposed) ? judgeWritesAfterExposure({
30841
31211
  accesses: writes,
30842
31212
  args,
30843
31213
  shellCommand: shellInput.kind === "shell" ? shellInput.command : null,
@@ -30864,14 +31234,14 @@ async function _authorizeHeadlessCore(toolName, args, metaArg, options) {
30864
31234
  const parts = held.map(
30865
31235
  (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`
30866
31236
  );
30867
- const home = os38.homedir();
31237
+ const home = os39.homedir();
30868
31238
  const short = (p) => p.startsWith(home + "/") ? "~" + p.slice(home.length) : p;
30869
31239
  for (const w of writeHolds) {
30870
31240
  parts.push(
30871
31241
  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}`
30872
31242
  );
30873
31243
  }
30874
- exposureReview = `\u{1F6E1}\uFE0F Node9: this session read ${read2}. This call ${parts.join("; and ")}.`;
31244
+ 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 ")}.`;
30875
31245
  exposureFromEid = last?.eid;
30876
31246
  if (exposureFromEid) exposureMeta = { exposureFromEid };
30877
31247
  exposureKeys = [
@@ -32163,11 +32533,11 @@ var init_version = __esm({
32163
32533
 
32164
32534
  // src/cli/commands/scan-cache.ts
32165
32535
  import fs45 from "fs";
32166
- import os39 from "os";
32536
+ import os40 from "os";
32167
32537
  import path50 from "path";
32168
32538
  import { createHash as createHash5 } from "crypto";
32169
32539
  function scanCacheRoot() {
32170
- return path50.join(os39.homedir(), ".node9", "scan-cache");
32540
+ return path50.join(os40.homedir(), ".node9", "scan-cache");
32171
32541
  }
32172
32542
  function configHashOf(config) {
32173
32543
  const h = createHash5("sha256");
@@ -32175,7 +32545,7 @@ function configHashOf(config) {
32175
32545
  h.update(JSON.stringify(config.policySource));
32176
32546
  h.update(JSON.stringify(config.policy));
32177
32547
  for (const name of SIDECAR_FILES) {
32178
- const st = fileStamp(path50.join(os39.homedir(), ".node9", name));
32548
+ const st = fileStamp(path50.join(os40.homedir(), ".node9", name));
32179
32549
  h.update(`|${name}:${st ? `${st.size}:${st.mtimeMs}` : "absent"}`);
32180
32550
  }
32181
32551
  return h.digest("hex").slice(0, 16);
@@ -32356,7 +32726,7 @@ var init_scan_cache = __esm({
32356
32726
 
32357
32727
  // src/cli/commands/scan-judge.ts
32358
32728
  import fs46 from "fs";
32359
- import os40 from "os";
32729
+ import os41 from "os";
32360
32730
  import path51 from "path";
32361
32731
  function findingDedupKey(f) {
32362
32732
  return `${f.source.rule.name ?? "<unnamed>"}|${preview(f.input)}|${f.project}|${f.repo ?? ""}`;
@@ -32476,7 +32846,7 @@ var init_scan_judge = __esm({
32476
32846
  if (parent === dir2) break;
32477
32847
  dir2 = parent;
32478
32848
  }
32479
- const home = os40.homedir();
32849
+ const home = os41.homedir();
32480
32850
  const label2 = found && found.startsWith(home) ? "~" + found.slice(home.length) : found;
32481
32851
  this.repos.set(cwd, label2);
32482
32852
  return label2;
@@ -32698,7 +33068,7 @@ var init_scan_history = __esm({
32698
33068
  import chalk6 from "chalk";
32699
33069
  import fs47 from "fs";
32700
33070
  import path52 from "path";
32701
- import os41 from "os";
33071
+ import os42 from "os";
32702
33072
  import stringWidth2 from "string-width";
32703
33073
  function dayKey(ts) {
32704
33074
  if (!ts) return "unknown";
@@ -33151,7 +33521,7 @@ async function processClaudeProjectAsync(proj, projectsDir, judge, cache7, start
33151
33521
  }
33152
33522
  function claudeProjectLabel(dirName) {
33153
33523
  const name = decodeURIComponent(dirName);
33154
- const homeDashed = os41.homedir().replace(/\//g, "-");
33524
+ const homeDashed = os42.homedir().replace(/\//g, "-");
33155
33525
  const label2 = name.startsWith(homeDashed) ? "~" + decodeProjectDirName(name.slice(homeDashed.length)) : decodeProjectDirName(name);
33156
33526
  return stripTerminalEscapes(label2).slice(0, 40);
33157
33527
  }
@@ -33262,7 +33632,7 @@ function mergeFileScan(result, file2, dedup, startDate = null) {
33262
33632
  }
33263
33633
  }
33264
33634
  async function scanClaudeHistoryAsync(startDate, onProgress, onLine, opts = {}) {
33265
- const projectsDir = path52.join(os41.homedir(), ".claude", "projects");
33635
+ const projectsDir = path52.join(os42.homedir(), ".claude", "projects");
33266
33636
  const result = emptyClaudeScan();
33267
33637
  if (!fs47.existsSync(projectsDir)) return result;
33268
33638
  let projDirs;
@@ -33295,7 +33665,7 @@ async function scanClaudeHistoryAsync(startDate, onProgress, onLine, opts = {})
33295
33665
  }
33296
33666
  async function scanGeminiHistory(startDate, onProgress, onLine, opts = {}) {
33297
33667
  const canaryVals = safeCanaryScanValues();
33298
- const tmpDir = path52.join(os41.homedir(), ".gemini", "tmp");
33668
+ const tmpDir = path52.join(os42.homedir(), ".gemini", "tmp");
33299
33669
  const result = {
33300
33670
  filesScanned: 0,
33301
33671
  sessions: 0,
@@ -33332,7 +33702,7 @@ async function scanGeminiHistory(startDate, onProgress, onLine, opts = {}) {
33332
33702
  try {
33333
33703
  projLabel = stripTerminalEscapes(
33334
33704
  fs47.readFileSync(path52.join(slugPath, ".project_root"), "utf-8").trim()
33335
- ).replace(os41.homedir(), "~").slice(0, 40);
33705
+ ).replace(os42.homedir(), "~").slice(0, 40);
33336
33706
  } catch {
33337
33707
  }
33338
33708
  const chatsDir = path52.join(slugPath, "chats");
@@ -33496,7 +33866,7 @@ async function scanGeminiHistory(startDate, onProgress, onLine, opts = {}) {
33496
33866
  return result;
33497
33867
  }
33498
33868
  function antigravityBrainDirs() {
33499
- return ["antigravity-cli", "antigravity-ide"].map((surface) => path52.join(os41.homedir(), ".gemini", surface, "brain")).filter((p) => fs47.existsSync(p));
33869
+ return ["antigravity-cli", "antigravity-ide"].map((surface) => path52.join(os42.homedir(), ".gemini", surface, "brain")).filter((p) => fs47.existsSync(p));
33500
33870
  }
33501
33871
  function antigravityTranscriptPath(convPath) {
33502
33872
  const logsDir = path52.join(convPath, ".system_generated", "logs");
@@ -33633,7 +34003,7 @@ async function scanAntigravityHistory(startDate, onProgress, onLine, opts = {})
33633
34003
  day.bashCalls++;
33634
34004
  const cwd = String(input.cwd ?? "");
33635
34005
  if (cwd && projLabel === conv.slice(0, 8)) {
33636
- projLabel = stripTerminalEscapes(cwd).replace(os41.homedir(), "~").slice(0, 40);
34006
+ projLabel = stripTerminalEscapes(cwd).replace(os42.homedir(), "~").slice(0, 40);
33637
34007
  }
33638
34008
  }
33639
34009
  const rawCmd = String(input.command ?? "").trimStart();
@@ -33682,7 +34052,7 @@ async function scanAntigravityHistory(startDate, onProgress, onLine, opts = {})
33682
34052
  }
33683
34053
  async function scanCopilotHistory(startDate, onProgress, onLine, opts = {}) {
33684
34054
  const canaryVals = safeCanaryScanValues();
33685
- const sessionDir = path52.join(os41.homedir(), ".copilot", "session-state");
34055
+ const sessionDir = path52.join(os42.homedir(), ".copilot", "session-state");
33686
34056
  const result = {
33687
34057
  filesScanned: 0,
33688
34058
  sessions: 0,
@@ -33750,7 +34120,7 @@ async function scanCopilotHistory(startDate, onProgress, onLine, opts = {}) {
33750
34120
  if (ev.type === "session.start") {
33751
34121
  const cwd = ev.data?.context?.cwd;
33752
34122
  if (typeof cwd === "string" && cwd) {
33753
- projLabel = stripTerminalEscapes(cwd).replace(os41.homedir(), "~").slice(0, 40);
34123
+ projLabel = stripTerminalEscapes(cwd).replace(os42.homedir(), "~").slice(0, 40);
33754
34124
  if (path52.isAbsolute(cwd)) sessionCwd = cwd;
33755
34125
  }
33756
34126
  continue;
@@ -33907,7 +34277,7 @@ async function scanCodexHistory(startDate, onProgress, onLine, opts = {}) {
33907
34277
  sessionId = String(payload["id"] ?? filePath);
33908
34278
  startTime = String(payload["timestamp"] ?? "");
33909
34279
  const cwd = String(payload["cwd"] ?? "");
33910
- projLabel = stripTerminalEscapes(cwd.replace(os41.homedir(), "~")).slice(0, 40);
34280
+ projLabel = stripTerminalEscapes(cwd.replace(os42.homedir(), "~")).slice(0, 40);
33911
34281
  if (path52.isAbsolute(cwd)) sessionCwd = cwd;
33912
34282
  continue;
33913
34283
  }
@@ -34108,12 +34478,12 @@ var init_scan = __esm({
34108
34478
 
34109
34479
  // src/tui/dashboard/data.ts
34110
34480
  import fs48 from "fs";
34111
- import os42 from "os";
34481
+ import os43 from "os";
34112
34482
  import path53 from "path";
34113
34483
  import http2 from "http";
34114
34484
  import { execFile } from "child_process";
34115
34485
  function auditLogPath() {
34116
- return path53.join(os42.homedir(), ".node9", "audit.log");
34486
+ return path53.join(os43.homedir(), ".node9", "audit.log");
34117
34487
  }
34118
34488
  function readAuditEntriesAsync(chunkSize = 1e3, customPath) {
34119
34489
  return new Promise((resolve) => {
@@ -34274,7 +34644,7 @@ async function runPostureDetached() {
34274
34644
  } catch {
34275
34645
  }
34276
34646
  }
34277
- return runPosture({ home: os42.homedir(), cwd: process.cwd() });
34647
+ return runPosture({ home: os43.homedir(), cwd: process.cwd() });
34278
34648
  }
34279
34649
  async function loadPosture(run = runPostureDetached) {
34280
34650
  try {
@@ -34291,13 +34661,13 @@ async function loadPosture(run = runPostureDetached) {
34291
34661
  }
34292
34662
  }
34293
34663
  function shortenPath(p) {
34294
- const home = os42.homedir();
34664
+ const home = os43.homedir();
34295
34665
  return p.startsWith(home) ? p.replace(home, "~") : p;
34296
34666
  }
34297
34667
  async function loadReportAuditAsync(period) {
34298
- const claudeProjectsDir = path53.join(os42.homedir(), ".claude", "projects");
34668
+ const claudeProjectsDir = path53.join(os43.homedir(), ".claude", "projects");
34299
34669
  const codexSessionsDir2 = codexSessionsDir();
34300
- const geminiTmpDir = path53.join(os42.homedir(), ".gemini", "tmp");
34670
+ const geminiTmpDir = path53.join(os43.homedir(), ".gemini", "tmp");
34301
34671
  const { start, end } = getDateRange(period, /* @__PURE__ */ new Date());
34302
34672
  const entries = await readAuditEntriesAsync();
34303
34673
  void ensurePricingLoaded();