@node9/proxy 2.51.0 → 2.53.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -436,15 +436,15 @@ function ssrfExemptMatches(entries, normalized) {
436
436
  continue;
437
437
  }
438
438
  if (!target) continue;
439
- const base2 = bitsOf(normalizeIpLiteral(entry.slice(0, slash)) ?? "");
439
+ const base3 = bitsOf(normalizeIpLiteral(entry.slice(0, slash)) ?? "");
440
440
  const prefixText = entry.slice(slash + 1);
441
441
  const prefix = /^\d+$/.test(prefixText) ? Number(prefixText) : NaN;
442
- if (!base2 || !Number.isInteger(prefix) || prefix < 0 || // A v4 range never matches a v6 address, and the reverse: the widths
442
+ if (!base3 || !Number.isInteger(prefix) || prefix < 0 || // A v4 range never matches a v6 address, and the reverse: the widths
443
443
  // differ, so `0.0.0.0/0` does not release `::1`.
444
- base2.length !== target.length || prefix > base2.length) {
444
+ base3.length !== target.length || prefix > base3.length) {
445
445
  continue;
446
446
  }
447
- if (base2.slice(0, prefix) === target.slice(0, prefix)) return true;
447
+ if (base3.slice(0, prefix) === target.slice(0, prefix)) return true;
448
448
  }
449
449
  return false;
450
450
  }
@@ -498,6 +498,10 @@ function reviewsEveryAgentFileWrite(mode) {
498
498
  const level = levelOf(mode);
499
499
  return level === "strict" || level === "block-all";
500
500
  }
501
+ function holdsSendsWithoutDaemon(mode) {
502
+ const level = levelOf(mode);
503
+ return level === "strict" || level === "block-all";
504
+ }
501
505
  function isBlockAll(mode) {
502
506
  return levelOf(mode) === "block-all";
503
507
  }
@@ -1702,6 +1706,39 @@ function protectedLiteral(code, isProtected) {
1702
1706
  }
1703
1707
  return null;
1704
1708
  }
1709
+ function inlineReadPaths(language, code, preload = {}) {
1710
+ const out = [];
1711
+ const before = READ_BEFORE[language];
1712
+ const after = READ_AFTER[language];
1713
+ const assigned = /* @__PURE__ */ new Map();
1714
+ for (const m of code.matchAll(STRING_LITERAL)) {
1715
+ const raw = m[2].trim();
1716
+ const value = raw.replace(/^[<+]\s*/, "");
1717
+ if (!value) continue;
1718
+ const head = code.slice(Math.max(0, m.index - 160), m.index);
1719
+ const tail = code.slice(m.index + m[0].length, m.index + m[0].length + 40);
1720
+ if (/^[>|]/.test(raw) || WRITE_MODE_AFTER.test(tail) || PERL_WRITE_BEFORE.test(head)) continue;
1721
+ if (before.test(head) || after?.test(tail) || language === "node" && DOTENV_PATH_OPTION.test(head)) {
1722
+ out.push(value);
1723
+ continue;
1724
+ }
1725
+ const name = ASSIGNED.exec(head)?.[1];
1726
+ if (name) assigned.set(name, [...assigned.get(name) ?? [], value]);
1727
+ }
1728
+ if (assigned.size > 0) {
1729
+ const opened = /* @__PURE__ */ new Set();
1730
+ for (const m of code.matchAll(OPENED_NAME[language])) opened.add(m[1] ?? m[2]);
1731
+ for (const [name, values] of assigned) if (opened.has(name)) out.push(...values);
1732
+ }
1733
+ if (language === "python" || language === "node") {
1734
+ const d = DOTENV[language];
1735
+ const readsEnv = d.env.test(code);
1736
+ const loads = /dotenv/.test(code) && d.load.test(code) && !d.literalPath.test(code);
1737
+ if ((loads || preload.dotenv) && (readsEnv || d.values.test(code))) out.push(".env");
1738
+ if (readsEnv) out.push(...preload.envFiles ?? []);
1739
+ }
1740
+ return out;
1741
+ }
1705
1742
  function amplifies(language, code) {
1706
1743
  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*\)/;
1707
1744
  const m = loop.exec(code);
