@node9/proxy 2.50.1 → 2.51.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli-main.js CHANGED
@@ -2123,9 +2123,15 @@ function detectDangerousShellExec(command) {
2123
2123
  if (f === PARSE_FAIL) return null;
2124
2124
  let result = null;
2125
2125
  const assigned = assignedNames(f);
2126
- walk(f, (node) => {
2126
+ const known = plainlyNamedPrograms(f);
2127
+ const calledByName = literallyCalledFunctions(f);
2128
+ const bodies = [];
2129
+ const inBody = (pos) => bodies.some(([a, b]) => pos >= a && pos < b);
2130
+ walkWithCalls(f, (node) => {
2127
2131
  if (!node || result === "block") return false;
2128
2132
  const n = node;
2133
+ if (n.kind === "FuncDecl" && n.Body && callDepth === 0 && calledByName.has(n.Name?.Value))
2134
+ bodies.push([n.Body.pos, n.Body.end]);
2129
2135
  if (n.kind === "BinaryCmd" && n.Y && !listOps().has(n.Op)) {
2130
2136
  const call = n.Y.Cmd;
2131
2137
  if (call && call.kind === "CallExpr") {
@@ -2149,7 +2155,8 @@ function detectDangerousShellExec(command) {
2149
2155
  );
2150
2156
  const head = unwrapCommandHead(headWords, false, true);
2151
2157
  const lookup = headWords[0]?.toLowerCase() === "command" && headWords.slice(1).some((w) => w === "-v" || w === "-V");
2152
- if (!lookup && (opaqueExecutable(args[0], assigned) || opaqueExecutable(args[head], assigned)))
2158
+ const unboundBody = callDepth === 0 && inBody(n.pos ?? -1);
2159
+ if (!lookup && !unboundBody && (opaqueExecutable(args[0], assigned, known) || opaqueExecutable(args[head], assigned, known)))
2153
2160
  result = "review";
2154
2161
  const firstParts = args[0].Parts || [];
2155
2162
  if (firstParts.length !== 1 || firstParts[0].kind !== "Lit") return true;
@@ -2209,11 +2216,105 @@ function assignedNames(f) {
2209
2216
  });
2210
2217
  return names;
2211
2218
  }
2212
- function executableParts(word, assigned, quoted = false) {
2219
+ function literallyCalledFunctions(f) {
2220
+ const declared = /* @__PURE__ */ new Set();
2221
+ const called = /* @__PURE__ */ new Set();
2222
+ walk(f, (node) => {
2223
+ if (!node) return false;
2224
+ if (node.kind === "FuncDecl" && node.Name?.Value) declared.add(node.Name.Value);
2225
+ if (node.kind === "CallExpr") {
2226
+ const head = resolveWordLiteral(node.Args?.[0]);
2227
+ if (head) called.add(head);
2228
+ }
2229
+ return true;
2230
+ });
2231
+ return new Set([...declared].filter((name) => called.has(name)));
2232
+ }
2233
+ function plainlyNamedPrograms(f) {
2234
+ const values = /* @__PURE__ */ new Map();
2235
+ const arrays = /* @__PURE__ */ new Set();
2236
+ const give = (name, value) => {
2237
+ if (value === null || !PLAIN_PROGRAM_NAME.test(value)) {
2238
+ values.set(name, null);
2239
+ return;
2240
+ }
2241
+ const cur = values.get(name);
2242
+ if (cur === null) return;
2243
+ if (cur === void 0) values.set(name, /* @__PURE__ */ new Set([value]));
2244
+ else cur.add(value);
2245
+ };
2246
+ const literalOnly = (w) => {
2247
+ const parts = w?.Parts ?? [];
2248
+ if (parts.length !== 1) return null;
2249
+ const p = parts[0];
2250
+ if (p.kind === "Lit") return p.Value ?? null;
2251
+ if (p.kind === "ParamExp" && expansionName(p) === "FUNCNAME") return "FUNCNAME";
2252
+ if (p.kind === "SglQuoted" && !p.Dollar) return p.Value ?? null;
2253
+ if (p.kind === "DblQuoted") {
2254
+ const inner = p.Parts ?? [];
2255
+ return inner.length === 1 && inner[0].kind === "Lit" ? inner[0].Value ?? null : null;
2256
+ }
2257
+ return null;
2258
+ };
2259
+ let ifsTouched = false;
2260
+ walk(f, (node) => {
2261
+ if (!node) return false;
2262
+ const kind = node.kind;
2263
+ if (kind === "FuncDecl" && node.Name?.Value) give(node.Name.Value, node.Name.Value);
2264
+ if (kind === "Assign" && node.Name?.Value) {
2265
+ const name = node.Name.Value;
2266
+ if (name === "IFS") ifsTouched = true;
2267
+ if (node.Append) values.set(name, null);
2268
+ else if (node.Array) {
2269
+ arrays.add(name);
2270
+ for (const el of node.Array.Elems ?? []) give(name, literalOnly(el.Value));
2271
+ } else if (node.Index) {
2272
+ arrays.add(name);
2273
+ give(name, node.Value ? literalOnly(node.Value) : null);
2274
+ } else give(name, node.Value ? literalOnly(node.Value) : null);
2275
+ }
2276
+ if (kind === "ForClause" && node.Loop?.Name?.Value) values.set(node.Loop.Name.Value, null);
2277
+ if (kind === "CallExpr") {
2278
+ const words = (node.Args ?? []).map((a) => resolveWordLiteral(a));
2279
+ if (VARIABLE_READERS.has(words[0] ?? "")) {
2280
+ for (const w of words.slice(1)) if (w && /^[A-Za-z_]\w*$/.test(w)) values.set(w, null);
2281
+ }
2282
+ }
2283
+ return true;
2284
+ });
2285
+ if (ifsTouched) return /* @__PURE__ */ new Set();
2286
+ const functions = /* @__PURE__ */ new Set();
2287
+ walk(f, (node) => {
2288
+ if (node && node.kind === "FuncDecl" && node.Name?.Value) functions.add(node.Name.Value);
2289
+ return true;
2290
+ });
2291
+ const callable = (v) => functions.has(v) || v === "FUNCNAME" || v === "true" || v === "false" || v === ":";
2292
+ for (const [name, vals] of values) {
2293
+ if (vals && ![...vals].every(callable)) values.set(name, null);
2294
+ }
2295
+ const known = /* @__PURE__ */ new Set();
2296
+ for (const [name, vals] of values)
2297
+ if (vals && (vals.size === 1 || arrays.has(name) && vals.size > 0)) known.add(name);
2298
+ return known;
2299
+ }
2300
+ function expansionName(part) {
2301
+ if (part.kind !== "ParamExp") return null;
2302
+ if (part.Excl || part.Length || part.Width || part.Slice || part.Repl || part.Exp) return null;
2303
+ return part.Param?.Value ?? null;
2304
+ }
2305
+ function executableParts(word, assigned, quoted = false, known = EMPTY_NAMES) {
2213
2306
  return (word?.Parts ?? []).flatMap((part) => {
2214
2307
  const kind = part.kind;
2215
- if (kind === "DblQuoted") return executableParts(part, assigned, true);
2308
+ if (kind === "DblQuoted") return executableParts(part, assigned, true, known);
2216
2309
  if (kind === "Lit") return [{ kind: "Lit", value: part.Value ?? "", quoted }];
2310
+ const name = expansionName(part);
2311
+ if (name !== null && (known.has(name) || name === "FUNCNAME"))
2312
+ return [{ kind: "Lit", value: name, quoted }];
2313
+ if (name !== null) {
2314
+ const bound = recordedExpansion(name === "@" || name === "*" ? "1" : name);
2315
+ if (typeof bound === "string" && PLAIN_PROGRAM_NAME.test(bound))
2316
+ return [{ kind: "Lit", value: bound, quoted }];
2317
+ }
2217
2318
  if (kind === "SglQuoted")
2218
2319
  return [{ kind: part.Dollar ? "Dynamic" : "Lit", value: part.Value ?? "", quoted: true }];
2219
2320
  if (kind === "ParamExp" && isDefaultExpansion(part) && !assigned.has(part.Param?.Value)) {
@@ -2221,14 +2322,14 @@ function executableParts(word, assigned, quoted = false) {
2221
2322
  if (fallback !== null) return [{ kind: "Lit", value: fallback, quoted }];
2222
2323
  }
2223
2324
  if (kind === "CmdSubst") {
2224
- const name = lookupSubstitution(part);
2225
- if (name !== null) return [{ kind: "Lit", value: name, quoted }];
2325
+ const name2 = lookupSubstitution(part);
2326
+ if (name2 !== null) return [{ kind: "Lit", value: name2, quoted }];
2226
2327
  }
2227
2328
  return [{ kind: "Dynamic", value: "", quoted }];
2228
2329
  });
2229
2330
  }
2230
- function opaqueExecutable(word, assigned) {
2231
- const parts = executableParts(word, assigned);
2331
+ function opaqueExecutable(word, assigned, known = EMPTY_NAMES) {
2332
+ const parts = executableParts(word, assigned, false, known);
2232
2333
  let lastSlash = -1;
2233
2334
  parts.forEach((p, i) => {
2234
2335
  if (p.kind === "Lit" && p.value.includes("/")) lastSlash = i;
@@ -2350,6 +2451,9 @@ function deriveBinaryOp(sample) {
2350
2451
  function isListOp(op) {
2351
2452
  return listOps().has(op);
2352
2453
  }
2454
+ function isPipeOp(op) {
2455
+ return pipeOps().has(op);
2456
+ }
2353
2457
  function listOps() {
2354
2458
  if (_listOps) return _listOps;
2355
2459
  _listOps = new Set(
@@ -2594,6 +2698,33 @@ function isProtectedCodePath(p) {
2594
2698
  if (matchSensitivePath(p) !== null) return true;
2595
2699
  return SYSTEM_AUTH_FILES.includes(p) || SYSTEM_AUTH_DIRS.some((d) => p === d || p.startsWith(d + "/"));
2596
2700
  }
2701
+ function isJailedCodePath(literal, jailPaths) {
2702
+ const value = trimTrailingSlashes(literal.replace(/\\/g, "/"));
2703
+ if (!value) return false;
2704
+ for (const raw of jailPaths) {
2705
+ const jail = trimTrailingSlashes(raw.trim().replace(/\\/g, "/"));
2706
+ if (!jail) continue;
2707
+ const home = homeTail(jail);
2708
+ if (home === null) {
2709
+ if (value === jail || value.startsWith(jail + "/")) return true;
2710
+ continue;
2711
+ }
2712
+ if (!home) continue;
2713
+ const tail = homeTail(value);
2714
+ if (tail !== null && (tail === home || tail.startsWith(home + "/"))) return true;
2715
+ }
2716
+ return false;
2717
+ }
2718
+ function trimTrailingSlashes(s) {
2719
+ let end = s.length;
2720
+ while (end > 0 && s.charCodeAt(end - 1) === 47) end--;
2721
+ return s.slice(0, end);
2722
+ }
2723
+ function homeTail(p) {
2724
+ const m = /^(?:~|\$\{?HOME\}?|\/home\/[^/]+|\/Users\/[^/]+|\/root)(?:\/(.*))?$/.exec(p);
2725
+ if (!m) return null;
2726
+ return m[1] ?? "";
2727
+ }
2597
2728
  function inlineExecDescription(r) {
2598
2729
  const lead = r ? `The AI is running code directly from the command line: ${inlineExecReason(r).replace(/^This/, "this")}.` : "The AI is running code directly from the command line.";
2599
2730
  return `${lead} Review the full script below before allowing it to execute.`;
@@ -2602,7 +2733,8 @@ function inlineExecReason(r) {
2602
2733
  const who = r.interp ? `This inline ${r.interp} code` : "This inline code";
2603
2734
  return r.evidence ? `${who} ${r.what} (${r.evidence})` : `${who} ${r.what}`;
2604
2735
  }
2605
- function inlineExecReview(command) {
2736
+ function inlineExecReview(command, jailPaths = []) {
2737
+ const isProtected = jailPaths.length === 0 ? isProtectedCodePath : (p) => isProtectedCodePath(p) || isJailedCodePath(p, jailPaths);
2606
2738
  const calls = inlineExecCalls(command);
2607
2739
  if (calls === PARSE_FAIL) {
2608
2740
  return INLINE_EXEC_FALLBACK.test(command) ? { outcome: "unreadable", interp: "", what: "could not be parsed" } : null;
@@ -2625,7 +2757,7 @@ function inlineExecReview(command) {
2625
2757
  what: `is ${call.interp} code, which node9 does not read`
2626
2758
  };
2627
2759
  } else {
2628
- const judged = judgeInlineCode(language, code2, isProtectedCodePath);
2760
+ const judged = judgeInlineCode(language, code2, isProtected);
2629
2761
  if (judged) finding = { ...judged, interp: call.interp };
2630
2762
  }
2631
2763
  if (finding?.outcome === "dangerous") return finding;
@@ -2806,6 +2938,11 @@ function recordTopLevelStmt(stmt) {
2806
2938
  if (t === "DeclClause") return ASSIGNMENT_HEADS.has(stmt.Cmd.Variant?.Value ?? "");
2807
2939
  return (stmt.Cmd.Args || []).length === 0 && (stmt.Cmd.Assigns || []).length > 0;
2808
2940
  }
2941
+ function arrayElements(value) {
2942
+ if (typeof value !== "string" || !value.startsWith(ARRAY_MARK)) return null;
2943
+ const body = value.slice(ARRAY_MARK.length);
2944
+ return body === "" ? [] : body.split(ARRAY_SEP);
2945
+ }
2809
2946
  function recordAssignments(n, at) {
2810
2947
  if (!assignmentTable) return;
2811
2948
  const t = n.kind;
@@ -2819,7 +2956,19 @@ function recordAssignments(n, at) {
2819
2956
  } else return;
2820
2957
  for (const a of assigns) {
2821
2958
  const name = a?.Name?.Value;
2822
- if (!name || !a.Value || a.Append) continue;
2959
+ if (!name) continue;
2960
+ if (a.Array) {
2961
+ const elems = (a.Array.Elems || []).map(
2962
+ (e) => e?.Index ? null : resolveWordLiteral(e?.Value)
2963
+ );
2964
+ const literal = elems.every((v) => v !== null);
2965
+ assignmentTable.set(name, {
2966
+ value: literal && !a.Append ? ARRAY_MARK + elems.join(ARRAY_SEP) : null,
2967
+ at
2968
+ });
2969
+ continue;
2970
+ }
2971
+ if (!a.Value || a.Append) continue;
2823
2972
  assignmentTable.set(name, { value: resolveWordLiteral(a.Value), at });
2824
2973
  }
2825
2974
  }
@@ -2830,7 +2979,33 @@ function resolveTrivialSubst(part) {
2830
2979
  const st = stmts[0];
2831
2980
  if ((st.Redirs || []).length > 0 || st.Negated || st.Background) return void 0;
2832
2981
  const cmd = st.Cmd;
2833
- if (!cmd || cmd.kind !== "CallExpr") return void 0;
2982
+ if (!cmd) return void 0;
2983
+ if (cmd.kind === "BinaryCmd" && pipeOps().has(cmd.Op)) {
2984
+ const left = cmd.X?.Cmd;
2985
+ const right = cmd.Y?.Cmd;
2986
+ if (!left || !right || left.kind !== "CallExpr" || right.kind !== "CallExpr") return void 0;
2987
+ if ((cmd.X.Redirs || []).length > 0 || (cmd.Y.Redirs || []).length > 0) return void 0;
2988
+ const input = trivialCallOutput(left);
2989
+ if (input === void 0) return void 0;
2990
+ const filter = (right.Args || []).map((a) => resolveWordLiteral(a));
2991
+ if (filter.some((w) => w === null)) return void 0;
2992
+ const f = baseWord(filter[0]);
2993
+ const flags = filter.slice(1);
2994
+ if (f === "base64" && flags.length === 1 && /^(-d|-D|--decode)$/.test(flags[0])) {
2995
+ try {
2996
+ const out = Buffer.from(input, "base64").toString("utf8");
2997
+ return /^[\x20-\x7e]*$/.test(out) ? out : void 0;
2998
+ } catch {
2999
+ return void 0;
3000
+ }
3001
+ }
3002
+ if (f === "rev" && flags.length === 0) return [...input].reverse().join("");
3003
+ return void 0;
3004
+ }
3005
+ if (cmd.kind !== "CallExpr") return void 0;
3006
+ return trivialCallOutput(cmd);
3007
+ }
3008
+ function trivialCallOutput(cmd) {
2834
3009
  if ((cmd.Assigns || []).length > 0) return void 0;
2835
3010
  const words = (cmd.Args || []).map((a) => resolveWordLiteral(a));
2836
3011
  if (words.length === 0 || words.some((w) => w === null)) return void 0;
@@ -2842,9 +3017,16 @@ function resolveTrivialSubst(part) {
2842
3017
  return rest.slice(i).join(" ");
2843
3018
  }
2844
3019
  if (head === "printf") {
3020
+ if (rest.length === 1 && /^(?:\\(?:[0-7]{1,3}|x[0-9A-Fa-f]{1,2}))+$/.test(rest[0])) {
3021
+ return rest[0].replace(
3022
+ /\\(?:x([0-9A-Fa-f]{1,2})|([0-7]{1,3}))/g,
3023
+ (_, hex, oct) => String.fromCharCode(hex ? parseInt(hex, 16) : parseInt(oct, 8))
3024
+ );
3025
+ }
2845
3026
  if (rest.length !== 2) return void 0;
2846
- if (!/^%s(\\n)?$/.test(rest[0])) return void 0;
2847
- return rest[1];
3027
+ const m = /^([^%]*)%s([^%]*?)(\\n)?$/.exec(rest[0]);
3028
+ if (!m) return void 0;
3029
+ return m[1] + rest[1] + m[2];
2848
3030
  }
2849
3031
  return void 0;
2850
3032
  }
@@ -2858,11 +3040,28 @@ function isPlainParam(p) {
2858
3040
  if (p.kind !== "ParamExp") return false;
2859
3041
  return !(p.Excl || p.Length || p.Width || p.Index || p.Slice || p.Repl || p.Exp);
2860
3042
  }
3043
+ function indexedParam(p) {
3044
+ if (p.kind !== "ParamExp" || !p.Index) return null;
3045
+ if (p.Excl || p.Length || p.Width || p.Slice || p.Repl || p.Exp) return null;
3046
+ const index = resolveWordLiteral(p.Index);
3047
+ const name = p.Param?.Value;
3048
+ return name && index !== null ? { name, index } : null;
3049
+ }
2861
3050
  function expandPlainParam(p) {
2862
3051
  if (!assignmentTable) return void 0;
3052
+ const indexed = indexedParam(p);
3053
+ if (indexed) {
3054
+ const elems = arrayElements(recordedExpansion(indexed.name));
3055
+ if (elems === null) return void 0;
3056
+ if (indexed.index === "@" || indexed.index === "*") return elems.join(" ");
3057
+ return /^\d+$/.test(indexed.index) ? elems[Number(indexed.index)] ?? null : null;
3058
+ }
2863
3059
  if (!isPlainParam(p)) return void 0;
2864
3060
  const recorded = recordedExpansion(p.Param?.Value);
2865
- if (recorded !== void 0) return recorded;
3061
+ if (recorded !== void 0) {
3062
+ const elems = arrayElements(recorded);
3063
+ return elems === null ? recorded : elems[0] ?? "";
3064
+ }
2866
3065
  return HOME_VARIABLES.has(p.Param?.Value) ? "~" : void 0;
2867
3066
  }
2868
3067
  function functionBodies(f) {
@@ -5935,7 +6134,59 @@ function dynamicOptionWord(word) {
5935
6134
  if (/^--[a-z-]+=/.test(prefix) || /^-[cC]/.test(prefix)) return `${prefix}$\u2026`;
5936
6135
  return null;
5937
6136
  }
5938
- function resolveGitWords(word) {
6137
+ function arrayExpansionWords(word) {
6138
+ let parts = word?.Parts || [];
6139
+ if (parts.length === 1 && parts[0].kind === "DblQuoted") parts = parts[0].Parts || [];
6140
+ if (parts.length !== 1 || parts[0].kind !== "ParamExp" || !parts[0].Index) return null;
6141
+ const index = resolveWordLiteral(parts[0].Index);
6142
+ if (index !== "@" && index !== "*") return null;
6143
+ const elems = arrayElements(recordedExpansion(parts[0].Param?.Value));
6144
+ return elems;
6145
+ }
6146
+ function producedLiteral(call) {
6147
+ if ((call.Assigns || []).length > 0) return null;
6148
+ const words = (call.Args || []).map((a) => resolveWordLiteral(a));
6149
+ if (words.length === 0 || words.some((w) => w === null)) return null;
6150
+ const head = baseWord(words[0]);
6151
+ const rest = words.slice(1);
6152
+ if (head === "git" && rest[0] === "rev-parse" && rest[1] === "--show-toplevel" && rest.length === 2)
6153
+ return ":/";
6154
+ if (head === "echo") {
6155
+ let i = 0;
6156
+ while (i < rest.length && /^-[neE]+$/.test(rest[i])) i++;
6157
+ return rest.length - i === 1 ? rest[i] : null;
6158
+ }
6159
+ if (head === "printf" && rest.length === 2 && /^%s(\\[0n])?$/.test(rest[0])) return rest[1];
6160
+ return null;
6161
+ }
6162
+ function treeTopWord(word, treeTopVars = NO_VARS) {
6163
+ let parts = word?.Parts || [];
6164
+ if (parts.length === 1 && parts[0].kind === "DblQuoted") parts = parts[0].Parts || [];
6165
+ if (parts.length !== 1) return false;
6166
+ const p = parts[0];
6167
+ if (p.kind === "ParamExp") {
6168
+ const name = plainParamName(word)?.name;
6169
+ return name === "PWD" || name !== void 0 && treeTopVars.has(name);
6170
+ }
6171
+ if (p.kind !== "CmdSubst") return false;
6172
+ const stmts = p.Stmts || [];
6173
+ if (stmts.length !== 1 || !stmts[0].Cmd || stmts[0].Cmd.kind !== "CallExpr") return false;
6174
+ const words = (stmts[0].Cmd.Args || []).map((a) => resolveWordLiteral(a));
6175
+ if (words.some((w) => w === null)) return false;
6176
+ const head = baseWord(words[0]);
6177
+ if (head === "pwd") return words.length === 1 || words[1] === "-P" || words[1] === "-L";
6178
+ return head === "git" && words[1] === "rev-parse" && words[2] === "--show-toplevel";
6179
+ }
6180
+ function resolveGitWords(word, treeTopVars = NO_VARS, appended = NO_APPENDS) {
6181
+ if (treeTopWord(word, treeTopVars)) return [":/"];
6182
+ const plain = plainParamName(word);
6183
+ if (plain && appended.has(plain.name)) {
6184
+ const v = appended.get(plain.name);
6185
+ if (v === null || v === void 0) return [null];
6186
+ return plain.quoted ? [v] : splitFields(v);
6187
+ }
6188
+ const elems = arrayExpansionWords(word);
6189
+ if (elems !== null) return elems;
5939
6190
  const value = resolveWordLiteral(word);
5940
6191
  if (value === null) {
5941
6192
  if (isPositionalParam(word)) return boundPositionals() ?? [POSITIONAL2];
@@ -6043,6 +6294,8 @@ function classify(inv) {
6043
6294
  if (o.flags.has("-n") || o.flags.has("--dry-run")) break;
6044
6295
  const forced = o.flags.has("-f") || o.flags.has("--force") || o.flags.has("--force-with-lease") || o.flags.has("--force-if-includes") || o.flags.has("--mirror") || o.operands.some((r) => r.startsWith("+"));
6045
6296
  if (forced) found(FORCE_PUSH);
6297
+ const deletes = o.flags.has("-d") || o.flags.has("--delete") || o.operands.some((r) => /^:[^:]/.test(r));
6298
+ if (deletes) found(DESTRUCTIVE);
6046
6299
  break;
6047
6300
  }
6048
6301
  case "reset": {
@@ -6070,11 +6323,72 @@ function classify(inv) {
6070
6323
  if (o.flags.has("-d") || o.flags.has("--delete")) found(DESTRUCTIVE);
6071
6324
  break;
6072
6325
  }
6326
+ // node9Firewall#385: history and work discarded through plumbing. A ref
6327
+ // deleted by hand, a stash dropped, the reflog expired or pruned: the
6328
+ // commits behind them are gone the way `branch -D` loses them.
6329
+ case "update-ref": {
6330
+ const o = parseOptions(inv.args, "m", /* @__PURE__ */ new Set());
6331
+ if (o.flags.has("-d") && (o.dynamic || o.operands.some(isHistoryRef))) found(DESTRUCTIVE);
6332
+ break;
6333
+ }
6334
+ case "reflog": {
6335
+ const o = parseOptions(inv.args, "", /* @__PURE__ */ new Set());
6336
+ if (o.operands[0] === "expire" || o.operands[0] === "delete") found(DESTRUCTIVE);
6337
+ break;
6338
+ }
6339
+ case "gc": {
6340
+ if (inv.config.get("gc.reflogexpire") === "now") found(DESTRUCTIVE);
6341
+ break;
6342
+ }
6343
+ case "stash": {
6344
+ const o = parseOptions(inv.args, "", /* @__PURE__ */ new Set());
6345
+ if (o.operands[0] === "clear" || o.operands[0] === "drop") found(DESTRUCTIVE);
6346
+ break;
6347
+ }
6348
+ case "worktree": {
6349
+ const o = parseOptions(inv.args, "", /* @__PURE__ */ new Set());
6350
+ if (o.operands[0] === "remove" && (o.flags.has("-f") || o.flags.has("--force")))
6351
+ found(DESTRUCTIVE);
6352
+ break;
6353
+ }
6354
+ // The working tree overwritten: `restore` from another ref, a forced
6355
+ // checkout, or `checkout -- .` (every local change dropped). A bare
6356
+ // `restore .` or `checkout .` stays an ordinary undo, as pinned.
6357
+ case "restore": {
6358
+ const o = parseOptions(inv.args, "s", RESTORE_LONG_VALUE);
6359
+ const src = restoreSource(inv.args);
6360
+ const whole = o.dynamic || o.operands.some(wholeTree);
6361
+ if (src !== null && src !== "HEAD" && whole) found(DESTRUCTIVE);
6362
+ break;
6363
+ }
6364
+ case "checkout": {
6365
+ const o = parseOptions(inv.args, "bB", CHECKOUT_LONG_VALUE);
6366
+ const forced = o.flags.has("-f") || o.flags.has("--force");
6367
+ const dropAll = inv.args.includes("--") && o.operands.some(wholeTree);
6368
+ if (forced || dropAll) found(DESTRUCTIVE);
6369
+ break;
6370
+ }
6073
6371
  default:
6074
6372
  break;
6075
6373
  }
6076
6374
  return out;
6077
6375
  }
6376
+ function isExcludePathspec(operand) {
6377
+ return /^:[!^]/.test(operand) || /^:\([^)]*\bexclude\b[^)]*\)/.test(operand);
6378
+ }
6379
+ function restoreSource(args) {
6380
+ for (let i = 0; i < args.length; i++) {
6381
+ const w = args[i];
6382
+ if (w === null) continue;
6383
+ if (w.startsWith("--source=")) return w.slice("--source=".length);
6384
+ if (w === "--source" || w === "-s") return args[i + 1] ?? null;
6385
+ if (/^-s./.test(w)) return w.slice(2);
6386
+ }
6387
+ return null;
6388
+ }
6389
+ function isHistoryRef(ref) {
6390
+ return /^refs\/(heads|stash)(\/|$)/.test(ref);
6391
+ }
6078
6392
  function killSwitchSet(name, value) {
6079
6393
  if (value === null) return false;
6080
6394
  switch (name) {
@@ -6132,6 +6446,7 @@ function hooksBypass(inv) {
6132
6446
  const o = parseOptions(inv.args, "mFCcut", COMMIT_LONG_VALUE);
6133
6447
  return o.flags.has("-n") || o.flags.has("--no-verify");
6134
6448
  }
6449
+ if (sub === "commit-tree") return true;
6135
6450
  if (sub !== void 0 && NO_VERIFY_SUBS.has(sub)) {
6136
6451
  return inv.args.some((a) => a === "--no-verify");
6137
6452
  }
@@ -6169,7 +6484,20 @@ function configWritesHooksPath(args) {
6169
6484
  return write2;
6170
6485
  }
6171
6486
  function isHookPath(word) {
6172
- return /(^|[\\/])(\.git[\\/]hooks|\.husky)([\\/]|$)/.test(word);
6487
+ return /(^|[\\/])(\.git[\\/]hooks|\.husky|\.githooks)([\\/]|$)/.test(word);
6488
+ }
6489
+ function isLiveHookFile(word) {
6490
+ if (!isHookPath(word)) return false;
6491
+ const m = /(^|[\\/])(\.git[\\/]hooks|\.husky|\.githooks)(?:[\\/](.*))?$/.exec(word);
6492
+ if (!m) return false;
6493
+ const rest = m[3] ?? "";
6494
+ if (rest === "" || rest === "_") return true;
6495
+ const leaf = rest.replace(/^_[\\/]/, "");
6496
+ return /^[a-z-]+$/.test(leaf);
6497
+ }
6498
+ function hookBodyIsEmpty(text) {
6499
+ const lines = text.split(/\r?\n/).map((l) => l.trim()).filter((l) => l !== "" && !l.startsWith("#"));
6500
+ return lines.every((l) => /^(exit(\s+0)?|true|:|set\s+-[a-zA-Z]+|set\s+-o\s+\w+)$/.test(l));
6173
6501
  }
6174
6502
  function modeRemovesExecute(mode) {
6175
6503
  if (/^[0-7]{3,4}$/.test(mode)) {
@@ -6178,6 +6506,22 @@ function modeRemovesExecute(mode) {
6178
6506
  }
6179
6507
  return mode.split(",").some((part) => /^[ugoa]*-[rwst]*x/.test(part));
6180
6508
  }
6509
+ function redirOpIsAppend(redir) {
6510
+ return redir?.Op === appendOp();
6511
+ }
6512
+ function appendOp() {
6513
+ if (_appendOp !== null) return _appendOp;
6514
+ try {
6515
+ const f = parseShared("echo x >> y");
6516
+ _appendOp = typeof f === "symbol" ? -1 : f.Stmts[0].Redirs[0].Op;
6517
+ } catch {
6518
+ _appendOp = -1;
6519
+ }
6520
+ return _appendOp;
6521
+ }
6522
+ function hooksBypassed(shown) {
6523
+ return { ...HOOKS_BYPASS, shown, reason: `${HOOKS_BYPASS.reason}: ${shown}` };
6524
+ }
6181
6525
  function chmodDisarmsHook(args, shown) {
6182
6526
  let i = 0;
6183
6527
  while (i < args.length) {
@@ -6220,6 +6564,7 @@ function stagesEverything(inv) {
6220
6564
  const sub = inv.sub?.toLowerCase();
6221
6565
  const prefix = chdirPrefix(inv);
6222
6566
  const judge = (operands2) => {
6567
+ if (operands2.length > 0 && operands2.every(isExcludePathspec)) return {};
6223
6568
  let conditional = false;
6224
6569
  for (const operand of operands2) {
6225
6570
  if (!wholeTree(operand)) continue;
@@ -6512,7 +6857,7 @@ function evaluateAgentFileWrite(args, accesses, context, settings, shell, hooks
6512
6857
  }
6513
6858
  return null;
6514
6859
  }
6515
- function isPipeOp(op) {
6860
+ function isPipeOp2(op) {
6516
6861
  return !isListOp(op);
6517
6862
  }
6518
6863
  function isDupInOp(op) {
@@ -8590,7 +8935,7 @@ async function evaluatePolicyCore(config, toolName, args, context = {}, hooks =
8590
8935
  if (v?.logged) noteLogged(v);
8591
8936
  else if (v) candidates2.push(v);
8592
8937
  };
8593
- const inline = reviewsEveryInline(config.settings.mode) ? detectInlineExec(shellCommand) ? void 0 : null : inlineExecReview(shellCommand);
8938
+ const inline = reviewsEveryInline(config.settings.mode) ? detectInlineExec(shellCommand) ? void 0 : null : inlineExecReview(shellCommand, config.policy.jailPaths);
8594
8939
  if (inline !== null) {
8595
8940
  consider("commands.inline-exec", {
8596
8941
  decision: "review",
@@ -8933,7 +9278,7 @@ function* validCardRuns(text, base22, exemptFractions = true) {
8933
9278
  let m;
8934
9279
  while ((m = re.exec(text)) !== null) {
8935
9280
  const digits = m[0].replace(/\D/g, "");
8936
- const fraction = exemptFractions && isDecimalFraction(text, m.index, digits.length === m[0].length);
9281
+ const fraction = exemptFractions && (isDecimalFraction(text, m.index, digits.length === m[0].length) || isInteriorDotNumber(m[0]));
8937
9282
  if (!fraction && validateLuhn(digits)) {
8938
9283
  yield { index: m.index, length: m[0].length, digits };
8939
9284
  } else {
@@ -8960,6 +9305,12 @@ function replaceValidCards(text, replace, { exemptFractions = true } = {}) {
8960
9305
  }
8961
9306
  return out;
8962
9307
  }
9308
+ function isInteriorDotNumber(run) {
9309
+ const first = run.indexOf(".");
9310
+ if (first <= 0 || first === run.length - 1) return false;
9311
+ if (run.indexOf(".", first + 1) !== -1) return false;
9312
+ return !/[-\s]/.test(run);
9313
+ }
8963
9314
  function isDecimalFraction(text, index, unseparated) {
8964
9315
  if (!unseparated) return false;
8965
9316
  const sep = text[index - 1];
@@ -10370,7 +10721,7 @@ function* stringLeavesWithKeys(obj, depth = 0, key) {
10370
10721
  }
10371
10722
  for (const [k, v] of Object.entries(obj)) yield* stringLeavesWithKeys(v, depth + 1, k);
10372
10723
  }
10373
- 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, REPLACED_SHIELDS, BUILTIN_SHIELDS, SSRF_MAX_HOST, METADATA_ADDRESSES, STRICT_TIERS, METADATA_HOSTNAMES, v4Octets, TIER_REASON, CHECK_TEXT, LEVELS, MODE_VALUES, SOFT, STRICT, BLOCK_ALL, LEVEL_CHECKS, ASI, ASI_IDS, PACK_SHIELD_OWASP, PACK_RULE_OWASP, POSTURE_CATEGORY_OWASP, VERDICTS, CHECK_VERDICT_RANK, CHECK_GROUPS, ALL, BUILTIN_DANGEROUS_WORDS, PRODUCT_CHECKS, SHIELD_RULE_NAME, SHIELD_RULES_FOLDED_INTO_PRODUCT, CHECKS, CHECK_BY_ID, LEGACY_TO_ID, PROJECT_JAIL_BLOCK_READ, OVERRIDE_PREFIX, LABEL_WRAPPERS, LEGACY_READERS, syntax, 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, VARIABLE_READERS, 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, 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, DLP_SCAN_LIMITS, BATCH_SEPARATOR, ENCODED_RUN_RE, IMPERATIVE, SECRET_TARGET, GUARD, SIGNALS, DESTINATION_RE, REUSED_SIGNALS, NEVER_NEGATED, NEGATION_BEFORE, RDR_IN, COPY_VERBS2, LITERAL_VERBS, GIT_PRESCREEN_RE, MAX_PAYLOAD_DEPTH2, MAX_ALIAS_DEPTH, CACHE_MAX, cache, PAYLOAD_SHELLS, CONFIG_SCOPE_FLAGS, CONFIG_VALUE_FLAGS, GLOBAL_VALUE_OPTIONS, GLOBAL_FLAG_OPTIONS, MAX_LOOP_ITEMS, GitWalk, POSITIONAL2, FORCE_PUSH, DESTRUCTIVE, UNREADABLE, PUSH_LONG_VALUE, BRANCH_LONG_VALUE, TAG_LONG_VALUE, CLEAN_LONG_VALUE, HOOKS_BYPASS, NO_VERIFY_SUBS, HOOK_KILL_SWITCHES, COMMIT_LONG_VALUE, CHMOD_OPTION_RE, 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, CONTROL_CHAR_RE, SHIELD_RULE, ACTION_RULE, LOOP_MAX_RECORDS, FINDING_TO_SIGNAL, SCAN_SIGNAL_WEIGHTS, LOOP_THRESHOLD_FOR_WASTE, MAX_BLAST_PATH, DESTRUCTIVE_OP_RE, SENSITIVE_PATH_RE, FILE_TOOLS, CANARY_MIN_LENGTH, MAX_TEXT, MAX_DEPTH4, MAX_JSON_PARSE, URL_DEPTH, B64_DEPTH, MIN_SEGMENT, SEPARATORS, stripSeparators, looksText, CANARY_DECODERS, VIEWS, LONG_OUTPUT_THRESHOLD_BYTES, CANONICAL_EXTRACTOR_VERSION, DEDUPE_PREVIEW_LEN, ENGINE_VERSION;
10724
+ 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, REPLACED_SHIELDS, BUILTIN_SHIELDS, SSRF_MAX_HOST, METADATA_ADDRESSES, STRICT_TIERS, METADATA_HOSTNAMES, v4Octets, TIER_REASON, CHECK_TEXT, LEVELS, MODE_VALUES, SOFT, STRICT, BLOCK_ALL, LEVEL_CHECKS, ASI, ASI_IDS, PACK_SHIELD_OWASP, PACK_RULE_OWASP, POSTURE_CATEGORY_OWASP, VERDICTS, CHECK_VERDICT_RANK, CHECK_GROUPS, ALL, BUILTIN_DANGEROUS_WORDS, PRODUCT_CHECKS, SHIELD_RULE_NAME, SHIELD_RULES_FOLDED_INTO_PRODUCT, CHECKS, CHECK_BY_ID, LEGACY_TO_ID, PROJECT_JAIL_BLOCK_READ, OVERRIDE_PREFIX, LABEL_WRAPPERS, LEGACY_READERS, syntax, 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, DLP_SCAN_LIMITS, BATCH_SEPARATOR, ENCODED_RUN_RE, IMPERATIVE, SECRET_TARGET, GUARD, SIGNALS, DESTINATION_RE, REUSED_SIGNALS, NEVER_NEGATED, NEGATION_BEFORE, RDR_IN, COPY_VERBS2, LITERAL_VERBS, GIT_PRESCREEN_RE, MAX_PAYLOAD_DEPTH2, MAX_ALIAS_DEPTH, CACHE_MAX, cache, PAYLOAD_SHELLS, CONFIG_SCOPE_FLAGS, CONFIG_VALUE_FLAGS, GLOBAL_VALUE_OPTIONS, GLOBAL_FLAG_OPTIONS, MAX_LOOP_ITEMS, GitWalk, POSITIONAL2, NO_VARS, NO_APPENDS, FORCE_PUSH, DESTRUCTIVE, UNREADABLE, PUSH_LONG_VALUE, BRANCH_LONG_VALUE, TAG_LONG_VALUE, CLEAN_LONG_VALUE, RESTORE_LONG_VALUE, CHECKOUT_LONG_VALUE, HOOKS_BYPASS, NO_VERIFY_SUBS, HOOK_KILL_SWITCHES, COMMIT_LONG_VALUE, CHMOD_OPTION_RE, _appendOp, STAGE_ALL, ADD_LONG_VALUE, CHECK_ID, RULE_NAME, INERT_LINE_RE, CHECK_ID2, INSTRUCTION_NAMES, SETTINGS_RE, HOOK_WORDS_RE, SHELLS, NETCATS, NET_CLIENTS, SENDERS, NETCAT_EXEC_LONG, DECL_DUMPS, PROC_ENVIRON_RE, DEV_NET_RE, SOCAT_PROGRAM_RE, SOCAT_NET_RE, PRESCREEN_RE, MAX_PAYLOAD_DEPTH3, CACHE_MAX2, cache2, dupInOp, REVERSE_SHELL_DESCRIPTION, ENV_EXFIL_DESCRIPTION, DYNAMIC_WORD, MULTICALL, SHELL_VALUE_OPTIONS, INTERPRETERS, OutboundWalk, NPM_INSTALL_VERBS, NPM_EXEC_VERBS, PNPM_INSTALL_VERBS, YARN_INSTALL_VERBS, BUN_INSTALL_VERBS, DLX_VERBS, NON_REGISTRY_RE, NPM_NAME_RE, EXACT_SEMVER_RE, NPM_VALUE_FLAGS, PNPM_VALUE_FLAGS, YARN_VALUE_FLAGS, BUN_VALUE_FLAGS, PIP_VALUE_FLAGS, UV_VALUE_FLAGS, POETRY_VALUE_FLAGS, PIPENV_VALUE_FLAGS, PIPX_VALUE_FLAGS, PY_SPEC_RE, PY_EXACT_RE, EXACT_VERSION_RE, GEM_NAME_RE, GEM_VALUE_FLAGS, BUNDLE_VALUE_FLAGS, CRATE_NAME_RE, CARGO_ADD_VALUE_FLAGS, CARGO_INSTALL_VALUE_FLAGS, GO_VALUE_FLAGS, GO_PATH_RE, NUGET_NAME_RE, DOTNET_ADD_VALUE_FLAGS, DOTNET_TOOL_VALUE_FLAGS, NUGET_VALUE_FLAGS, MAVEN_COORD_RE, COMPOSER_NAME_RE, COMPOSER_VALUE_FLAGS, EDITOR_HEADS, EXTENSION_ID_RE, MANAGER_HEADS, SHELLS2, MAX_NESTING, SHELL_VALUE_FLAGS, NPM_FAMILY, PIP_FAMILY, NPM_FETCH_VERBS, PIP_FETCH_VERBS, NPM_FLAGS, PIP_FLAGS, NPM_VARS, PIP_VARS, PRESCREEN_RE2, PIP_ATTACHED_RE, MAX_PAYLOAD_DEPTH4, PUBLIC_REGISTRY_HOSTS, TUNING_FIELDS, MAP_GOVERNED, KNOB_GOVERNED, CONFIGURABLE_CHECK_IDS, DEFAULT_EGRESS_ALLOWLIST, PRIVATE_HOST_SUFFIXES, SOURCE_COMMANDS, SINK_COMMANDS, OBFUSCATORS, SENSITIVE_PATTERNS, FLAGS_WITH_VALUES, DESTINATION_ARGS, MCP_DESTINATION_KEYS, ARGUMENT_SCAN_LIMITS, RUN_CHAR, runChar, VERDICT_RANK, SQL_DML_KEYWORDS, MAX_DESTINATIONS, MAX_SCAN_CHARS, EMAIL_RE, URL_RE, IBAN_RE, RECIPIENT_KEYS, DESTRUCTIVE_VERB_RE, PII_EMAIL_RE, PII_SSN_RE, PII_PHONE_RE, PII_CC16_RE, PII_CC15_RE, PII_CC14_RE, PII_CC13_RE, PII_CC19_RE, CARD_PATTERNS, PII_ROUTING_RE, ROUTING_LABEL_RE, ROUTING_LABEL_WINDOW, NATIONAL_ID_SHAPES, NATIONAL_ID_LABEL_RE, NATIONAL_ID_LABEL_CS_RE, idLabelIn, NATIONAL_ID_LABEL_WINDOW, NATIONAL_ID_GUARD_RE, PII_IBAN_RE, REALTIME_PII_PATTERNS, MAX_PII_SCAN_BYTES, IBAN_DESTINATION_KEYS, WHOLE_IBAN_RE, JSON_ESCAPE_RE, JSON_ESCAPES, MASKED_KINDS, ENTRY_RE, IBAN_TEXT_RE, EMAIL_TEXT_RE, ACCOUNT_KEY_RE, ACCOUNT_VALUE_RE, MAX_MASK_DEPTH, EMPTY, CURL_SEND_FLAGS, WGET_SEND_FLAGS, IWR_SEND_FLAGS, WRITE_METHODS, GH_READ, GH_WRITE, GH_HOSTNAME_FLAGS, GH_REPO_VALUE_FLAGS, GH_API_VALUE_FLAGS, GIT_FETCH, PUBLISH, SHELLS3, OWN_HEADS, MAX_DEPTH3, CWD_CHANGERS, TERMINAL_ESCAPE_RE, CONTROL_CHAR_RE, SHIELD_RULE, ACTION_RULE, LOOP_MAX_RECORDS, FINDING_TO_SIGNAL, SCAN_SIGNAL_WEIGHTS, LOOP_THRESHOLD_FOR_WASTE, MAX_BLAST_PATH, DESTRUCTIVE_OP_RE, SENSITIVE_PATH_RE, FILE_TOOLS, CANARY_MIN_LENGTH, MAX_TEXT, MAX_DEPTH4, MAX_JSON_PARSE, URL_DEPTH, B64_DEPTH, MIN_SEGMENT, SEPARATORS, stripSeparators, looksText, CANARY_DECODERS, VIEWS, LONG_OUTPUT_THRESHOLD_BYTES, CANONICAL_EXTRACTOR_VERSION, DEDUPE_PREVIEW_LEN, ENGINE_VERSION;
10374
10725
  var init_dist = __esm({
10375
10726
  "packages/policy-engine/dist/index.mjs"() {
10376
10727
  "use strict";
@@ -13684,6 +14035,7 @@ var init_dist = __esm({
13684
14035
  "sqlite3"
13685
14036
  ]);
13686
14037
  defaultExpansionOps = null;
14038
+ EMPTY_NAMES = /* @__PURE__ */ new Set();
13687
14039
  VARIABLE_READERS = /* @__PURE__ */ new Set([
13688
14040
  "read",
13689
14041
  "mapfile",
@@ -13696,6 +14048,7 @@ var init_dist = __esm({
13696
14048
  "readonly",
13697
14049
  "printf"
13698
14050
  ]);
14051
+ PLAIN_PROGRAM_NAME = /^[A-Za-z_][\w.+-]*$/;
13699
14052
  FS_READ_TOOLS = /* @__PURE__ */ new Set([
13700
14053
  "cat",
13701
14054
  "less",
@@ -14590,6 +14943,8 @@ var init_dist = __esm({
14590
14943
  AND_OR_OPS: new Set([and, or].filter((o) => o !== null))
14591
14944
  };
14592
14945
  });
14946
+ ARRAY_MARK = "array";
14947
+ ARRAY_SEP = "";
14593
14948
  MAX_CALL_DEPTH2 = 4;
14594
14949
  MAX_CALL_WALKS2 = 256;
14595
14950
  callDepth = 0;
@@ -17927,7 +18282,22 @@ zoo`.split("\n");
17927
18282
  found;
17928
18283
  /** Aliases defined earlier in this command: name -> value (null = dynamic). */
17929
18284
  aliases = /* @__PURE__ */ new Map();
18285
+ /** node9Firewall#385: variables assigned a tree-top word (`scope="$PWD"`,
18286
+ * `root=$(git rev-parse --show-toplevel)`, `DIR=$(pwd)`), so the
18287
+ * variable reads as the top where it is used. */
18288
+ treeTopVars = /* @__PURE__ */ new Set();
18289
+ /** node9Firewall#385 (c): a string a script builds with `+=` from
18290
+ * literals (`mode=--ha; mode+=rd`). The jail's table does not follow an
18291
+ * append (JAIL-14a); the git rules read the built word, since a flag
18292
+ * spelled in two halves is still that flag. name -> value, null once a
18293
+ * half is unknown. */
18294
+ appended = /* @__PURE__ */ new Map();
17930
18295
  payloads = /* @__PURE__ */ new Set();
18296
+ /** node9Firewall#385 (a): the text this command writes to each path
18297
+ * (`cat > /tmp/x.sh <<'SH' ... SH`, `echo '...' > f`, `>>` appends), so
18298
+ * a later statement that runs the path (`bash f`, `sh f`, `./f`) is
18299
+ * judged on what it runs. null once a write to the path is unknown. */
18300
+ written = /* @__PURE__ */ new Map();
17931
18301
  /** `for NAME in a b c`: the literal items, or null when one is dynamic. */
17932
18302
  loopVars = /* @__PURE__ */ new Map();
17933
18303
  /** Walk one command string. Returns false when it did not parse. */
@@ -17946,6 +18316,25 @@ zoo`.split("\n");
17946
18316
  const type = n.kind;
17947
18317
  if (type === "Stmt") {
17948
18318
  this.visitHeredocShell(n, depth);
18319
+ this.visitPipeline(n, depth);
18320
+ this.visitHookWrite(n);
18321
+ this.recordScriptWrite(n);
18322
+ this.visitWrittenScript(n, depth);
18323
+ return true;
18324
+ }
18325
+ if (type === "Assign" && n.Name?.Value && n.Value) {
18326
+ const name = n.Name.Value;
18327
+ if (n.Append) {
18328
+ const prev = this.appended.has(name) ? this.appended.get(name) : resolveWordLiteral(n.Value) === null ? null : void 0;
18329
+ const base22 = prev === void 0 ? recordedExpansion(name) : prev;
18330
+ const piece = resolveWordLiteral(n.Value);
18331
+ this.appended.set(name, typeof base22 === "string" && piece !== null ? base22 + piece : null);
18332
+ this.treeTopVars.delete(name);
18333
+ return true;
18334
+ }
18335
+ this.appended.delete(name);
18336
+ if (treeTopWord(n.Value, this.treeTopVars)) this.treeTopVars.add(name);
18337
+ else this.treeTopVars.delete(name);
17949
18338
  return true;
17950
18339
  }
17951
18340
  if (type === "ForClause") {
@@ -17974,13 +18363,13 @@ zoo`.split("\n");
17974
18363
  const param = plainParamName(rawArgs[loopArg]);
17975
18364
  for (const item of this.loopVars.get(param.name)) {
17976
18365
  const words2 = rawArgs.flatMap(
17977
- (a, i) => i === loopArg ? param.quoted ? [item] : splitFields(item) : resolveGitWords(a)
18366
+ (a, i) => i === loopArg ? param.quoted ? [item] : splitFields(item) : resolveGitWords(a, this.treeTopVars, this.appended)
17978
18367
  );
17979
18368
  this.visitWords(words2, new Map(prefixEnv), depth, false);
17980
18369
  }
17981
18370
  return true;
17982
18371
  }
17983
- const words = rawArgs.flatMap((a) => resolveGitWords(a));
18372
+ const words = rawArgs.flatMap((a) => resolveGitWords(a, this.treeTopVars, this.appended));
17984
18373
  this.visitWords(words, prefixEnv, depth, false);
17985
18374
  return true;
17986
18375
  }
@@ -18005,6 +18394,23 @@ zoo`.split("\n");
18005
18394
  if (f) this.found(f);
18006
18395
  return;
18007
18396
  }
18397
+ if (headWord === "rm" || headWord === "unlink") {
18398
+ if (words.slice(head + 1).some((w) => w !== null && isLiveHookFile(w)))
18399
+ this.found(hooksBypassed(shownOf(words)));
18400
+ return;
18401
+ }
18402
+ if (headWord === "mv") {
18403
+ const operands2 = words.slice(head + 1).filter((w) => w === null || !w.startsWith("-"));
18404
+ const src = operands2[0];
18405
+ if (typeof src === "string" && isLiveHookFile(src)) this.found(hooksBypassed(shownOf(words)));
18406
+ return;
18407
+ }
18408
+ if (headWord === "perl") {
18409
+ const code2 = words.slice(head + 1).find((w) => w !== null && /\brename\b/.test(w));
18410
+ const m = code2 ? /rename\s*\(?\s*q[qw]?\s*[(\[{<'"]([^)\]}>'"]+)/.exec(code2) : null;
18411
+ if (m && isLiveHookFile(m[1])) this.found(hooksBypassed(shownOf(words)));
18412
+ return;
18413
+ }
18008
18414
  if (depth < MAX_PAYLOAD_DEPTH2) {
18009
18415
  const payload = literalShellPayload(words, baseWord(words[0]));
18010
18416
  if (payload !== null) this.runPayload(payload, depth);
@@ -18015,6 +18421,127 @@ zoo`.split("\n");
18015
18421
  this.payloads.add(payload);
18016
18422
  this.run(payload, depth + 1);
18017
18423
  }
18424
+ /**
18425
+ * node9Firewall#385: a pipeline whose last stage reads its pathspec from
18426
+ * stdin (`git add --pathspec-from-file=-`) or runs git over the names it
18427
+ * is fed (`xargs -0 git add --`), with a producer that is one literal
18428
+ * (`git rev-parse --show-toplevel`, `printf '%s\0' .`): the literal is
18429
+ * the operand, and the call is judged with it.
18430
+ */
18431
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
18432
+ visitPipeline(stmt, depth) {
18433
+ const cmd = stmt.Cmd;
18434
+ if (!cmd || cmd.kind !== "BinaryCmd" || !isPipeOp(cmd.Op)) return;
18435
+ const stages = [];
18436
+ let cur = cmd;
18437
+ while (cur?.kind === "BinaryCmd" && isPipeOp(cur.Op)) {
18438
+ stages.unshift(cur.Y);
18439
+ const left = cur.X;
18440
+ if (left?.Cmd?.kind === "BinaryCmd" && isPipeOp(left.Cmd.Op)) cur = left.Cmd;
18441
+ else {
18442
+ stages.unshift(left);
18443
+ break;
18444
+ }
18445
+ }
18446
+ if (stages.length < 2) return;
18447
+ const producer = stages[stages.length - 2]?.Cmd;
18448
+ const consumer = stages[stages.length - 1]?.Cmd;
18449
+ if (!producer || !consumer || producer.kind !== "CallExpr" || consumer.kind !== "CallExpr")
18450
+ return;
18451
+ const fed = producedLiteral(producer);
18452
+ if (fed === null) return;
18453
+ const words = (consumer.Args || []).flatMap(
18454
+ (a) => resolveGitWords(a, this.treeTopVars, this.appended)
18455
+ );
18456
+ const head = unwrapCommandHead(words);
18457
+ const viaXargs = words.slice(0, head).some((w) => baseWord(w) === "xargs");
18458
+ const fromFile = words.findIndex(
18459
+ (w) => w === "--pathspec-from-file=-" || w === "--pathspec-from-file"
18460
+ );
18461
+ if (!viaXargs && fromFile < 0) return;
18462
+ let fedWords;
18463
+ if (fromFile >= 0) {
18464
+ fedWords = words.filter(
18465
+ (w, k) => !(k === fromFile || w === "-" && words[k - 1] === "--pathspec-from-file")
18466
+ );
18467
+ fedWords.push(fed);
18468
+ } else fedWords = [...words, fed];
18469
+ this.visitWords(fedWords, /* @__PURE__ */ new Map(), depth, false);
18470
+ }
18471
+ /**
18472
+ * node9Firewall#385 (e): a hook file written with a body that checks
18473
+ * nothing (`cat > .githooks/pre-commit <<'SH' exit 0 SH`, `echo 'exit 0'
18474
+ * > .git/hooks/pre-push`, `: > .githooks/pre-commit`). A real body (a
18475
+ * test, a linter) is ordinary work on the hooks.
18476
+ */
18477
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
18478
+ visitHookWrite(stmt) {
18479
+ const cmd = stmt.Cmd;
18480
+ if (!cmd || cmd.kind !== "CallExpr") return;
18481
+ const redirs = stmt.Redirs || [];
18482
+ const target = redirs.find((r) => r?.Word && !r.Hdoc && (r.N == null || r.N.Value === "1"));
18483
+ if (!target) return;
18484
+ const path210 = resolveWordLiteral(target.Word);
18485
+ if (path210 === null || !isLiveHookFile(path210)) return;
18486
+ const hdoc = redirs.find((r) => r?.Hdoc);
18487
+ const words = (cmd.Args || []).map((a) => resolveWordLiteral(a));
18488
+ const head = baseWord(words[0]);
18489
+ let body = null;
18490
+ if (hdoc) body = resolveWordLiteral(hdoc.Hdoc);
18491
+ else if (head === ":" || head === "true" || words.length === 0) body = "";
18492
+ else if (head === "echo" || head === "printf") {
18493
+ const rest = words.slice(1);
18494
+ body = rest.some((w) => w === null) ? null : rest.filter((w) => !/^-[neE]+$/.test(w)).join(" ");
18495
+ if (body !== null) body = body.replace(/\\n/g, "\n");
18496
+ }
18497
+ if (body !== null && hookBodyIsEmpty(body)) this.found(hooksBypassed(shownOf(words)));
18498
+ }
18499
+ /** A statement that writes a literal text to a path, by redirect. */
18500
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
18501
+ recordScriptWrite(stmt) {
18502
+ const cmd = stmt.Cmd;
18503
+ if (!cmd || cmd.kind !== "CallExpr") return;
18504
+ const redirs = stmt.Redirs || [];
18505
+ const target = redirs.find((r) => r?.Word && !r.Hdoc && (r.N == null || r.N.Value === "1"));
18506
+ if (!target) return;
18507
+ const path210 = resolveWordLiteral(target.Word);
18508
+ if (path210 === null) return;
18509
+ const append = /^>>/.test(target.Op?.toString() ?? "") || redirOpIsAppend(target);
18510
+ const hdoc = redirs.find((r) => r?.Hdoc);
18511
+ const words = (cmd.Args || []).map((a) => resolveWordLiteral(a));
18512
+ const head = baseWord(words[0]);
18513
+ let text = null;
18514
+ if (hdoc && (head === "cat" || head === "tee")) text = resolveWordLiteral(hdoc.Hdoc);
18515
+ else if (head === "echo" || head === "printf") {
18516
+ const rest = words.slice(1);
18517
+ text = rest.some((w) => w === null) ? null : rest.filter((w) => !/^-[neE]+$/.test(w)).join(" ");
18518
+ if (text !== null && head === "printf") text = text.replace(/\\n/g, "\n");
18519
+ } else return;
18520
+ const prev = this.written.get(path210);
18521
+ if (text === null || append && prev === null) this.written.set(path210, null);
18522
+ else this.written.set(path210, append && typeof prev === "string" ? `${prev}
18523
+ ${text}` : text);
18524
+ }
18525
+ /** A statement that runs a path this command wrote: the text is the script. */
18526
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
18527
+ visitWrittenScript(stmt, depth) {
18528
+ const cmd = stmt.Cmd;
18529
+ if (!cmd || cmd.kind !== "CallExpr" || this.written.size === 0 || depth >= MAX_PAYLOAD_DEPTH2)
18530
+ return;
18531
+ const words = (cmd.Args || []).map((a) => resolveWordLiteral(a));
18532
+ const head = unwrapCommandHead(words);
18533
+ const name = baseWord(words[head]);
18534
+ let ran = null;
18535
+ if (PAYLOAD_SHELLS.has(name) && name !== "eval") {
18536
+ const operand = words.slice(head + 1).find((w) => w === null || !w.startsWith("-"));
18537
+ if (typeof operand === "string") ran = operand;
18538
+ } else if (typeof words[head] === "string" && this.written.has(words[head])) {
18539
+ ran = words[head];
18540
+ }
18541
+ if (ran === null) return;
18542
+ const text = this.written.get(ran);
18543
+ if (typeof text === "string") this.runPayload(text, depth);
18544
+ }
18018
18545
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
18019
18546
  visitHeredocShell(stmt, depth) {
18020
18547
  const cmd = stmt.Cmd;
@@ -18166,6 +18693,8 @@ zoo`.split("\n");
18166
18693
  }
18167
18694
  };
18168
18695
  POSITIONAL2 = "\0positional";
18696
+ NO_VARS = /* @__PURE__ */ new Set();
18697
+ NO_APPENDS = /* @__PURE__ */ new Map();
18169
18698
  FORCE_PUSH = {
18170
18699
  checkId: "commands.git-destructive",
18171
18700
  ruleName: "review-force-push",
@@ -18224,6 +18753,8 @@ zoo`.split("\n");
18224
18753
  "--cleanup"
18225
18754
  ]);
18226
18755
  CLEAN_LONG_VALUE = /* @__PURE__ */ new Set(["--exclude"]);
18756
+ RESTORE_LONG_VALUE = /* @__PURE__ */ new Set(["--source", "--pathspec-from-file"]);
18757
+ CHECKOUT_LONG_VALUE = /* @__PURE__ */ new Set(["--pathspec-from-file", "--orphan", "--conflict"]);
18227
18758
  HOOKS_BYPASS = {
18228
18759
  checkId: "commands.git-hooks-bypass",
18229
18760
  ruleName: "review-git-hooks-bypass",
@@ -18249,6 +18780,7 @@ zoo`.split("\n");
18249
18780
  "--gpg-sign"
18250
18781
  ]);
18251
18782
  CHMOD_OPTION_RE = /^-[RvcfHLP]+$/;
18783
+ _appendOp = null;
18252
18784
  STAGE_ALL = {
18253
18785
  checkId: "commands.git-stage-all",
18254
18786
  ruleName: "review-git-stage-all",
@@ -18373,7 +18905,7 @@ zoo`.split("\n");
18373
18905
  this.visitStmt(n, depth);
18374
18906
  return true;
18375
18907
  }
18376
- if (type === "BinaryCmd" && isPipeOp(n.Op)) {
18908
+ if (type === "BinaryCmd" && isPipeOp2(n.Op)) {
18377
18909
  this.visitPipeline(n);
18378
18910
  return true;
18379
18911
  }
@@ -18474,7 +19006,7 @@ zoo`.split("\n");
18474
19006
  const stages = [];
18475
19007
  const flatten = (s) => {
18476
19008
  const c = s?.Cmd;
18477
- if (c && c.kind === "BinaryCmd" && isPipeOp(c.Op)) {
19009
+ if (c && c.kind === "BinaryCmd" && isPipeOp2(c.Op)) {
18478
19010
  flatten(c.X);
18479
19011
  flatten(c.Y);
18480
19012
  } else stages.push(s);
@@ -19705,7 +20237,7 @@ zoo`.split("\n");
19705
20237
  { view: "separators-stripped", decoder: "separators", stripped: true }
19706
20238
  ];
19707
20239
  LONG_OUTPUT_THRESHOLD_BYTES = 100 * 1024;
19708
- CANONICAL_EXTRACTOR_VERSION = "canonical-v36";
20240
+ CANONICAL_EXTRACTOR_VERSION = "canonical-v37";
19709
20241
  DEDUPE_PREVIEW_LEN = 120;
19710
20242
  ENGINE_VERSION = "1.4.0";
19711
20243
  }
@@ -20211,11 +20743,18 @@ function slug(s) {
20211
20743
  return s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "rule";
20212
20744
  }
20213
20745
  function pathToRegexFragment(rawPath) {
20214
- const tail = rawPath.trim().replace(/^~[\\/]?/, "").replace(/^\$\{?HOME\}?[\\/]?/, "").replace(/^\/(?:home|Users)\/[^\\/]+[\\/]?/, "").replace(/^[A-Za-z]:[\\/]Users[\\/][^\\/]+[\\/]?/, "").replace(/^[\\/]+/, "").replace(/[\\/]+$/, "");
20746
+ const trimmed = rawPath.trim();
20747
+ const tail = trimmed.replace(/^~[\\/]?/, "").replace(/^\$\{?HOME\}?[\\/]?/, "").replace(/^\/(?:home|Users)\/[^\\/]+[\\/]?/, "").replace(/^[A-Za-z]:[\\/]Users[\\/][^\\/]+[\\/]?/, "").replace(/^[\\/]+/, "").replace(/[\\/]+$/, "");
20215
20748
  const segments2 = tail.split(/[\\/]+/).filter(Boolean).map(escapeRegex);
20216
20749
  if (segments2.length === 0) return "";
20750
+ if (isAbsoluteOutsideHome(trimmed)) {
20751
+ return `(^|[^A-Za-z0-9_.~\\/-])/${segments2.join(SEP)}(${B}|$)`;
20752
+ }
20217
20753
  return `(^|${B})${segments2.join(SEP)}(${B}|$)`;
20218
20754
  }
20755
+ function isAbsoluteOutsideHome(p) {
20756
+ return p.startsWith("/") && !/^\/(?:home|Users|root)(?:\/|$)/.test(p);
20757
+ }
20219
20758
  function toolRule(tool, verdict, reason) {
20220
20759
  return {
20221
20760
  name: `${verdict}-${slug(tool)}`,
@@ -20636,13 +21175,115 @@ var init_managed = __esm({
20636
21175
  }
20637
21176
  });
20638
21177
 
21178
+ // src/shields/jail.ts
21179
+ function jailStorePath() {
21180
+ return import_path3.default.join(import_os3.default.homedir(), ".node9", "jail-paths.json");
21181
+ }
21182
+ function readJailPaths() {
21183
+ let text;
21184
+ try {
21185
+ text = import_fs3.default.readFileSync(jailStorePath(), "utf8");
21186
+ } catch (err2) {
21187
+ if (err2.code === "ENOENT") return [];
21188
+ throw err2;
21189
+ }
21190
+ let parsed;
21191
+ try {
21192
+ parsed = JSON.parse(text);
21193
+ } catch {
21194
+ throw new Error(`${jailStorePath()} is not valid JSON \u2014 fix it before changing the jail.`);
21195
+ }
21196
+ if (!Array.isArray(parsed.paths)) return [];
21197
+ return parsed.paths.filter(
21198
+ (p) => !!p && typeof p.path === "string" && (p.verdict === "block" || p.verdict === "review")
21199
+ );
21200
+ }
21201
+ function writeJailPaths(paths) {
21202
+ const p = jailStorePath();
21203
+ import_fs3.default.mkdirSync(import_path3.default.dirname(p), { recursive: true });
21204
+ import_fs3.default.writeFileSync(p, JSON.stringify({ paths }, null, 2) + "\n", { mode: 384 });
21205
+ }
21206
+ function addJailPath(rawPath, verdict) {
21207
+ const norm = rawPath.trim();
21208
+ const fragment = pathToRegexFragment(norm);
21209
+ if (!fragment) {
21210
+ throw new Error(
21211
+ `"${rawPath}" is too broad to jail \u2014 give a specific path (e.g. ~/.gmail-mcp), not a home or root directory.`
21212
+ );
21213
+ }
21214
+ const rejection = validateRegex(fragment);
21215
+ if (rejection !== null) {
21216
+ throw new Error(
21217
+ `"${rawPath}" cannot be jailed: the generated match pattern was rejected by the policy engine (${rejection}). Try a shorter or more specific path.`
21218
+ );
21219
+ }
21220
+ const next = [...readJailPaths().filter((p) => p.path !== norm), { path: norm, verdict }];
21221
+ writeJailPaths(next);
21222
+ return next;
21223
+ }
21224
+ function findJailedPath(candidate) {
21225
+ return findJailedPathIn(candidate, readJailPaths());
21226
+ }
21227
+ function findJailedPathIn(candidate, paths) {
21228
+ if (!candidate) return null;
21229
+ for (const entry of paths) {
21230
+ if (pathMatchesFragment(candidate, entry.path)) return entry;
21231
+ }
21232
+ return null;
21233
+ }
21234
+ function removeJailPath(rawPath) {
21235
+ const norm = rawPath.trim();
21236
+ const before = readJailPaths();
21237
+ const after = before.filter((p) => p.path !== norm);
21238
+ const removed = after.length !== before.length;
21239
+ if (removed) writeJailPaths(after);
21240
+ return { removed, paths: after };
21241
+ }
21242
+ function regenerateUserJail(paths) {
21243
+ const file2 = import_path3.default.join(USER_SHIELDS_DIR_PATH, `${USER_JAIL_SHIELD}.json`);
21244
+ if (paths.length === 0) {
21245
+ const active2 = readActiveShields();
21246
+ if (active2.includes(USER_JAIL_SHIELD)) {
21247
+ writeActiveShields(active2.filter((s) => s !== USER_JAIL_SHIELD));
21248
+ }
21249
+ try {
21250
+ import_fs3.default.rmSync(file2, { force: true });
21251
+ } catch {
21252
+ }
21253
+ return;
21254
+ }
21255
+ const def = buildShield({
21256
+ name: USER_JAIL_SHIELD,
21257
+ description: "User-added credential jail paths (node9 jail add)",
21258
+ blockPaths: paths.filter((p) => p.verdict === "block").map((p) => p.path),
21259
+ reviewPaths: paths.filter((p) => p.verdict === "review").map((p) => p.path)
21260
+ });
21261
+ installShield(USER_JAIL_SHIELD, def);
21262
+ const active = readActiveShields();
21263
+ if (!active.includes(USER_JAIL_SHIELD)) writeActiveShields([...active, USER_JAIL_SHIELD]);
21264
+ }
21265
+ var import_fs3, import_os3, import_path3, USER_JAIL_SHIELD;
21266
+ var init_jail = __esm({
21267
+ "src/shields/jail.ts"() {
21268
+ "use strict";
21269
+ init_cjs_shims();
21270
+ import_fs3 = __toESM(require("fs"));
21271
+ import_os3 = __toESM(require("os"));
21272
+ import_path3 = __toESM(require("path"));
21273
+ init_build();
21274
+ init_dist();
21275
+ init_shields();
21276
+ USER_JAIL_SHIELD = "user-jail";
21277
+ }
21278
+ });
21279
+
20639
21280
  // src/auth/trusted-hosts.ts
20640
21281
  function getTrustedHostsPath() {
20641
- return import_path3.default.join(import_os3.default.homedir(), ".node9", "trusted-hosts.json");
21282
+ return import_path4.default.join(import_os4.default.homedir(), ".node9", "trusted-hosts.json");
20642
21283
  }
20643
21284
  function readTrustedHosts() {
20644
21285
  try {
20645
- const raw = import_fs3.default.readFileSync(getTrustedHostsPath(), "utf8");
21286
+ const raw = import_fs4.default.readFileSync(getTrustedHostsPath(), "utf8");
20646
21287
  const parsed = JSON.parse(raw);
20647
21288
  return Array.isArray(parsed.hosts) ? parsed.hosts : [];
20648
21289
  } catch {
@@ -20651,7 +21292,7 @@ function readTrustedHosts() {
20651
21292
  }
20652
21293
  function getFileMtime() {
20653
21294
  try {
20654
- return import_fs3.default.statSync(getTrustedHostsPath()).mtimeMs;
21295
+ return import_fs4.default.statSync(getTrustedHostsPath()).mtimeMs;
20655
21296
  } catch {
20656
21297
  return 0;
20657
21298
  }
@@ -20668,10 +21309,10 @@ function getCachedHosts() {
20668
21309
  }
20669
21310
  function writeTrustedHosts(hosts) {
20670
21311
  const filePath = getTrustedHostsPath();
20671
- import_fs3.default.mkdirSync(import_path3.default.dirname(filePath), { recursive: true });
21312
+ import_fs4.default.mkdirSync(import_path4.default.dirname(filePath), { recursive: true });
20672
21313
  const tmp = filePath + ".node9-tmp";
20673
- import_fs3.default.writeFileSync(tmp, JSON.stringify({ hosts }, null, 2), { mode: 384 });
20674
- import_fs3.default.renameSync(tmp, filePath);
21314
+ import_fs4.default.writeFileSync(tmp, JSON.stringify({ hosts }, null, 2), { mode: 384 });
21315
+ import_fs4.default.renameSync(tmp, filePath);
20675
21316
  _cache = { hosts, expiry: Date.now() + CACHE_TTL_MS, mtime: getFileMtime() };
20676
21317
  }
20677
21318
  function addTrustedHost(host2) {
@@ -20716,14 +21357,14 @@ function isTrustedHost(host2) {
20716
21357
  getCachedHosts().map((entry) => entry.host)
20717
21358
  );
20718
21359
  }
20719
- var import_fs3, import_path3, import_os3, _cache, CACHE_TTL_MS;
21360
+ var import_fs4, import_path4, import_os4, _cache, CACHE_TTL_MS;
20720
21361
  var init_trusted_hosts = __esm({
20721
21362
  "src/auth/trusted-hosts.ts"() {
20722
21363
  "use strict";
20723
21364
  init_cjs_shims();
20724
- import_fs3 = __toESM(require("fs"));
20725
- import_path3 = __toESM(require("path"));
20726
- import_os3 = __toESM(require("os"));
21365
+ import_fs4 = __toESM(require("fs"));
21366
+ import_path4 = __toESM(require("path"));
21367
+ import_os4 = __toESM(require("os"));
20727
21368
  _cache = null;
20728
21369
  CACHE_TTL_MS = 5e3;
20729
21370
  }
@@ -21004,9 +21645,9 @@ function _resetConfigCache() {
21004
21645
  }
21005
21646
  function getGlobalSettings() {
21006
21647
  try {
21007
- const globalConfigPath2 = import_path4.default.join(import_os4.default.homedir(), ".node9", "config.json");
21008
- if (import_fs4.default.existsSync(globalConfigPath2)) {
21009
- const raw = JSON.parse(import_fs4.default.readFileSync(globalConfigPath2, "utf-8"));
21648
+ const globalConfigPath2 = import_path5.default.join(import_os5.default.homedir(), ".node9", "config.json");
21649
+ if (import_fs5.default.existsSync(globalConfigPath2)) {
21650
+ const raw = JSON.parse(import_fs5.default.readFileSync(globalConfigPath2, "utf-8"));
21010
21651
  const parsed = isV2File(raw) ? v2ToLegacy(raw).legacy : raw;
21011
21652
  const settings = parsed.settings || {};
21012
21653
  return {
@@ -21035,8 +21676,8 @@ function noteRejectedApiUrl(raw) {
21035
21676
  if (rejectedApiUrlsSeen.has(key)) return;
21036
21677
  rejectedApiUrlsSeen.add(key);
21037
21678
  try {
21038
- import_fs4.default.appendFileSync(
21039
- import_path4.default.join(import_os4.default.homedir(), ".node9", "hook-debug.log"),
21679
+ import_fs5.default.appendFileSync(
21680
+ import_path5.default.join(import_os5.default.homedir(), ".node9", "hook-debug.log"),
21040
21681
  `[${(/* @__PURE__ */ new Date()).toISOString()}] REFUSED apiUrl, using the default instead: ${String(raw).slice(0, 200).replace(/[\r\n]+/g, " ")}
21041
21682
  `
21042
21683
  );
@@ -21053,9 +21694,9 @@ function getCredentials() {
21053
21694
  };
21054
21695
  }
21055
21696
  try {
21056
- const credPath = import_path4.default.join(import_os4.default.homedir(), ".node9", "credentials.json");
21057
- if (import_fs4.default.existsSync(credPath)) {
21058
- const creds = JSON.parse(import_fs4.default.readFileSync(credPath, "utf-8"));
21697
+ const credPath = import_path5.default.join(import_os5.default.homedir(), ".node9", "credentials.json");
21698
+ if (import_fs5.default.existsSync(credPath)) {
21699
+ const creds = JSON.parse(import_fs5.default.readFileSync(credPath, "utf-8"));
21059
21700
  const profileName = process.env.NODE9_PROFILE || "default";
21060
21701
  const profile = creds[profileName];
21061
21702
  if (typeof profile?.apiKey === "string" && profile.apiKey.length > 0) {
@@ -21087,7 +21728,7 @@ function readRulesCacheResilient(cacheFile) {
21087
21728
  for (let attempt = 0; attempt < 3; attempt++) {
21088
21729
  let content;
21089
21730
  try {
21090
- content = import_fs4.default.readFileSync(cacheFile, "utf-8");
21731
+ content = import_fs5.default.readFileSync(cacheFile, "utf-8");
21091
21732
  existed = true;
21092
21733
  } catch (err2) {
21093
21734
  if (err2.code === "ENOENT") return {};
@@ -21102,10 +21743,10 @@ function readRulesCacheResilient(cacheFile) {
21102
21743
  }
21103
21744
  }
21104
21745
  if (existed || sawReadError) {
21105
- const backup = import_path4.default.join(import_path4.default.dirname(cacheFile), "rules-cache.last-good.json");
21746
+ const backup = import_path5.default.join(import_path5.default.dirname(cacheFile), "rules-cache.last-good.json");
21106
21747
  if (backup !== cacheFile) {
21107
21748
  try {
21108
- const raw = JSON.parse(import_fs4.default.readFileSync(backup, "utf-8"));
21749
+ const raw = JSON.parse(import_fs5.default.readFileSync(backup, "utf-8"));
21109
21750
  logCacheReadIssue(cacheFile, "RULES_CACHE_CORRUPT_USED_BACKUP");
21110
21751
  lastParsedRulesCache = raw;
21111
21752
  return raw;
@@ -21125,8 +21766,8 @@ function logCacheReadIssue(cacheFile, kind) {
21125
21766
  if (now - cacheReadLastLoggedAt < CACHE_LOG_REARM_MS) return;
21126
21767
  cacheReadLastLoggedAt = now;
21127
21768
  try {
21128
- import_fs4.default.appendFileSync(
21129
- import_path4.default.join(import_os4.default.homedir(), ".node9", "hook-debug.log"),
21769
+ import_fs5.default.appendFileSync(
21770
+ import_path5.default.join(import_os5.default.homedir(), ".node9", "hook-debug.log"),
21130
21771
  `[${(/* @__PURE__ */ new Date()).toISOString()}] ${kind} ${cacheFile}
21131
21772
  `
21132
21773
  );
@@ -21143,8 +21784,8 @@ function catalogSettingsFrom(settings, policy) {
21143
21784
  }
21144
21785
  function getConfig(cwd) {
21145
21786
  if (!cwd && cachedConfig) return cachedConfig;
21146
- const globalPath = import_path4.default.join(import_os4.default.homedir(), ".node9", "config.json");
21147
- const projectPath = import_path4.default.join(cwd ?? process.cwd(), "node9.config.json");
21787
+ const globalPath = import_path5.default.join(import_os5.default.homedir(), ".node9", "config.json");
21788
+ const projectPath = import_path5.default.join(cwd ?? process.cwd(), "node9.config.json");
21148
21789
  const globalConfig = tryLoadConfig(globalPath);
21149
21790
  const projectConfig = tryLoadConfig(projectPath, { project: true });
21150
21791
  const mergedSettings = {
@@ -21194,7 +21835,8 @@ function getConfig(cwd) {
21194
21835
  trustedHostsManaged: false,
21195
21836
  appPermissions: {},
21196
21837
  appLinks: {},
21197
- managedJailPaths: []
21838
+ managedJailPaths: [],
21839
+ jailPaths: []
21198
21840
  };
21199
21841
  const mergedEnvironments = { ...DEFAULT_CONFIG.environments };
21200
21842
  const rank = (v) => {
@@ -21389,7 +22031,7 @@ function getConfig(cwd) {
21389
22031
  let cloudMandatesEnforcement = false;
21390
22032
  let cloudMandatesAppPerm = false;
21391
22033
  {
21392
- const cacheFile = import_path4.default.join(import_os4.default.homedir(), ".node9", "rules-cache.json");
22034
+ const cacheFile = import_path5.default.join(import_os5.default.homedir(), ".node9", "rules-cache.json");
21393
22035
  try {
21394
22036
  const raw = readRulesCacheResilient(cacheFile);
21395
22037
  if (Array.isArray(raw.rules) && raw.rules.length > 0) {
@@ -21589,6 +22231,7 @@ function getConfig(cwd) {
21589
22231
  mergedPolicy.smartRules.push({ ...r, name: `org:${r.name}` });
21590
22232
  }
21591
22233
  mergedPolicy.managedJailPaths.push({ path: path114, verdict });
22234
+ mergedPolicy.jailPaths.push(path114);
21592
22235
  }
21593
22236
  }
21594
22237
  if (Array.isArray(mc.trustedHosts)) {
@@ -21664,6 +22307,11 @@ function getConfig(cwd) {
21664
22307
  }
21665
22308
  }
21666
22309
  mergedPolicy.appliedShields = appliedShields.sort();
22310
+ if (appliedShields.includes(USER_JAIL_SHIELD)) {
22311
+ for (const { path: path114 } of readJailPaths()) {
22312
+ if (!mergedPolicy.jailPaths.includes(path114)) mergedPolicy.jailPaths.push(path114);
22313
+ }
22314
+ }
21667
22315
  const existingAdvisoryNames = new Set(mergedPolicy.smartRules.map((r) => r.name));
21668
22316
  const cc = mergedPolicy.commandChecks ?? {};
21669
22317
  const advisoryKnobKey = (name) => {
@@ -21821,10 +22469,10 @@ function getConfig(cwd) {
21821
22469
  }
21822
22470
  function tryLoadConfig(filePath, options = {}) {
21823
22471
  v2ChecksByPath.delete(filePath);
21824
- if (!import_fs4.default.existsSync(filePath)) return null;
22472
+ if (!import_fs5.default.existsSync(filePath)) return null;
21825
22473
  let raw;
21826
22474
  try {
21827
- raw = JSON.parse(import_fs4.default.readFileSync(filePath, "utf-8"));
22475
+ raw = JSON.parse(import_fs5.default.readFileSync(filePath, "utf-8"));
21828
22476
  } catch (err2) {
21829
22477
  const msg = err2 instanceof Error ? err2.message : String(err2);
21830
22478
  process.stderr.write(
@@ -21922,18 +22570,19 @@ function applyUntrustedContent(cur, raw, replaceLists = false, isProject = false
21922
22570
  cur.roles = next;
21923
22571
  }
21924
22572
  }
21925
- var import_fs4, import_path4, import_os4, DANGEROUS_WORDS, RUNTIME_ONLY_CONFIG_KEYS, DEFAULT_CONFIG, RM_SAFE_PATH_PATTERN, VERDICT_ORDER, ADVISORY_SMART_RULES, cachedConfig, rejectedApiUrlsSeen, lastParsedRulesCache, CACHE_LOG_REARM_MS, cacheReadLastLoggedAt, v2ChecksByPath, KNOB_TO_CHECK;
22573
+ var import_fs5, import_path5, import_os5, DANGEROUS_WORDS, RUNTIME_ONLY_CONFIG_KEYS, DEFAULT_CONFIG, RM_SAFE_PATH_PATTERN, VERDICT_ORDER, ADVISORY_SMART_RULES, cachedConfig, rejectedApiUrlsSeen, lastParsedRulesCache, CACHE_LOG_REARM_MS, cacheReadLastLoggedAt, v2ChecksByPath, KNOB_TO_CHECK;
21926
22574
  var init_config = __esm({
21927
22575
  "src/config/index.ts"() {
21928
22576
  "use strict";
21929
22577
  init_cjs_shims();
21930
- import_fs4 = __toESM(require("fs"));
21931
- import_path4 = __toESM(require("path"));
21932
- import_os4 = __toESM(require("os"));
22578
+ import_fs5 = __toESM(require("fs"));
22579
+ import_path5 = __toESM(require("path"));
22580
+ import_os5 = __toESM(require("os"));
21933
22581
  init_config_schema();
21934
22582
  init_shields();
21935
22583
  init_managed();
21936
22584
  init_build();
22585
+ init_jail();
21937
22586
  init_trusted_hosts();
21938
22587
  init_dist();
21939
22588
  init_dist();
@@ -22202,7 +22851,8 @@ var init_config = __esm({
22202
22851
  trustedHostsManaged: false,
22203
22852
  appPermissions: {},
22204
22853
  appLinks: {},
22205
- managedJailPaths: []
22854
+ managedJailPaths: [],
22855
+ jailPaths: []
22206
22856
  },
22207
22857
  environments: {}
22208
22858
  };
@@ -22295,18 +22945,18 @@ function readCapped(file2, maxBytes, openFlags = "r") {
22295
22945
  if (maxBytes < 0) return null;
22296
22946
  let fd;
22297
22947
  try {
22298
- fd = import_fs5.default.openSync(file2, openFlags);
22948
+ fd = import_fs6.default.openSync(file2, openFlags);
22299
22949
  } catch {
22300
22950
  return null;
22301
22951
  }
22302
22952
  try {
22303
- const st = import_fs5.default.fstatSync(fd);
22953
+ const st = import_fs6.default.fstatSync(fd);
22304
22954
  if (!st.isFile()) return null;
22305
22955
  const want = Math.min(st.size, maxBytes + 1);
22306
22956
  const buf = Buffer.alloc(want);
22307
22957
  let read3 = 0;
22308
22958
  while (read3 < want) {
22309
- const n = import_fs5.default.readSync(fd, buf, read3, want - read3, read3);
22959
+ const n = import_fs6.default.readSync(fd, buf, read3, want - read3, read3);
22310
22960
  if (n <= 0) break;
22311
22961
  read3 += n;
22312
22962
  }
@@ -22318,7 +22968,7 @@ function readCapped(file2, maxBytes, openFlags = "r") {
22318
22968
  return null;
22319
22969
  } finally {
22320
22970
  try {
22321
- import_fs5.default.closeSync(fd);
22971
+ import_fs6.default.closeSync(fd);
22322
22972
  } catch {
22323
22973
  }
22324
22974
  }
@@ -22327,12 +22977,12 @@ function readCappedText(file2, maxBytes, openFlags = "r") {
22327
22977
  const r = readCapped(file2, maxBytes, openFlags);
22328
22978
  return r ? { text: r.bytes.toString("utf8"), truncated: r.truncated } : null;
22329
22979
  }
22330
- var import_fs5;
22980
+ var import_fs6;
22331
22981
  var init_read_capped = __esm({
22332
22982
  "src/utils/read-capped.ts"() {
22333
22983
  "use strict";
22334
22984
  init_cjs_shims();
22335
- import_fs5 = __toESM(require("fs"));
22985
+ import_fs6 = __toESM(require("fs"));
22336
22986
  }
22337
22987
  });
22338
22988
 
@@ -22341,25 +22991,25 @@ function scanFilePath(filePath, cwd = process.cwd(), options = {}) {
22341
22991
  if (!filePath) return null;
22342
22992
  let resolved;
22343
22993
  try {
22344
- const absolute2 = import_path5.default.resolve(cwd, filePath);
22345
- resolved = import_fs6.default.realpathSync.native(absolute2);
22994
+ const absolute2 = import_path6.default.resolve(cwd, filePath);
22995
+ resolved = import_fs7.default.realpathSync.native(absolute2);
22346
22996
  } catch (err2) {
22347
22997
  const code2 = err2.code;
22348
22998
  if (code2 === "ENOENT" || code2 === "ENOTDIR") {
22349
- resolved = import_path5.default.resolve(cwd, filePath);
22999
+ resolved = import_path6.default.resolve(cwd, filePath);
22350
23000
  } else {
22351
23001
  return sensitivePathMatch(filePath);
22352
23002
  }
22353
23003
  }
22354
23004
  return matchSensitivePath2(resolved, filePath, options);
22355
23005
  }
22356
- var import_fs6, import_path5;
23006
+ var import_fs7, import_path6;
22357
23007
  var init_dlp = __esm({
22358
23008
  "src/dlp.ts"() {
22359
23009
  "use strict";
22360
23010
  init_cjs_shims();
22361
- import_fs6 = __toESM(require("fs"));
22362
- import_path5 = __toESM(require("path"));
23011
+ import_fs7 = __toESM(require("fs"));
23012
+ import_path6 = __toESM(require("path"));
22363
23013
  init_dist();
22364
23014
  init_dist();
22365
23015
  }
@@ -22586,24 +23236,24 @@ var init_gate_order = __esm({
22586
23236
  // src/policy/file-access.ts
22587
23237
  function absoluteSpelling(p, cwd, home) {
22588
23238
  if (p === "~" || p.startsWith("~/")) p = home + p.slice(1);
22589
- return import_path6.default.isAbsolute(p) ? p : `${cwd}/${p}`;
23239
+ return import_path7.default.isAbsolute(p) ? p : `${cwd}/${p}`;
22590
23240
  }
22591
23241
  function physical(p, followFinal, links = 0) {
22592
23242
  if (links > 40) throw new Error("Too many symbolic links");
22593
23243
  if (!followFinal) {
22594
- const parent = physical(import_path6.default.dirname(p), true, links);
22595
- return { path: `${parent.path}/${import_path6.default.basename(p)}`, missing: parent.missing };
23244
+ const parent = physical(import_path7.default.dirname(p), true, links);
23245
+ return { path: `${parent.path}/${import_path7.default.basename(p)}`, missing: parent.missing };
22596
23246
  }
22597
23247
  try {
22598
- return { path: import_fs7.default.realpathSync.native(p), missing: false };
23248
+ return { path: import_fs8.default.realpathSync.native(p), missing: false };
22599
23249
  } catch (error) {
22600
23250
  const code2 = error.code;
22601
23251
  if (!["ENOENT", "ENOTDIR"].includes(code2 ?? "")) throw error;
22602
23252
  try {
22603
- if (import_fs7.default.lstatSync(p).isSymbolicLink()) {
22604
- const target = import_fs7.default.readlinkSync(p);
23253
+ if (import_fs8.default.lstatSync(p).isSymbolicLink()) {
23254
+ const target = import_fs8.default.readlinkSync(p);
22605
23255
  return physical(
22606
- import_path6.default.isAbsolute(target) ? target : `${import_path6.default.dirname(p)}/${target}`,
23256
+ import_path7.default.isAbsolute(target) ? target : `${import_path7.default.dirname(p)}/${target}`,
22607
23257
  true,
22608
23258
  links + 1
22609
23259
  );
@@ -22612,13 +23262,13 @@ function physical(p, followFinal, links = 0) {
22612
23262
  if (!["ENOENT", "ENOTDIR"].includes(linkError.code ?? ""))
22613
23263
  throw linkError;
22614
23264
  }
22615
- const parent = import_path6.default.dirname(p);
23265
+ const parent = import_path7.default.dirname(p);
22616
23266
  if (parent === p) return { path: p, missing: true };
22617
23267
  const resolved = physical(parent, true, links);
22618
- return { path: `${resolved.path}/${import_path6.default.basename(p)}`, missing: true };
23268
+ return { path: `${resolved.path}/${import_path7.default.basename(p)}`, missing: true };
22619
23269
  }
22620
23270
  }
22621
- function prepareFileAccesses(tool, args, cwd = process.cwd(), inspection = {}, home = import_os5.default.homedir()) {
23271
+ function prepareFileAccesses(tool, args, cwd = process.cwd(), inspection = {}, home = import_os6.default.homedir()) {
22622
23272
  const context = { cwd, homeDirs: [home, "/root"] };
22623
23273
  const accesses = collectFileAccesses(tool, args, context, inspection);
22624
23274
  const handledPaths = /* @__PURE__ */ new Set();
@@ -22641,14 +23291,14 @@ function prepareFileAccesses(tool, args, cwd = process.cwd(), inspection = {}, h
22641
23291
  const raw = absoluteSpelling(access.path, access.cwd ?? cwd, home);
22642
23292
  if (access.directoryTarget) {
22643
23293
  try {
22644
- if (!import_fs7.default.statSync(absoluteSpelling(access.directoryTarget, access.cwd ?? cwd, home)).isDirectory())
23294
+ if (!import_fs8.default.statSync(absoluteSpelling(access.directoryTarget, access.cwd ?? cwd, home)).isDirectory())
22645
23295
  continue;
22646
23296
  } catch {
22647
23297
  }
22648
23298
  }
22649
23299
  if (access.operation === "create") {
22650
23300
  try {
22651
- import_fs7.default.statSync(raw);
23301
+ import_fs8.default.statSync(raw);
22652
23302
  continue;
22653
23303
  } catch {
22654
23304
  }
@@ -22695,59 +23345,59 @@ function prepareFileAccesses(tool, args, cwd = process.cwd(), inspection = {}, h
22695
23345
  }
22696
23346
  return { ...context, accesses: prepared, handledPaths };
22697
23347
  }
22698
- var import_fs7, import_os5, import_path6;
23348
+ var import_fs8, import_os6, import_path7;
22699
23349
  var init_file_access = __esm({
22700
23350
  "src/policy/file-access.ts"() {
22701
23351
  "use strict";
22702
23352
  init_cjs_shims();
22703
- import_fs7 = __toESM(require("fs"));
22704
- import_os5 = __toESM(require("os"));
22705
- import_path6 = __toESM(require("path"));
23353
+ import_fs8 = __toESM(require("fs"));
23354
+ import_os6 = __toESM(require("os"));
23355
+ import_path7 = __toESM(require("path"));
22706
23356
  init_dist();
22707
23357
  }
22708
23358
  });
22709
23359
 
22710
23360
  // src/utils/atomic-write.ts
22711
23361
  function atomicWriteSync(filePath, data, options) {
22712
- const dir2 = import_path7.default.dirname(filePath);
22713
- if (!import_fs8.default.existsSync(dir2)) import_fs8.default.mkdirSync(dir2, { recursive: true });
23362
+ const dir2 = import_path8.default.dirname(filePath);
23363
+ if (!import_fs9.default.existsSync(dir2)) import_fs9.default.mkdirSync(dir2, { recursive: true });
22714
23364
  const tmpPath = `${filePath}.${(0, import_crypto8.randomUUID)()}.tmp`;
22715
23365
  try {
22716
- import_fs8.default.writeFileSync(tmpPath, data, options);
23366
+ import_fs9.default.writeFileSync(tmpPath, data, options);
22717
23367
  } catch (err2) {
22718
23368
  try {
22719
- import_fs8.default.unlinkSync(tmpPath);
23369
+ import_fs9.default.unlinkSync(tmpPath);
22720
23370
  } catch {
22721
23371
  }
22722
23372
  throw err2;
22723
23373
  }
22724
23374
  try {
22725
- import_fs8.default.renameSync(tmpPath, filePath);
23375
+ import_fs9.default.renameSync(tmpPath, filePath);
22726
23376
  } catch (err2) {
22727
23377
  try {
22728
- import_fs8.default.unlinkSync(tmpPath);
23378
+ import_fs9.default.unlinkSync(tmpPath);
22729
23379
  } catch {
22730
23380
  }
22731
23381
  throw err2;
22732
23382
  }
22733
23383
  }
22734
- var import_fs8, import_path7, import_crypto8;
23384
+ var import_fs9, import_path8, import_crypto8;
22735
23385
  var init_atomic_write = __esm({
22736
23386
  "src/utils/atomic-write.ts"() {
22737
23387
  "use strict";
22738
23388
  init_cjs_shims();
22739
- import_fs8 = __toESM(require("fs"));
22740
- import_path7 = __toESM(require("path"));
23389
+ import_fs9 = __toESM(require("fs"));
23390
+ import_path8 = __toESM(require("path"));
22741
23391
  import_crypto8 = require("crypto");
22742
23392
  }
22743
23393
  });
22744
23394
 
22745
23395
  // src/supply-chain/osv-index.ts
22746
23396
  function osvRoot() {
22747
- return import_path8.default.join(import_os6.default.homedir(), ".node9", "osv");
23397
+ return import_path9.default.join(import_os7.default.homedir(), ".node9", "osv");
22748
23398
  }
22749
23399
  function ecoDir(eco) {
22750
- return import_path8.default.join(osvRoot(), eco);
23400
+ return import_path9.default.join(osvRoot(), eco);
22751
23401
  }
22752
23402
  function indexKey(eco, name) {
22753
23403
  switch (eco) {
@@ -22769,11 +23419,11 @@ function shardOf(key) {
22769
23419
  return (0, import_crypto9.createHash)("sha1").update(key).digest("hex").slice(0, 2);
22770
23420
  }
22771
23421
  function shardPath(eco, shard) {
22772
- return import_path8.default.join(ecoDir(eco), "shards", `${shard}.json`);
23422
+ return import_path9.default.join(ecoDir(eco), "shards", `${shard}.json`);
22773
23423
  }
22774
23424
  function readMeta(eco) {
22775
23425
  try {
22776
- const m = JSON.parse(import_fs9.default.readFileSync(import_path8.default.join(ecoDir(eco), "meta.json"), "utf8"));
23426
+ const m = JSON.parse(import_fs10.default.readFileSync(import_path9.default.join(ecoDir(eco), "meta.json"), "utf8"));
22777
23427
  if (typeof m?.syncedAt !== "string" || typeof m?.lastModifiedMs !== "number") return null;
22778
23428
  return m;
22779
23429
  } catch {
@@ -22781,11 +23431,11 @@ function readMeta(eco) {
22781
23431
  }
22782
23432
  }
22783
23433
  function writeMeta(eco, meta) {
22784
- atomicWriteSync(import_path8.default.join(ecoDir(eco), "meta.json"), JSON.stringify(meta));
23434
+ atomicWriteSync(import_path9.default.join(ecoDir(eco), "meta.json"), JSON.stringify(meta));
22785
23435
  }
22786
23436
  function readShard(eco, shard) {
22787
23437
  try {
22788
- const s = JSON.parse(import_fs9.default.readFileSync(shardPath(eco, shard), "utf8"));
23438
+ const s = JSON.parse(import_fs10.default.readFileSync(shardPath(eco, shard), "utf8"));
22789
23439
  return s && typeof s === "object" && !Array.isArray(s) ? s : {};
22790
23440
  } catch {
22791
23441
  return {};
@@ -22880,14 +23530,14 @@ function entryCovers(entry, version2) {
22880
23530
  if (entry.versions?.includes(version2)) return true;
22881
23531
  return (entry.ranges ?? []).some((r) => inRange(version2, r));
22882
23532
  }
22883
- var import_fs9, import_os6, import_path8, import_crypto9, INDEX_FRESH_MS;
23533
+ var import_fs10, import_os7, import_path9, import_crypto9, INDEX_FRESH_MS;
22884
23534
  var init_osv_index = __esm({
22885
23535
  "src/supply-chain/osv-index.ts"() {
22886
23536
  "use strict";
22887
23537
  init_cjs_shims();
22888
- import_fs9 = __toESM(require("fs"));
22889
- import_os6 = __toESM(require("os"));
22890
- import_path8 = __toESM(require("path"));
23538
+ import_fs10 = __toESM(require("fs"));
23539
+ import_os7 = __toESM(require("os"));
23540
+ import_path9 = __toESM(require("path"));
22891
23541
  import_crypto9 = require("crypto");
22892
23542
  init_dist();
22893
23543
  init_atomic_write();
@@ -23229,14 +23879,14 @@ var init_registry = __esm({
23229
23879
 
23230
23880
  // src/supply-chain/local-resolve.ts
23231
23881
  function resolveInstalled(name, cwd) {
23232
- if (!cwd || !import_path9.default.isAbsolute(cwd)) return null;
23882
+ if (!cwd || !import_path10.default.isAbsolute(cwd)) return null;
23233
23883
  if (!/^(?:@[^/\\]+\/)?[^/\\]+$/.test(name)) return null;
23234
- let dir2 = import_path9.default.resolve(cwd);
23884
+ let dir2 = import_path10.default.resolve(cwd);
23235
23885
  for (let i = 0; i < MAX_LEVELS; i++) {
23236
- const pkgDir = import_path9.default.join(dir2, "node_modules", ...name.split("/"));
23237
- const manifest = import_path9.default.join(pkgDir, "package.json");
23886
+ const pkgDir = import_path10.default.join(dir2, "node_modules", ...name.split("/"));
23887
+ const manifest = import_path10.default.join(pkgDir, "package.json");
23238
23888
  try {
23239
- const raw = import_fs10.default.readFileSync(manifest, "utf8");
23889
+ const raw = import_fs11.default.readFileSync(manifest, "utf8");
23240
23890
  const parsed = JSON.parse(raw);
23241
23891
  if (typeof parsed.version === "string" && parsed.version.length > 0) {
23242
23892
  return { version: parsed.version, dir: pkgDir };
@@ -23244,19 +23894,19 @@ function resolveInstalled(name, cwd) {
23244
23894
  return null;
23245
23895
  } catch {
23246
23896
  }
23247
- const parent = import_path9.default.dirname(dir2);
23897
+ const parent = import_path10.default.dirname(dir2);
23248
23898
  if (parent === dir2) break;
23249
23899
  dir2 = parent;
23250
23900
  }
23251
23901
  return null;
23252
23902
  }
23253
- var import_fs10, import_path9, MAX_LEVELS, LOCAL_FIRST_MANAGERS;
23903
+ var import_fs11, import_path10, MAX_LEVELS, LOCAL_FIRST_MANAGERS;
23254
23904
  var init_local_resolve = __esm({
23255
23905
  "src/supply-chain/local-resolve.ts"() {
23256
23906
  "use strict";
23257
23907
  init_cjs_shims();
23258
- import_fs10 = __toESM(require("fs"));
23259
- import_path9 = __toESM(require("path"));
23908
+ import_fs11 = __toESM(require("fs"));
23909
+ import_path10 = __toESM(require("path"));
23260
23910
  MAX_LEVELS = 32;
23261
23911
  LOCAL_FIRST_MANAGERS = /* @__PURE__ */ new Set(["npx", "bunx", "npm exec"]);
23262
23912
  }
@@ -23364,7 +24014,7 @@ function checkInstalled(p, version2, misses) {
23364
24014
  }
23365
24015
  async function checkOne(p, cfg, misses, networkCapped, cwd) {
23366
24016
  if (p.ecosystem === "npm" && LOCAL_FIRST_MANAGERS.has(p.manager) && p.localCwd !== null && (p.raw === p.name || p.version)) {
23367
- const localCwd = cwd && import_path10.default.isAbsolute(cwd) ? (p.localCwd ?? []).reduce((dir2, step) => import_path10.default.resolve(dir2, step), cwd) : void 0;
24017
+ const localCwd = cwd && import_path11.default.isAbsolute(cwd) ? (p.localCwd ?? []).reduce((dir2, step) => import_path11.default.resolve(dir2, step), cwd) : void 0;
23368
24018
  const installed = resolveInstalled(p.name, localCwd);
23369
24019
  if (installed && (!p.version || p.version === installed.version))
23370
24020
  return checkInstalled(p, installed.version, misses);
@@ -23505,13 +24155,13 @@ async function runPackageCheck(toolName, args, cfg, cwd, opts = {}) {
23505
24155
  return { ...ALLOW, misses: [`package check error: ${err2?.message ?? err2}`] };
23506
24156
  }
23507
24157
  }
23508
- var import_picomatch4, import_path10, MAX_NETWORK_PACKAGES, ALLOW, KIND, SOFT2;
24158
+ var import_picomatch4, import_path11, MAX_NETWORK_PACKAGES, ALLOW, KIND, SOFT2;
23509
24159
  var init_check = __esm({
23510
24160
  "src/supply-chain/check.ts"() {
23511
24161
  "use strict";
23512
24162
  init_cjs_shims();
23513
24163
  import_picomatch4 = __toESM(require("picomatch"));
23514
- import_path10 = __toESM(require("path"));
24164
+ import_path11 = __toESM(require("path"));
23515
24165
  init_dist();
23516
24166
  init_osv_index();
23517
24167
  init_osv_online();
@@ -23537,29 +24187,29 @@ var init_check = __esm({
23537
24187
 
23538
24188
  // src/supply-chain/registries.ts
23539
24189
  function userFiles(home, env) {
23540
- const xdg = env.XDG_CONFIG_HOME || import_path11.default.join(home, ".config");
24190
+ const xdg = env.XDG_CONFIG_HOME || import_path12.default.join(home, ".config");
23541
24191
  return [
23542
- env.NPM_CONFIG_USERCONFIG || env.npm_config_userconfig || import_path11.default.join(home, ".npmrc"),
23543
- import_path11.default.join(home, ".yarnrc"),
23544
- import_path11.default.join(home, ".yarnrc.yml"),
23545
- import_path11.default.join(home, ".bunfig.toml"),
24192
+ env.NPM_CONFIG_USERCONFIG || env.npm_config_userconfig || import_path12.default.join(home, ".npmrc"),
24193
+ import_path12.default.join(home, ".yarnrc"),
24194
+ import_path12.default.join(home, ".yarnrc.yml"),
24195
+ import_path12.default.join(home, ".bunfig.toml"),
23546
24196
  env.PIP_CONFIG_FILE || "",
23547
- import_path11.default.join(xdg, "pip", "pip.conf"),
23548
- import_path11.default.join(home, ".pip", "pip.conf"),
24197
+ import_path12.default.join(xdg, "pip", "pip.conf"),
24198
+ import_path12.default.join(home, ".pip", "pip.conf"),
23549
24199
  "/etc/pip.conf",
23550
- env.VIRTUAL_ENV ? import_path11.default.join(env.VIRTUAL_ENV, "pip.conf") : "",
24200
+ env.VIRTUAL_ENV ? import_path12.default.join(env.VIRTUAL_ENV, "pip.conf") : "",
23551
24201
  env.UV_CONFIG_FILE || "",
23552
- import_path11.default.join(xdg, "uv", "uv.toml"),
24202
+ import_path12.default.join(xdg, "uv", "uv.toml"),
23553
24203
  "/etc/uv/uv.toml"
23554
24204
  ].filter(Boolean);
23555
24205
  }
23556
24206
  function projectFiles(cwd) {
23557
- if (!cwd || !import_path11.default.isAbsolute(cwd)) return [];
24207
+ if (!cwd || !import_path12.default.isAbsolute(cwd)) return [];
23558
24208
  const out = [];
23559
24209
  let dir2 = cwd;
23560
24210
  for (let i = 0; i < MAX_PARENT_DIRS; i++) {
23561
- for (const f of PROJECT_FILES) out.push(import_path11.default.join(dir2, f));
23562
- const parent = import_path11.default.dirname(dir2);
24211
+ for (const f of PROJECT_FILES) out.push(import_path12.default.join(dir2, f));
24212
+ const parent = import_path12.default.dirname(dir2);
23563
24213
  if (parent === dir2) break;
23564
24214
  dir2 = parent;
23565
24215
  }
@@ -23598,7 +24248,7 @@ function hostsInText(text, out) {
23598
24248
  }
23599
24249
  }
23600
24250
  }
23601
- function configuredRegistryHosts(cwd, env = process.env, home = import_os7.default.homedir()) {
24251
+ function configuredRegistryHosts(cwd, env = process.env, home = import_os8.default.homedir()) {
23602
24252
  const key = [
23603
24253
  cwd ?? "",
23604
24254
  home,
@@ -23620,21 +24270,21 @@ function configuredRegistryHosts(cwd, env = process.env, home = import_os7.defau
23620
24270
  const r = readCappedText(
23621
24271
  file2,
23622
24272
  MAX_CONFIG_BYTES,
23623
- import_fs11.default.constants.O_RDONLY | (import_fs11.default.constants.O_NONBLOCK ?? 0)
24273
+ import_fs12.default.constants.O_RDONLY | (import_fs12.default.constants.O_NONBLOCK ?? 0)
23624
24274
  );
23625
24275
  if (r) hostsInText(r.text, hosts);
23626
24276
  }
23627
24277
  cache3.set(key, { at: Date.now(), hosts });
23628
24278
  return hosts;
23629
24279
  }
23630
- var import_os7, import_path11, import_fs11, MAX_CONFIG_BYTES, MAX_PARENT_DIRS, TTL_MS, PROJECT_FILES, ENV_VARS, PATH_VARS, REGISTRY_KEY_RE, REGISTRY_SECTION_RE, URL_RE2, cache3;
24280
+ var import_os8, import_path12, import_fs12, MAX_CONFIG_BYTES, MAX_PARENT_DIRS, TTL_MS, PROJECT_FILES, ENV_VARS, PATH_VARS, REGISTRY_KEY_RE, REGISTRY_SECTION_RE, URL_RE2, cache3;
23631
24281
  var init_registries = __esm({
23632
24282
  "src/supply-chain/registries.ts"() {
23633
24283
  "use strict";
23634
24284
  init_cjs_shims();
23635
- import_os7 = __toESM(require("os"));
23636
- import_path11 = __toESM(require("path"));
23637
- import_fs11 = __toESM(require("fs"));
24285
+ import_os8 = __toESM(require("os"));
24286
+ import_path12 = __toESM(require("path"));
24287
+ import_fs12 = __toESM(require("fs"));
23638
24288
  init_dist();
23639
24289
  init_read_capped();
23640
24290
  MAX_CONFIG_BYTES = 256 * 1024;
@@ -23682,13 +24332,13 @@ var init_registries = __esm({
23682
24332
 
23683
24333
  // src/utils/provenance.ts
23684
24334
  function findInPath(cmd) {
23685
- if (import_path12.default.posix.isAbsolute(cmd)) return cmd;
24335
+ if (import_path13.default.posix.isAbsolute(cmd)) return cmd;
23686
24336
  const pathEnv = process.env.PATH ?? "";
23687
- for (const dir2 of pathEnv.split(import_path12.default.delimiter)) {
24337
+ for (const dir2 of pathEnv.split(import_path13.default.delimiter)) {
23688
24338
  if (!dir2) continue;
23689
- const full = import_path12.default.join(dir2, cmd);
24339
+ const full = import_path13.default.join(dir2, cmd);
23690
24340
  try {
23691
- import_fs12.default.accessSync(full, import_fs12.default.constants.X_OK);
24341
+ import_fs13.default.accessSync(full, import_fs13.default.constants.X_OK);
23692
24342
  return full;
23693
24343
  } catch {
23694
24344
  }
@@ -23699,7 +24349,7 @@ function _classifyPath(resolved, cwd) {
23699
24349
  if (cwd && resolved.startsWith(cwd + "/")) {
23700
24350
  return { trustLevel: "user", reason: "binary in project directory" };
23701
24351
  }
23702
- const osTmp = import_os8.default.tmpdir();
24352
+ const osTmp = import_os9.default.tmpdir();
23703
24353
  const allSuspect = osTmp ? [...SUSPECT_PREFIXES, osTmp] : SUSPECT_PREFIXES;
23704
24354
  if (allSuspect.some((p) => resolved === p || resolved.startsWith(p + "/"))) {
23705
24355
  return { trustLevel: "suspect", reason: `binary in temp directory: ${resolved}` };
@@ -23717,7 +24367,7 @@ function _classifyPath(resolved, cwd) {
23717
24367
  }
23718
24368
  function checkProvenance(cmd, cwd) {
23719
24369
  const bare = cmd.startsWith("./") ? cmd.slice(2) : cmd;
23720
- if (import_path12.default.posix.isAbsolute(bare)) {
24370
+ if (import_path13.default.posix.isAbsolute(bare)) {
23721
24371
  const early = _classifyPath(bare, cwd);
23722
24372
  if (early.trustLevel === "suspect") {
23723
24373
  return { resolvedPath: bare, ...early };
@@ -23733,7 +24383,7 @@ function checkProvenance(cmd, cwd) {
23733
24383
  reason: "binary not found in PATH"
23734
24384
  };
23735
24385
  }
23736
- resolved = import_fs12.default.realpathSync(found);
24386
+ resolved = import_fs13.default.realpathSync(found);
23737
24387
  } catch {
23738
24388
  return {
23739
24389
  resolvedPath: cmd,
@@ -23742,7 +24392,7 @@ function checkProvenance(cmd, cwd) {
23742
24392
  };
23743
24393
  }
23744
24394
  try {
23745
- const stat = import_fs12.default.statSync(resolved);
24395
+ const stat = import_fs13.default.statSync(resolved);
23746
24396
  if (stat.mode & 2) {
23747
24397
  return {
23748
24398
  resolvedPath: resolved,
@@ -23760,22 +24410,22 @@ function checkProvenance(cmd, cwd) {
23760
24410
  const classify2 = _classifyPath(resolved, cwd);
23761
24411
  return { resolvedPath: resolved, ...classify2 };
23762
24412
  }
23763
- var import_fs12, import_path12, import_os8, SYSTEM_PREFIXES, MANAGED_PREFIXES, USER_PREFIXES, SUSPECT_PREFIXES;
24413
+ var import_fs13, import_path13, import_os9, SYSTEM_PREFIXES, MANAGED_PREFIXES, USER_PREFIXES, SUSPECT_PREFIXES;
23764
24414
  var init_provenance2 = __esm({
23765
24415
  "src/utils/provenance.ts"() {
23766
24416
  "use strict";
23767
24417
  init_cjs_shims();
23768
- import_fs12 = __toESM(require("fs"));
23769
- import_path12 = __toESM(require("path"));
23770
- import_os8 = __toESM(require("os"));
24418
+ import_fs13 = __toESM(require("fs"));
24419
+ import_path13 = __toESM(require("path"));
24420
+ import_os9 = __toESM(require("os"));
23771
24421
  SYSTEM_PREFIXES = ["/usr/bin", "/usr/sbin", "/bin", "/sbin"];
23772
24422
  MANAGED_PREFIXES = ["/usr/local/bin", "/opt/homebrew", "/home/linuxbrew", "/nix/store"];
23773
24423
  USER_PREFIXES = [
23774
- import_path12.default.join(import_os8.default.homedir(), "bin"),
23775
- import_path12.default.join(import_os8.default.homedir(), ".local", "bin"),
23776
- import_path12.default.join(import_os8.default.homedir(), ".cargo", "bin"),
23777
- import_path12.default.join(import_os8.default.homedir(), ".npm-global", "bin"),
23778
- import_path12.default.join(import_os8.default.homedir(), ".volta", "bin")
24424
+ import_path13.default.join(import_os9.default.homedir(), "bin"),
24425
+ import_path13.default.join(import_os9.default.homedir(), ".local", "bin"),
24426
+ import_path13.default.join(import_os9.default.homedir(), ".cargo", "bin"),
24427
+ import_path13.default.join(import_os9.default.homedir(), ".npm-global", "bin"),
24428
+ import_path13.default.join(import_os9.default.homedir(), ".volta", "bin")
23779
24429
  ];
23780
24430
  SUSPECT_PREFIXES = ["/tmp", "/var/tmp", "/dev/shm"];
23781
24431
  }
@@ -23813,9 +24463,9 @@ async function evaluatePolicy2(toolName, args, agent, cwd, opts) {
23813
24463
  );
23814
24464
  }
23815
24465
  function isGitWorkTreeTop(dir2, cwd) {
23816
- if (!import_path13.default.isAbsolute(cwd)) return true;
24466
+ if (!import_path14.default.isAbsolute(cwd)) return true;
23817
24467
  try {
23818
- return import_fs13.default.existsSync(import_path13.default.join(import_path13.default.resolve(cwd, dir2), ".git"));
24468
+ return import_fs14.default.existsSync(import_path14.default.join(import_path14.default.resolve(cwd, dir2), ".git"));
23819
24469
  } catch {
23820
24470
  return true;
23821
24471
  }
@@ -23824,7 +24474,7 @@ function readAgentSettings(absPath) {
23824
24474
  const r = readCappedText(
23825
24475
  absPath,
23826
24476
  MAX_AGENT_SETTINGS_BYTES,
23827
- import_fs13.default.constants.O_RDONLY | (import_fs13.default.constants.O_NONBLOCK ?? 0)
24477
+ import_fs14.default.constants.O_RDONLY | (import_fs14.default.constants.O_NONBLOCK ?? 0)
23828
24478
  );
23829
24479
  return r && !r.truncated ? r.text : null;
23830
24480
  }
@@ -24017,9 +24667,9 @@ async function explainEngineVerdict(toolName, args, agent, config, prepared) {
24017
24667
  };
24018
24668
  }
24019
24669
  function explainWaterfall(config) {
24020
- const globalPath = import_path13.default.join(import_os9.default.homedir(), ".node9", "config.json");
24021
- const projectPath = import_path13.default.join(process.cwd(), "node9.config.json");
24022
- const credsPath = import_path13.default.join(import_os9.default.homedir(), ".node9", "credentials.json");
24670
+ const globalPath = import_path14.default.join(import_os10.default.homedir(), ".node9", "config.json");
24671
+ const projectPath = import_path14.default.join(process.cwd(), "node9.config.json");
24672
+ const credsPath = import_path14.default.join(import_os10.default.homedir(), ".node9", "credentials.json");
24023
24673
  const wsGoverned = config.policySource === "workspace";
24024
24674
  const waterfall = [
24025
24675
  {
@@ -24031,22 +24681,22 @@ function explainWaterfall(config) {
24031
24681
  {
24032
24682
  tier: 2,
24033
24683
  label: wsGoverned ? "Workspace config" : "Cloud policy",
24034
- status: import_fs13.default.existsSync(credsPath) ? "active" : "missing",
24035
- note: import_fs13.default.existsSync(credsPath) ? wsGoverned ? "authoritative \u2014 this machine follows the workspace configuration (app.node9.ai)" : "credentials found (not evaluated in explain mode)" : "not connected \u2014 run: node9 login"
24684
+ status: import_fs14.default.existsSync(credsPath) ? "active" : "missing",
24685
+ note: import_fs14.default.existsSync(credsPath) ? wsGoverned ? "authoritative \u2014 this machine follows the workspace configuration (app.node9.ai)" : "credentials found (not evaluated in explain mode)" : "not connected \u2014 run: node9 login"
24036
24686
  },
24037
24687
  {
24038
24688
  tier: 3,
24039
24689
  label: "Project config",
24040
- status: import_fs13.default.existsSync(projectPath) ? "active" : "missing",
24690
+ status: import_fs14.default.existsSync(projectPath) ? "active" : "missing",
24041
24691
  path: projectPath,
24042
- ...wsGoverned && import_fs13.default.existsSync(projectPath) ? { note: "present \u2014 ignored (workspace config governs policy)" } : {}
24692
+ ...wsGoverned && import_fs14.default.existsSync(projectPath) ? { note: "present \u2014 ignored (workspace config governs policy)" } : {}
24043
24693
  },
24044
24694
  {
24045
24695
  tier: 4,
24046
24696
  label: "Global config",
24047
- status: import_fs13.default.existsSync(globalPath) ? "active" : "missing",
24697
+ status: import_fs14.default.existsSync(globalPath) ? "active" : "missing",
24048
24698
  path: globalPath,
24049
- ...wsGoverned && import_fs13.default.existsSync(globalPath) ? { note: "present \u2014 ignored (workspace config governs policy)" } : {}
24699
+ ...wsGoverned && import_fs14.default.existsSync(globalPath) ? { note: "present \u2014 ignored (workspace config governs policy)" } : {}
24050
24700
  },
24051
24701
  {
24052
24702
  tier: 5,
@@ -24133,7 +24783,7 @@ async function deriveExplainTrace(toolName, args) {
24133
24783
  });
24134
24784
  const everyInline = reviewsEveryInline(config.settings.mode);
24135
24785
  const strictInline = everyInline && detectInlineExec(shellCommand);
24136
- const inline = everyInline ? null : inlineExecReview(shellCommand);
24786
+ const inline = everyInline ? null : inlineExecReview(shellCommand, config.policy.jailPaths);
24137
24787
  const inlineValue = checkValue(
24138
24788
  catalogSettingsFromConfig(config.settings, config.policy),
24139
24789
  "commands.inline-exec"
@@ -24306,16 +24956,16 @@ async function deriveExplainTrace(toolName, args) {
24306
24956
  });
24307
24957
  return { tool: toolName, args, waterfall, steps, decision: "allow" };
24308
24958
  }
24309
- var import_fs13, import_path13, import_os9, MAX_AGENT_SETTINGS_BYTES, SQL_DML_KEYWORDS2, EXPLAIN_AGENT, PACKAGE_STEP, ENGINE_STEP, GATE_STEP, GATE_PLAIN;
24959
+ var import_fs14, import_path14, import_os10, MAX_AGENT_SETTINGS_BYTES, SQL_DML_KEYWORDS2, EXPLAIN_AGENT, PACKAGE_STEP, ENGINE_STEP, GATE_STEP, GATE_PLAIN;
24310
24960
  var init_policy = __esm({
24311
24961
  "src/policy/index.ts"() {
24312
24962
  "use strict";
24313
24963
  init_cjs_shims();
24314
24964
  init_dist();
24315
- import_fs13 = __toESM(require("fs"));
24965
+ import_fs14 = __toESM(require("fs"));
24316
24966
  init_read_capped();
24317
- import_path13 = __toESM(require("path"));
24318
- import_os9 = __toESM(require("os"));
24967
+ import_path14 = __toESM(require("path"));
24968
+ import_os10 = __toESM(require("os"));
24319
24969
  init_dlp();
24320
24970
  init_gate_order();
24321
24971
  init_file_access();
@@ -24360,11 +25010,11 @@ function extractCommandPattern(toolName, args) {
24360
25010
  function checkPause() {
24361
25011
  if (forecastRun()) return { paused: false };
24362
25012
  try {
24363
- if (!import_fs14.default.existsSync(PAUSED_FILE)) return { paused: false };
24364
- const state = JSON.parse(import_fs14.default.readFileSync(PAUSED_FILE, "utf-8"));
25013
+ if (!import_fs15.default.existsSync(PAUSED_FILE)) return { paused: false };
25014
+ const state = JSON.parse(import_fs15.default.readFileSync(PAUSED_FILE, "utf-8"));
24365
25015
  if (state.expiry > 0 && Date.now() >= state.expiry) {
24366
25016
  try {
24367
- import_fs14.default.unlinkSync(PAUSED_FILE);
25017
+ import_fs15.default.unlinkSync(PAUSED_FILE);
24368
25018
  } catch {
24369
25019
  }
24370
25020
  return { paused: false };
@@ -24380,15 +25030,15 @@ function pauseNode9(durationMs, durationStr) {
24380
25030
  }
24381
25031
  function resumeNode9() {
24382
25032
  try {
24383
- if (import_fs14.default.existsSync(PAUSED_FILE)) import_fs14.default.unlinkSync(PAUSED_FILE);
25033
+ if (import_fs15.default.existsSync(PAUSED_FILE)) import_fs15.default.unlinkSync(PAUSED_FILE);
24384
25034
  } catch {
24385
25035
  }
24386
25036
  }
24387
25037
  function getActiveTrustSession(toolName, args) {
24388
25038
  if (forecastRun()) return false;
24389
25039
  try {
24390
- if (!import_fs14.default.existsSync(TRUST_FILE)) return false;
24391
- const trust = JSON.parse(import_fs14.default.readFileSync(TRUST_FILE, "utf-8"));
25040
+ if (!import_fs15.default.existsSync(TRUST_FILE)) return false;
25041
+ const trust = JSON.parse(import_fs15.default.readFileSync(TRUST_FILE, "utf-8"));
24392
25042
  const now = Date.now();
24393
25043
  const active = trust.entries.filter((e) => e.expiry > now);
24394
25044
  if (active.length !== trust.entries.length) {
@@ -24411,8 +25061,8 @@ function writeTrustSession(toolName, durationMs, args) {
24411
25061
  try {
24412
25062
  let trust = { entries: [] };
24413
25063
  try {
24414
- if (import_fs14.default.existsSync(TRUST_FILE)) {
24415
- trust = JSON.parse(import_fs14.default.readFileSync(TRUST_FILE, "utf-8"));
25064
+ if (import_fs15.default.existsSync(TRUST_FILE)) {
25065
+ trust = JSON.parse(import_fs15.default.readFileSync(TRUST_FILE, "utf-8"));
24416
25066
  }
24417
25067
  } catch {
24418
25068
  }
@@ -24434,28 +25084,28 @@ function writeTrustSession(toolName, durationMs, args) {
24434
25084
  }
24435
25085
  function getPersistentDecision(toolName) {
24436
25086
  try {
24437
- const file2 = import_path14.default.join(import_os10.default.homedir(), ".node9", "decisions.json");
24438
- if (!import_fs14.default.existsSync(file2)) return null;
24439
- const decisions = JSON.parse(import_fs14.default.readFileSync(file2, "utf-8"));
25087
+ const file2 = import_path15.default.join(import_os11.default.homedir(), ".node9", "decisions.json");
25088
+ if (!import_fs15.default.existsSync(file2)) return null;
25089
+ const decisions = JSON.parse(import_fs15.default.readFileSync(file2, "utf-8"));
24440
25090
  const d = decisions[toolName];
24441
25091
  if (d === "allow" || d === "deny") return d;
24442
25092
  } catch {
24443
25093
  }
24444
25094
  return null;
24445
25095
  }
24446
- var import_fs14, import_path14, import_os10, PAUSED_FILE, TRUST_FILE;
25096
+ var import_fs15, import_path15, import_os11, PAUSED_FILE, TRUST_FILE;
24447
25097
  var init_state = __esm({
24448
25098
  "src/auth/state.ts"() {
24449
25099
  "use strict";
24450
25100
  init_cjs_shims();
24451
- import_fs14 = __toESM(require("fs"));
24452
- import_path14 = __toESM(require("path"));
24453
- import_os10 = __toESM(require("os"));
25101
+ import_fs15 = __toESM(require("fs"));
25102
+ import_path15 = __toESM(require("path"));
25103
+ import_os11 = __toESM(require("os"));
24454
25104
  init_policy();
24455
25105
  init_atomic_write();
24456
25106
  init_forecast_context();
24457
- PAUSED_FILE = import_path14.default.join(import_os10.default.homedir(), ".node9", "PAUSED");
24458
- TRUST_FILE = import_path14.default.join(import_os10.default.homedir(), ".node9", "trust.json");
25107
+ PAUSED_FILE = import_path15.default.join(import_os11.default.homedir(), ".node9", "PAUSED");
25108
+ TRUST_FILE = import_path15.default.join(import_os11.default.homedir(), ".node9", "trust.json");
24459
25109
  }
24460
25110
  });
24461
25111
 
@@ -24469,8 +25119,8 @@ function ownRealDir(st) {
24469
25119
  }
24470
25120
  function ensureOwnDir(dir2) {
24471
25121
  try {
24472
- import_fs15.default.mkdirSync(dir2, { recursive: true, mode: 448 });
24473
- const st = import_fs15.default.lstatSync(dir2);
25122
+ import_fs16.default.mkdirSync(dir2, { recursive: true, mode: 448 });
25123
+ const st = import_fs16.default.lstatSync(dir2);
24474
25124
  return ownRealDir(st) ? { dev: st.dev, ino: st.ino } : null;
24475
25125
  } catch {
24476
25126
  return null;
@@ -24478,7 +25128,7 @@ function ensureOwnDir(dir2) {
24478
25128
  }
24479
25129
  function isSameOwnDir(dir2, seen) {
24480
25130
  try {
24481
- const st = import_fs15.default.lstatSync(dir2);
25131
+ const st = import_fs16.default.lstatSync(dir2);
24482
25132
  return ownRealDir(st) && st.dev === seen.dev && st.ino === seen.ino;
24483
25133
  } catch {
24484
25134
  return false;
@@ -24486,22 +25136,22 @@ function isSameOwnDir(dir2, seen) {
24486
25136
  }
24487
25137
  function removeOldFlag(dir2, seen, name, cutoffMs) {
24488
25138
  if (!isSessionFlagName(name)) return;
24489
- const p = import_path15.default.join(dir2, name);
25139
+ const p = import_path16.default.join(dir2, name);
24490
25140
  try {
24491
- const st = import_fs15.default.lstatSync(p);
25141
+ const st = import_fs16.default.lstatSync(p);
24492
25142
  if (!st.isFile() || st.mtimeMs >= cutoffMs) return;
24493
25143
  if (!isSameOwnDir(dir2, seen)) return;
24494
- import_fs15.default.unlinkSync(p);
25144
+ import_fs16.default.unlinkSync(p);
24495
25145
  } catch {
24496
25146
  }
24497
25147
  }
24498
- var import_fs15, import_path15, SESSION_FLAG_SUFFIX, SESSION_FLAG_NAME;
25148
+ var import_fs16, import_path16, SESSION_FLAG_SUFFIX, SESSION_FLAG_NAME;
24499
25149
  var init_own_dir = __esm({
24500
25150
  "src/utils/own-dir.ts"() {
24501
25151
  "use strict";
24502
25152
  init_cjs_shims();
24503
- import_fs15 = __toESM(require("fs"));
24504
- import_path15 = __toESM(require("path"));
25153
+ import_fs16 = __toESM(require("fs"));
25154
+ import_path16 = __toESM(require("path"));
24505
25155
  SESSION_FLAG_SUFFIX = ".node9-flag";
24506
25156
  SESSION_FLAG_NAME = /^[A-Za-z0-9_-]{1,128}\.node9-flag$/;
24507
25157
  }
@@ -24513,7 +25163,7 @@ function maxSocketPathBytes(platform) {
24513
25163
  }
24514
25164
  function isPrivateDir(dir2, uid = currentUid()) {
24515
25165
  try {
24516
- const st = import_fs16.default.lstatSync(dir2);
25166
+ const st = import_fs17.default.lstatSync(dir2);
24517
25167
  if (st.isSymbolicLink() || !st.isDirectory()) return false;
24518
25168
  if (uid !== void 0 && st.uid !== uid) return false;
24519
25169
  return (st.mode & 18) === 0;
@@ -24524,8 +25174,8 @@ function isPrivateDir(dir2, uid = currentUid()) {
24524
25174
  function privateDir(dir2) {
24525
25175
  if (!ensureOwnDir(dir2)) return false;
24526
25176
  try {
24527
- const st = import_fs16.default.lstatSync(dir2);
24528
- if ((st.mode & 63) !== 0) import_fs16.default.chmodSync(dir2, 448);
25177
+ const st = import_fs17.default.lstatSync(dir2);
25178
+ if ((st.mode & 63) !== 0) import_fs17.default.chmodSync(dir2, 448);
24529
25179
  } catch {
24530
25180
  return false;
24531
25181
  }
@@ -24533,23 +25183,23 @@ function privateDir(dir2) {
24533
25183
  }
24534
25184
  function perHomeRuntimeDir(env, home) {
24535
25185
  const xdg = env.XDG_RUNTIME_DIR;
24536
- if (!xdg || !import_path16.default.isAbsolute(xdg)) return null;
24537
- return import_path16.default.join(xdg, `node9-${(0, import_crypto10.createHash)("sha256").update(home).digest("hex").slice(0, 12)}`);
25186
+ if (!xdg || !import_path17.default.isAbsolute(xdg)) return null;
25187
+ return import_path17.default.join(xdg, `node9-${(0, import_crypto10.createHash)("sha256").update(home).digest("hex").slice(0, 12)}`);
24538
25188
  }
24539
25189
  function chooseActivitySocketPath(env, home, platform = process.platform) {
24540
25190
  const candidates2 = [];
24541
25191
  const xdgDir = perHomeRuntimeDir(env, home);
24542
- if (xdgDir && isPrivateDir(import_path16.default.dirname(xdgDir))) candidates2.push(xdgDir);
24543
- candidates2.push(import_path16.default.join(home, ".node9", "run"));
25192
+ if (xdgDir && isPrivateDir(import_path17.default.dirname(xdgDir))) candidates2.push(xdgDir);
25193
+ candidates2.push(import_path17.default.join(home, ".node9", "run"));
24544
25194
  for (const dir2 of candidates2) {
24545
- const socketPath = import_path16.default.join(dir2, SOCKET_NAME);
25195
+ const socketPath = import_path17.default.join(dir2, SOCKET_NAME);
24546
25196
  if (Buffer.byteLength(socketPath) >= maxSocketPathBytes(platform)) continue;
24547
25197
  if (privateDir(dir2)) return socketPath;
24548
25198
  }
24549
25199
  return null;
24550
25200
  }
24551
25201
  function ensureActivitySocketDir(socketPath) {
24552
- return privateDir(import_path16.default.dirname(socketPath));
25202
+ return privateDir(import_path17.default.dirname(socketPath));
24553
25203
  }
24554
25204
  function windowsActivityPipeName() {
24555
25205
  return `\\\\.\\pipe\\node9-activity-${(0, import_crypto10.randomBytes)(16).toString("hex")}`;
@@ -24557,17 +25207,17 @@ function windowsActivityPipeName() {
24557
25207
  function isTrustedActivitySocket(socketPath, uid = currentUid()) {
24558
25208
  if (process.platform === "win32") return WINDOWS_PIPE.test(socketPath);
24559
25209
  try {
24560
- const st = import_fs16.default.lstatSync(socketPath);
25210
+ const st = import_fs17.default.lstatSync(socketPath);
24561
25211
  if (!st.isSocket()) return false;
24562
25212
  if (uid !== void 0 && st.uid !== uid) return false;
24563
25213
  } catch {
24564
25214
  return false;
24565
25215
  }
24566
- return isPrivateDir(import_path16.default.dirname(socketPath), uid);
25216
+ return isPrivateDir(import_path17.default.dirname(socketPath), uid);
24567
25217
  }
24568
25218
  function isOwnStaleSocket(socketPath) {
24569
25219
  try {
24570
- const st = import_fs16.default.lstatSync(socketPath);
25220
+ const st = import_fs17.default.lstatSync(socketPath);
24571
25221
  const uid = currentUid();
24572
25222
  return st.isSocket() && (uid === void 0 || st.uid === uid);
24573
25223
  } catch {
@@ -24576,24 +25226,24 @@ function isOwnStaleSocket(socketPath) {
24576
25226
  }
24577
25227
  function removeOwnSocketDir(socketPath, env, home, platform = process.platform) {
24578
25228
  if (platform === "win32") return false;
24579
- const dir2 = import_path16.default.dirname(socketPath);
25229
+ const dir2 = import_path17.default.dirname(socketPath);
24580
25230
  if (dir2 !== perHomeRuntimeDir(env, home)) return false;
24581
25231
  if (!isPrivateDir(dir2)) return false;
24582
25232
  try {
24583
- if (import_fs16.default.readdirSync(dir2).length > 0) return false;
24584
- import_fs16.default.rmdirSync(dir2);
25233
+ if (import_fs17.default.readdirSync(dir2).length > 0) return false;
25234
+ import_fs17.default.rmdirSync(dir2);
24585
25235
  return true;
24586
25236
  } catch {
24587
25237
  return false;
24588
25238
  }
24589
25239
  }
24590
- var import_fs16, import_path16, import_crypto10, SOCKET_NAME, currentUid, WINDOWS_PIPE;
25240
+ var import_fs17, import_path17, import_crypto10, SOCKET_NAME, currentUid, WINDOWS_PIPE;
24591
25241
  var init_activity_socket = __esm({
24592
25242
  "src/utils/activity-socket.ts"() {
24593
25243
  "use strict";
24594
25244
  init_cjs_shims();
24595
- import_fs16 = __toESM(require("fs"));
24596
- import_path16 = __toESM(require("path"));
25245
+ import_fs17 = __toESM(require("fs"));
25246
+ import_path17 = __toESM(require("path"));
24597
25247
  import_crypto10 = require("crypto");
24598
25248
  init_own_dir();
24599
25249
  SOCKET_NAME = "activity.sock";
@@ -24799,9 +25449,9 @@ var init_decision_record = __esm({
24799
25449
  });
24800
25450
 
24801
25451
  // src/auth/daemon.ts
24802
- function daemonActivitySocket(home = import_os11.default.homedir()) {
25452
+ function daemonActivitySocket(home = import_os12.default.homedir()) {
24803
25453
  try {
24804
- const raw = import_fs17.default.readFileSync(import_path17.default.join(home, ".node9", "daemon.pid"), "utf-8");
25454
+ const raw = import_fs18.default.readFileSync(import_path18.default.join(home, ".node9", "daemon.pid"), "utf-8");
24805
25455
  const p = JSON.parse(raw).activitySocket;
24806
25456
  return typeof p === "string" && isTrustedActivitySocket(p) ? p : null;
24807
25457
  } catch {
@@ -24861,9 +25511,9 @@ function resolveDaemonPort(env = process.env) {
24861
25511
  function getInternalToken() {
24862
25512
  if (forecastRun()) return null;
24863
25513
  try {
24864
- const pidFile = import_path17.default.join(import_os11.default.homedir(), ".node9", "daemon.pid");
24865
- if (!import_fs17.default.existsSync(pidFile)) return null;
24866
- const data = JSON.parse(import_fs17.default.readFileSync(pidFile, "utf-8"));
25514
+ const pidFile = import_path18.default.join(import_os12.default.homedir(), ".node9", "daemon.pid");
25515
+ if (!import_fs18.default.existsSync(pidFile)) return null;
25516
+ const data = JSON.parse(import_fs18.default.readFileSync(pidFile, "utf-8"));
24867
25517
  process.kill(data.pid, 0);
24868
25518
  return data.internalToken ?? null;
24869
25519
  } catch {
@@ -24872,12 +25522,12 @@ function getInternalToken() {
24872
25522
  }
24873
25523
  function isDaemonRunning() {
24874
25524
  if (forecastRun()) return false;
24875
- const pidFile = import_path17.default.join(import_os11.default.homedir(), ".node9", "daemon.pid");
24876
- if (import_fs17.default.existsSync(pidFile)) {
25525
+ const pidFile = import_path18.default.join(import_os12.default.homedir(), ".node9", "daemon.pid");
25526
+ if (import_fs18.default.existsSync(pidFile)) {
24877
25527
  let pid;
24878
25528
  let port;
24879
25529
  try {
24880
- const data = JSON.parse(import_fs17.default.readFileSync(pidFile, "utf-8"));
25530
+ const data = JSON.parse(import_fs18.default.readFileSync(pidFile, "utf-8"));
24881
25531
  pid = data.pid;
24882
25532
  port = data.port;
24883
25533
  } catch {
@@ -24895,7 +25545,7 @@ function isDaemonRunning() {
24895
25545
  } catch (err2) {
24896
25546
  if (err2 instanceof Error && "code" in err2 && err2.code === "ESRCH") {
24897
25547
  try {
24898
- import_fs17.default.unlinkSync(pidFile);
25548
+ import_fs18.default.unlinkSync(pidFile);
24899
25549
  } catch {
24900
25550
  }
24901
25551
  }
@@ -25367,16 +26017,16 @@ async function resolveViaDaemon(id, decision, internalToken, source, record2) {
25367
26017
  const winner = parseDecisionRecord(body?.record);
25368
26018
  return winner && typeof body?.won === "boolean" ? { won: body.won, record: winner } : null;
25369
26019
  }
25370
- var import_fs17, import_http, import_net4, import_path17, import_os11, ACTIVITY_SOCKET_TIMEOUT_MS, DEFAULT_DAEMON_PORT, DAEMON_PORT, DAEMON_HOST;
26020
+ var import_fs18, import_http, import_net4, import_path18, import_os12, ACTIVITY_SOCKET_TIMEOUT_MS, DEFAULT_DAEMON_PORT, DAEMON_PORT, DAEMON_HOST;
25371
26021
  var init_daemon = __esm({
25372
26022
  "src/auth/daemon.ts"() {
25373
26023
  "use strict";
25374
26024
  init_cjs_shims();
25375
- import_fs17 = __toESM(require("fs"));
26025
+ import_fs18 = __toESM(require("fs"));
25376
26026
  import_http = __toESM(require("http"));
25377
26027
  import_net4 = __toESM(require("net"));
25378
- import_path17 = __toESM(require("path"));
25379
- import_os11 = __toESM(require("os"));
26028
+ import_path18 = __toESM(require("path"));
26029
+ import_os12 = __toESM(require("os"));
25380
26030
  init_forecast_context();
25381
26031
  init_activity_socket();
25382
26032
  init_decision_record();
@@ -25639,13 +26289,13 @@ function deriveServerName(cmd) {
25639
26289
  return strip(base3) || "MCP Server";
25640
26290
  }
25641
26291
  function getMcpToolsFile() {
25642
- return import_path18.default.join(import_os12.default.homedir(), ".node9", "mcp-tools.json");
26292
+ return import_path19.default.join(import_os13.default.homedir(), ".node9", "mcp-tools.json");
25643
26293
  }
25644
26294
  function readMcpToolsConfig() {
25645
26295
  try {
25646
26296
  const file2 = getMcpToolsFile();
25647
- if (!import_fs18.default.existsSync(file2)) return {};
25648
- const raw = import_fs18.default.readFileSync(file2, "utf-8");
26297
+ if (!import_fs19.default.existsSync(file2)) return {};
26298
+ const raw = import_fs19.default.readFileSync(file2, "utf-8");
25649
26299
  return JSON.parse(raw);
25650
26300
  } catch {
25651
26301
  return {};
@@ -25654,11 +26304,11 @@ function readMcpToolsConfig() {
25654
26304
  function writeMcpToolsConfig(config) {
25655
26305
  try {
25656
26306
  const file2 = getMcpToolsFile();
25657
- const dir2 = import_path18.default.dirname(file2);
25658
- if (!import_fs18.default.existsSync(dir2)) import_fs18.default.mkdirSync(dir2, { recursive: true });
25659
- const tmpPath = `${file2}.${import_os12.default.hostname()}.${process.pid}.tmp`;
25660
- import_fs18.default.writeFileSync(tmpPath, JSON.stringify(config, null, 2));
25661
- import_fs18.default.renameSync(tmpPath, file2);
26307
+ const dir2 = import_path19.default.dirname(file2);
26308
+ if (!import_fs19.default.existsSync(dir2)) import_fs19.default.mkdirSync(dir2, { recursive: true });
26309
+ const tmpPath = `${file2}.${import_os13.default.hostname()}.${process.pid}.tmp`;
26310
+ import_fs19.default.writeFileSync(tmpPath, JSON.stringify(config, null, 2));
26311
+ import_fs19.default.renameSync(tmpPath, file2);
25662
26312
  } catch (e) {
25663
26313
  console.error("Failed to write mcp-tools.json", e);
25664
26314
  }
@@ -25713,21 +26363,21 @@ function approveServer(serverKey, disabledTools) {
25713
26363
  writeMcpToolsConfig(config);
25714
26364
  }
25715
26365
  }
25716
- var import_fs18, import_path18, import_os12;
26366
+ var import_fs19, import_path19, import_os13;
25717
26367
  var init_mcp_tools = __esm({
25718
26368
  "src/daemon/mcp-tools.ts"() {
25719
26369
  "use strict";
25720
26370
  init_cjs_shims();
25721
- import_fs18 = __toESM(require("fs"));
25722
- import_path18 = __toESM(require("path"));
25723
- import_os12 = __toESM(require("os"));
26371
+ import_fs19 = __toESM(require("fs"));
26372
+ import_path19 = __toESM(require("path"));
26373
+ import_os13 = __toESM(require("os"));
25724
26374
  init_mcp_cmd();
25725
26375
  }
25726
26376
  });
25727
26377
 
25728
26378
  // src/apps/state.ts
25729
- function appsFilePath(home = import_os13.default.homedir()) {
25730
- return import_path19.default.join(home, ".node9", "apps.json");
26379
+ function appsFilePath(home = import_os14.default.homedir()) {
26380
+ return import_path20.default.join(home, ".node9", "apps.json");
25731
26381
  }
25732
26382
  function emptyFile() {
25733
26383
  return { version: 1, apps: /* @__PURE__ */ Object.create(null), links: /* @__PURE__ */ Object.create(null) };
@@ -25774,7 +26424,7 @@ function readAppsFile(home) {
25774
26424
  function readAppsFileStrict(home) {
25775
26425
  let text;
25776
26426
  try {
25777
- text = import_fs19.default.readFileSync(appsFilePath(home), "utf8");
26427
+ text = import_fs20.default.readFileSync(appsFilePath(home), "utf8");
25778
26428
  } catch (e) {
25779
26429
  return e.code === "ENOENT" ? emptyFile() : null;
25780
26430
  }
@@ -25786,10 +26436,10 @@ function readAppsFileStrict(home) {
25786
26436
  }
25787
26437
  function writeAppsFile(data, home) {
25788
26438
  const file2 = appsFilePath(home);
25789
- import_fs19.default.mkdirSync(import_path19.default.dirname(file2), { recursive: true });
26439
+ import_fs20.default.mkdirSync(import_path20.default.dirname(file2), { recursive: true });
25790
26440
  const tmp = `${file2}.${import_crypto11.default.randomBytes(6).toString("hex")}.tmp`;
25791
- import_fs19.default.writeFileSync(tmp, JSON.stringify(data, null, 2) + "\n", { mode: 384 });
25792
- import_fs19.default.renameSync(tmp, file2);
26441
+ import_fs20.default.writeFileSync(tmp, JSON.stringify(data, null, 2) + "\n", { mode: 384 });
26442
+ import_fs20.default.renameSync(tmp, file2);
25793
26443
  }
25794
26444
  function applyDetection(found, opts = { managed: false }) {
25795
26445
  const result = { turnedOn: [], recordedOnly: [] };
@@ -25855,7 +26505,7 @@ function setLink(server, slug2, home) {
25855
26505
  function localLinks() {
25856
26506
  const file2 = appsFilePath();
25857
26507
  try {
25858
- const mtimeMs = import_fs19.default.statSync(file2).mtimeMs;
26508
+ const mtimeMs = import_fs20.default.statSync(file2).mtimeMs;
25859
26509
  if (!linkCache || linkCache.file !== file2 || linkCache.mtimeMs !== mtimeMs) {
25860
26510
  linkCache = { mtimeMs, file: file2, links: readAppsFile().links };
25861
26511
  }
@@ -25893,14 +26543,14 @@ function resolveLinkedServer(label3, opts = {}) {
25893
26543
  }
25894
26544
  return label3;
25895
26545
  }
25896
- var import_fs19, import_os13, import_path19, import_crypto11, SIGNAL_KINDS, linkCache, NO_LINKS;
26546
+ var import_fs20, import_os14, import_path20, import_crypto11, SIGNAL_KINDS, linkCache, NO_LINKS;
25897
26547
  var init_state2 = __esm({
25898
26548
  "src/apps/state.ts"() {
25899
26549
  "use strict";
25900
26550
  init_cjs_shims();
25901
- import_fs19 = __toESM(require("fs"));
25902
- import_os13 = __toESM(require("os"));
25903
- import_path19 = __toESM(require("path"));
26551
+ import_fs20 = __toESM(require("fs"));
26552
+ import_os14 = __toESM(require("os"));
26553
+ import_path20 = __toESM(require("path"));
25904
26554
  import_crypto11 = __toESM(require("crypto"));
25905
26555
  init_catalog();
25906
26556
  init_shields();
@@ -25964,7 +26614,7 @@ function computeRiskMetadata(args, tier, blockedByLabel, matchedField, matchedWo
25964
26614
  intent = "EDIT";
25965
26615
  if (obj.file_path) {
25966
26616
  editFilePath = String(obj.file_path);
25967
- editFileName = import_path20.default.basename(editFilePath);
26617
+ editFileName = import_path21.default.basename(editFilePath);
25968
26618
  }
25969
26619
  const result = extractContext(String(obj.new_string), matchedWord);
25970
26620
  contextSnippet = result.snippet;
@@ -25996,12 +26646,12 @@ function computeRiskMetadata(args, tier, blockedByLabel, matchedField, matchedWo
25996
26646
  ...ruleName && { ruleName }
25997
26647
  };
25998
26648
  }
25999
- var import_path20, CODE_KEYS;
26649
+ var import_path21, CODE_KEYS;
26000
26650
  var init_context_sniper = __esm({
26001
26651
  "src/context-sniper.ts"() {
26002
26652
  "use strict";
26003
26653
  init_cjs_shims();
26004
- import_path20 = __toESM(require("path"));
26654
+ import_path21 = __toESM(require("path"));
26005
26655
  CODE_KEYS = [
26006
26656
  "command",
26007
26657
  "cmd",
@@ -26058,7 +26708,7 @@ function formatArgs(args, matchedField, matchedWord) {
26058
26708
  if (typeof parsed === "object" && !Array.isArray(parsed)) {
26059
26709
  const obj = parsed;
26060
26710
  if (obj.old_string !== void 0 && obj.new_string !== void 0) {
26061
- const file2 = obj.file_path ? import_path21.default.basename(String(obj.file_path)) : "file";
26711
+ const file2 = obj.file_path ? import_path22.default.basename(String(obj.file_path)) : "file";
26062
26712
  const oldPreview = smartTruncate(String(obj.old_string), 120);
26063
26713
  const newPreview = extractContext(String(obj.new_string), matchedWord).snippet;
26064
26714
  return {
@@ -26275,13 +26925,13 @@ end run`;
26275
26925
  }
26276
26926
  });
26277
26927
  }
26278
- var import_child_process, import_path21, isTestEnv, MIN_INTERACTION_MS;
26928
+ var import_child_process, import_path22, isTestEnv, MIN_INTERACTION_MS;
26279
26929
  var init_native = __esm({
26280
26930
  "src/ui/native.ts"() {
26281
26931
  "use strict";
26282
26932
  init_cjs_shims();
26283
26933
  import_child_process = require("child_process");
26284
- import_path21 = __toESM(require("path"));
26934
+ import_path22 = __toESM(require("path"));
26285
26935
  init_context_sniper();
26286
26936
  init_safe_text();
26287
26937
  isTestEnv = () => {
@@ -26291,108 +26941,6 @@ var init_native = __esm({
26291
26941
  }
26292
26942
  });
26293
26943
 
26294
- // src/shields/jail.ts
26295
- function jailStorePath() {
26296
- return import_path22.default.join(import_os14.default.homedir(), ".node9", "jail-paths.json");
26297
- }
26298
- function readJailPaths() {
26299
- let text;
26300
- try {
26301
- text = import_fs20.default.readFileSync(jailStorePath(), "utf8");
26302
- } catch (err2) {
26303
- if (err2.code === "ENOENT") return [];
26304
- throw err2;
26305
- }
26306
- let parsed;
26307
- try {
26308
- parsed = JSON.parse(text);
26309
- } catch {
26310
- throw new Error(`${jailStorePath()} is not valid JSON \u2014 fix it before changing the jail.`);
26311
- }
26312
- if (!Array.isArray(parsed.paths)) return [];
26313
- return parsed.paths.filter(
26314
- (p) => !!p && typeof p.path === "string" && (p.verdict === "block" || p.verdict === "review")
26315
- );
26316
- }
26317
- function writeJailPaths(paths) {
26318
- const p = jailStorePath();
26319
- import_fs20.default.mkdirSync(import_path22.default.dirname(p), { recursive: true });
26320
- import_fs20.default.writeFileSync(p, JSON.stringify({ paths }, null, 2) + "\n", { mode: 384 });
26321
- }
26322
- function addJailPath(rawPath, verdict) {
26323
- const norm = rawPath.trim();
26324
- const fragment = pathToRegexFragment(norm);
26325
- if (!fragment) {
26326
- throw new Error(
26327
- `"${rawPath}" is too broad to jail \u2014 give a specific path (e.g. ~/.gmail-mcp), not a home or root directory.`
26328
- );
26329
- }
26330
- const rejection = validateRegex(fragment);
26331
- if (rejection !== null) {
26332
- throw new Error(
26333
- `"${rawPath}" cannot be jailed: the generated match pattern was rejected by the policy engine (${rejection}). Try a shorter or more specific path.`
26334
- );
26335
- }
26336
- const next = [...readJailPaths().filter((p) => p.path !== norm), { path: norm, verdict }];
26337
- writeJailPaths(next);
26338
- return next;
26339
- }
26340
- function findJailedPath(candidate) {
26341
- return findJailedPathIn(candidate, readJailPaths());
26342
- }
26343
- function findJailedPathIn(candidate, paths) {
26344
- if (!candidate) return null;
26345
- for (const entry of paths) {
26346
- if (pathMatchesFragment(candidate, entry.path)) return entry;
26347
- }
26348
- return null;
26349
- }
26350
- function removeJailPath(rawPath) {
26351
- const norm = rawPath.trim();
26352
- const before = readJailPaths();
26353
- const after = before.filter((p) => p.path !== norm);
26354
- const removed = after.length !== before.length;
26355
- if (removed) writeJailPaths(after);
26356
- return { removed, paths: after };
26357
- }
26358
- function regenerateUserJail(paths) {
26359
- const file2 = import_path22.default.join(USER_SHIELDS_DIR_PATH, `${USER_JAIL_SHIELD}.json`);
26360
- if (paths.length === 0) {
26361
- const active2 = readActiveShields();
26362
- if (active2.includes(USER_JAIL_SHIELD)) {
26363
- writeActiveShields(active2.filter((s) => s !== USER_JAIL_SHIELD));
26364
- }
26365
- try {
26366
- import_fs20.default.rmSync(file2, { force: true });
26367
- } catch {
26368
- }
26369
- return;
26370
- }
26371
- const def = buildShield({
26372
- name: USER_JAIL_SHIELD,
26373
- description: "User-added credential jail paths (node9 jail add)",
26374
- blockPaths: paths.filter((p) => p.verdict === "block").map((p) => p.path),
26375
- reviewPaths: paths.filter((p) => p.verdict === "review").map((p) => p.path)
26376
- });
26377
- installShield(USER_JAIL_SHIELD, def);
26378
- const active = readActiveShields();
26379
- if (!active.includes(USER_JAIL_SHIELD)) writeActiveShields([...active, USER_JAIL_SHIELD]);
26380
- }
26381
- var import_fs20, import_os14, import_path22, USER_JAIL_SHIELD;
26382
- var init_jail = __esm({
26383
- "src/shields/jail.ts"() {
26384
- "use strict";
26385
- init_cjs_shims();
26386
- import_fs20 = __toESM(require("fs"));
26387
- import_os14 = __toESM(require("os"));
26388
- import_path22 = __toESM(require("path"));
26389
- init_build();
26390
- init_dist();
26391
- init_shields();
26392
- USER_JAIL_SHIELD = "user-jail";
26393
- }
26394
- });
26395
-
26396
26944
  // src/canary/registry.ts
26397
26945
  function canaryStorePath() {
26398
26946
  return import_path23.default.join(import_os15.default.homedir(), ".node9", "canaries.json");
@@ -44697,15 +45245,68 @@ function startActivitySocket() {
44697
45245
  return;
44698
45246
  }
44699
45247
  bindActivitySocket();
44700
- activityHealthInterval = setInterval(() => {
44701
- if (ACTIVITY_SOCKET_PATH && !import_fs65.default.existsSync(ACTIVITY_SOCKET_PATH)) attemptRebind("health-probe");
44702
- }, ACTIVITY_HEALTH_PROBE_MS);
44703
- activityHealthInterval.unref();
45248
+ startHealthProbe();
44704
45249
  process.on("exit", stopActivitySocket);
44705
45250
  }
44706
- function stopActivitySocket() {
45251
+ function startHealthProbe() {
45252
+ if (activityHealthInterval) clearInterval(activityHealthInterval);
45253
+ activityHealthInterval = setInterval(probeActivitySocket, ACTIVITY_HEALTH_PROBE_MS);
45254
+ activityHealthInterval.unref();
45255
+ }
45256
+ function stopHealthProbe() {
44707
45257
  if (activityHealthInterval) clearInterval(activityHealthInterval);
44708
45258
  activityHealthInterval = null;
45259
+ }
45260
+ function isLiveOtherDaemonPid(pid) {
45261
+ if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0 || pid > 4194304)
45262
+ return false;
45263
+ if (pid === process.pid) return false;
45264
+ try {
45265
+ process.kill(pid, 0);
45266
+ return true;
45267
+ } catch {
45268
+ return false;
45269
+ }
45270
+ }
45271
+ function anotherDaemonOwnsPidFile() {
45272
+ try {
45273
+ const raw = import_fs65.default.readFileSync(import_path68.default.join(homeDir, ".node9", "daemon.pid"), "utf-8");
45274
+ return isLiveOtherDaemonPid(JSON.parse(raw).pid);
45275
+ } catch {
45276
+ return false;
45277
+ }
45278
+ }
45279
+ function yieldToOtherDaemon() {
45280
+ if (!activityYieldLogged) {
45281
+ logActivitySocket("another daemon owns daemon.pid; this one leaves the socket to it");
45282
+ activityYieldLogged = true;
45283
+ }
45284
+ }
45285
+ function probeActivitySocket() {
45286
+ const socketPath = ACTIVITY_SOCKET_PATH;
45287
+ if (!socketPath) return;
45288
+ if (process.platform === "win32") {
45289
+ if (!import_fs65.default.existsSync(socketPath)) attemptRebind("health-probe");
45290
+ return;
45291
+ }
45292
+ let st;
45293
+ try {
45294
+ st = import_fs65.default.lstatSync(socketPath);
45295
+ } catch {
45296
+ attemptRebind("health-probe");
45297
+ return;
45298
+ }
45299
+ const bound = activitySocketIdentity;
45300
+ if (!bound || st.dev === bound.dev && st.ino === bound.ino) return;
45301
+ if (anotherDaemonOwnsPidFile()) {
45302
+ yieldToOtherDaemon();
45303
+ return;
45304
+ }
45305
+ activityYieldLogged = false;
45306
+ attemptRebind("socket-replaced");
45307
+ }
45308
+ function stopActivitySocket() {
45309
+ stopHealthProbe();
44709
45310
  const socketPath = ACTIVITY_SOCKET_PATH;
44710
45311
  const bound = activitySocketIdentity;
44711
45312
  ACTIVITY_SOCKET_PATH = null;
@@ -44728,6 +45329,7 @@ function attemptRebind(reason) {
44728
45329
  reason: "rebind-loop",
44729
45330
  message: "Activity socket repeatedly disappearing \u2014 run: node9 daemon restart"
44730
45331
  });
45332
+ stopHealthProbe();
44731
45333
  return;
44732
45334
  }
44733
45335
  logActivitySocket(`rebinding (reason: ${reason}, attempt ${activityRebindAttempts.length})`);
@@ -44860,7 +45462,7 @@ function bindActivitySocket() {
44860
45462
  });
44861
45463
  activitySocketServer = unixServer;
44862
45464
  }
44863
- var import_net5, import_fs65, import_path68, import_os55, import_crypto20, homeDir, DAEMON_PID_FILE, DECISIONS_FILE, AUDIT_LOG_FILE, TRUST_FILE2, GLOBAL_CONFIG_FILE, CREDENTIALS_FILE, INSIGHT_COUNTS_FILE, sseClients, cards, suggestionTracker, taintStore, sessionTaintStore, sessionExposureStore, sessionWriteStore, insightCounts, _abandonTimer, _hadBrowserClient, _daemonServer, daemonRejectionHandlerRegistered, daemonSignalHandlersRegistered, AUTO_DENY_MS, TRUST_DURATIONS, autoStarted, ACTIVITY_SOCKET_PATH, activitySocketPathListeners, ACTIVITY_RING_SIZE, activityRing, LARGE_RESPONSE_RING_SIZE, largeResponseRing, cachedScanResult, cachedScanTs, SCAN_CACHE_TTL_MS, SECRET_KEY_RE, INPUT_PRICE_PER_1M, OUTPUT_PRICE_PER_1M, BYTES_PER_TOKEN, CRITICAL_FORENSIC_CATEGORIES, WRITE_TOOL_NAMES, ACTIVITY_REBIND_MAX_ATTEMPTS, ACTIVITY_REBIND_WINDOW_MS, ACTIVITY_HEALTH_PROBE_MS, activitySocketServer, activitySocketIdentity, activityHealthInterval, activityRebindAttempts, activityCircuitTripped;
45465
+ var import_net5, import_fs65, import_path68, import_os55, import_crypto20, homeDir, DAEMON_PID_FILE, DECISIONS_FILE, AUDIT_LOG_FILE, TRUST_FILE2, GLOBAL_CONFIG_FILE, CREDENTIALS_FILE, INSIGHT_COUNTS_FILE, sseClients, cards, suggestionTracker, taintStore, sessionTaintStore, sessionExposureStore, sessionWriteStore, insightCounts, _abandonTimer, _hadBrowserClient, _daemonServer, daemonRejectionHandlerRegistered, daemonSignalHandlersRegistered, AUTO_DENY_MS, TRUST_DURATIONS, autoStarted, ACTIVITY_SOCKET_PATH, activitySocketPathListeners, ACTIVITY_RING_SIZE, activityRing, LARGE_RESPONSE_RING_SIZE, largeResponseRing, cachedScanResult, cachedScanTs, SCAN_CACHE_TTL_MS, SECRET_KEY_RE, INPUT_PRICE_PER_1M, OUTPUT_PRICE_PER_1M, BYTES_PER_TOKEN, CRITICAL_FORENSIC_CATEGORIES, WRITE_TOOL_NAMES, ACTIVITY_REBIND_MAX_ATTEMPTS, ACTIVITY_REBIND_WINDOW_MS, ACTIVITY_HEALTH_PROBE_MS, activitySocketServer, activitySocketIdentity, activityHealthInterval, activityRebindAttempts, activityCircuitTripped, activityYieldLogged;
44864
45466
  var init_state3 = __esm({
44865
45467
  "src/daemon/state.ts"() {
44866
45468
  "use strict";
@@ -44950,6 +45552,7 @@ var init_state3 = __esm({
44950
45552
  activityHealthInterval = null;
44951
45553
  activityRebindAttempts = [];
44952
45554
  activityCircuitTripped = false;
45555
+ activityYieldLogged = false;
44953
45556
  }
44954
45557
  });
44955
45558