@@ -2556,15 +2593,28 @@ function inlineExecCall(stmt, pipeFed) {
2556
2593
  const fromStdin = () => {
2557
2594
  if (stdinFed) {
2558
2595
  const { code, source } = stdinCodeOf(redirs);
2559
- return { interp, code: [code], source };
2596
+ return { interp, code: [code], source, preload };
2560
2597
  }
2561
2598
  return { interp, code: [null], source: "pipe" };
2562
2599
  };
2563
2600
  let positionals2 = 0;
2564
2601
  let selectedProgram = false;
2565
2602
  const codes = [];
2603
+ const preload = {};
2604
+ const nodeFamily = inlineLanguageOf(interp) === "node";
2566
2605
  for (let i = 0; i < args.length; i++) {
2567
2606
  const a = args[i];
2607
+ if (nodeFamily && !selectedProgram && codes.length === 0 && a != null) {
2608
+ const flag = NODE_VALUE_FLAG.exec(a);
2609
+ if (flag) {
2610
+ const value = flag[2] ?? args[++i];
2611
+ if (value != null) {
2612
+ if (/^--env-file/.test(flag[1])) (preload.envFiles ??= []).push(value);
2613
+ else if (/^dotenv\/config(?:\.js)?$/.test(value)) preload.dotenv = true;
2614
+ }
2615
+ continue;
2616
+ }
2617
+ }
2568
2618
  if (a == null) {
2569
2619
  if (codes.length > 0) break;
2570
2620
  positionals2++;
@@ -2601,7 +2651,12 @@ function inlineExecCall(stmt, pipeFed) {
2601
2651
  }
2602
2652
  }
2603
2653
  if (codes.length > 0) {
2604
- return { interp, code: codes, source: codes.includes(null) ? "variable" : void 0 };
2654
+ return {
2655
+ interp,
2656
+ code: codes,
2657
+ source: codes.includes(null) ? "variable" : void 0,
2658
+ preload
2659
+ };
2605
2660
  }
2606
2661
  if (positionals2 === 0 && (stdinFed || pipeFed)) {
2607
2662
  if (!/^(bash|sh|zsh)$/i.test(interp)) return fromStdin();
@@ -3424,7 +3479,7 @@ function extractShellDestTokens(command) {
3424
3479
  }
3425
3480
  function analyzeFsOperation(command) {
3426
3481
  const normalized = normalizeCommandForPolicy(command);
3427
- if (!FS_OP_PRESCREEN_RE.test(normalized) && !FS_OP_PRESCREEN_RE.test(command) && !SOURCES_FILE_RE.test(command))
3482
+ if (!FS_OP_PRESCREEN_RE.test(normalized) && !FS_OP_PRESCREEN_RE.test(command) && !SOURCES_FILE_RE.test(command) && !runsInlineInterpreter(command, normalized))
3428
3483
  return null;
3429
3484
  if (fsOpCache.has(command)) {
3430
3485
  const hit = fsOpCache.get(command) ?? null;
@@ -3432,7 +3487,8 @@ function analyzeFsOperation(command) {
3432
3487
  fsOpCache.set(command, hit);
3433
3488
  return hit;
3434
3489
  }
3435
- const computed = analyzeFsOperationImpl(command) ?? (INDIRECT_SHAPE_RE.test(command) ? structuralJailRead(command) : null);
3490
+ const direct = analyzeFsOperationImpl(command) ?? (INDIRECT_SHAPE_RE.test(command) ? structuralJailRead(command) : null);
3491
+ const computed = direct?.verdict === "block" || !runsInlineInterpreter(command, normalized) ? direct : stricter(direct, inlineCodeJailRead(command));
3436
3492
  if (fsOpCache.size >= FS_OP_CACHE_MAX) {
3437
3493
  const oldest = fsOpCache.keys().next().value;
3438
3494
  if (oldest !== void 0) fsOpCache.delete(oldest);
@@ -3441,8 +3497,8 @@ function analyzeFsOperation(command) {
3441
3497
  return computed;
3442
3498
  }
3443
3499
  function isSensitiveCleanupName(p) {
3444
- const base2 = p.replace(/^.*[\\/]/, "");
3445
- return /^\.env(\.|$)/i.test(base2) || /(?:^|[\\/])\.(?:ssh|aws|gnupg|git)(?:[\\/]|$)/i.test(p) || /\.(?:pem|key|p12|pfx|crt)$/i.test(base2) || /^\.?(?:netrc|npmrc|pgpass|htpasswd)$/i.test(base2) || /^id_(?:rsa|dsa|ecdsa|ed25519)/i.test(base2) || /credential/i.test(p) || /secret/i.test(base2);
3500
+ const base3 = p.replace(/^.*[\\/]/, "");
3501
+ return /^\.env(\.|$)/i.test(base3) || /(?:^|[\\/])\.(?:ssh|aws|gnupg|git)(?:[\\/]|$)/i.test(p) || /\.(?:pem|key|p12|pfx|crt)$/i.test(base3) || /^\.?(?:netrc|npmrc|pgpass|htpasswd)$/i.test(base3) || /^id_(?:rsa|dsa|ecdsa|ed25519)/i.test(base3) || /credential/i.test(p) || /secret/i.test(base3);
3446
3502
  }
3447
3503
  function isWaivableCleanupTarget(p) {
3448
3504
  if (/^[/~]/.test(p) || /^\$/.test(p)) return false;
@@ -4171,6 +4227,24 @@ function structuralJailRead(command) {
4171
4227
  }
4172
4228
  return review;
4173
4229
  }
4230
+ function inlineCodeJailRead(command) {
4231
+ const calls = inlineExecCalls(command);
4232
+ if (calls === PARSE_FAIL) return null;
4233
+ let review = null;
4234
+ for (const call of calls) {
4235
+ const language = inlineLanguageOf(call.interp);
4236
+ if (language === null) continue;
4237
+ for (const code of call.code) {
4238
+ if (code === null) continue;
4239
+ for (const p of inlineReadPaths(language, code, call.preload)) {
4240
+ const hit = matchSensitivePath(p);
4241
+ if (hit?.verdict === "block") return hit;
4242
+ review ??= hit;
4243
+ }
4244
+ }
4245
+ }
4246
+ return review;
4247
+ }
4174
4248
  function baseWord(w) {
4175
4249
  return (w ?? "").split("/").pop()?.toLowerCase() ?? "";
4176
4250
  }
@@ -4728,6 +4802,21 @@ function hits(access, paths, directories, context) {
4728
4802
  }
4729
4803
  return false;
4730
4804
  }
4805
+ function hitsNode9Dir(access, dirs, context) {
4806
+ const home = context.homeDirs?.[0] ?? "~";
4807
+ if (access.glob) {
4808
+ const pattern = normalizeFilePath(access.glob, access.cwd ?? context.cwd, home);
4809
+ const magic = pattern.search(/[*?[{]/);
4810
+ const literal = magic < 0 ? pattern : pattern.slice(0, pattern.lastIndexOf("/", magic));
4811
+ return dirs.some((d) => within(literal, d));
4812
+ }
4813
+ const candidates = access.candidates ?? [
4814
+ normalizeFilePath(access.path, access.cwd ?? context.cwd, home)
4815
+ ];
4816
+ return candidates.some(
4817
+ (p) => dirs.some((d) => within(p, d)) || access.recursive === true && dirs.some((d) => within(d, p))
4818
+ );
4819
+ }
4731
4820
  function hitsSystemDir(access, context, dirs = SYSTEM_DIRS) {
4732
4821
  const home = context.homeDirs?.[0] ?? "~";
4733
4822
  const candidates = access.candidates ?? [
@@ -4768,6 +4857,7 @@ function evaluateProtectedFileAccess(accesses, context, settings) {
4768
4857
  const consider = (result) => {
4769
4858
  if (!best || RANK[result.decision] > RANK[best.decision]) best = result;
4770
4859
  };
4860
+ const node9Dirs = homes.map((home) => `${home.replace(/\/$/, "")}/.node9`);
4771
4861
  for (const access of accesses) {
4772
4862
  const mutation = access.operation !== "read";
4773
4863
  let kind;
@@ -4796,6 +4886,10 @@ function evaluateProtectedFileAccess(accesses, context, settings) {
4796
4886
  "files.system-dirs",
4797
4887
  "review-root-home-read",
4798
4888
  "Reading the root account's home requires approval"
4889
+ ] : mutation && hitsNode9Dir(access, node9Dirs, context) ? [
4890
+ "commands.node9-disarm",
4891
+ "review-node9-disarm",
4892
+ "Changing node9's own files requires approval"
4799
4893
  ] : null;
4800
4894
  } catch {
4801
4895
  consider(incompleteFileVerdict(access.operation));
@@ -5401,9 +5495,9 @@ function validateBelgianNationalNumber(raw) {
5401
5495
  if (!/^\d{11}$/.test(d)) return false;
5402
5496
  const month = Number(d.slice(2, 4)) % 20;
5403
5497
  if (month !== 0 && !plausibleDate(String(month).padStart(2, "0"), d.slice(4, 6))) return false;
5404
- const base2 = Number(d.slice(0, 9));
5498
+ const base3 = Number(d.slice(0, 9));
5405
5499
  const check = Number(d.slice(9, 11));
5406
- return 97 - base2 % 97 === check || 97 - (2e9 + base2) % 97 === check;
5500
+ return 97 - base3 % 97 === check || 97 - (2e9 + base3) % 97 === check;
5407
5501
  }
5408
5502
  function validateCuit(raw) {
5409
5503
  const d = raw.replace(/-/g, "");
@@ -6505,9 +6599,9 @@ function evaluateGitHooksFileAccess(args, accesses, context, settings, shell) {
6505
6599
  }
6506
6600
  function agentFileKind(p) {
6507
6601
  const lower2 = p.toLowerCase();
6508
- const base2 = lower2.slice(lower2.lastIndexOf("/") + 1);
6509
- if (INSTRUCTION_NAMES.has(base2)) return "instructions";
6510
- if (base2 === "copilot-instructions.md" && lower2.endsWith("/.github/" + base2))
6602
+ const base3 = lower2.slice(lower2.lastIndexOf("/") + 1);
6603
+ if (INSTRUCTION_NAMES.has(base3)) return "instructions";
6604
+ if (base3 === "copilot-instructions.md" && lower2.endsWith("/.github/" + base3))
6511
6605
  return "instructions";
6512
6606
  if (/\/\.cursor\/rules(?:\/|$)/.test(lower2)) return "instructions";
6513
6607
  if (/\/\.claude\/projects\/[^/]+\/memory(?:\/|$)/.test(lower2)) return "memory";
@@ -6676,7 +6770,7 @@ function evaluateAgentFileWrite(args, accesses, context, settings, shell, hooks
6676
6770
  skills: "agent skill",
6677
6771
  settings: "agent settings"
6678
6772
  };
6679
- const base2 = `${kindLabel[kind]} written: ${access.path}`;
6773
+ const base3 = `${kindLabel[kind]} written: ${access.path}`;
6680
6774
  if (!signal && !every) {
6681
6775
  return {
6682
6776
  decision: "allow",
@@ -6686,14 +6780,14 @@ function evaluateAgentFileWrite(args, accesses, context, settings, shell, hooks
6686
6780
  path: access.path,
6687
6781
  operation: access.operation,
6688
6782
  kind,
6689
- reason: base2,
6783
+ reason: base3,
6690
6784
  ruleDescription: "The AI writes to a file every later agent session loads as instructions.",
6691
- blockedByLabel: `Node9 Files: ${base2}`,
6785
+ blockedByLabel: `Node9 Files: ${base3}`,
6692
6786
  tier: 2
6693
6787
  };
6694
6788
  }
6695
6789
  const ruleName = signal === "content" ? "review-agent-file-content" : signal === "hooks" ? "review-agent-hooks-change" : "review-agent-file-write";
6696
- const reason = signal ? `${base2} (${detail})` : base2;
6790
+ const reason = signal ? `${base3} (${detail})` : base3;
6697
6791
  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.";
6698
6792
  return {
6699
6793
  decision: value === "log" ? "allow" : value,
@@ -8301,6 +8395,139 @@ function scanArgumentSecrets(args) {
8301
8395
  severity: "review"
8302
8396
  } : null;
8303
8397
  }
8398
+ function patternSelects(pattern, targets) {
8399
+ const pieces = pattern.toLowerCase().split(/[\\^$.*+?()[\]{}|]+/).filter(Boolean);
8400
+ if (pieces.length === 0) return false;
8401
+ return targets.some((t) => pieces.every((p) => t.includes(p)));
8402
+ }
8403
+ function sourceText(bytes, pos, end) {
8404
+ if (typeof pos !== "number" || typeof end !== "number" || pos < 0 || end < pos) return "";
8405
+ return new TextDecoder().decode(bytes.subarray(pos, end));
8406
+ }
8407
+ function nextOperand(words, from) {
8408
+ for (let i = from; i < words.length; i++) {
8409
+ const w = words[i];
8410
+ if (w !== null && w.startsWith("-")) continue;
8411
+ return i;
8412
+ }
8413
+ return -1;
8414
+ }
8415
+ function node9Command(words, h) {
8416
+ const head = base2(words[h]);
8417
+ if (head !== "node9" && head !== "node9.cmd" && words[h] !== "@node9/proxy") return null;
8418
+ const s = nextOperand(words, h + 1);
8419
+ const sub = s < 0 ? "" : (words[s] ?? "").toLowerCase();
8420
+ if (sub === "uninstall") return "node9 uninstall";
8421
+ if (sub !== "daemon") return null;
8422
+ const a = nextOperand(words, s + 1);
8423
+ const action = a < 0 ? "" : (words[a] ?? "").toLowerCase();
8424
+ return action === "stop" || action === "uninstall" ? `node9 daemon ${action}` : null;
8425
+ }
8426
+ function killsDaemon(words, texts, h, daemonPid) {
8427
+ const head = base2(words[h]);
8428
+ const args = words.slice(h + 1);
8429
+ if (head === "kill") {
8430
+ for (let i = 0; i < args.length; i++) {
8431
+ const w = args[i];
8432
+ if (w === "-s" || w === "-n") {
8433
+ i++;
8434
+ continue;
8435
+ }
8436
+ if (w !== null && w.startsWith("-")) continue;
8437
+ if (w === null) {
8438
+ if (HOME_PID_FILE_RE.test(texts[h + 1 + i] ?? "")) return "kill of the pid in daemon.pid";
8439
+ continue;
8440
+ }
8441
+ if (daemonPid !== null && w === String(daemonPid)) return `kill ${w} (the node9 daemon)`;
8442
+ }
8443
+ return null;
8444
+ }
8445
+ if (head === "pkill") {
8446
+ let full = false;
8447
+ let pattern = null;
8448
+ for (let i = 0; i < args.length; i++) {
8449
+ const w = args[i];
8450
+ if (w === null) continue;
8451
+ if (PKILL_VALUE_FLAGS.has(w)) {
8452
+ i++;
8453
+ continue;
8454
+ }
8455
+ if (w === "--full") full = true;
8456
+ else if (/^-[a-zA-Z]+$/.test(w) && w.includes("f")) full = true;
8457
+ if (w.startsWith("-")) continue;
8458
+ pattern = w;
8459
+ }
8460
+ if (pattern === null) return null;
8461
+ return patternSelects(pattern, full ? DAEMON_CMDLINES : DAEMON_NAMES) ? `pkill ${full ? "-f " : ""}${pattern}` : null;
8462
+ }
8463
+ if (head === "killall") {
8464
+ const regex = args.some((w) => w === "-r" || w === "--regexp");
8465
+ for (const w of args) {
8466
+ if (w === null || w.startsWith("-")) continue;
8467
+ if (regex ? patternSelects(w, DAEMON_NAMES) : KILLALL_NAMES.has(w.toLowerCase()))
8468
+ return `killall ${w}`;
8469
+ }
8470
+ return null;
8471
+ }
8472
+ if (head === "taskkill" || head === "taskkill.exe") {
8473
+ for (let i = 0; i < args.length - 1; i++) {
8474
+ const flag = (args[i] ?? "").toLowerCase();
8475
+ const value = (args[i + 1] ?? "").toLowerCase();
8476
+ if ((flag === "/im" || flag === "-im") && KILLALL_NAMES.has(value))
8477
+ return `taskkill /IM ${value}`;
8478
+ if ((flag === "/pid" || flag === "-pid") && daemonPid !== null && value === String(daemonPid))
8479
+ return `taskkill /PID ${value} (the node9 daemon)`;
8480
+ }
8481
+ }
8482
+ return null;
8483
+ }
8484
+ function stopsService(words, h) {
8485
+ const head = base2(words[h]);
8486
+ const args = words.slice(h + 1).map((w) => (w ?? "").toLowerCase());
8487
+ if (head === "systemctl") {
8488
+ const action = args.find((w) => STOP_SYSTEMCTL.has(w));
8489
+ if (action && args.some((w) => /^node9-daemon(\.service)?$/.test(w)))
8490
+ return `systemctl ${action} node9-daemon`;
8491
+ }
8492
+ if (head === "launchctl") {
8493
+ const action = args.find((w) => STOP_LAUNCHCTL.has(w));
8494
+ if (action && args.some((w) => w.includes("ai.node9.daemon")))
8495
+ return `launchctl ${action} ai.node9.daemon`;
8496
+ }
8497
+ return null;
8498
+ }
8499
+ function analyzeNode9Disarm(command, daemonPid = null, depth = 0) {
8500
+ if (depth > 3 || !NAME_PRESCREEN_RE.test(command.replace(/[\\'"]/g, ""))) return null;
8501
+ let shown = null;
8502
+ const bytes = new TextEncoder().encode(command);
8503
+ try {
8504
+ walkCommandScope(command, (node) => {
8505
+ if (shown !== null) return false;
8506
+ const n = node;
8507
+ if (!n || n.kind !== "CallExpr") return true;
8508
+ const words = (n.Args || []).map((a) => resolveWordLiteral(a));
8509
+ if (words.length === 0) return true;
8510
+ const texts = (n.Args || []).map((a) => sourceText(bytes, a.pos, a.end));
8511
+ const h = unwrapCommandHead(words);
8512
+ shown = node9Command(words, h) ?? killsDaemon(words, texts, h, daemonPid) ?? stopsService(words, h);
8513
+ if (shown === null) {
8514
+ const payload = literalShellPayload(words, base2(words[0]));
8515
+ if (payload) {
8516
+ const inner = analyzeNode9Disarm(payload, daemonPid, depth + 1);
8517
+ if (inner) shown = inner.reason.replace(/^node9 switched off by the agent: /, "");
8518
+ }
8519
+ }
8520
+ return true;
8521
+ });
8522
+ } catch {
8523
+ }
8524
+ if (shown === null) return null;
8525
+ return {
8526
+ ruleName: RULE_NAME2,
8527
+ reason: `node9 switched off by the agent: ${shown}`,
8528
+ description: DESCRIPTION
8529
+ };
8530
+ }
8304
8531
  function catalogSettings(config) {
8305
8532
  return catalogSettingsFromConfig(config.settings, config.policy);
8306
8533
  }
@@ -8394,6 +8621,10 @@ function egressPolicyVerdict(eg) {
8394
8621
  };
8395
8622
  }
8396
8623
  async function evaluatePolicy(config, toolName, args, context = {}, hostHooks = {}) {
8624
+ const v = await evaluatePolicyVerdict(config, toolName, args, context, hostHooks);
8625
+ return v.decision === "block" && v.checkId !== void 0 && NOT_OVERRIDABLE_CHECKS.has(v.checkId) ? { ...v, overridable: false } : v;
8626
+ }
8627
+ async function evaluatePolicyVerdict(config, toolName, args, context = {}, hostHooks = {}) {
8397
8628
  let protectedVerdict = null;
8398
8629
  const layers = inspectionLayers(config.policy);
8399
8630
  if (context.agent !== "Terminal") {
@@ -8451,7 +8682,7 @@ async function evaluatePolicy(config, toolName, args, context = {}, hostHooks =
8451
8682
  }
8452
8683
  async function evaluatePolicyCore(config, toolName, args, context = {}, hooks = {}) {
8453
8684
  const { agent, cwd, activeEnvironment } = context;
8454
- const { checkProvenance: checkProvenance2, isTrustedHost: isTrustedHost2, isConfiguredRegistry, isGitWorkTreeTop: isGitWorkTreeTop2 } = hooks;
8685
+ const { checkProvenance: checkProvenance2, isTrustedHost: isTrustedHost2, isConfiguredRegistry, isGitWorkTreeTop: isGitWorkTreeTop2, daemonPid } = hooks;
8455
8686
  const settingsView = catalogSettings(config);
8456
8687
  const layers = inspectionLayers(config.policy);
8457
8688
  const cv = (id) => checkValue(settingsView, id);
@@ -8636,6 +8867,23 @@ async function evaluatePolicyCore(config, toolName, args, context = {}, hooks =
8636
8867
  else if (v && !pendingAstReview) pendingAstReview = v;
8637
8868
  }
8638
8869
  }
8870
+ if (cv("commands.node9-disarm") !== "off") {
8871
+ const finding = analyzeNode9Disarm(bashCommand, daemonPid?.() ?? null);
8872
+ if (finding) {
8873
+ const v = applyCheckValue(cv("commands.node9-disarm"), {
8874
+ decision: "review",
8875
+ blockedByLabel: `Node9 (AST): ${finding.ruleName}`,
8876
+ reason: finding.reason,
8877
+ tier: 2,
8878
+ ruleName: finding.ruleName,
8879
+ ruleDescription: finding.description,
8880
+ checkId: "commands.node9-disarm"
8881
+ });
8882
+ if (v?.logged) noteLogged(v);
8883
+ else if (v?.decision === "block") return v;
8884
+ else if (v && !pendingAstReview) pendingAstReview = v;
8885
+ }
8886
+ }
8639
8887
  }
8640
8888
  if (config.policy.smartRules.length > 0) {
8641
8889
  const rmCleanupWaiver = bashCommand !== null && isRmCreatedInCommandCleanup(bashCommand);
@@ -9083,8 +9331,8 @@ function hasRoutingNumber(text) {
9083
9331
  }
9084
9332
  return false;
9085
9333
  }
9086
- function* validCardRuns(text, base2, exemptFractions = true) {
9087
- const re = new RegExp(base2.source, "g");
9334
+ function* validCardRuns(text, base3, exemptFractions = true) {
9335
+ const re = new RegExp(base3.source, "g");
9088
9336
  let m;
9089
9337
  while ((m = re.exec(text)) !== null) {
9090
9338
  const digits = m[0].replace(/\D/g, "");
@@ -9097,17 +9345,17 @@ function* validCardRuns(text, base2, exemptFractions = true) {
9097
9345
  }
9098
9346
  }
9099
9347
  function hasValidCard(text) {
9100
- for (const base2 of CARD_PATTERNS) {
9101
- if (!validCardRuns(text, base2).next().done) return true;
9348
+ for (const base3 of CARD_PATTERNS) {
9349
+ if (!validCardRuns(text, base3).next().done) return true;
9102
9350
  }
9103
9351
  return false;
9104
9352
  }
9105
9353
  function replaceValidCards(text, replace, { exemptFractions = true } = {}) {
9106
9354
  let out = text;
9107
- for (const base2 of CARD_PATTERNS) {
9355
+ for (const base3 of CARD_PATTERNS) {
9108
9356
  let res = "";
9109
9357
  let last = 0;
9110
- for (const run of validCardRuns(out, base2, exemptFractions)) {
9358
+ for (const run of validCardRuns(out, base3, exemptFractions)) {
9111
9359
  res += out.slice(last, run.index) + replace(run.digits);
9112
9360
  last = run.index + run.length;
9113
9361
  }
@@ -9348,9 +9596,9 @@ function maskLeaf(value, key, depth) {
9348
9596
  const used = /* @__PURE__ */ new Set();
9349
9597
  return Object.fromEntries(
9350
9598
  Object.entries(value).map(([k, v]) => {
9351
- const base2 = maskPersonalData(k);
9352
- let name = base2;
9353
- for (let n = 2; used.has(name); n++) name = `${base2}#${n}`;
9599
+ const base3 = maskPersonalData(k);
9600
+ let name = base3;
9601
+ for (let n = 2; used.has(name); n++) name = `${base3}#${n}`;
9354
9602
  used.add(name);
9355
9603
  return [name, maskLeaf(v, k, depth + 1)];
9356
9604
  })
@@ -9844,7 +10092,7 @@ function matchCanaryArgs(args, values) {
9844
10092
  return null;
9845
10093
  }
9846
10094
  }
9847
- var import_url, import_picomatch, import_safe_regex2, import_picomatch2, import_module, import_picomatch3, import_safe_regex22, import_crypto2, import_crypto3, import_crypto4, import_crypto5, getFilename, __filename2, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, finance_default, identity_default, publishing_default, settings_default, branches_default, ACTION_TEMPLATES, ACTION_CHECKS, GAP, prog, httpTo, httpWrite, HTTP_WRITE, HTTP_WRITE_ANY, NOT_STRIPE_TEST, postTo, STRIPE_OBJECTS, APP_SHIELD_SPECS, APP_SHIELDS, APP_SHIELD_NAMES, APP_SERVER_GLOB, GENERAL_ACTION_RULES, BUILTIN_SHIELDS, SSRF_MAX_HOST, METADATA_ADDRESSES, STRICT_TIERS, METADATA_HOSTNAMES, v4Octets, TIER_REASON, CHECK_TEXT, LEVELS, MODE_VALUES, SOFT, STRICT, BLOCK_ALL, LEVEL_CHECKS, ASI, ASI_IDS, PACK_SHIELD_OWASP, PACK_RULE_OWASP, VERDICTS, CHECK_VERDICT_RANK, ALL, BUILTIN_DANGEROUS_WORDS, PRODUCT_CHECKS, SHIELD_RULE_NAME, SHIELD_RULES_FOLDED_INTO_PRODUCT, CHECKS, CHECK_BY_ID, LEGACY_TO_ID, PROJECT_JAIL_BLOCK_READ, OVERRIDE_PREFIX, LABEL_WRAPPERS, LEGACY_READERS, syntax, KINDS, STRUCTS, unknownKinds, INVISIBLE_RE, INVISIBLE_ONE, HOMOGLYPHS, HOMOGLYPH_RE, MAX_BLOBS, MIN_DECODED, BASE64_RUN_RE, HEX_RUN_RE, HEX_ESCAPE_RUN_RE, SYSTEM_AUTH_FILES, SYSTEM_AUTH_DIRS, PASSWORD_HASH_FILES, STARTUP_NAMES, SYSTEM_DIRS, SYSTEM_DIRS_EXEMPT, UPLOAD_SYSTEM_DIRS, AGENT_INSTRUCTION_FILES, AGENT_INSTRUCTION_DIRS, LEGACY_SYSTEM_PATH_RE, MAX_ALTERNATIVES, MAX_CALL_DEPTH, MAX_CALL_WALKS, MAX_XARGS_PAYLOAD, POSITIONAL, MAX_DEPTH, MAX_PAYLOAD_DEPTH, unique, clone, SENDING_STDIN, DOWNLOAD_SHORT_VALUE, DOWNLOAD_LONG_VALUE, DOWNLOAD_OUTPUT_FLAGS, DOWNLOAD_DIR_FLAGS, base, quoteGlob, asWord, ops, MAX_INLINE_CODE, WHAT, OUTSIDE, pyImport, either, PYTHON, NODE, PERL, RUBY, PHP, TABLES, STRING_LITERAL, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, codeBodiesCache, AST_CACHE_MAX, astCache, PARSE_FAIL, INTERPRETER_LINE, PROGRAM_DIR, ACCESS_VIEW_CACHE_MAX, accessViewCache, MAX_READING_PAYLOADS, STDIN_CODE_INTERPRETERS, RUNS_TEXT_ELSEWHERE, defaultExpansionOps, EMPTY_NAMES, VARIABLE_READERS, PLAIN_PROGRAM_NAME, FS_READ_TOOLS, GREP_SHAPE, PATTERN_VERBS, PATTERN_VERB_NAMES, GREP_FILE_OPERANDS, READER_VALUE_LETTERS, FILE_OPERAND_FLAGS, SCP_VALUE_FLAGS, CURL_SOURCE_FLAGS, CURL_VALUE_LETTERS, WGET_VALUE_LETTERS, TAR_VALUE_LETTERS, ZIP_VALUE_LETTERS, RSYNC_VALUE_LETTERS, RSYNC_SKIP, CP_VALUE_LETTERS, INSTALL_VALUE_LETTERS, COPY_VERBS, TAR_MODE_WORD, COPY_VERB_HEADS, FS_OP_PRESCREEN_RE, HOME_CACHE_ALLOWLIST, SENSITIVE_PATH_RULES, AST_FS_REGEX_RULES, SQL_DB_CLIS, SQL_DDL_RE, CHMOD_OPEN_PERM_TOKENS, COMMAND_WRAPPERS, INLINE_INTERPRETER, RUNNER_WRAPPERS, _redirStdinOps, _listOps, WRAPPER_TAKES_TARGET, FIND_EXEC_FLAGS, FS_MUTATION_TOOLS, INTERP_LEADING_TARGET, MAX_PAYLOAD_NESTING, payloadNesting, MAX_INLINE_CALLS, INLINE_EXEC_FALLBACK, UNREADABLE_SOURCE, SCRIPT_RUNNERS, SCRIPT_RUNNER_VALUE_FLAGS, MAX_SCRIPT_OPERANDS, NET_BINARIES, VALUE_FLAGS, HOME_VARIABLES, assignmentTable, currentStmtOffset, PAYLOAD_BUDGET, payloadBudget, seenPayloads, UNANALYSABLE_NESTING, ASSIGNMENT_HEADS, andOrOps, ARRAY_MARK, ARRAY_SEP, MAX_CALL_DEPTH2, MAX_CALL_WALKS2, callDepth, callWalks, FS_OP_CACHE_MAX, fsOpCache, stripDotSlash, REDIR_TRUNCATE_OPS, REDIR_FILE_IN_OPS, REDIR_HEREDOC_OPS, FIND_OPTIONS, COPY_RULE_OF, INDIRECT_SHAPE_RE, SOURCES_FILE_RE, SHELL_SET_VARS, pipeOps, callWords, JAILED_GLOB_NAMES, JAILED_DIR_NAMES, shellGlob, GLOB_META, isReaderWord, positionalAfter, NONE, UNKNOWN, FORBIDDEN_PATH_SEGMENTS, BASH_TOOL_NAMES, MCP_EXECUTORS, BUILTIN_FIELDS, ARGV_FIELDS, BARE_WORD, MAX_ARGV_CHARACTERS, exempt, RANK, SEQUENCE_OPS, BIP39_ENGLISH, BIP39_PUBLIC_VECTORS, indices, lengths, hash, MAX_MNEMONIC_HASHES, SECRET_VIEW_LIMITS, IBAN_LENGTH, IBAN_BBAN_SHAPE, B58, B58_INDEX, XPRV_VERSIONS, B62, CRC32_TABLE, GITHUB_TOKEN_RE, B64URL, B64URL_INDEX, CASK_TWO_ZERO_SUFFIX, CASK_FOUR_ZERO_SUFFIX, CF_ODD, CF_EVEN, SPANISH_LETTERS, HETU_CHARS, VERHOEFF_D, VERHOEFF_P, MAX, UNTRUSTED_TOOLS, INJECTION_SIGNALS, MENTION_QUOTE_RE, MENTION_WORD_RE, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, REFERENCE_VALUE_RE, IDENTIFIER_VALUE_RE, PATH_VALUE_RE, DOTTED_REFERENCE_RE, CONCATENATION_RE, EMAIL_VALUE_RE, FIXTURE_NAME_RE, DERIVATIVE_NAME_RE, QUERY_CREDENTIAL_VALUE_RE, OTP_SECRET_RE, OTP_DOC_SECRETS, ASSIGNED_SECRET_VALUE_RE, ASSIGNED_SECRET_NAME_RE, NAME_KEYWORD_RE, ARN_CONTEXT_RE, CIPHER_KEY_NAME_RE, CIPHER_NAME_PARTS, CIPHER_KEY_VALUE_RE, LARAVEL_KEY_VALUE_RE, SQL_PASSWORD_VALUE_RE, GRAFANA_KEY_VALUE_RE, BASIC_VALUE_RE, BASIC_PAIR_RE, BASIC_HEADER_RE, BASIC_HEADER_PAIR_RE, BASIC_PLACEHOLDER_RE, BASIC_TEMPLATE_MARKERS, DROPBOX_SECRET_RE, OTP_CAPTURE_RE, DB_URL_PASSWORD_RE, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, SENSITIVE_PATH_REGEXES, MAX_DEPTH2, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, BATCH_SEPARATOR, IMPERATIVE, SECRET_TARGET, GUARD, SIGNALS, DESTINATION_RE, REUSED_SIGNALS, NEVER_NEGATED, NEGATION_BEFORE, RDR_IN, COPY_VERBS2, LITERAL_VERBS, GIT_PRESCREEN_RE, MAX_PAYLOAD_DEPTH2, MAX_ALIAS_DEPTH, CACHE_MAX, cache, PAYLOAD_SHELLS, CONFIG_SCOPE_FLAGS, CONFIG_VALUE_FLAGS, GLOBAL_VALUE_OPTIONS, GLOBAL_FLAG_OPTIONS, MAX_LOOP_ITEMS, GitWalk, POSITIONAL2, NO_VARS, NO_APPENDS, FORCE_PUSH, DESTRUCTIVE, UNREADABLE, PUSH_LONG_VALUE, BRANCH_LONG_VALUE, TAG_LONG_VALUE, CLEAN_LONG_VALUE, RESTORE_LONG_VALUE, CHECKOUT_LONG_VALUE, HOOKS_BYPASS, NO_VERIFY_SUBS, HOOK_KILL_SWITCHES, COMMIT_LONG_VALUE, CHMOD_OPTION_RE, _appendOp, STAGE_ALL, ADD_LONG_VALUE, CHECK_ID, RULE_NAME, INERT_LINE_RE, CHECK_ID2, INSTRUCTION_NAMES, SETTINGS_RE, HOOK_WORDS_RE, SHELLS, NETCATS, NET_CLIENTS, SENDERS, NETCAT_EXEC_LONG, DECL_DUMPS, PROC_ENVIRON_RE, DEV_NET_RE, SOCAT_PROGRAM_RE, SOCAT_NET_RE, PRESCREEN_RE, MAX_PAYLOAD_DEPTH3, CACHE_MAX2, cache2, dupInOp, REVERSE_SHELL_DESCRIPTION, ENV_EXFIL_DESCRIPTION, DYNAMIC_WORD, MULTICALL, SHELL_VALUE_OPTIONS, INTERPRETERS, OutboundWalk, NPM_INSTALL_VERBS, NPM_EXEC_VERBS, PNPM_INSTALL_VERBS, YARN_INSTALL_VERBS, BUN_INSTALL_VERBS, DLX_VERBS, NON_REGISTRY_RE, NPM_NAME_RE, EXACT_SEMVER_RE, NPM_VALUE_FLAGS, PNPM_VALUE_FLAGS, YARN_VALUE_FLAGS, BUN_VALUE_FLAGS, PIP_VALUE_FLAGS, UV_VALUE_FLAGS, POETRY_VALUE_FLAGS, PIPENV_VALUE_FLAGS, PIPX_VALUE_FLAGS, PY_SPEC_RE, PY_EXACT_RE, EXACT_VERSION_RE, GEM_NAME_RE, GEM_VALUE_FLAGS, BUNDLE_VALUE_FLAGS, CRATE_NAME_RE, CARGO_ADD_VALUE_FLAGS, CARGO_INSTALL_VALUE_FLAGS, GO_VALUE_FLAGS, GO_PATH_RE, NUGET_NAME_RE, DOTNET_ADD_VALUE_FLAGS, DOTNET_TOOL_VALUE_FLAGS, NUGET_VALUE_FLAGS, MAVEN_COORD_RE, COMPOSER_NAME_RE, COMPOSER_VALUE_FLAGS, EDITOR_HEADS, EXTENSION_ID_RE, MANAGER_HEADS, SHELLS2, MAX_NESTING, SHELL_VALUE_FLAGS, NPM_FAMILY, PIP_FAMILY, NPM_FETCH_VERBS, PIP_FETCH_VERBS, NPM_FLAGS, PIP_FLAGS, NPM_VARS, PIP_VARS, PRESCREEN_RE2, PIP_ATTACHED_RE, MAX_PAYLOAD_DEPTH4, PUBLIC_REGISTRY_HOSTS, TUNING_FIELDS, MAP_GOVERNED, KNOB_GOVERNED, CONFIGURABLE_CHECK_IDS, DEFAULT_EGRESS_ALLOWLIST, PRIVATE_HOST_SUFFIXES, SOURCE_COMMANDS, SINK_COMMANDS, OBFUSCATORS, SENSITIVE_PATTERNS, FLAGS_WITH_VALUES, DESTINATION_ARGS, MCP_DESTINATION_KEYS, ARGUMENT_SCAN_LIMITS, RUN_CHAR, runChar, VERDICT_RANK, SQL_DML_KEYWORDS, MAX_DESTINATIONS, MAX_SCAN_CHARS, EMAIL_RE, URL_RE, IBAN_RE, RECIPIENT_KEYS, DESTRUCTIVE_VERB_RE, PII_EMAIL_RE, PII_SSN_RE, PII_PHONE_RE, PII_CC16_RE, PII_CC15_RE, PII_CC14_RE, PII_CC13_RE, PII_CC19_RE, CARD_PATTERNS, PII_ROUTING_RE, ROUTING_LABEL_RE, ROUTING_LABEL_WINDOW, NATIONAL_ID_SHAPES, NATIONAL_ID_LABEL_RE, NATIONAL_ID_LABEL_CS_RE, idLabelIn, NATIONAL_ID_LABEL_WINDOW, NATIONAL_ID_GUARD_RE, PII_IBAN_RE, REALTIME_PII_PATTERNS, MAX_PII_SCAN_BYTES, IBAN_DESTINATION_KEYS, WHOLE_IBAN_RE, JSON_ESCAPE_RE, JSON_ESCAPES, MASKED_KINDS, ENTRY_RE, IBAN_TEXT_RE, EMAIL_TEXT_RE, ACCOUNT_KEY_RE, ACCOUNT_VALUE_RE, MAX_MASK_DEPTH, EMPTY, CURL_SEND_FLAGS, WGET_SEND_FLAGS, IWR_SEND_FLAGS, WRITE_METHODS, GH_READ, GH_WRITE, GH_HOSTNAME_FLAGS, GH_REPO_VALUE_FLAGS, GH_API_VALUE_FLAGS, GIT_FETCH, PUBLISH, SHELLS3, OWN_HEADS, MAX_DEPTH3, CWD_CHANGERS, TERMINAL_ESCAPE_RE, LOOP_MAX_RECORDS, CANARY_MIN_LENGTH, MAX_TEXT, MAX_DEPTH4, MAX_JSON_PARSE, URL_DEPTH, B64_DEPTH, MIN_SEGMENT, SEPARATORS, stripSeparators, looksText, CANARY_DECODERS, VIEWS, LONG_OUTPUT_THRESHOLD_BYTES;
10095
+ var import_url, import_picomatch, import_safe_regex2, import_picomatch2, import_module, import_picomatch3, import_safe_regex22, import_crypto2, import_crypto3, import_crypto4, import_crypto5, getFilename, __filename2, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, finance_default, identity_default, publishing_default, settings_default, branches_default, ACTION_TEMPLATES, ACTION_CHECKS, GAP, prog, httpTo, httpWrite, HTTP_WRITE, HTTP_WRITE_ANY, NOT_STRIPE_TEST, postTo, STRIPE_OBJECTS, APP_SHIELD_SPECS, APP_SHIELDS, APP_SHIELD_NAMES, APP_SERVER_GLOB, GENERAL_ACTION_RULES, BUILTIN_SHIELDS, SSRF_MAX_HOST, METADATA_ADDRESSES, STRICT_TIERS, METADATA_HOSTNAMES, v4Octets, TIER_REASON, CHECK_TEXT, LEVELS, MODE_VALUES, SOFT, STRICT, BLOCK_ALL, LEVEL_CHECKS, ASI, ASI_IDS, PACK_SHIELD_OWASP, PACK_RULE_OWASP, VERDICTS, CHECK_VERDICT_RANK, ALL, BUILTIN_DANGEROUS_WORDS, PRODUCT_CHECKS, SHIELD_RULE_NAME, SHIELD_RULES_FOLDED_INTO_PRODUCT, CHECKS, CHECK_BY_ID, LEGACY_TO_ID, PROJECT_JAIL_BLOCK_READ, OVERRIDE_PREFIX, LABEL_WRAPPERS, LEGACY_READERS, syntax, KINDS, STRUCTS, unknownKinds, INVISIBLE_RE, INVISIBLE_ONE, HOMOGLYPHS, HOMOGLYPH_RE, MAX_BLOBS, MIN_DECODED, BASE64_RUN_RE, HEX_RUN_RE, HEX_ESCAPE_RUN_RE, SYSTEM_AUTH_FILES, SYSTEM_AUTH_DIRS, PASSWORD_HASH_FILES, STARTUP_NAMES, SYSTEM_DIRS, SYSTEM_DIRS_EXEMPT, UPLOAD_SYSTEM_DIRS, AGENT_INSTRUCTION_FILES, AGENT_INSTRUCTION_DIRS, LEGACY_SYSTEM_PATH_RE, MAX_ALTERNATIVES, MAX_CALL_DEPTH, MAX_CALL_WALKS, MAX_XARGS_PAYLOAD, POSITIONAL, MAX_DEPTH, MAX_PAYLOAD_DEPTH, unique, clone, SENDING_STDIN, DOWNLOAD_SHORT_VALUE, DOWNLOAD_LONG_VALUE, DOWNLOAD_OUTPUT_FLAGS, DOWNLOAD_DIR_FLAGS, base, quoteGlob, asWord, ops, MAX_INLINE_CODE, WHAT, OUTSIDE, pyImport, either, PYTHON, NODE, PERL, RUBY, PHP, TABLES, STRING_LITERAL, READ_CALL, ARG, WRAP, STR_PREFIX, WRITE_MODE_LITERAL, WRITE_MODE, WRITE_MODE_AFTER, PERL_WRITE_BEFORE, readCallBefore, READ_BEFORE, READ_AFTER, ASSIGNED, openedName, OPENED_NAME, DOTENV, DOTENV_PATH_OPTION, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, codeBodiesCache, AST_CACHE_MAX, astCache, PARSE_FAIL, INTERPRETER_LINE, PROGRAM_DIR, ACCESS_VIEW_CACHE_MAX, accessViewCache, MAX_READING_PAYLOADS, STDIN_CODE_INTERPRETERS, RUNS_TEXT_ELSEWHERE, defaultExpansionOps, EMPTY_NAMES, VARIABLE_READERS, PLAIN_PROGRAM_NAME, FS_READ_TOOLS, GREP_SHAPE, PATTERN_VERBS, PATTERN_VERB_NAMES, GREP_FILE_OPERANDS, READER_VALUE_LETTERS, FILE_OPERAND_FLAGS, SCP_VALUE_FLAGS, CURL_SOURCE_FLAGS, CURL_VALUE_LETTERS, WGET_VALUE_LETTERS, TAR_VALUE_LETTERS, ZIP_VALUE_LETTERS, RSYNC_VALUE_LETTERS, RSYNC_SKIP, CP_VALUE_LETTERS, INSTALL_VALUE_LETTERS, COPY_VERBS, TAR_MODE_WORD, COPY_VERB_HEADS, FS_OP_PRESCREEN_RE, HOME_CACHE_ALLOWLIST, SENSITIVE_PATH_RULES, AST_FS_REGEX_RULES, SQL_DB_CLIS, SQL_DDL_RE, CHMOD_OPEN_PERM_TOKENS, COMMAND_WRAPPERS, INLINE_INTERPRETER, RUNNER_WRAPPERS, _redirStdinOps, _listOps, WRAPPER_TAKES_TARGET, FIND_EXEC_FLAGS, FS_MUTATION_TOOLS, INTERP_LEADING_TARGET, NODE_VALUE_FLAG, MAX_PAYLOAD_NESTING, payloadNesting, MAX_INLINE_CALLS, INLINE_EXEC_FALLBACK, UNREADABLE_SOURCE, SCRIPT_RUNNERS, SCRIPT_RUNNER_VALUE_FLAGS, MAX_SCRIPT_OPERANDS, NET_BINARIES, VALUE_FLAGS, HOME_VARIABLES, assignmentTable, currentStmtOffset, PAYLOAD_BUDGET, payloadBudget, seenPayloads, UNANALYSABLE_NESTING, ASSIGNMENT_HEADS, andOrOps, ARRAY_MARK, ARRAY_SEP, MAX_CALL_DEPTH2, MAX_CALL_WALKS2, callDepth, callWalks, FS_OP_CACHE_MAX, fsOpCache, stripDotSlash, REDIR_TRUNCATE_OPS, REDIR_FILE_IN_OPS, REDIR_HEREDOC_OPS, FIND_OPTIONS, COPY_RULE_OF, INDIRECT_SHAPE_RE, SOURCES_FILE_RE, SHELL_SET_VARS, pipeOps, callWords, JAILED_GLOB_NAMES, JAILED_DIR_NAMES, shellGlob, GLOB_META, INLINE_INTERP_PRESCREEN_RE, runsInlineInterpreter, isReaderWord, positionalAfter, NONE, UNKNOWN, FORBIDDEN_PATH_SEGMENTS, BASH_TOOL_NAMES, MCP_EXECUTORS, BUILTIN_FIELDS, ARGV_FIELDS, BARE_WORD, MAX_ARGV_CHARACTERS, exempt, RANK, SEQUENCE_OPS, BIP39_ENGLISH, BIP39_PUBLIC_VECTORS, indices, lengths, hash, MAX_MNEMONIC_HASHES, SECRET_VIEW_LIMITS, IBAN_LENGTH, IBAN_BBAN_SHAPE, B58, B58_INDEX, XPRV_VERSIONS, B62, CRC32_TABLE, GITHUB_TOKEN_RE, B64URL, B64URL_INDEX, CASK_TWO_ZERO_SUFFIX, CASK_FOUR_ZERO_SUFFIX, CF_ODD, CF_EVEN, SPANISH_LETTERS, HETU_CHARS, VERHOEFF_D, VERHOEFF_P, MAX, UNTRUSTED_TOOLS, INJECTION_SIGNALS, MENTION_QUOTE_RE, MENTION_WORD_RE, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, REFERENCE_VALUE_RE, IDENTIFIER_VALUE_RE, PATH_VALUE_RE, DOTTED_REFERENCE_RE, CONCATENATION_RE, EMAIL_VALUE_RE, FIXTURE_NAME_RE, DERIVATIVE_NAME_RE, QUERY_CREDENTIAL_VALUE_RE, OTP_SECRET_RE, OTP_DOC_SECRETS, ASSIGNED_SECRET_VALUE_RE, ASSIGNED_SECRET_NAME_RE, NAME_KEYWORD_RE, ARN_CONTEXT_RE, CIPHER_KEY_NAME_RE, CIPHER_NAME_PARTS, CIPHER_KEY_VALUE_RE, LARAVEL_KEY_VALUE_RE, SQL_PASSWORD_VALUE_RE, GRAFANA_KEY_VALUE_RE, BASIC_VALUE_RE, BASIC_PAIR_RE, BASIC_HEADER_RE, BASIC_HEADER_PAIR_RE, BASIC_PLACEHOLDER_RE, BASIC_TEMPLATE_MARKERS, DROPBOX_SECRET_RE, OTP_CAPTURE_RE, DB_URL_PASSWORD_RE, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, SENSITIVE_PATH_REGEXES, MAX_DEPTH2, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, BATCH_SEPARATOR, IMPERATIVE, SECRET_TARGET, GUARD, SIGNALS, DESTINATION_RE, REUSED_SIGNALS, NEVER_NEGATED, NEGATION_BEFORE, RDR_IN, COPY_VERBS2, LITERAL_VERBS, GIT_PRESCREEN_RE, MAX_PAYLOAD_DEPTH2, MAX_ALIAS_DEPTH, CACHE_MAX, cache, PAYLOAD_SHELLS, CONFIG_SCOPE_FLAGS, CONFIG_VALUE_FLAGS, GLOBAL_VALUE_OPTIONS, GLOBAL_FLAG_OPTIONS, MAX_LOOP_ITEMS, GitWalk, POSITIONAL2, NO_VARS, NO_APPENDS, FORCE_PUSH, DESTRUCTIVE, UNREADABLE, PUSH_LONG_VALUE, BRANCH_LONG_VALUE, TAG_LONG_VALUE, CLEAN_LONG_VALUE, RESTORE_LONG_VALUE, CHECKOUT_LONG_VALUE, HOOKS_BYPASS, NO_VERIFY_SUBS, HOOK_KILL_SWITCHES, COMMIT_LONG_VALUE, CHMOD_OPTION_RE, _appendOp, STAGE_ALL, ADD_LONG_VALUE, CHECK_ID, RULE_NAME, INERT_LINE_RE, CHECK_ID2, INSTRUCTION_NAMES, SETTINGS_RE, HOOK_WORDS_RE, SHELLS, NETCATS, NET_CLIENTS, SENDERS, NETCAT_EXEC_LONG, DECL_DUMPS, PROC_ENVIRON_RE, DEV_NET_RE, SOCAT_PROGRAM_RE, SOCAT_NET_RE, PRESCREEN_RE, MAX_PAYLOAD_DEPTH3, CACHE_MAX2, cache2, dupInOp, REVERSE_SHELL_DESCRIPTION, ENV_EXFIL_DESCRIPTION, DYNAMIC_WORD, MULTICALL, SHELL_VALUE_OPTIONS, INTERPRETERS, OutboundWalk, NPM_INSTALL_VERBS, NPM_EXEC_VERBS, PNPM_INSTALL_VERBS, YARN_INSTALL_VERBS, BUN_INSTALL_VERBS, DLX_VERBS, NON_REGISTRY_RE, NPM_NAME_RE, EXACT_SEMVER_RE, NPM_VALUE_FLAGS, PNPM_VALUE_FLAGS, YARN_VALUE_FLAGS, BUN_VALUE_FLAGS, PIP_VALUE_FLAGS, UV_VALUE_FLAGS, POETRY_VALUE_FLAGS, PIPENV_VALUE_FLAGS, PIPX_VALUE_FLAGS, PY_SPEC_RE, PY_EXACT_RE, EXACT_VERSION_RE, GEM_NAME_RE, GEM_VALUE_FLAGS, BUNDLE_VALUE_FLAGS, CRATE_NAME_RE, CARGO_ADD_VALUE_FLAGS, CARGO_INSTALL_VALUE_FLAGS, GO_VALUE_FLAGS, GO_PATH_RE, NUGET_NAME_RE, DOTNET_ADD_VALUE_FLAGS, DOTNET_TOOL_VALUE_FLAGS, NUGET_VALUE_FLAGS, MAVEN_COORD_RE, COMPOSER_NAME_RE, COMPOSER_VALUE_FLAGS, EDITOR_HEADS, EXTENSION_ID_RE, MANAGER_HEADS, SHELLS2, MAX_NESTING, SHELL_VALUE_FLAGS, NPM_FAMILY, PIP_FAMILY, NPM_FETCH_VERBS, PIP_FETCH_VERBS, NPM_FLAGS, PIP_FLAGS, NPM_VARS, PIP_VARS, PRESCREEN_RE2, PIP_ATTACHED_RE, MAX_PAYLOAD_DEPTH4, PUBLIC_REGISTRY_HOSTS, TUNING_FIELDS, MAP_GOVERNED, KNOB_GOVERNED, CONFIGURABLE_CHECK_IDS, DEFAULT_EGRESS_ALLOWLIST, PRIVATE_HOST_SUFFIXES, SOURCE_COMMANDS, SINK_COMMANDS, OBFUSCATORS, SENSITIVE_PATTERNS, FLAGS_WITH_VALUES, DESTINATION_ARGS, MCP_DESTINATION_KEYS, ARGUMENT_SCAN_LIMITS, RUN_CHAR, runChar, RULE_NAME2, DESCRIPTION, DAEMON_CMDLINES, DAEMON_NAMES, KILLALL_NAMES, STOP_SYSTEMCTL, STOP_LAUNCHCTL, PKILL_VALUE_FLAGS, NAME_PRESCREEN_RE, base2, HOME_PID_FILE_RE, VERDICT_RANK, SQL_DML_KEYWORDS, NOT_OVERRIDABLE_CHECKS, MAX_DESTINATIONS, MAX_SCAN_CHARS, EMAIL_RE, URL_RE, IBAN_RE, RECIPIENT_KEYS, DESTRUCTIVE_VERB_RE, PII_EMAIL_RE, PII_SSN_RE, PII_PHONE_RE, PII_CC16_RE, PII_CC15_RE, PII_CC14_RE, PII_CC13_RE, PII_CC19_RE, CARD_PATTERNS, PII_ROUTING_RE, ROUTING_LABEL_RE, ROUTING_LABEL_WINDOW, NATIONAL_ID_SHAPES, NATIONAL_ID_LABEL_RE, NATIONAL_ID_LABEL_CS_RE, idLabelIn, NATIONAL_ID_LABEL_WINDOW, NATIONAL_ID_GUARD_RE, PII_IBAN_RE, REALTIME_PII_PATTERNS, MAX_PII_SCAN_BYTES, IBAN_DESTINATION_KEYS, WHOLE_IBAN_RE, JSON_ESCAPE_RE, JSON_ESCAPES, MASKED_KINDS, ENTRY_RE, IBAN_TEXT_RE, EMAIL_TEXT_RE, ACCOUNT_KEY_RE, ACCOUNT_VALUE_RE, MAX_MASK_DEPTH, EMPTY, CURL_SEND_FLAGS, WGET_SEND_FLAGS, IWR_SEND_FLAGS, WRITE_METHODS, GH_READ, GH_WRITE, GH_HOSTNAME_FLAGS, GH_REPO_VALUE_FLAGS, GH_API_VALUE_FLAGS, GIT_FETCH, PUBLISH, SHELLS3, OWN_HEADS, MAX_DEPTH3, CWD_CHANGERS, TERMINAL_ESCAPE_RE, LOOP_MAX_RECORDS, CANARY_MIN_LENGTH, MAX_TEXT, MAX_DEPTH4, MAX_JSON_PARSE, URL_DEPTH, B64_DEPTH, MIN_SEGMENT, SEPARATORS, stripSeparators, looksText, CANARY_DECODERS, VIEWS, LONG_OUTPUT_THRESHOLD_BYTES;
9848
10096
  var init_dist = __esm({
9849
10097
  "packages/policy-engine/dist/index.mjs"() {
9850
10098
  "use strict";
@@ -11854,7 +12102,7 @@ var init_dist = __esm({
11854
12102
  advice: "Fixed at Review for now."
11855
12103
  },
11856
12104
  "commands.node9-disarm": {
11857
- 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.",
12105
+ 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.",
11858
12106
  example: "After reading a web page, the agent runs node9 session-exposure clear --all",
11859
12107
  advice: "Keep Review: these are your commands to run. Block if the agent should never touch node9."
11860
12108
  },
@@ -12167,7 +12415,7 @@ var init_dist = __esm({
12167
12415
  group: "commands",
12168
12416
  owasp: "ASI10",
12169
12417
  title: "node9 disarmed from the shell",
12170
- catches: "a node9 command that clears a session flag, trusts a destination, pauses the gate or weakens a shield, run by the agent",
12418
+ 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",
12171
12419
  values: ALL,
12172
12420
  defaultValue: "review",
12173
12421
  legacy: ["review-node9-disarm"]
@@ -13048,6 +13296,62 @@ var init_dist = __esm({
13048
13296
  php: PHP
13049
13297
  };
13050
13298
  STRING_LITERAL = /(['"`])((?:\\.|(?!\1)[^\\\n]){1,4096})\1/g;
13299
+ READ_CALL = {
13300
+ python: String.raw`(?:\b(?:io|codecs)\.)?\bopen|\b(?:dotenv_values|load_dotenv|get_key|read_file|loadtxt|read_csv)`,
13301
+ node: String.raw`\b(?:readFileSync|readFile|createReadStream|readTextFile(?:Sync)?|readFileStr|openSync|file)`,
13302
+ perl: String.raw`\b(?:open|sysopen|read_file|slurp)`,
13303
+ ruby: String.raw`\b(?:File|IO)\.(?:read|binread|readlines|foreach|open)|\bDotenv\.(?:load|parse|overload)`,
13304
+ php: String.raw`\b(?:file_get_contents|fopen|file|readfile|parse_ini_file|highlight_file|show_source)`
13305
+ };
13306
+ ARG = String.raw`(?:(?:(?:my|our|local)\s+)?[\w.$]+(?:\s*\(\s*\))?|'[^'\n]{0,8}'|"[^"\n]{0,8}")\s*,\s*`;
13307
+ WRAP = String.raw`(?:(?:new\s+)?[\w.$]+\s*\(\s*(?:${ARG}){0,3}){0,3}`;
13308
+ STR_PREFIX = String.raw`[rRbBuUfF]{0,2}`;
13309
+ WRITE_MODE_LITERAL = String.raw`['"](?=[rbtU+]*w|(?![^'"]*\+)[rbtU]*[ax])[rwaxbtU+]{1,4}['"]`;
13310
+ WRITE_MODE = String.raw`\s*,\s*(?:mode\s*=\s*)?${WRITE_MODE_LITERAL}`;
13311
+ WRITE_MODE_AFTER = new RegExp(`^(?:\\s*\\)){0,3}${WRITE_MODE}`);
13312
+ PERL_WRITE_BEFORE = /['"]\s*(?:\+?[>|]|>>)\s*['"]\s*,\s*$/;
13313
+ readCallBefore = (language) => new RegExp(
13314
+ String.raw`(?:${READ_CALL[language]})(?![\w$])\s*\(?\s*(?:${ARG}){0,3}${WRAP}${STR_PREFIX}$`
13315
+ );
13316
+ READ_BEFORE = {
13317
+ python: readCallBefore("python"),
13318
+ node: readCallBefore("node"),
13319
+ perl: readCallBefore("perl"),
13320
+ ruby: readCallBefore("ruby"),
13321
+ php: readCallBefore("php")
13322
+ };
13323
+ READ_AFTER = {
13324
+ python: new RegExp(
13325
+ String.raw`^\s*\)\s*\.\s*(?:read_text|read_bytes|open)\s*\((?!\s*(?:mode\s*=\s*)?${WRITE_MODE_LITERAL})`
13326
+ )
13327
+ };
13328
+ ASSIGNED = new RegExp(String.raw`([$\w]+)\s*=\s*${WRAP}${STR_PREFIX}$`);
13329
+ openedName = (language) => new RegExp(
13330
+ 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*\(` : ""),
13331
+ "g"
13332
+ );
13333
+ OPENED_NAME = {
13334
+ python: openedName("python"),
13335
+ node: openedName("node"),
13336
+ perl: openedName("perl"),
13337
+ ruby: openedName("ruby"),
13338
+ php: openedName("php")
13339
+ };
13340
+ DOTENV = {
13341
+ node: {
13342
+ load: /\brequire\s*\(\s*['"`]dotenv(?:\/config)?['"`]\s*\)|\bimport\s*(?:[\w${}\s,*]+\s+from\s*)?['"`]dotenv(?:\/config)?['"`]|\bimport\s*\(\s*['"`]dotenv(?:\/config)?['"`]\s*\)/,
13343
+ values: /\.parsed\b|[(,]\s*(?:require\s*\(\s*['"`]dotenv['"`]\s*\)|dotenv)\s*\.\s*config\s*\(/,
13344
+ env: /\b(?:process|Deno|Bun)\s*\.\s*env\b/,
13345
+ literalPath: /\bconfig\s*\(\s*\{[^)]*\bpath\s*:\s*['"`]/
13346
+ },
13347
+ python: {
13348
+ load: /\b(?:load_dotenv|dotenv_values|get_key)\s*\(/,
13349
+ values: /\b(?:dotenv_values|get_key)\s*\(/,
13350
+ env: /\b(?:environ|getenv)\b/,
13351
+ literalPath: /\b(?:load_dotenv|dotenv_values)\s*\(\s*(?:dotenv_path\s*=\s*)?[rRbBuU]?['"]/
13352
+ }
13353
+ };
13354
+ DOTENV_PATH_OPTION = /\bconfig\s*\(\s*\{[^)]*\bpath\s*:\s*$/;
13051
13355
  sharedParser = lazy(() => getSyntax().NewParser());
13052
13356
  MESSAGE_FLAGS = /* @__PURE__ */ new Set([
13053
13357
  "-m",
@@ -13842,6 +14146,7 @@ var init_dist = __esm({
13842
14146
  "rmdir"
13843
14147
  ]);
13844
14148
  INTERP_LEADING_TARGET = /* @__PURE__ */ new Set(["su"]);
14149
+ NODE_VALUE_FLAG = /^(-r|--require|--import|--loader|--experimental-loader|--env-file|--env-file-if-exists|--input-type|-C|--conditions)(?:=(.*))?$/;
13845
14150
  MAX_PAYLOAD_NESTING = 8;
13846
14151
  payloadNesting = 0;
13847
14152
  MAX_INLINE_CALLS = 16;
@@ -14090,6 +14395,8 @@ var init_dist = __esm({
14090
14395
  JAILED_DIR_NAMES = [".ssh", ".aws"];
14091
14396
  shellGlob = (p) => p.replace(/\[!/g, "[^");
14092
14397
  GLOB_META = /(?<!\\)[*?[]/;
14398
+ INLINE_INTERP_PRESCREEN_RE = /(?:^|[^\w-])(?:python|node|tsx|ts-node|deno|bun|perl|ruby|irb|php)(?![A-Za-z_])/i;
14399
+ runsInlineInterpreter = (command, normalized) => INLINE_INTERP_PRESCREEN_RE.test(command) || INLINE_INTERP_PRESCREEN_RE.test(normalized);
14093
14400
  isReaderWord = (w) => w !== null && FS_READ_TOOLS.has(baseWord(w));
14094
14401
  positionalAfter = (words, from, to = words.length) => positionedArgs(words, from, to).map((a) => a.value);
14095
14402
  NONE = { kind: "none" };
@@ -17405,9 +17712,9 @@ zoo`.split("\n");
17405
17712
  const name = n.Name.Value;
17406
17713
  if (n.Append) {
17407
17714
  const prev = this.appended.has(name) ? this.appended.get(name) : resolveWordLiteral(n.Value) === null ? null : void 0;
17408
- const base2 = prev === void 0 ? recordedExpansion(name) : prev;
17715
+ const base3 = prev === void 0 ? recordedExpansion(name) : prev;
17409
17716
  const piece = resolveWordLiteral(n.Value);
17410
- this.appended.set(name, typeof base2 === "string" && piece !== null ? base2 + piece : null);
17717
+ this.appended.set(name, typeof base3 === "string" && piece !== null ? base3 + piece : null);
17411
17718
  this.treeTopVars.delete(name);
17412
17719
  return true;
17413
17720
  }
@@ -18923,12 +19230,50 @@ ${text}` : text);
18923
19230
  const char = value[index] ?? "";
18924
19231
  return RUN_CHAR.test(char) || char !== "" && isInvisibleChar(char);
18925
19232
  };
19233
+ RULE_NAME2 = "review-node9-disarm";
19234
+ 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.";
19235
+ DAEMON_CMDLINES = [
19236
+ "node /usr/lib/node_modules/@node9/proxy/dist/cli.js daemon start",
19237
+ "node9 daemon start"
19238
+ ];
19239
+ DAEMON_NAMES = ["node", "node9"];
19240
+ KILLALL_NAMES = /* @__PURE__ */ new Set(["node", "node9", "node.exe", "node9.exe"]);
19241
+ STOP_SYSTEMCTL = /* @__PURE__ */ new Set(["stop", "disable", "mask", "kill"]);
19242
+ STOP_LAUNCHCTL = /* @__PURE__ */ new Set(["unload", "bootout", "remove", "stop", "kill", "disable"]);
19243
+ PKILL_VALUE_FLAGS = /* @__PURE__ */ new Set([
19244
+ "-u",
19245
+ "-U",
19246
+ "-g",
19247
+ "-G",
19248
+ "-P",
19249
+ "-s",
19250
+ "-t",
19251
+ "-F",
19252
+ "--signal",
19253
+ "--euid",
19254
+ "--uid",
19255
+ "--pgroup",
19256
+ "--group",
19257
+ "--parent",
19258
+ "--session",
19259
+ "--terminal",
19260
+ "--pidfile",
19261
+ "--ns",
19262
+ "--nslist"
19263
+ ]);
19264
+ NAME_PRESCREEN_RE = /node9|kill|systemctl|launchctl|daemon/i;
19265
+ base2 = (w) => (w ?? "").split(/[\\/]/).pop()?.toLowerCase() ?? "";
19266
+ HOME_PID_FILE_RE = /(?:^|[\s"'(=`])(?:~|\$HOME|\$\{HOME\}|\/home\/[^/\s]+|\/Users\/[^/\s]+|\/root)\/\.node9\/daemon\.pid/;
18926
19267
  VERDICT_RANK = {
18927
19268
  allow: 0,
18928
19269
  review: 1,
18929
19270
  block: 2
18930
19271
  };
18931
19272
  SQL_DML_KEYWORDS = /* @__PURE__ */ new Set(["select", "insert", "update", "delete", "merge", "upsert"]);
19273
+ NOT_OVERRIDABLE_CHECKS = /* @__PURE__ */ new Set([
19274
+ "data.credential-files",
19275
+ "data.credential-files-other"
19276
+ ]);
18932
19277
  MAX_DESTINATIONS = 200;
18933
19278
  MAX_SCAN_CHARS = 1e5;
18934
19279
  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;
@@ -20347,8 +20692,8 @@ function safeApiUrl(raw, onReject) {
20347
20692
  function sanitizeSsrfAllow(entries, source) {
20348
20693
  const kept = [];
20349
20694
  for (const entry of entries) {
20350
- const base2 = entry.includes("/") ? entry.slice(0, entry.indexOf("/")).trim() : entry;
20351
- const m = classifySsrf(base2);
20695
+ const base3 = entry.includes("/") ? entry.slice(0, entry.indexOf("/")).trim() : entry;
20696
+ const m = classifySsrf(base3);
20352
20697
  if (m && !m.overridable) {
20353
20698
  const what = entry.includes("/") ? "covers only protected addresses" : "is a protected address";
20354
20699
  process.emitWarning(
@@ -21448,12 +21793,12 @@ function getConfig(cwd) {
21448
21793
  continue;
21449
21794
  explicit[id] = v;
21450
21795
  }
21451
- const base2 = resolveCheckMap({
21796
+ const base3 = resolveCheckMap({
21452
21797
  ...catalogSettingsFrom(mergedSettings, mergedPolicy),
21453
21798
  checks: explicit
21454
21799
  });
21455
21800
  for (const [id, v] of Object.entries(v2ChecksByPath.get(projectPath)?.checks ?? {})) {
21456
- if (CHECK_VERDICT_RANK[v] > CHECK_VERDICT_RANK[base2[id] ?? "off"]) {
21801
+ if (CHECK_VERDICT_RANK[v] > CHECK_VERDICT_RANK[base3[id] ?? "off"]) {
21457
21802
  explicit[id] = v;
21458
21803
  checkSources[id] = "project";
21459
21804
  }
@@ -21605,7 +21950,7 @@ init_dist();
21605
21950
  // src/policy/index.ts
21606
21951
  init_cjs_shims();
21607
21952
  init_dist();
21608
- var import_fs14 = __toESM(require("fs"));
21953
+ var import_fs15 = __toESM(require("fs"));
21609
21954
 
21610
21955
  // src/utils/read-capped.ts
21611
21956
  init_cjs_shims();
@@ -21649,10 +21994,42 @@ function readCappedText(file2, maxBytes, openFlags = "r") {
21649
21994
 
21650
21995
  // src/policy/index.ts
21651
21996
  var import_path14 = __toESM(require("path"));
21997
+ var import_os10 = __toESM(require("os"));
21652
21998
 
21653
- // src/dlp.ts
21999
+ // src/utils/read-small-file.ts
21654
22000
  init_cjs_shims();
21655
22001
  var import_fs7 = __toESM(require("fs"));
22002
+ function readSmallFile(file2, maxBytes) {
22003
+ let fd;
22004
+ try {
22005
+ fd = import_fs7.default.openSync(file2, import_fs7.default.constants.O_RDONLY | (import_fs7.default.constants.O_NONBLOCK ?? 0));
22006
+ } catch {
22007
+ return null;
22008
+ }
22009
+ try {
22010
+ const st = import_fs7.default.fstatSync(fd);
22011
+ if (!st.isFile() || st.size > maxBytes) return null;
22012
+ const buf = Buffer.alloc(Math.min(st.size, maxBytes));
22013
+ let read2 = 0;
22014
+ while (read2 < buf.length) {
22015
+ const n = import_fs7.default.readSync(fd, buf, read2, buf.length - read2, read2);
22016
+ if (n === 0) break;
22017
+ read2 += n;
22018
+ }
22019
+ return buf.toString("utf8", 0, read2);
22020
+ } catch {
22021
+ return null;
22022
+ } finally {
22023
+ import_fs7.default.closeSync(fd);
22024
+ }
22025
+ }
22026
+
22027
+ // src/policy/index.ts
22028
+ init_forecast_context();
22029
+
22030
+ // src/dlp.ts
22031
+ init_cjs_shims();
22032
+ var import_fs8 = __toESM(require("fs"));
21656
22033
  var import_path6 = __toESM(require("path"));
21657
22034
  init_dist();
21658
22035
  init_dist();
@@ -21661,7 +22038,7 @@ function scanFilePath(filePath, cwd = process.cwd(), options = {}) {
21661
22038
  let resolved;
21662
22039
  try {
21663
22040
  const absolute2 = import_path6.default.resolve(cwd, filePath);
21664
- resolved = import_fs7.default.realpathSync.native(absolute2);
22041
+ resolved = import_fs8.default.realpathSync.native(absolute2);
21665
22042
  } catch (err) {
21666
22043
  const code = err.code;
21667
22044
  if (code === "ENOENT" || code === "ENOTDIR") {
@@ -21887,7 +22264,7 @@ function combineWithEngine(gates, engine, opts = { panicMode: false }) {
21887
22264
 
21888
22265
  // src/policy/file-access.ts
21889
22266
  init_cjs_shims();
21890
- var import_fs8 = __toESM(require("fs"));
22267
+ var import_fs9 = __toESM(require("fs"));
21891
22268
  var import_os6 = __toESM(require("os"));
21892
22269
  var import_path7 = __toESM(require("path"));
21893
22270
  init_dist();
@@ -21902,13 +22279,13 @@ function physical(p, followFinal, links = 0) {
21902
22279
  return { path: `${parent.path}/${import_path7.default.basename(p)}`, missing: parent.missing };
21903
22280
  }
21904
22281
  try {
21905
- return { path: import_fs8.default.realpathSync.native(p), missing: false };
22282
+ return { path: import_fs9.default.realpathSync.native(p), missing: false };
21906
22283
  } catch (error) {
21907
22284
  const code = error.code;
21908
22285
  if (!["ENOENT", "ENOTDIR"].includes(code ?? "")) throw error;
21909
22286
  try {
21910
- if (import_fs8.default.lstatSync(p).isSymbolicLink()) {
21911
- const target = import_fs8.default.readlinkSync(p);
22287
+ if (import_fs9.default.lstatSync(p).isSymbolicLink()) {
22288
+ const target = import_fs9.default.readlinkSync(p);
21912
22289
  return physical(
21913
22290
  import_path7.default.isAbsolute(target) ? target : `${import_path7.default.dirname(p)}/${target}`,
21914
22291
  true,
@@ -21948,14 +22325,14 @@ function prepareFileAccesses(tool, args, cwd = process.cwd(), inspection = {}, h
21948
22325
  const raw = absoluteSpelling(access.path, access.cwd ?? cwd, home);
21949
22326
  if (access.directoryTarget) {
21950
22327
  try {
21951
- if (!import_fs8.default.statSync(absoluteSpelling(access.directoryTarget, access.cwd ?? cwd, home)).isDirectory())
22328
+ if (!import_fs9.default.statSync(absoluteSpelling(access.directoryTarget, access.cwd ?? cwd, home)).isDirectory())
21952
22329
  continue;
21953
22330
  } catch {
21954
22331
  }
21955
22332
  }
21956
22333
  if (access.operation === "create") {
21957
22334
  try {
21958
- import_fs8.default.statSync(raw);
22335
+ import_fs9.default.statSync(raw);
21959
22336
  continue;
21960
22337
  } catch {
21961
22338
  }
@@ -22011,7 +22388,7 @@ init_dist();
22011
22388
 
22012
22389
  // src/supply-chain/osv-index.ts
22013
22390
  init_cjs_shims();
22014
- var import_fs10 = __toESM(require("fs"));
22391
+ var import_fs11 = __toESM(require("fs"));
22015
22392
  var import_os7 = __toESM(require("os"));
22016
22393
  var import_path9 = __toESM(require("path"));
22017
22394
  var import_crypto8 = require("crypto");
@@ -22019,27 +22396,27 @@ init_dist();
22019
22396
 
22020
22397
  // src/utils/atomic-write.ts
22021
22398
  init_cjs_shims();
22022
- var import_fs9 = __toESM(require("fs"));
22399
+ var import_fs10 = __toESM(require("fs"));
22023
22400
  var import_path8 = __toESM(require("path"));
22024
22401
  var import_crypto7 = require("crypto");
22025
22402
  function atomicWriteSync(filePath, data, options) {
22026
22403
  const dir2 = import_path8.default.dirname(filePath);
22027
- if (!import_fs9.default.existsSync(dir2)) import_fs9.default.mkdirSync(dir2, { recursive: true });
22404
+ if (!import_fs10.default.existsSync(dir2)) import_fs10.default.mkdirSync(dir2, { recursive: true });
22028
22405
  const tmpPath = `${filePath}.${(0, import_crypto7.randomUUID)()}.tmp`;
22029
22406
  try {
22030
- import_fs9.default.writeFileSync(tmpPath, data, options);
22407
+ import_fs10.default.writeFileSync(tmpPath, data, options);
22031
22408
  } catch (err) {
22032
22409
  try {
22033
- import_fs9.default.unlinkSync(tmpPath);
22410
+ import_fs10.default.unlinkSync(tmpPath);
22034
22411
  } catch {
22035
22412
  }
22036
22413
  throw err;
22037
22414
  }
22038
22415
  try {
22039
- import_fs9.default.renameSync(tmpPath, filePath);
22416
+ import_fs10.default.renameSync(tmpPath, filePath);
22040
22417
  } catch (err) {
22041
22418
  try {
22042
- import_fs9.default.unlinkSync(tmpPath);
22419
+ import_fs10.default.unlinkSync(tmpPath);
22043
22420
  } catch {
22044
22421
  }
22045
22422
  throw err;
@@ -22075,7 +22452,7 @@ function shardPath(eco, shard) {
22075
22452
  }
22076
22453
  function readMeta(eco) {
22077
22454
  try {
22078
- const m = JSON.parse(import_fs10.default.readFileSync(import_path9.default.join(ecoDir(eco), "meta.json"), "utf8"));
22455
+ const m = JSON.parse(import_fs11.default.readFileSync(import_path9.default.join(ecoDir(eco), "meta.json"), "utf8"));
22079
22456
  if (typeof m?.syncedAt !== "string" || typeof m?.lastModifiedMs !== "number") return null;
22080
22457
  return m;
22081
22458
  } catch {
@@ -22084,7 +22461,7 @@ function readMeta(eco) {
22084
22461
  }
22085
22462
  function readShard(eco, shard) {
22086
22463
  try {
22087
- const s = JSON.parse(import_fs10.default.readFileSync(shardPath(eco, shard), "utf8"));
22464
+ const s = JSON.parse(import_fs11.default.readFileSync(shardPath(eco, shard), "utf8"));
22088
22465
  return s && typeof s === "object" && !Array.isArray(s) ? s : {};
22089
22466
  } catch {
22090
22467
  return {};
@@ -22194,15 +22571,15 @@ async function fetchJson(url, opts) {
22194
22571
  // src/supply-chain/osv-online.ts
22195
22572
  var TIMEOUT_MS = 2500;
22196
22573
  async function queryOsvMalicious(queries) {
22197
- const base2 = endpoints().osvApi;
22198
- if (!base2 || queries.length === 0) return null;
22574
+ const base3 = endpoints().osvApi;
22575
+ if (!base3 || queries.length === 0) return null;
22199
22576
  const body = JSON.stringify({
22200
22577
  queries: queries.map((q) => ({
22201
22578
  package: { name: q.name, ecosystem: q.ecosystem },
22202
22579
  ...q.version ? { version: q.version } : {}
22203
22580
  }))
22204
22581
  });
22205
- const res = await fetchJson(`${base2}/v1/querybatch`, {
22582
+ const res = await fetchJson(`${base3}/v1/querybatch`, {
22206
22583
  timeoutMs: TIMEOUT_MS,
22207
22584
  maxBytes: 1024 * 1024,
22208
22585
  body
@@ -22263,9 +22640,9 @@ function pypiRepoOf(doc) {
22263
22640
  return null;
22264
22641
  }
22265
22642
  async function npmSourceRepo(name, version) {
22266
- const base2 = endpoints().npmRegistry;
22267
- if (!base2) return "unknown";
22268
- const url = `${base2}/-/npm/v1/attestations/${name.replaceAll("/", "%2F")}@${encodeURIComponent(version)}`;
22643
+ const base3 = endpoints().npmRegistry;
22644
+ if (!base3) return "unknown";
22645
+ const url = `${base3}/-/npm/v1/attestations/${name.replaceAll("/", "%2F")}@${encodeURIComponent(version)}`;
22269
22646
  return read(url, npmRepoOf);
22270
22647
  }
22271
22648
  async function read(url, repoOf) {
@@ -22278,9 +22655,9 @@ async function read(url, repoOf) {
22278
22655
  }
22279
22656
  }
22280
22657
  async function pypiSourceRepo(name, version, filename) {
22281
- const base2 = endpoints().pypi;
22282
- if (!base2) return "unknown";
22283
- const url = `${base2}/integrity/${encodeURIComponent(name)}/${encodeURIComponent(version)}/${encodeURIComponent(filename)}/provenance`;
22658
+ const base3 = endpoints().pypi;
22659
+ if (!base3) return "unknown";
22660
+ const url = `${base3}/integrity/${encodeURIComponent(name)}/${encodeURIComponent(version)}/${encodeURIComponent(filename)}/provenance`;
22284
22661
  return read(url, pypiRepoOf);
22285
22662
  }
22286
22663
  var PYPI_FINAL = /^\d+(?:\.\d+)*$/;
@@ -22325,9 +22702,9 @@ async function npmVersionDocument(url, selector) {
22325
22702
  };
22326
22703
  }
22327
22704
  async function npmInfo(name, version, freshWindowMs, raw) {
22328
- const base2 = endpoints().npmRegistry;
22329
- if (!base2) return null;
22330
- const url = `${base2}/${name.replaceAll("/", "%2F")}`;
22705
+ const base3 = endpoints().npmRegistry;
22706
+ if (!base3) return null;
22707
+ const url = `${base3}/${name.replaceAll("/", "%2F")}`;
22331
22708
  const aliasStart = raw?.indexOf("@npm:") ?? -1;
22332
22709
  const target = aliasStart > 0 ? raw.slice(aliasStart + 5) : raw?.replace(/^npm:/, "");
22333
22710
  const specStart = target?.indexOf("@", 1) ?? -1;
@@ -22395,9 +22772,9 @@ async function npmInfo(name, version, freshWindowMs, raw) {
22395
22772
  return info;
22396
22773
  }
22397
22774
  async function pypiInfo(name, version) {
22398
- const base2 = endpoints().pypi;
22399
- if (!base2) return null;
22400
- const url = version ? `${base2}/pypi/${encodeURIComponent(name)}/${encodeURIComponent(version)}/json` : `${base2}/pypi/${encodeURIComponent(name)}/json`;
22775
+ const base3 = endpoints().pypi;
22776
+ if (!base3) return null;
22777
+ const url = version ? `${base3}/pypi/${encodeURIComponent(name)}/${encodeURIComponent(version)}/json` : `${base3}/pypi/${encodeURIComponent(name)}/json`;
22401
22778
  const doc = await fetchJson(url, { timeoutMs: TIMEOUT_MS3, maxBytes: MAX_BYTES2 });
22402
22779
  if (!doc?.info?.version) return null;
22403
22780
  const times = (doc.urls ?? []).map((u) => u.upload_time_iso_8601 ? Date.parse(u.upload_time_iso_8601) : NaN).filter((t) => Number.isFinite(t));
@@ -22421,9 +22798,9 @@ function pypiHistoryOf(releases, version) {
22421
22798
  };
22422
22799
  }
22423
22800
  async function pypiHistory(name, version) {
22424
- const base2 = endpoints().pypi;
22425
- if (!base2) return null;
22426
- const doc = await fetchJson(`${base2}/pypi/${encodeURIComponent(name)}/json`, {
22801
+ const base3 = endpoints().pypi;
22802
+ if (!base3) return null;
22803
+ const doc = await fetchJson(`${base3}/pypi/${encodeURIComponent(name)}/json`, {
22427
22804
  timeoutMs: TIMEOUT_MS3,
22428
22805
  maxBytes: MAX_BYTES2
22429
22806
  });
@@ -22438,7 +22815,7 @@ async function registryInfo(eco, name, version, freshWindowMs, raw) {
22438
22815
 
22439
22816
  // src/supply-chain/local-resolve.ts
22440
22817
  init_cjs_shims();
22441
- var import_fs11 = __toESM(require("fs"));
22818
+ var import_fs12 = __toESM(require("fs"));
22442
22819
  var import_path10 = __toESM(require("path"));
22443
22820
  var MAX_LEVELS = 32;
22444
22821
  var LOCAL_FIRST_MANAGERS = /* @__PURE__ */ new Set(["npx", "bunx", "npm exec"]);
@@ -22450,7 +22827,7 @@ function resolveInstalled(name, cwd) {
22450
22827
  const pkgDir = import_path10.default.join(dir2, "node_modules", ...name.split("/"));
22451
22828
  const manifest = import_path10.default.join(pkgDir, "package.json");
22452
22829
  try {
22453
- const raw = import_fs11.default.readFileSync(manifest, "utf8");
22830
+ const raw = import_fs12.default.readFileSync(manifest, "utf8");
22454
22831
  const parsed = JSON.parse(raw);
22455
22832
  if (typeof parsed.version === "string" && parsed.version.length > 0) {
22456
22833
  return { version: parsed.version, dir: pkgDir };
@@ -22727,7 +23104,7 @@ async function runPackageCheck(toolName, args, cfg, cwd, opts = {}) {
22727
23104
  init_cjs_shims();
22728
23105
  var import_os8 = __toESM(require("os"));
22729
23106
  var import_path12 = __toESM(require("path"));
22730
- var import_fs12 = __toESM(require("fs"));
23107
+ var import_fs13 = __toESM(require("fs"));
22731
23108
  init_dist();
22732
23109
  var MAX_CONFIG_BYTES = 256 * 1024;
22733
23110
  var MAX_PARENT_DIRS = 12;
@@ -22853,7 +23230,7 @@ function configuredRegistryHosts(cwd, env = process.env, home = import_os8.defau
22853
23230
  const r = readCappedText(
22854
23231
  file2,
22855
23232
  MAX_CONFIG_BYTES,
22856
- import_fs12.default.constants.O_RDONLY | (import_fs12.default.constants.O_NONBLOCK ?? 0)
23233
+ import_fs13.default.constants.O_RDONLY | (import_fs13.default.constants.O_NONBLOCK ?? 0)
22857
23234
  );
22858
23235
  if (r) hostsInText(r.text, hosts);
22859
23236
  }
@@ -22863,7 +23240,7 @@ function configuredRegistryHosts(cwd, env = process.env, home = import_os8.defau
22863
23240
 
22864
23241
  // src/utils/provenance.ts
22865
23242
  init_cjs_shims();
22866
- var import_fs13 = __toESM(require("fs"));
23243
+ var import_fs14 = __toESM(require("fs"));
22867
23244
  var import_path13 = __toESM(require("path"));
22868
23245
  var import_os9 = __toESM(require("os"));
22869
23246
  var SYSTEM_PREFIXES = ["/usr/bin", "/usr/sbin", "/bin", "/sbin"];
@@ -22883,7 +23260,7 @@ function findInPath(cmd) {
22883
23260
  if (!dir2) continue;
22884
23261
  const full = import_path13.default.join(dir2, cmd);
22885
23262
  try {
22886
- import_fs13.default.accessSync(full, import_fs13.default.constants.X_OK);
23263
+ import_fs14.default.accessSync(full, import_fs14.default.constants.X_OK);
22887
23264
  return full;
22888
23265
  } catch {
22889
23266
  }
@@ -22928,7 +23305,7 @@ function checkProvenance(cmd, cwd) {
22928
23305
  reason: "binary not found in PATH"
22929
23306
  };
22930
23307
  }
22931
- resolved = import_fs13.default.realpathSync(found);
23308
+ resolved = import_fs14.default.realpathSync(found);
22932
23309
  } catch {
22933
23310
  return {
22934
23311
  resolvedPath: cmd,
@@ -22937,7 +23314,7 @@ function checkProvenance(cmd, cwd) {
22937
23314
  };
22938
23315
  }
22939
23316
  try {
22940
- const stat = import_fs13.default.statSync(resolved);
23317
+ const stat = import_fs14.default.statSync(resolved);
22941
23318
  if (stat.mode & 2) {
22942
23319
  return {
22943
23320
  resolvedPath: resolved,
@@ -22986,14 +23363,26 @@ async function evaluatePolicy2(toolName, args, agent, cwd, opts) {
22986
23363
  isTrustedHost: config.policy.trustedHostsManaged ? (host2) => matchesTrustedHost(host2, config.policy.trustedHosts) : isTrustedHost,
22987
23364
  readAgentSettings,
22988
23365
  isConfiguredRegistry: (host2, at) => configuredRegistryHosts(at).has(host2),
22989
- isGitWorkTreeTop
23366
+ isGitWorkTreeTop,
23367
+ daemonPid: daemonPidFromFile
22990
23368
  }
22991
23369
  );
22992
23370
  }
23371
+ function daemonPidFromFile() {
23372
+ if (forecastRun()) return null;
23373
+ const text = readSmallFile(import_path14.default.join(import_os10.default.homedir(), ".node9", "daemon.pid"), 4096);
23374
+ if (text === null) return null;
23375
+ try {
23376
+ const pid = JSON.parse(text).pid;
23377
+ return typeof pid === "number" && Number.isInteger(pid) && pid > 0 && pid <= 4194304 ? pid : null;
23378
+ } catch {
23379
+ return null;
23380
+ }
23381
+ }
22993
23382
  function isGitWorkTreeTop(dir2, cwd) {
22994
23383
  if (!import_path14.default.isAbsolute(cwd)) return true;
22995
23384
  try {
22996
- return import_fs14.default.existsSync(import_path14.default.join(import_path14.default.resolve(cwd, dir2), ".git"));
23385
+ return import_fs15.default.existsSync(import_path14.default.join(import_path14.default.resolve(cwd, dir2), ".git"));
22997
23386
  } catch {
22998
23387
  return true;
22999
23388
  }
@@ -23003,7 +23392,7 @@ function readAgentSettings(absPath) {
23003
23392
  const r = readCappedText(
23004
23393
  absPath,
23005
23394
  MAX_AGENT_SETTINGS_BYTES,
23006
- import_fs14.default.constants.O_RDONLY | (import_fs14.default.constants.O_NONBLOCK ?? 0)
23395
+ import_fs15.default.constants.O_RDONLY | (import_fs15.default.constants.O_NONBLOCK ?? 0)
23007
23396
  );
23008
23397
  return r && !r.truncated ? r.text : null;
23009
23398
  }
@@ -23013,12 +23402,12 @@ function isIgnoredTool2(toolName) {
23013
23402
 
23014
23403
  // src/auth/state.ts
23015
23404
  init_cjs_shims();
23016
- var import_fs15 = __toESM(require("fs"));
23405
+ var import_fs16 = __toESM(require("fs"));
23017
23406
  var import_path15 = __toESM(require("path"));
23018
- var import_os10 = __toESM(require("os"));
23407
+ var import_os11 = __toESM(require("os"));
23019
23408
  init_forecast_context();
23020
- var PAUSED_FILE = import_path15.default.join(import_os10.default.homedir(), ".node9", "PAUSED");
23021
- var TRUST_FILE = import_path15.default.join(import_os10.default.homedir(), ".node9", "trust.json");
23409
+ var PAUSED_FILE = import_path15.default.join(import_os11.default.homedir(), ".node9", "PAUSED");
23410
+ var TRUST_FILE = import_path15.default.join(import_os11.default.homedir(), ".node9", "trust.json");
23022
23411
  function extractCommandPattern(toolName, args) {
23023
23412
  const lower2 = toolName.toLowerCase();
23024
23413
  if (lower2 !== "bash" && lower2 !== "execute_bash" && lower2 !== "shell") return void 0;
@@ -23031,11 +23420,11 @@ function extractCommandPattern(toolName, args) {
23031
23420
  function checkPause() {
23032
23421
  if (forecastRun()) return { paused: false };
23033
23422
  try {
23034
- if (!import_fs15.default.existsSync(PAUSED_FILE)) return { paused: false };
23035
- const state = JSON.parse(import_fs15.default.readFileSync(PAUSED_FILE, "utf-8"));
23423
+ if (!import_fs16.default.existsSync(PAUSED_FILE)) return { paused: false };
23424
+ const state = JSON.parse(import_fs16.default.readFileSync(PAUSED_FILE, "utf-8"));
23036
23425
  if (state.expiry > 0 && Date.now() >= state.expiry) {
23037
23426
  try {
23038
- import_fs15.default.unlinkSync(PAUSED_FILE);
23427
+ import_fs16.default.unlinkSync(PAUSED_FILE);
23039
23428
  } catch {
23040
23429
  }
23041
23430
  return { paused: false };
@@ -23048,8 +23437,8 @@ function checkPause() {
23048
23437
  function getActiveTrustSession(toolName, args) {
23049
23438
  if (forecastRun()) return false;
23050
23439
  try {
23051
- if (!import_fs15.default.existsSync(TRUST_FILE)) return false;
23052
- const trust = JSON.parse(import_fs15.default.readFileSync(TRUST_FILE, "utf-8"));
23440
+ if (!import_fs16.default.existsSync(TRUST_FILE)) return false;
23441
+ const trust = JSON.parse(import_fs16.default.readFileSync(TRUST_FILE, "utf-8"));
23053
23442
  const now = Date.now();
23054
23443
  const active = trust.entries.filter((e) => e.expiry > now);
23055
23444
  if (active.length !== trust.entries.length) {
@@ -23072,8 +23461,8 @@ function writeTrustSession(toolName, durationMs, args) {
23072
23461
  try {
23073
23462
  let trust = { entries: [] };
23074
23463
  try {
23075
- if (import_fs15.default.existsSync(TRUST_FILE)) {
23076
- trust = JSON.parse(import_fs15.default.readFileSync(TRUST_FILE, "utf-8"));
23464
+ if (import_fs16.default.existsSync(TRUST_FILE)) {
23465
+ trust = JSON.parse(import_fs16.default.readFileSync(TRUST_FILE, "utf-8"));
23077
23466
  }
23078
23467
  } catch {
23079
23468
  }
@@ -23095,9 +23484,9 @@ function writeTrustSession(toolName, durationMs, args) {
23095
23484
  }
23096
23485
  function getPersistentDecision(toolName) {
23097
23486
  try {
23098
- const file2 = import_path15.default.join(import_os10.default.homedir(), ".node9", "decisions.json");
23099
- if (!import_fs15.default.existsSync(file2)) return null;
23100
- const decisions = JSON.parse(import_fs15.default.readFileSync(file2, "utf-8"));
23487
+ const file2 = import_path15.default.join(import_os11.default.homedir(), ".node9", "decisions.json");
23488
+ if (!import_fs16.default.existsSync(file2)) return null;
23489
+ const decisions = JSON.parse(import_fs16.default.readFileSync(file2, "utf-8"));
23101
23490
  const d = decisions[toolName];
23102
23491
  if (d === "allow" || d === "deny") return d;
23103
23492
  } catch {
@@ -23107,16 +23496,16 @@ function getPersistentDecision(toolName) {
23107
23496
 
23108
23497
  // src/auth/daemon.ts
23109
23498
  init_cjs_shims();
23110
- var import_fs17 = __toESM(require("fs"));
23499
+ var import_fs18 = __toESM(require("fs"));
23111
23500
  var import_http = __toESM(require("http"));
23112
23501
  var import_net4 = __toESM(require("net"));
23113
23502
  var import_path17 = __toESM(require("path"));
23114
- var import_os11 = __toESM(require("os"));
23503
+ var import_os12 = __toESM(require("os"));
23115
23504
  init_forecast_context();
23116
23505
 
23117
23506
  // src/utils/activity-socket.ts
23118
23507
  init_cjs_shims();
23119
- var import_fs16 = __toESM(require("fs"));
23508
+ var import_fs17 = __toESM(require("fs"));
23120
23509
  var import_path16 = __toESM(require("path"));
23121
23510
 
23122
23511
  // src/utils/own-dir.ts
@@ -23126,7 +23515,7 @@ init_cjs_shims();
23126
23515
  var currentUid = () => typeof process.getuid === "function" ? process.getuid() : void 0;
23127
23516
  function isPrivateDir(dir2, uid = currentUid()) {
23128
23517
  try {
23129
- const st = import_fs16.default.lstatSync(dir2);
23518
+ const st = import_fs17.default.lstatSync(dir2);
23130
23519
  if (st.isSymbolicLink() || !st.isDirectory()) return false;
23131
23520
  if (uid !== void 0 && st.uid !== uid) return false;
23132
23521
  return (st.mode & 18) === 0;
@@ -23138,7 +23527,7 @@ var WINDOWS_PIPE = /^\\\\\.\\pipe\\node9-activity-[0-9a-f]{32}$/;
23138
23527
  function isTrustedActivitySocket(socketPath, uid = currentUid()) {
23139
23528
  if (process.platform === "win32") return WINDOWS_PIPE.test(socketPath);
23140
23529
  try {
23141
- const st = import_fs16.default.lstatSync(socketPath);
23530
+ const st = import_fs17.default.lstatSync(socketPath);
23142
23531
  if (!st.isSocket()) return false;
23143
23532
  if (uid !== void 0 && st.uid !== uid) return false;
23144
23533
  } catch {
@@ -23296,9 +23685,9 @@ function recordFromLegacyWait(body, now = Date.now()) {
23296
23685
  }
23297
23686
 
23298
23687
  // src/auth/daemon.ts
23299
- function daemonActivitySocket(home = import_os11.default.homedir()) {
23688
+ function daemonActivitySocket(home = import_os12.default.homedir()) {
23300
23689
  try {
23301
- const raw = import_fs17.default.readFileSync(import_path17.default.join(home, ".node9", "daemon.pid"), "utf-8");
23690
+ const raw = import_fs18.default.readFileSync(import_path17.default.join(home, ".node9", "daemon.pid"), "utf-8");
23302
23691
  const p = JSON.parse(raw).activitySocket;
23303
23692
  return typeof p === "string" && isTrustedActivitySocket(p) ? p : null;
23304
23693
  } catch {
@@ -23362,9 +23751,9 @@ var DAEMON_HOST = "127.0.0.1";
23362
23751
  function getInternalToken() {
23363
23752
  if (forecastRun()) return null;
23364
23753
  try {
23365
- const pidFile = import_path17.default.join(import_os11.default.homedir(), ".node9", "daemon.pid");
23366
- if (!import_fs17.default.existsSync(pidFile)) return null;
23367
- const data = JSON.parse(import_fs17.default.readFileSync(pidFile, "utf-8"));
23754
+ const pidFile = import_path17.default.join(import_os12.default.homedir(), ".node9", "daemon.pid");
23755
+ if (!import_fs18.default.existsSync(pidFile)) return null;
23756
+ const data = JSON.parse(import_fs18.default.readFileSync(pidFile, "utf-8"));
23368
23757
  process.kill(data.pid, 0);
23369
23758
  return data.internalToken ?? null;
23370
23759
  } catch {
@@ -23373,12 +23762,12 @@ function getInternalToken() {
23373
23762
  }
23374
23763
  function isDaemonRunning() {
23375
23764
  if (forecastRun()) return false;
23376
- const pidFile = import_path17.default.join(import_os11.default.homedir(), ".node9", "daemon.pid");
23377
- if (import_fs17.default.existsSync(pidFile)) {
23765
+ const pidFile = import_path17.default.join(import_os12.default.homedir(), ".node9", "daemon.pid");
23766
+ if (import_fs18.default.existsSync(pidFile)) {
23378
23767
  let pid;
23379
23768
  let port;
23380
23769
  try {
23381
- const data = JSON.parse(import_fs17.default.readFileSync(pidFile, "utf-8"));
23770
+ const data = JSON.parse(import_fs18.default.readFileSync(pidFile, "utf-8"));
23382
23771
  pid = data.pid;
23383
23772
  port = data.port;
23384
23773
  } catch {
@@ -23396,7 +23785,7 @@ function isDaemonRunning() {
23396
23785
  } catch (err) {
23397
23786
  if (err instanceof Error && "code" in err && err.code === "ESRCH") {
23398
23787
  try {
23399
- import_fs17.default.unlinkSync(pidFile);
23788
+ import_fs18.default.unlinkSync(pidFile);
23400
23789
  } catch {
23401
23790
  }
23402
23791
  }
@@ -23420,11 +23809,11 @@ async function daemonHasInteractiveApprover(timeoutMs = 400) {
23420
23809
  }
23421
23810
  }
23422
23811
  async function registerDaemonEntry(toolName, args, meta, riskMetadata, activityId, cwd, recoveryCommand, skipBackgroundAuth, viewOnly, localSmartRuleMatched, socketActivitySent, sessionId, card) {
23423
- const base2 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
23812
+ const base3 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
23424
23813
  const ctrl = new AbortController();
23425
23814
  const timer = setTimeout(() => ctrl.abort(), 5e3);
23426
23815
  try {
23427
- const res = await fetch(`${base2}/check`, {
23816
+ const res = await fetch(`${base3}/check`, {
23428
23817
  method: "POST",
23429
23818
  headers: { "Content-Type": "application/json" },
23430
23819
  body: JSON.stringify({
@@ -23534,8 +23923,8 @@ function daemonGet(urlPath, signal, token = getInternalToken()) {
23534
23923
  });
23535
23924
  }
23536
23925
  async function notifyDaemonViewer(toolName, args, meta, riskMetadata, activityId, socketActivitySent, extra) {
23537
- const base2 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
23538
- const res = await fetch(`${base2}/check`, {
23926
+ const base3 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
23927
+ const res = await fetch(`${base3}/check`, {
23539
23928
  method: "POST",
23540
23929
  headers: { "Content-Type": "application/json" },
23541
23930
  body: JSON.stringify({
@@ -23567,9 +23956,9 @@ async function notifyTaint(filePath, source, fromEid) {
23567
23956
  return;
23568
23957
  }
23569
23958
  if (!isDaemonRunning()) return;
23570
- const base2 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
23959
+ const base3 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
23571
23960
  try {
23572
- await fetch(`${base2}/taint`, {
23961
+ await fetch(`${base3}/taint`, {
23573
23962
  method: "POST",
23574
23963
  headers: { "Content-Type": "application/json" },
23575
23964
  // Phase D2 — fromEid is the audit eid that created this taint (edge source).
@@ -23587,9 +23976,9 @@ async function checkTaint(paths) {
23587
23976
  return record ? { tainted: true, record } : { tainted: false };
23588
23977
  }
23589
23978
  if (!isDaemonRunning()) return { tainted: false, daemonUnavailable: true };
23590
- const base2 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
23979
+ const base3 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
23591
23980
  try {
23592
- const res = await fetch(`${base2}/taint/check`, {
23981
+ const res = await fetch(`${base3}/taint/check`, {
23593
23982
  method: "POST",
23594
23983
  headers: { "Content-Type": "application/json" },
23595
23984
  body: JSON.stringify({ paths }),
@@ -23617,9 +24006,9 @@ async function notifyWritten(paths) {
23617
24006
  return;
23618
24007
  }
23619
24008
  if (paths.length === 0 || !isDaemonRunning()) return;
23620
- const base2 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
24009
+ const base3 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
23621
24010
  try {
23622
- await fetch(`${base2}/written`, {
24011
+ await fetch(`${base3}/written`, {
23623
24012
  method: "POST",
23624
24013
  headers: exposureHeaders(),
23625
24014
  body: JSON.stringify({ paths: paths.slice(0, 64) }),
@@ -23633,9 +24022,9 @@ async function checkWritten(paths) {
23633
24022
  const forecast = forecastRun();
23634
24023
  if (forecast) return { written: forecast.session.written(paths) };
23635
24024
  if (!isDaemonRunning()) return { written: [], daemonUnavailable: true };
23636
- const base2 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
24025
+ const base3 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
23637
24026
  try {
23638
- const res = await fetch(`${base2}/written/check`, {
24027
+ const res = await fetch(`${base3}/written/check`, {
23639
24028
  method: "POST",
23640
24029
  headers: exposureHeaders(),
23641
24030
  body: JSON.stringify({ paths: paths.slice(0, 64) }),
@@ -23657,9 +24046,9 @@ async function checkSessionTaint(sessionId) {
23657
24046
  return record ? { tainted: true, record, exposed } : { tainted: false, exposed };
23658
24047
  }
23659
24048
  if (!sessionId || !isDaemonRunning()) return { tainted: false };
23660
- const base2 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
24049
+ const base3 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
23661
24050
  try {
23662
- const res = await fetch(`${base2}/session-taint/check`, {
24051
+ const res = await fetch(`${base3}/session-taint/check`, {
23663
24052
  method: "POST",
23664
24053
  headers: exposureHeaders(),
23665
24054
  body: JSON.stringify({ sessionId }),
@@ -23677,8 +24066,8 @@ function exposureHeaders() {
23677
24066
  };
23678
24067
  }
23679
24068
  async function exposurePost(route, body, timeoutMs) {
23680
- const base2 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
23681
- const res = await fetch(`${base2}${route}`, {
24069
+ const base3 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
24070
+ const res = await fetch(`${base3}${route}`, {
23682
24071
  method: "POST",
23683
24072
  headers: exposureHeaders(),
23684
24073
  body: JSON.stringify(body),
@@ -23693,7 +24082,8 @@ async function checkSessionExposure(sessionId) {
23693
24082
  const record = forecast.session.checkExposure(forecast.now);
23694
24083
  return record ? { exposed: true, record } : { exposed: false };
23695
24084
  }
23696
- if (!sessionId || !isDaemonRunning()) return { exposed: false };
24085
+ if (!sessionId) return { exposed: false };
24086
+ if (!isDaemonRunning()) return { exposed: false, daemonUnavailable: true };
23697
24087
  try {
23698
24088
  return await exposurePost(
23699
24089
  "/session-exposure/check",
@@ -23717,8 +24107,8 @@ async function approveSessionExposure(sessionId, keys) {
23717
24107
  }
23718
24108
  }
23719
24109
  async function resolveViaDaemon(id, decision, internalToken, source, record) {
23720
- const base2 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
23721
- const res = await fetch(`${base2}/resolve/${id}`, {
24110
+ const base3 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
24111
+ const res = await fetch(`${base3}/resolve/${id}`, {
23722
24112
  method: "POST",
23723
24113
  headers: { "Content-Type": "application/json", "X-Node9-Internal": internalToken },
23724
24114
  body: JSON.stringify({ decision, ...source && { source }, ...record && { record } }),
@@ -23736,8 +24126,8 @@ var import_crypto10 = require("crypto");
23736
24126
 
23737
24127
  // src/apps/state.ts
23738
24128
  init_cjs_shims();
23739
- var import_fs19 = __toESM(require("fs"));
23740
- var import_os13 = __toESM(require("os"));
24129
+ var import_fs20 = __toESM(require("fs"));
24130
+ var import_os14 = __toESM(require("os"));
23741
24131
  var import_path19 = __toESM(require("path"));
23742
24132
 
23743
24133
  // src/apps/catalog.ts
@@ -23872,22 +24262,22 @@ init_audit();
23872
24262
 
23873
24263
  // src/daemon/mcp-tools.ts
23874
24264
  init_cjs_shims();
23875
- var import_fs18 = __toESM(require("fs"));
24265
+ var import_fs19 = __toESM(require("fs"));
23876
24266
  var import_path18 = __toESM(require("path"));
23877
- var import_os12 = __toESM(require("os"));
24267
+ var import_os13 = __toESM(require("os"));
23878
24268
 
23879
24269
  // src/mcp-cmd.ts
23880
24270
  init_cjs_shims();
23881
24271
 
23882
24272
  // src/daemon/mcp-tools.ts
23883
24273
  function getMcpToolsFile() {
23884
- return import_path18.default.join(import_os12.default.homedir(), ".node9", "mcp-tools.json");
24274
+ return import_path18.default.join(import_os13.default.homedir(), ".node9", "mcp-tools.json");
23885
24275
  }
23886
24276
  function readMcpToolsConfig() {
23887
24277
  try {
23888
24278
  const file2 = getMcpToolsFile();
23889
- if (!import_fs18.default.existsSync(file2)) return {};
23890
- const raw = import_fs18.default.readFileSync(file2, "utf-8");
24279
+ if (!import_fs19.default.existsSync(file2)) return {};
24280
+ const raw = import_fs19.default.readFileSync(file2, "utf-8");
23891
24281
  return JSON.parse(raw);
23892
24282
  } catch {
23893
24283
  return {};
@@ -23895,7 +24285,7 @@ function readMcpToolsConfig() {
23895
24285
  }
23896
24286
 
23897
24287
  // src/apps/state.ts
23898
- function appsFilePath(home = import_os13.default.homedir()) {
24288
+ function appsFilePath(home = import_os14.default.homedir()) {
23899
24289
  return import_path19.default.join(home, ".node9", "apps.json");
23900
24290
  }
23901
24291
  var SIGNAL_KINDS = /* @__PURE__ */ new Set(["mcp-server", "command", "package", "env-name"]);
@@ -23944,7 +24334,7 @@ function readAppsFile(home) {
23944
24334
  function readAppsFileStrict(home) {
23945
24335
  let text;
23946
24336
  try {
23947
- text = import_fs19.default.readFileSync(appsFilePath(home), "utf8");
24337
+ text = import_fs20.default.readFileSync(appsFilePath(home), "utf8");
23948
24338
  } catch (e) {
23949
24339
  return e.code === "ENOENT" ? emptyFile() : null;
23950
24340
  }
@@ -23959,7 +24349,7 @@ var NO_LINKS = /* @__PURE__ */ Object.create(null);
23959
24349
  function localLinks() {
23960
24350
  const file2 = appsFilePath();
23961
24351
  try {
23962
- const mtimeMs = import_fs19.default.statSync(file2).mtimeMs;
24352
+ const mtimeMs = import_fs20.default.statSync(file2).mtimeMs;
23963
24353
  if (!linkCache || linkCache.file !== file2 || linkCache.mtimeMs !== mtimeMs) {
23964
24354
  linkCache = { mtimeMs, file: file2, links: readAppsFile().links };
23965
24355
  }
@@ -23999,7 +24389,7 @@ function resolveLinkedServer(label2, opts = {}) {
23999
24389
  }
24000
24390
 
24001
24391
  // src/auth/orchestrator.ts
24002
- var import_os21 = __toESM(require("os"));
24392
+ var import_os22 = __toESM(require("os"));
24003
24393
  var import_path31 = __toESM(require("path"));
24004
24394
 
24005
24395
  // src/ui/native.ts
@@ -24350,12 +24740,12 @@ end run`;
24350
24740
 
24351
24741
  // src/canary/registry.ts
24352
24742
  init_cjs_shims();
24353
- var import_fs20 = __toESM(require("fs"));
24354
- var import_os14 = __toESM(require("os"));
24743
+ var import_fs21 = __toESM(require("fs"));
24744
+ var import_os15 = __toESM(require("os"));
24355
24745
  var import_path22 = __toESM(require("path"));
24356
24746
  init_dist();
24357
24747
  function canaryStorePath() {
24358
- return import_path22.default.join(import_os14.default.homedir(), ".node9", "canaries.json");
24748
+ return import_path22.default.join(import_os15.default.homedir(), ".node9", "canaries.json");
24359
24749
  }
24360
24750
  function isRecord(x) {
24361
24751
  if (!x || typeof x !== "object") return false;
@@ -24366,7 +24756,7 @@ function loadCanaries(opts) {
24366
24756
  const p = canaryStorePath();
24367
24757
  let raw;
24368
24758
  try {
24369
- raw = import_fs20.default.readFileSync(p, "utf-8");
24759
+ raw = import_fs21.default.readFileSync(p, "utf-8");
24370
24760
  } catch (e) {
24371
24761
  if (e.code === "ENOENT") return [];
24372
24762
  throw e;
@@ -24393,8 +24783,8 @@ init_dist();
24393
24783
 
24394
24784
  // src/auth/session-script.ts
24395
24785
  init_cjs_shims();
24396
- var import_fs21 = __toESM(require("fs"));
24397
- var import_os15 = __toESM(require("os"));
24786
+ var import_fs22 = __toESM(require("fs"));
24787
+ var import_os16 = __toESM(require("os"));
24398
24788
  var import_path23 = __toESM(require("path"));
24399
24789
  init_dist();
24400
24790
  var MAX_SCRIPT_BYTES = 64 * 1024;
@@ -24402,13 +24792,13 @@ var MAX_RECORDED_WRITES = 32;
24402
24792
  function readScriptFile(resolved) {
24403
24793
  let fd;
24404
24794
  try {
24405
- fd = import_fs21.default.openSync(resolved, import_fs21.default.constants.O_RDONLY | (import_fs21.default.constants.O_NONBLOCK ?? 0));
24406
- const st = import_fs21.default.fstatSync(fd);
24795
+ fd = import_fs22.default.openSync(resolved, import_fs22.default.constants.O_RDONLY | (import_fs22.default.constants.O_NONBLOCK ?? 0));
24796
+ const st = import_fs22.default.fstatSync(fd);
24407
24797
  if (!st.isFile() || st.size > MAX_SCRIPT_BYTES) return null;
24408
24798
  const buf = Buffer.alloc(MAX_SCRIPT_BYTES + 1);
24409
24799
  let n = 0;
24410
24800
  while (n < buf.length) {
24411
- const got = import_fs21.default.readSync(fd, buf, n, buf.length - n, n);
24801
+ const got = import_fs22.default.readSync(fd, buf, n, buf.length - n, n);
24412
24802
  if (got === 0) break;
24413
24803
  n += got;
24414
24804
  }
@@ -24419,7 +24809,7 @@ function readScriptFile(resolved) {
24419
24809
  } finally {
24420
24810
  if (fd !== void 0) {
24421
24811
  try {
24422
- import_fs21.default.closeSync(fd);
24812
+ import_fs22.default.closeSync(fd);
24423
24813
  } catch {
24424
24814
  }
24425
24815
  }
@@ -24429,16 +24819,16 @@ function absolute(p, cwd, home) {
24429
24819
  const spelled = p === "~" || p.startsWith("~/") ? home + p.slice(1) : p;
24430
24820
  return import_path23.default.resolve(cwd, spelled);
24431
24821
  }
24432
- async function resolveSessionScript(toolName, args, cwd, layers, deps, home = import_os15.default.homedir()) {
24822
+ async function resolveSessionScript(toolName, args, cwd, layers, deps, home = import_os16.default.homedir()) {
24433
24823
  try {
24434
24824
  const d = deps ?? { checkWritten, readScript: readScriptFile };
24435
24825
  const shell = resolveShellInput(toolName, args, layers);
24436
24826
  if (shell.kind !== "shell") return null;
24437
24827
  const operands2 = shellScriptOperands(shell.command);
24438
24828
  if (operands2.length === 0) return null;
24439
- const base2 = cwd ?? process.cwd();
24829
+ const base3 = cwd ?? process.cwd();
24440
24830
  const byResolved = /* @__PURE__ */ new Map();
24441
- for (const op of operands2) byResolved.set(absolute(op, base2, home), op);
24831
+ for (const op of operands2) byResolved.set(absolute(op, base3, home), op);
24442
24832
  const { written } = await d.checkWritten([...byResolved.keys()]);
24443
24833
  for (const resolved of written) {
24444
24834
  const spelled = byResolved.get(resolved);
@@ -24450,7 +24840,7 @@ async function resolveSessionScript(toolName, args, cwd, layers, deps, home = im
24450
24840
  path: spelled,
24451
24841
  resolved,
24452
24842
  args: scriptArgs,
24453
- fileAccess: prepareFileAccesses("Bash", scriptArgs, base2, layers, home)
24843
+ fileAccess: prepareFileAccesses("Bash", scriptArgs, base3, layers, home)
24454
24844
  };
24455
24845
  }
24456
24846
  return null;
@@ -24458,17 +24848,17 @@ async function resolveSessionScript(toolName, args, cwd, layers, deps, home = im
24458
24848
  return null;
24459
24849
  }
24460
24850
  }
24461
- function sessionWriteTargets(toolName, args, fileAccess, cwd, home = import_os15.default.homedir()) {
24462
- const base2 = cwd ?? process.cwd();
24851
+ function sessionWriteTargets(toolName, args, fileAccess, cwd, home = import_os16.default.homedir()) {
24852
+ const base3 = cwd ?? process.cwd();
24463
24853
  const out = /* @__PURE__ */ new Set();
24464
24854
  const argsObj = args && typeof args === "object" ? args : {};
24465
24855
  const named = argsObj.file_path ?? argsObj.path ?? argsObj.filename;
24466
24856
  if (typeof named === "string" && named && /write|edit|create|save|append/i.test(toolName))
24467
- out.add(absolute(named, base2, home));
24857
+ out.add(absolute(named, base3, home));
24468
24858
  for (const a of fileAccess?.accesses ?? []) {
24469
24859
  if (a.operation !== "write" && a.operation !== "create") continue;
24470
24860
  if (a.glob || a.recursive) continue;
24471
- out.add(absolute(a.path, a.cwd ?? base2, home));
24861
+ out.add(absolute(a.path, a.cwd ?? base3, home));
24472
24862
  if (out.size >= MAX_RECORDED_WRITES) break;
24473
24863
  }
24474
24864
  return [...out];
@@ -24489,27 +24879,27 @@ init_dist();
24489
24879
 
24490
24880
  // src/exposure/known-hosts.ts
24491
24881
  init_cjs_shims();
24492
- var import_fs23 = __toESM(require("fs"));
24882
+ var import_fs24 = __toESM(require("fs"));
24493
24883
  var import_path25 = __toESM(require("path"));
24494
24884
 
24495
24885
  // src/utils/git-remotes.ts
24496
24886
  init_cjs_shims();
24497
- var import_fs22 = __toESM(require("fs"));
24887
+ var import_fs23 = __toESM(require("fs"));
24498
24888
  var import_path24 = __toESM(require("path"));
24499
24889
  init_dist();
24500
- function readSmallFile(p, max) {
24890
+ function readSmallFile2(p, max) {
24501
24891
  let fd;
24502
24892
  try {
24503
- fd = import_fs22.default.openSync(p, import_fs22.default.constants.O_RDONLY | (import_fs22.default.constants.O_NOFOLLOW ?? 0));
24504
- const st = import_fs22.default.fstatSync(fd);
24893
+ fd = import_fs23.default.openSync(p, import_fs23.default.constants.O_RDONLY | (import_fs23.default.constants.O_NOFOLLOW ?? 0));
24894
+ const st = import_fs23.default.fstatSync(fd);
24505
24895
  if (!st.isFile() || st.size > max) return null;
24506
24896
  const buf = Buffer.alloc(st.size);
24507
- const n = import_fs22.default.readSync(fd, buf, 0, st.size, 0);
24897
+ const n = import_fs23.default.readSync(fd, buf, 0, st.size, 0);
24508
24898
  return buf.subarray(0, n).toString("utf8");
24509
24899
  } catch {
24510
24900
  return null;
24511
24901
  } finally {
24512
- if (fd !== void 0) import_fs22.default.closeSync(fd);
24902
+ if (fd !== void 0) import_fs23.default.closeSync(fd);
24513
24903
  }
24514
24904
  }
24515
24905
  function gitRemoteHost(url) {
@@ -24531,15 +24921,15 @@ function findGitConfig(dir2, walkUp = true) {
24531
24921
  let cur = import_path24.default.resolve(dir2);
24532
24922
  for (let i = 0; i < (walkUp ? 64 : 1); i++) {
24533
24923
  const dotGit = import_path24.default.join(cur, ".git");
24534
- const direct = readSmallFile(import_path24.default.join(dotGit, "config"), 1e6);
24924
+ const direct = readSmallFile2(import_path24.default.join(dotGit, "config"), 1e6);
24535
24925
  if (direct !== null) return direct;
24536
- const pointer = readSmallFile(dotGit, 4096);
24926
+ const pointer = readSmallFile2(dotGit, 4096);
24537
24927
  const m = pointer && /^gitdir:\s*(.+?)\s*$/m.exec(pointer);
24538
24928
  if (m) {
24539
24929
  let gitDir = import_path24.default.resolve(cur, m[1]);
24540
- const common = readSmallFile(import_path24.default.join(gitDir, "commondir"), 4096);
24930
+ const common = readSmallFile2(import_path24.default.join(gitDir, "commondir"), 4096);
24541
24931
  if (common !== null) gitDir = import_path24.default.resolve(gitDir, common.trim());
24542
- return readSmallFile(import_path24.default.join(gitDir, "config"), 1e6);
24932
+ return readSmallFile2(import_path24.default.join(gitDir, "config"), 1e6);
24543
24933
  }
24544
24934
  const parent = import_path24.default.dirname(cur);
24545
24935
  if (parent === cur) return null;
@@ -24596,7 +24986,7 @@ function manifestRoot(dir2) {
24596
24986
  for (let i = 0; i < 64; i++) {
24597
24987
  for (const [m] of ECOSYSTEMS) {
24598
24988
  try {
24599
- if (import_fs23.default.existsSync(import_path25.default.join(cur, m))) return cur;
24989
+ if (import_fs24.default.existsSync(import_path25.default.join(cur, m))) return cur;
24600
24990
  } catch {
24601
24991
  }
24602
24992
  }
@@ -24622,10 +25012,10 @@ function knownProjectHosts(dir2) {
24622
25012
  const root = manifestRoot(key);
24623
25013
  if (root !== null) {
24624
25014
  for (const [m, registry] of ECOSYSTEMS) {
24625
- if (import_fs23.default.existsSync(import_path25.default.join(root, m))) out.hosts.add(registry);
25015
+ if (import_fs24.default.existsSync(import_path25.default.join(root, m))) out.hosts.add(registry);
24626
25016
  }
24627
25017
  try {
24628
- if (import_fs23.default.readdirSync(root).some((f) => f.endsWith(".gemspec")))
25018
+ if (import_fs24.default.readdirSync(root).some((f) => f.endsWith(".gemspec")))
24629
25019
  out.hosts.add("host:rubygems.org");
24630
25020
  } catch {
24631
25021
  }
@@ -24638,8 +25028,8 @@ function knownProjectHosts(dir2) {
24638
25028
 
24639
25029
  // src/exposure/writes.ts
24640
25030
  init_cjs_shims();
24641
- var import_fs24 = __toESM(require("fs"));
24642
- var import_os16 = __toESM(require("os"));
25031
+ var import_fs25 = __toESM(require("fs"));
25032
+ var import_os17 = __toESM(require("os"));
24643
25033
  var import_path26 = __toESM(require("path"));
24644
25034
  init_dist();
24645
25035
  var WRITE_OPS = /* @__PURE__ */ new Set(["write", "create", "remove"]);
@@ -24670,7 +25060,7 @@ function packageScriptsTouched(args, absPath, shellCommand) {
24670
25060
  if (typeof a.content === "string") {
24671
25061
  try {
24672
25062
  const next = JSON.parse(a.content);
24673
- const prev = JSON.parse(import_fs24.default.readFileSync(absPath, "utf8"));
25063
+ const prev = JSON.parse(import_fs25.default.readFileSync(absPath, "utf8"));
24674
25064
  return JSON.stringify(next.scripts ?? null) !== JSON.stringify(prev.scripts ?? null);
24675
25065
  } catch {
24676
25066
  return LIFECYCLE_RE.test(a.content);
@@ -24680,8 +25070,8 @@ function packageScriptsTouched(args, absPath, shellCommand) {
24680
25070
  }
24681
25071
  function judgeWritesAfterExposure(opts) {
24682
25072
  const holds = [];
24683
- const home = opts.home ?? import_os16.default.homedir();
24684
- const tmp = normalizeFilePath(import_os16.default.tmpdir());
25073
+ const home = opts.home ?? import_os17.default.homedir();
25074
+ const tmp = normalizeFilePath(import_os17.default.tmpdir());
24685
25075
  const root = opts.cwd ? normalizeFilePath(opts.cwd, "", home) : null;
24686
25076
  const seen = /* @__PURE__ */ new Set();
24687
25077
  for (const access of opts.accesses) {
@@ -24714,8 +25104,8 @@ function judgeWritesAfterExposure(opts) {
24714
25104
  // src/exposure/marker.ts
24715
25105
  init_cjs_shims();
24716
25106
  var import_crypto9 = __toESM(require("crypto"));
24717
- var import_fs25 = __toESM(require("fs"));
24718
- var import_os17 = __toESM(require("os"));
25107
+ var import_fs26 = __toESM(require("fs"));
25108
+ var import_os18 = __toESM(require("os"));
24719
25109
  var import_path27 = __toESM(require("path"));
24720
25110
 
24721
25111
  // src/daemon/exposure-store.ts
@@ -24725,7 +25115,7 @@ var SESSION_EXPOSURE_TTL_MS = 4 * 60 * 60 * 1e3;
24725
25115
  // src/exposure/marker.ts
24726
25116
  init_forecast_context();
24727
25117
  function dir() {
24728
- return import_path27.default.join(import_os17.default.homedir(), ".node9", "exposure-sessions");
25118
+ return import_path27.default.join(import_os18.default.homedir(), ".node9", "exposure-sessions");
24729
25119
  }
24730
25120
  function file(sessionId) {
24731
25121
  const id = import_crypto9.default.createHash("sha256").update(sessionId).digest("hex").slice(0, 32);
@@ -24736,7 +25126,7 @@ function maybeSessionExposed(sessionId) {
24736
25126
  const forecast = forecastRun();
24737
25127
  if (forecast) return forecast.session.checkExposure(forecast.now) !== null;
24738
25128
  try {
24739
- const st = import_fs25.default.statSync(file(sessionId));
25129
+ const st = import_fs26.default.statSync(file(sessionId));
24740
25130
  return Date.now() - st.mtimeMs < SESSION_EXPOSURE_TTL_MS;
24741
25131
  } catch (e) {
24742
25132
  return e.code !== "ENOENT";
@@ -24745,7 +25135,7 @@ function maybeSessionExposed(sessionId) {
24745
25135
 
24746
25136
  // src/exposure/roles.ts
24747
25137
  init_cjs_shims();
24748
- var import_fs26 = __toESM(require("fs"));
25138
+ var import_fs27 = __toESM(require("fs"));
24749
25139
  var EXPOSURE_LABEL = "\u{1F6E1}\uFE0F Node9 Exposure";
24750
25140
  var AGENT_INTERNAL_PREFIXES = ["ccd_", "visualize"];
24751
25141
  function foldServerName(name) {
@@ -24755,7 +25145,7 @@ var inventoryCache = null;
24755
25145
  var inventoryStamp = null;
24756
25146
  function inventoryFileStamp() {
24757
25147
  try {
24758
- const st = import_fs26.default.statSync(getMcpToolsFile());
25148
+ const st = import_fs27.default.statSync(getMcpToolsFile());
24759
25149
  return `${st.mtimeMs}:${st.size}`;
24760
25150
  } catch {
24761
25151
  return "";
@@ -24840,16 +25230,16 @@ function sourceWord(toolName) {
24840
25230
 
24841
25231
  // src/auth/trusted-recipients.ts
24842
25232
  init_cjs_shims();
24843
- var import_fs27 = __toESM(require("fs"));
25233
+ var import_fs28 = __toESM(require("fs"));
24844
25234
  var import_path28 = __toESM(require("path"));
24845
- var import_os18 = __toESM(require("os"));
25235
+ var import_os19 = __toESM(require("os"));
24846
25236
  init_dist();
24847
25237
  function getTrustedRecipientsPath() {
24848
- return import_path28.default.join(import_os18.default.homedir(), ".node9", "trusted-recipients.json");
25238
+ return import_path28.default.join(import_os19.default.homedir(), ".node9", "trusted-recipients.json");
24849
25239
  }
24850
25240
  function readTrustedRecipients() {
24851
25241
  try {
24852
- const raw = import_fs27.default.readFileSync(getTrustedRecipientsPath(), "utf8");
25242
+ const raw = import_fs28.default.readFileSync(getTrustedRecipientsPath(), "utf8");
24853
25243
  const parsed = JSON.parse(raw);
24854
25244
  return Array.isArray(parsed.entries) ? parsed.entries.filter((e) => e && typeof e.value === "string") : [];
24855
25245
  } catch {
@@ -24858,10 +25248,10 @@ function readTrustedRecipients() {
24858
25248
  }
24859
25249
  function writeTrustedRecipients(entries) {
24860
25250
  const filePath = getTrustedRecipientsPath();
24861
- import_fs27.default.mkdirSync(import_path28.default.dirname(filePath), { recursive: true });
25251
+ import_fs28.default.mkdirSync(import_path28.default.dirname(filePath), { recursive: true });
24862
25252
  const tmp = filePath + ".node9-tmp";
24863
- import_fs27.default.writeFileSync(tmp, JSON.stringify({ entries }, null, 2), { mode: 384 });
24864
- import_fs27.default.renameSync(tmp, filePath);
25253
+ import_fs28.default.writeFileSync(tmp, JSON.stringify({ entries }, null, 2), { mode: 384 });
25254
+ import_fs28.default.renameSync(tmp, filePath);
24865
25255
  }
24866
25256
  function addTrustedRecipient(raw, addedBy = "user") {
24867
25257
  const value = normalizeRecipient(raw);
@@ -24876,7 +25266,7 @@ function addTrustedRecipient(raw, addedBy = "user") {
24876
25266
  var cache6 = null;
24877
25267
  function mtimeOf() {
24878
25268
  try {
24879
- return import_fs27.default.statSync(getTrustedRecipientsPath()).mtimeMs;
25269
+ return import_fs28.default.statSync(getTrustedRecipientsPath()).mtimeMs;
24880
25270
  } catch {
24881
25271
  return 0;
24882
25272
  }
@@ -24894,8 +25284,8 @@ init_forecast_context();
24894
25284
 
24895
25285
  // src/auth/cloud.ts
24896
25286
  init_cjs_shims();
24897
- var import_fs28 = __toESM(require("fs"));
24898
- var import_os19 = __toESM(require("os"));
25287
+ var import_fs29 = __toESM(require("fs"));
25288
+ var import_os20 = __toESM(require("os"));
24899
25289
  var import_path29 = __toESM(require("path"));
24900
25290
  init_audit();
24901
25291
  init_dist();
@@ -24932,7 +25322,7 @@ async function initNode9SaaS(toolName, args, creds, meta, riskMetadata, agentPol
24932
25322
  let ciContext;
24933
25323
  if (process.env.CI) {
24934
25324
  try {
24935
- const ciContextPath = import_path29.default.join(import_os19.default.homedir(), ".node9", "ci-context.json");
25325
+ const ciContextPath = import_path29.default.join(import_os20.default.homedir(), ".node9", "ci-context.json");
24936
25326
  const ci = readCappedText(ciContextPath, 1e4);
24937
25327
  if (!ci) throw new Error("ci-context.json unreadable");
24938
25328
  if (ci.truncated) throw new Error("ci-context.json exceeds 10 KB");
@@ -24974,9 +25364,9 @@ async function initNode9SaaS(toolName, args, creds, meta, riskMetadata, agentPol
24974
25364
  context: {
24975
25365
  agent: meta?.agent,
24976
25366
  mcpServer: meta?.mcpServer,
24977
- hostname: import_os19.default.hostname(),
25367
+ hostname: import_os20.default.hostname(),
24978
25368
  cwd: process.cwd(),
24979
- platform: import_os19.default.platform()
25369
+ platform: import_os20.default.platform()
24980
25370
  },
24981
25371
  // The SaaS validates riskMetadata strictly and would drop it whole on
24982
25372
  // an unknown key, so only fields its schema declares go on the wire.
@@ -25050,14 +25440,14 @@ async function resolveNode9SaaS(requestId, creds, approved, decidedBy) {
25050
25440
  });
25051
25441
  clearTimeout(timer);
25052
25442
  if (!res.ok) {
25053
- import_fs28.default.appendFileSync(
25443
+ import_fs29.default.appendFileSync(
25054
25444
  HOOK_DEBUG_LOG,
25055
25445
  `[resolve-cloud] PATCH ${safeMessage(resolveUrl, 200)} \u2192 HTTP ${res.status}
25056
25446
  `
25057
25447
  );
25058
25448
  }
25059
25449
  } catch (err) {
25060
- import_fs28.default.appendFileSync(
25450
+ import_fs29.default.appendFileSync(
25061
25451
  HOOK_DEBUG_LOG,
25062
25452
  `[resolve-cloud] PATCH failed for ${safeMessage(requestId, 64)}: ${safeMessage(err)}
25063
25453
  `
@@ -25067,19 +25457,19 @@ async function resolveNode9SaaS(requestId, creds, approved, decidedBy) {
25067
25457
 
25068
25458
  // src/loop-detector.ts
25069
25459
  init_cjs_shims();
25070
- var import_fs29 = __toESM(require("fs"));
25460
+ var import_fs30 = __toESM(require("fs"));
25071
25461
  var import_path30 = __toESM(require("path"));
25072
- var import_os20 = __toESM(require("os"));
25462
+ var import_os21 = __toESM(require("os"));
25073
25463
  init_dist();
25074
25464
  init_forecast_context();
25075
25465
  init_dist();
25076
25466
  function loopStateFile() {
25077
- return import_path30.default.join(import_os20.default.homedir(), ".node9", "loop-state.json");
25467
+ return import_path30.default.join(import_os21.default.homedir(), ".node9", "loop-state.json");
25078
25468
  }
25079
25469
  function readState() {
25080
25470
  try {
25081
- if (!import_fs29.default.existsSync(loopStateFile())) return [];
25082
- const raw = import_fs29.default.readFileSync(loopStateFile(), "utf-8");
25471
+ if (!import_fs30.default.existsSync(loopStateFile())) return [];
25472
+ const raw = import_fs30.default.readFileSync(loopStateFile(), "utf-8");
25083
25473
  const parsed = JSON.parse(raw);
25084
25474
  if (!Array.isArray(parsed)) return [];
25085
25475
  return parsed;
@@ -25089,10 +25479,10 @@ function readState() {
25089
25479
  }
25090
25480
  function writeState(records) {
25091
25481
  const dir2 = import_path30.default.dirname(loopStateFile());
25092
- if (!import_fs29.default.existsSync(dir2)) import_fs29.default.mkdirSync(dir2, { recursive: true });
25093
- const tmpPath = `${loopStateFile()}.${import_os20.default.hostname()}.${process.pid}.tmp`;
25094
- import_fs29.default.writeFileSync(tmpPath, JSON.stringify(records));
25095
- import_fs29.default.renameSync(tmpPath, loopStateFile());
25482
+ if (!import_fs30.default.existsSync(dir2)) import_fs30.default.mkdirSync(dir2, { recursive: true });
25483
+ const tmpPath = `${loopStateFile()}.${import_os21.default.hostname()}.${process.pid}.tmp`;
25484
+ import_fs30.default.writeFileSync(tmpPath, JSON.stringify(records));
25485
+ import_fs30.default.renameSync(tmpPath, loopStateFile());
25096
25486
  }
25097
25487
  function recordAndCheck(tool, args, threshold = 3, windowMs = 12e4) {
25098
25488
  try {
@@ -25291,7 +25681,7 @@ function isMcpToolName(toolName) {
25291
25681
  return /^mcp__[^_](?:[^_]|_(?!_))*?__/i.test(toolName);
25292
25682
  }
25293
25683
  function absoluteUploadPath(spelled, cwd) {
25294
- const expanded = spelled === "~" || spelled.startsWith("~/") ? import_path31.default.join(import_os21.default.homedir(), spelled.slice(1)) : spelled;
25684
+ const expanded = spelled === "~" || spelled.startsWith("~/") ? import_path31.default.join(import_os22.default.homedir(), spelled.slice(1)) : spelled;
25295
25685
  if (import_path31.default.isAbsolute(expanded)) return expanded;
25296
25686
  return cwd && import_path31.default.isAbsolute(cwd) ? import_path31.default.resolve(cwd, expanded) : expanded;
25297
25687
  }
@@ -25617,8 +26007,17 @@ async function _authorizeHeadlessCore(toolName, args, metaArg, options) {
25617
26007
  const judged = sendsSomething || (!!shellExp?.fetches || writes.length > 0) && mayBeExposed;
25618
26008
  if (judged) {
25619
26009
  const exposure = await checkSessionExposure(meta.sessionId);
25620
- if (exposure.exposed && exposure.record) {
25621
- const rec = exposure.record;
26010
+ const unknownRead = exposure.daemonUnavailable === true && holdsSendsWithoutDaemon(config.settings.mode);
26011
+ const rec = exposure.exposed && exposure.record ? exposure.record : unknownRead ? {
26012
+ sessionId: meta.sessionId,
26013
+ sources: [],
26014
+ destinations: [],
26015
+ userMentioned: [],
26016
+ approved: [],
26017
+ createdAt: 0,
26018
+ expiresAt: 0
26019
+ } : null;
26020
+ if (rec) {
25622
26021
  const dests = new Set(
25623
26022
  acts && !shellExp && !mcpFileWrite ? extractArgDestinations(args) : []
25624
26023
  );
@@ -25677,7 +26076,7 @@ async function _authorizeHeadlessCore(toolName, args, metaArg, options) {
25677
26076
  if (dests.size === 0 && acts && !mcpFileWrite && (role.destructive === true || isDestructiveToolName(bare))) {
25678
26077
  held.push({ dest: "", why: "destructive" });
25679
26078
  }
25680
- const writeHolds = writes.length > 0 ? judgeWritesAfterExposure({
26079
+ const writeHolds = writes.length > 0 && !(unknownRead && !exposure.exposed) ? judgeWritesAfterExposure({
25681
26080
  accesses: writes,
25682
26081
  args,
25683
26082
  shellCommand: shellInput.kind === "shell" ? shellInput.command : null,
@@ -25704,14 +26103,14 @@ async function _authorizeHeadlessCore(toolName, args, metaArg, options) {
25704
26103
  const parts = held.map(
25705
26104
  (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`
25706
26105
  );
25707
- const home = import_os21.default.homedir();
26106
+ const home = import_os22.default.homedir();
25708
26107
  const short = (p) => p.startsWith(home + "/") ? "~" + p.slice(home.length) : p;
25709
26108
  for (const w of writeHolds) {
25710
26109
  parts.push(
25711
26110
  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}`
25712
26111
  );
25713
26112
  }
25714
- exposureReview = `\u{1F6E1}\uFE0F Node9: this session read ${read2}. This call ${parts.join("; and ")}.`;
26113
+ 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 ")}.`;
25715
26114
  exposureFromEid = last?.eid;
25716
26115
  if (exposureFromEid) exposureMeta = { exposureFromEid };
25717
26116
  exposureKeys = [