@node9/proxy 2.50.0 → 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/index.mjs CHANGED
@@ -2091,9 +2091,15 @@ function detectDangerousShellExec(command) {
2091
2091
  if (f === PARSE_FAIL) return null;
2092
2092
  let result = null;
2093
2093
  const assigned = assignedNames(f);
2094
- walk(f, (node) => {
2094
+ const known = plainlyNamedPrograms(f);
2095
+ const calledByName = literallyCalledFunctions(f);
2096
+ const bodies = [];
2097
+ const inBody = (pos) => bodies.some(([a, b]) => pos >= a && pos < b);
2098
+ walkWithCalls(f, (node) => {
2095
2099
  if (!node || result === "block") return false;
2096
2100
  const n = node;
2101
+ if (n.kind === "FuncDecl" && n.Body && callDepth === 0 && calledByName.has(n.Name?.Value))
2102
+ bodies.push([n.Body.pos, n.Body.end]);
2097
2103
  if (n.kind === "BinaryCmd" && n.Y && !listOps().has(n.Op)) {
2098
2104
  const call = n.Y.Cmd;
2099
2105
  if (call && call.kind === "CallExpr") {
@@ -2117,7 +2123,8 @@ function detectDangerousShellExec(command) {
2117
2123
  );
2118
2124
  const head = unwrapCommandHead(headWords, false, true);
2119
2125
  const lookup = headWords[0]?.toLowerCase() === "command" && headWords.slice(1).some((w) => w === "-v" || w === "-V");
2120
- if (!lookup && (opaqueExecutable(args[0], assigned) || opaqueExecutable(args[head], assigned)))
2126
+ const unboundBody = callDepth === 0 && inBody(n.pos ?? -1);
2127
+ if (!lookup && !unboundBody && (opaqueExecutable(args[0], assigned, known) || opaqueExecutable(args[head], assigned, known)))
2121
2128
  result = "review";
2122
2129
  const firstParts = args[0].Parts || [];
2123
2130
  if (firstParts.length !== 1 || firstParts[0].kind !== "Lit") return true;
@@ -2177,11 +2184,105 @@ function assignedNames(f) {
2177
2184
  });
2178
2185
  return names;
2179
2186
  }
2180
- function executableParts(word, assigned, quoted = false) {
2187
+ function literallyCalledFunctions(f) {
2188
+ const declared = /* @__PURE__ */ new Set();
2189
+ const called = /* @__PURE__ */ new Set();
2190
+ walk(f, (node) => {
2191
+ if (!node) return false;
2192
+ if (node.kind === "FuncDecl" && node.Name?.Value) declared.add(node.Name.Value);
2193
+ if (node.kind === "CallExpr") {
2194
+ const head = resolveWordLiteral(node.Args?.[0]);
2195
+ if (head) called.add(head);
2196
+ }
2197
+ return true;
2198
+ });
2199
+ return new Set([...declared].filter((name) => called.has(name)));
2200
+ }
2201
+ function plainlyNamedPrograms(f) {
2202
+ const values = /* @__PURE__ */ new Map();
2203
+ const arrays = /* @__PURE__ */ new Set();
2204
+ const give = (name, value) => {
2205
+ if (value === null || !PLAIN_PROGRAM_NAME.test(value)) {
2206
+ values.set(name, null);
2207
+ return;
2208
+ }
2209
+ const cur = values.get(name);
2210
+ if (cur === null) return;
2211
+ if (cur === void 0) values.set(name, /* @__PURE__ */ new Set([value]));
2212
+ else cur.add(value);
2213
+ };
2214
+ const literalOnly = (w) => {
2215
+ const parts = w?.Parts ?? [];
2216
+ if (parts.length !== 1) return null;
2217
+ const p = parts[0];
2218
+ if (p.kind === "Lit") return p.Value ?? null;
2219
+ if (p.kind === "ParamExp" && expansionName(p) === "FUNCNAME") return "FUNCNAME";
2220
+ if (p.kind === "SglQuoted" && !p.Dollar) return p.Value ?? null;
2221
+ if (p.kind === "DblQuoted") {
2222
+ const inner = p.Parts ?? [];
2223
+ return inner.length === 1 && inner[0].kind === "Lit" ? inner[0].Value ?? null : null;
2224
+ }
2225
+ return null;
2226
+ };
2227
+ let ifsTouched = false;
2228
+ walk(f, (node) => {
2229
+ if (!node) return false;
2230
+ const kind = node.kind;
2231
+ if (kind === "FuncDecl" && node.Name?.Value) give(node.Name.Value, node.Name.Value);
2232
+ if (kind === "Assign" && node.Name?.Value) {
2233
+ const name = node.Name.Value;
2234
+ if (name === "IFS") ifsTouched = true;
2235
+ if (node.Append) values.set(name, null);
2236
+ else if (node.Array) {
2237
+ arrays.add(name);
2238
+ for (const el of node.Array.Elems ?? []) give(name, literalOnly(el.Value));
2239
+ } else if (node.Index) {
2240
+ arrays.add(name);
2241
+ give(name, node.Value ? literalOnly(node.Value) : null);
2242
+ } else give(name, node.Value ? literalOnly(node.Value) : null);
2243
+ }
2244
+ if (kind === "ForClause" && node.Loop?.Name?.Value) values.set(node.Loop.Name.Value, null);
2245
+ if (kind === "CallExpr") {
2246
+ const words = (node.Args ?? []).map((a) => resolveWordLiteral(a));
2247
+ if (VARIABLE_READERS.has(words[0] ?? "")) {
2248
+ for (const w of words.slice(1)) if (w && /^[A-Za-z_]\w*$/.test(w)) values.set(w, null);
2249
+ }
2250
+ }
2251
+ return true;
2252
+ });
2253
+ if (ifsTouched) return /* @__PURE__ */ new Set();
2254
+ const functions = /* @__PURE__ */ new Set();
2255
+ walk(f, (node) => {
2256
+ if (node && node.kind === "FuncDecl" && node.Name?.Value) functions.add(node.Name.Value);
2257
+ return true;
2258
+ });
2259
+ const callable = (v) => functions.has(v) || v === "FUNCNAME" || v === "true" || v === "false" || v === ":";
2260
+ for (const [name, vals] of values) {
2261
+ if (vals && ![...vals].every(callable)) values.set(name, null);
2262
+ }
2263
+ const known = /* @__PURE__ */ new Set();
2264
+ for (const [name, vals] of values)
2265
+ if (vals && (vals.size === 1 || arrays.has(name) && vals.size > 0)) known.add(name);
2266
+ return known;
2267
+ }
2268
+ function expansionName(part) {
2269
+ if (part.kind !== "ParamExp") return null;
2270
+ if (part.Excl || part.Length || part.Width || part.Slice || part.Repl || part.Exp) return null;
2271
+ return part.Param?.Value ?? null;
2272
+ }
2273
+ function executableParts(word, assigned, quoted = false, known = EMPTY_NAMES) {
2181
2274
  return (word?.Parts ?? []).flatMap((part) => {
2182
2275
  const kind = part.kind;
2183
- if (kind === "DblQuoted") return executableParts(part, assigned, true);
2276
+ if (kind === "DblQuoted") return executableParts(part, assigned, true, known);
2184
2277
  if (kind === "Lit") return [{ kind: "Lit", value: part.Value ?? "", quoted }];
2278
+ const name = expansionName(part);
2279
+ if (name !== null && (known.has(name) || name === "FUNCNAME"))
2280
+ return [{ kind: "Lit", value: name, quoted }];
2281
+ if (name !== null) {
2282
+ const bound = recordedExpansion(name === "@" || name === "*" ? "1" : name);
2283
+ if (typeof bound === "string" && PLAIN_PROGRAM_NAME.test(bound))
2284
+ return [{ kind: "Lit", value: bound, quoted }];
2285
+ }
2185
2286
  if (kind === "SglQuoted")
2186
2287
  return [{ kind: part.Dollar ? "Dynamic" : "Lit", value: part.Value ?? "", quoted: true }];
2187
2288
  if (kind === "ParamExp" && isDefaultExpansion(part) && !assigned.has(part.Param?.Value)) {
@@ -2189,14 +2290,14 @@ function executableParts(word, assigned, quoted = false) {
2189
2290
  if (fallback !== null) return [{ kind: "Lit", value: fallback, quoted }];
2190
2291
  }
2191
2292
  if (kind === "CmdSubst") {
2192
- const name = lookupSubstitution(part);
2193
- if (name !== null) return [{ kind: "Lit", value: name, quoted }];
2293
+ const name2 = lookupSubstitution(part);
2294
+ if (name2 !== null) return [{ kind: "Lit", value: name2, quoted }];
2194
2295
  }
2195
2296
  return [{ kind: "Dynamic", value: "", quoted }];
2196
2297
  });
2197
2298
  }
2198
- function opaqueExecutable(word, assigned) {
2199
- const parts = executableParts(word, assigned);
2299
+ function opaqueExecutable(word, assigned, known = EMPTY_NAMES) {
2300
+ const parts = executableParts(word, assigned, false, known);
2200
2301
  let lastSlash = -1;
2201
2302
  parts.forEach((p, i) => {
2202
2303
  if (p.kind === "Lit" && p.value.includes("/")) lastSlash = i;
@@ -2318,6 +2419,9 @@ function deriveBinaryOp(sample) {
2318
2419
  function isListOp(op) {
2319
2420
  return listOps().has(op);
2320
2421
  }
2422
+ function isPipeOp(op) {
2423
+ return pipeOps().has(op);
2424
+ }
2321
2425
  function listOps() {
2322
2426
  if (_listOps) return _listOps;
2323
2427
  _listOps = new Set(
@@ -2562,6 +2666,33 @@ function isProtectedCodePath(p) {
2562
2666
  if (matchSensitivePath(p) !== null) return true;
2563
2667
  return SYSTEM_AUTH_FILES.includes(p) || SYSTEM_AUTH_DIRS.some((d) => p === d || p.startsWith(d + "/"));
2564
2668
  }
2669
+ function isJailedCodePath(literal, jailPaths) {
2670
+ const value = trimTrailingSlashes(literal.replace(/\\/g, "/"));
2671
+ if (!value) return false;
2672
+ for (const raw of jailPaths) {
2673
+ const jail = trimTrailingSlashes(raw.trim().replace(/\\/g, "/"));
2674
+ if (!jail) continue;
2675
+ const home = homeTail(jail);
2676
+ if (home === null) {
2677
+ if (value === jail || value.startsWith(jail + "/")) return true;
2678
+ continue;
2679
+ }
2680
+ if (!home) continue;
2681
+ const tail = homeTail(value);
2682
+ if (tail !== null && (tail === home || tail.startsWith(home + "/"))) return true;
2683
+ }
2684
+ return false;
2685
+ }
2686
+ function trimTrailingSlashes(s) {
2687
+ let end = s.length;
2688
+ while (end > 0 && s.charCodeAt(end - 1) === 47) end--;
2689
+ return s.slice(0, end);
2690
+ }
2691
+ function homeTail(p) {
2692
+ const m = /^(?:~|\$\{?HOME\}?|\/home\/[^/]+|\/Users\/[^/]+|\/root)(?:\/(.*))?$/.exec(p);
2693
+ if (!m) return null;
2694
+ return m[1] ?? "";
2695
+ }
2565
2696
  function inlineExecDescription(r) {
2566
2697
  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.";
2567
2698
  return `${lead} Review the full script below before allowing it to execute.`;
@@ -2570,7 +2701,8 @@ function inlineExecReason(r) {
2570
2701
  const who = r.interp ? `This inline ${r.interp} code` : "This inline code";
2571
2702
  return r.evidence ? `${who} ${r.what} (${r.evidence})` : `${who} ${r.what}`;
2572
2703
  }
2573
- function inlineExecReview(command) {
2704
+ function inlineExecReview(command, jailPaths = []) {
2705
+ const isProtected = jailPaths.length === 0 ? isProtectedCodePath : (p) => isProtectedCodePath(p) || isJailedCodePath(p, jailPaths);
2574
2706
  const calls = inlineExecCalls(command);
2575
2707
  if (calls === PARSE_FAIL) {
2576
2708
  return INLINE_EXEC_FALLBACK.test(command) ? { outcome: "unreadable", interp: "", what: "could not be parsed" } : null;
@@ -2593,7 +2725,7 @@ function inlineExecReview(command) {
2593
2725
  what: `is ${call.interp} code, which node9 does not read`
2594
2726
  };
2595
2727
  } else {
2596
- const judged = judgeInlineCode(language, code, isProtectedCodePath);
2728
+ const judged = judgeInlineCode(language, code, isProtected);
2597
2729
  if (judged) finding = { ...judged, interp: call.interp };
2598
2730
  }
2599
2731
  if (finding?.outcome === "dangerous") return finding;
@@ -2774,6 +2906,11 @@ function recordTopLevelStmt(stmt) {
2774
2906
  if (t === "DeclClause") return ASSIGNMENT_HEADS.has(stmt.Cmd.Variant?.Value ?? "");
2775
2907
  return (stmt.Cmd.Args || []).length === 0 && (stmt.Cmd.Assigns || []).length > 0;
2776
2908
  }
2909
+ function arrayElements(value) {
2910
+ if (typeof value !== "string" || !value.startsWith(ARRAY_MARK)) return null;
2911
+ const body = value.slice(ARRAY_MARK.length);
2912
+ return body === "" ? [] : body.split(ARRAY_SEP);
2913
+ }
2777
2914
  function recordAssignments(n, at) {
2778
2915
  if (!assignmentTable) return;
2779
2916
  const t = n.kind;
@@ -2787,7 +2924,19 @@ function recordAssignments(n, at) {
2787
2924
  } else return;
2788
2925
  for (const a of assigns) {
2789
2926
  const name = a?.Name?.Value;
2790
- if (!name || !a.Value || a.Append) continue;
2927
+ if (!name) continue;
2928
+ if (a.Array) {
2929
+ const elems = (a.Array.Elems || []).map(
2930
+ (e) => e?.Index ? null : resolveWordLiteral(e?.Value)
2931
+ );
2932
+ const literal = elems.every((v) => v !== null);
2933
+ assignmentTable.set(name, {
2934
+ value: literal && !a.Append ? ARRAY_MARK + elems.join(ARRAY_SEP) : null,
2935
+ at
2936
+ });
2937
+ continue;
2938
+ }
2939
+ if (!a.Value || a.Append) continue;
2791
2940
  assignmentTable.set(name, { value: resolveWordLiteral(a.Value), at });
2792
2941
  }
2793
2942
  }
@@ -2798,7 +2947,33 @@ function resolveTrivialSubst(part) {
2798
2947
  const st = stmts[0];
2799
2948
  if ((st.Redirs || []).length > 0 || st.Negated || st.Background) return void 0;
2800
2949
  const cmd = st.Cmd;
2801
- if (!cmd || cmd.kind !== "CallExpr") return void 0;
2950
+ if (!cmd) return void 0;
2951
+ if (cmd.kind === "BinaryCmd" && pipeOps().has(cmd.Op)) {
2952
+ const left = cmd.X?.Cmd;
2953
+ const right = cmd.Y?.Cmd;
2954
+ if (!left || !right || left.kind !== "CallExpr" || right.kind !== "CallExpr") return void 0;
2955
+ if ((cmd.X.Redirs || []).length > 0 || (cmd.Y.Redirs || []).length > 0) return void 0;
2956
+ const input = trivialCallOutput(left);
2957
+ if (input === void 0) return void 0;
2958
+ const filter = (right.Args || []).map((a) => resolveWordLiteral(a));
2959
+ if (filter.some((w) => w === null)) return void 0;
2960
+ const f = baseWord(filter[0]);
2961
+ const flags = filter.slice(1);
2962
+ if (f === "base64" && flags.length === 1 && /^(-d|-D|--decode)$/.test(flags[0])) {
2963
+ try {
2964
+ const out = Buffer.from(input, "base64").toString("utf8");
2965
+ return /^[\x20-\x7e]*$/.test(out) ? out : void 0;
2966
+ } catch {
2967
+ return void 0;
2968
+ }
2969
+ }
2970
+ if (f === "rev" && flags.length === 0) return [...input].reverse().join("");
2971
+ return void 0;
2972
+ }
2973
+ if (cmd.kind !== "CallExpr") return void 0;
2974
+ return trivialCallOutput(cmd);
2975
+ }
2976
+ function trivialCallOutput(cmd) {
2802
2977
  if ((cmd.Assigns || []).length > 0) return void 0;
2803
2978
  const words = (cmd.Args || []).map((a) => resolveWordLiteral(a));
2804
2979
  if (words.length === 0 || words.some((w) => w === null)) return void 0;
@@ -2810,9 +2985,16 @@ function resolveTrivialSubst(part) {
2810
2985
  return rest.slice(i).join(" ");
2811
2986
  }
2812
2987
  if (head === "printf") {
2988
+ if (rest.length === 1 && /^(?:\\(?:[0-7]{1,3}|x[0-9A-Fa-f]{1,2}))+$/.test(rest[0])) {
2989
+ return rest[0].replace(
2990
+ /\\(?:x([0-9A-Fa-f]{1,2})|([0-7]{1,3}))/g,
2991
+ (_, hex, oct) => String.fromCharCode(hex ? parseInt(hex, 16) : parseInt(oct, 8))
2992
+ );
2993
+ }
2813
2994
  if (rest.length !== 2) return void 0;
2814
- if (!/^%s(\\n)?$/.test(rest[0])) return void 0;
2815
- return rest[1];
2995
+ const m = /^([^%]*)%s([^%]*?)(\\n)?$/.exec(rest[0]);
2996
+ if (!m) return void 0;
2997
+ return m[1] + rest[1] + m[2];
2816
2998
  }
2817
2999
  return void 0;
2818
3000
  }
@@ -2826,11 +3008,28 @@ function isPlainParam(p) {
2826
3008
  if (p.kind !== "ParamExp") return false;
2827
3009
  return !(p.Excl || p.Length || p.Width || p.Index || p.Slice || p.Repl || p.Exp);
2828
3010
  }
3011
+ function indexedParam(p) {
3012
+ if (p.kind !== "ParamExp" || !p.Index) return null;
3013
+ if (p.Excl || p.Length || p.Width || p.Slice || p.Repl || p.Exp) return null;
3014
+ const index = resolveWordLiteral(p.Index);
3015
+ const name = p.Param?.Value;
3016
+ return name && index !== null ? { name, index } : null;
3017
+ }
2829
3018
  function expandPlainParam(p) {
2830
3019
  if (!assignmentTable) return void 0;
3020
+ const indexed = indexedParam(p);
3021
+ if (indexed) {
3022
+ const elems = arrayElements(recordedExpansion(indexed.name));
3023
+ if (elems === null) return void 0;
3024
+ if (indexed.index === "@" || indexed.index === "*") return elems.join(" ");
3025
+ return /^\d+$/.test(indexed.index) ? elems[Number(indexed.index)] ?? null : null;
3026
+ }
2831
3027
  if (!isPlainParam(p)) return void 0;
2832
3028
  const recorded = recordedExpansion(p.Param?.Value);
2833
- if (recorded !== void 0) return recorded;
3029
+ if (recorded !== void 0) {
3030
+ const elems = arrayElements(recorded);
3031
+ return elems === null ? recorded : elems[0] ?? "";
3032
+ }
2834
3033
  return HOME_VARIABLES.has(p.Param?.Value) ? "~" : void 0;
2835
3034
  }
2836
3035
  function functionBodies(f) {
@@ -5777,7 +5976,59 @@ function dynamicOptionWord(word) {
5777
5976
  if (/^--[a-z-]+=/.test(prefix) || /^-[cC]/.test(prefix)) return `${prefix}$\u2026`;
5778
5977
  return null;
5779
5978
  }
5780
- function resolveGitWords(word) {
5979
+ function arrayExpansionWords(word) {
5980
+ let parts = word?.Parts || [];
5981
+ if (parts.length === 1 && parts[0].kind === "DblQuoted") parts = parts[0].Parts || [];
5982
+ if (parts.length !== 1 || parts[0].kind !== "ParamExp" || !parts[0].Index) return null;
5983
+ const index = resolveWordLiteral(parts[0].Index);
5984
+ if (index !== "@" && index !== "*") return null;
5985
+ const elems = arrayElements(recordedExpansion(parts[0].Param?.Value));
5986
+ return elems;
5987
+ }
5988
+ function producedLiteral(call) {
5989
+ if ((call.Assigns || []).length > 0) return null;
5990
+ const words = (call.Args || []).map((a) => resolveWordLiteral(a));
5991
+ if (words.length === 0 || words.some((w) => w === null)) return null;
5992
+ const head = baseWord(words[0]);
5993
+ const rest = words.slice(1);
5994
+ if (head === "git" && rest[0] === "rev-parse" && rest[1] === "--show-toplevel" && rest.length === 2)
5995
+ return ":/";
5996
+ if (head === "echo") {
5997
+ let i = 0;
5998
+ while (i < rest.length && /^-[neE]+$/.test(rest[i])) i++;
5999
+ return rest.length - i === 1 ? rest[i] : null;
6000
+ }
6001
+ if (head === "printf" && rest.length === 2 && /^%s(\\[0n])?$/.test(rest[0])) return rest[1];
6002
+ return null;
6003
+ }
6004
+ function treeTopWord(word, treeTopVars = NO_VARS) {
6005
+ let parts = word?.Parts || [];
6006
+ if (parts.length === 1 && parts[0].kind === "DblQuoted") parts = parts[0].Parts || [];
6007
+ if (parts.length !== 1) return false;
6008
+ const p = parts[0];
6009
+ if (p.kind === "ParamExp") {
6010
+ const name = plainParamName(word)?.name;
6011
+ return name === "PWD" || name !== void 0 && treeTopVars.has(name);
6012
+ }
6013
+ if (p.kind !== "CmdSubst") return false;
6014
+ const stmts = p.Stmts || [];
6015
+ if (stmts.length !== 1 || !stmts[0].Cmd || stmts[0].Cmd.kind !== "CallExpr") return false;
6016
+ const words = (stmts[0].Cmd.Args || []).map((a) => resolveWordLiteral(a));
6017
+ if (words.some((w) => w === null)) return false;
6018
+ const head = baseWord(words[0]);
6019
+ if (head === "pwd") return words.length === 1 || words[1] === "-P" || words[1] === "-L";
6020
+ return head === "git" && words[1] === "rev-parse" && words[2] === "--show-toplevel";
6021
+ }
6022
+ function resolveGitWords(word, treeTopVars = NO_VARS, appended = NO_APPENDS) {
6023
+ if (treeTopWord(word, treeTopVars)) return [":/"];
6024
+ const plain = plainParamName(word);
6025
+ if (plain && appended.has(plain.name)) {
6026
+ const v = appended.get(plain.name);
6027
+ if (v === null || v === void 0) return [null];
6028
+ return plain.quoted ? [v] : splitFields(v);
6029
+ }
6030
+ const elems = arrayExpansionWords(word);
6031
+ if (elems !== null) return elems;
5781
6032
  const value = resolveWordLiteral(word);
5782
6033
  if (value === null) {
5783
6034
  if (isPositionalParam(word)) return boundPositionals() ?? [POSITIONAL2];
@@ -5885,6 +6136,8 @@ function classify(inv) {
5885
6136
  if (o.flags.has("-n") || o.flags.has("--dry-run")) break;
5886
6137
  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("+"));
5887
6138
  if (forced) found(FORCE_PUSH);
6139
+ const deletes = o.flags.has("-d") || o.flags.has("--delete") || o.operands.some((r) => /^:[^:]/.test(r));
6140
+ if (deletes) found(DESTRUCTIVE);
5888
6141
  break;
5889
6142
  }
5890
6143
  case "reset": {
@@ -5912,11 +6165,72 @@ function classify(inv) {
5912
6165
  if (o.flags.has("-d") || o.flags.has("--delete")) found(DESTRUCTIVE);
5913
6166
  break;
5914
6167
  }
6168
+ // node9Firewall#385: history and work discarded through plumbing. A ref
6169
+ // deleted by hand, a stash dropped, the reflog expired or pruned: the
6170
+ // commits behind them are gone the way `branch -D` loses them.
6171
+ case "update-ref": {
6172
+ const o = parseOptions(inv.args, "m", /* @__PURE__ */ new Set());
6173
+ if (o.flags.has("-d") && (o.dynamic || o.operands.some(isHistoryRef))) found(DESTRUCTIVE);
6174
+ break;
6175
+ }
6176
+ case "reflog": {
6177
+ const o = parseOptions(inv.args, "", /* @__PURE__ */ new Set());
6178
+ if (o.operands[0] === "expire" || o.operands[0] === "delete") found(DESTRUCTIVE);
6179
+ break;
6180
+ }
6181
+ case "gc": {
6182
+ if (inv.config.get("gc.reflogexpire") === "now") found(DESTRUCTIVE);
6183
+ break;
6184
+ }
6185
+ case "stash": {
6186
+ const o = parseOptions(inv.args, "", /* @__PURE__ */ new Set());
6187
+ if (o.operands[0] === "clear" || o.operands[0] === "drop") found(DESTRUCTIVE);
6188
+ break;
6189
+ }
6190
+ case "worktree": {
6191
+ const o = parseOptions(inv.args, "", /* @__PURE__ */ new Set());
6192
+ if (o.operands[0] === "remove" && (o.flags.has("-f") || o.flags.has("--force")))
6193
+ found(DESTRUCTIVE);
6194
+ break;
6195
+ }
6196
+ // The working tree overwritten: `restore` from another ref, a forced
6197
+ // checkout, or `checkout -- .` (every local change dropped). A bare
6198
+ // `restore .` or `checkout .` stays an ordinary undo, as pinned.
6199
+ case "restore": {
6200
+ const o = parseOptions(inv.args, "s", RESTORE_LONG_VALUE);
6201
+ const src = restoreSource(inv.args);
6202
+ const whole = o.dynamic || o.operands.some(wholeTree);
6203
+ if (src !== null && src !== "HEAD" && whole) found(DESTRUCTIVE);
6204
+ break;
6205
+ }
6206
+ case "checkout": {
6207
+ const o = parseOptions(inv.args, "bB", CHECKOUT_LONG_VALUE);
6208
+ const forced = o.flags.has("-f") || o.flags.has("--force");
6209
+ const dropAll = inv.args.includes("--") && o.operands.some(wholeTree);
6210
+ if (forced || dropAll) found(DESTRUCTIVE);
6211
+ break;
6212
+ }
5915
6213
  default:
5916
6214
  break;
5917
6215
  }
5918
6216
  return out;
5919
6217
  }
6218
+ function isExcludePathspec(operand) {
6219
+ return /^:[!^]/.test(operand) || /^:\([^)]*\bexclude\b[^)]*\)/.test(operand);
6220
+ }
6221
+ function restoreSource(args) {
6222
+ for (let i = 0; i < args.length; i++) {
6223
+ const w = args[i];
6224
+ if (w === null) continue;
6225
+ if (w.startsWith("--source=")) return w.slice("--source=".length);
6226
+ if (w === "--source" || w === "-s") return args[i + 1] ?? null;
6227
+ if (/^-s./.test(w)) return w.slice(2);
6228
+ }
6229
+ return null;
6230
+ }
6231
+ function isHistoryRef(ref) {
6232
+ return /^refs\/(heads|stash)(\/|$)/.test(ref);
6233
+ }
5920
6234
  function killSwitchSet(name, value) {
5921
6235
  if (value === null) return false;
5922
6236
  switch (name) {
@@ -5974,6 +6288,7 @@ function hooksBypass(inv) {
5974
6288
  const o = parseOptions(inv.args, "mFCcut", COMMIT_LONG_VALUE);
5975
6289
  return o.flags.has("-n") || o.flags.has("--no-verify");
5976
6290
  }
6291
+ if (sub === "commit-tree") return true;
5977
6292
  if (sub !== void 0 && NO_VERIFY_SUBS.has(sub)) {
5978
6293
  return inv.args.some((a) => a === "--no-verify");
5979
6294
  }
@@ -6011,7 +6326,20 @@ function configWritesHooksPath(args) {
6011
6326
  return write;
6012
6327
  }
6013
6328
  function isHookPath(word) {
6014
- return /(^|[\\/])(\.git[\\/]hooks|\.husky)([\\/]|$)/.test(word);
6329
+ return /(^|[\\/])(\.git[\\/]hooks|\.husky|\.githooks)([\\/]|$)/.test(word);
6330
+ }
6331
+ function isLiveHookFile(word) {
6332
+ if (!isHookPath(word)) return false;
6333
+ const m = /(^|[\\/])(\.git[\\/]hooks|\.husky|\.githooks)(?:[\\/](.*))?$/.exec(word);
6334
+ if (!m) return false;
6335
+ const rest = m[3] ?? "";
6336
+ if (rest === "" || rest === "_") return true;
6337
+ const leaf = rest.replace(/^_[\\/]/, "");
6338
+ return /^[a-z-]+$/.test(leaf);
6339
+ }
6340
+ function hookBodyIsEmpty(text) {
6341
+ const lines = text.split(/\r?\n/).map((l) => l.trim()).filter((l) => l !== "" && !l.startsWith("#"));
6342
+ return lines.every((l) => /^(exit(\s+0)?|true|:|set\s+-[a-zA-Z]+|set\s+-o\s+\w+)$/.test(l));
6015
6343
  }
6016
6344
  function modeRemovesExecute(mode) {
6017
6345
  if (/^[0-7]{3,4}$/.test(mode)) {
@@ -6020,6 +6348,22 @@ function modeRemovesExecute(mode) {
6020
6348
  }
6021
6349
  return mode.split(",").some((part) => /^[ugoa]*-[rwst]*x/.test(part));
6022
6350
  }
6351
+ function redirOpIsAppend(redir) {
6352
+ return redir?.Op === appendOp();
6353
+ }
6354
+ function appendOp() {
6355
+ if (_appendOp !== null) return _appendOp;
6356
+ try {
6357
+ const f = parseShared("echo x >> y");
6358
+ _appendOp = typeof f === "symbol" ? -1 : f.Stmts[0].Redirs[0].Op;
6359
+ } catch {
6360
+ _appendOp = -1;
6361
+ }
6362
+ return _appendOp;
6363
+ }
6364
+ function hooksBypassed(shown) {
6365
+ return { ...HOOKS_BYPASS, shown, reason: `${HOOKS_BYPASS.reason}: ${shown}` };
6366
+ }
6023
6367
  function chmodDisarmsHook(args, shown) {
6024
6368
  let i = 0;
6025
6369
  while (i < args.length) {
@@ -6062,6 +6406,7 @@ function stagesEverything(inv) {
6062
6406
  const sub = inv.sub?.toLowerCase();
6063
6407
  const prefix = chdirPrefix(inv);
6064
6408
  const judge = (operands2) => {
6409
+ if (operands2.length > 0 && operands2.every(isExcludePathspec)) return {};
6065
6410
  let conditional = false;
6066
6411
  for (const operand of operands2) {
6067
6412
  if (!wholeTree(operand)) continue;
@@ -6354,7 +6699,7 @@ function evaluateAgentFileWrite(args, accesses, context, settings, shell, hooks
6354
6699
  }
6355
6700
  return null;
6356
6701
  }
6357
- function isPipeOp(op) {
6702
+ function isPipeOp2(op) {
6358
6703
  return !isListOp(op);
6359
6704
  }
6360
6705
  function isDupInOp(op) {
@@ -8387,7 +8732,7 @@ async function evaluatePolicyCore(config, toolName, args, context = {}, hooks =
8387
8732
  if (v?.logged) noteLogged(v);
8388
8733
  else if (v) candidates.push(v);
8389
8734
  };
8390
- const inline = reviewsEveryInline(config.settings.mode) ? detectInlineExec(shellCommand) ? void 0 : null : inlineExecReview(shellCommand);
8735
+ const inline = reviewsEveryInline(config.settings.mode) ? detectInlineExec(shellCommand) ? void 0 : null : inlineExecReview(shellCommand, config.policy.jailPaths);
8391
8736
  if (inline !== null) {
8392
8737
  consider("commands.inline-exec", {
8393
8738
  decision: "review",
@@ -8730,7 +9075,7 @@ function* validCardRuns(text, base2, exemptFractions = true) {
8730
9075
  let m;
8731
9076
  while ((m = re.exec(text)) !== null) {
8732
9077
  const digits = m[0].replace(/\D/g, "");
8733
- const fraction = exemptFractions && isDecimalFraction(text, m.index, digits.length === m[0].length);
9078
+ const fraction = exemptFractions && (isDecimalFraction(text, m.index, digits.length === m[0].length) || isInteriorDotNumber(m[0]));
8734
9079
  if (!fraction && validateLuhn(digits)) {
8735
9080
  yield { index: m.index, length: m[0].length, digits };
8736
9081
  } else {
@@ -8757,6 +9102,12 @@ function replaceValidCards(text, replace, { exemptFractions = true } = {}) {
8757
9102
  }
8758
9103
  return out;
8759
9104
  }
9105
+ function isInteriorDotNumber(run) {
9106
+ const first = run.indexOf(".");
9107
+ if (first <= 0 || first === run.length - 1) return false;
9108
+ if (run.indexOf(".", first + 1) !== -1) return false;
9109
+ return !/[-\s]/.test(run);
9110
+ }
8760
9111
  function isDecimalFraction(text, index, unseparated) {
8761
9112
  if (!unseparated) return false;
8762
9113
  const sep = text[index - 1];
@@ -9480,7 +9831,7 @@ function matchCanaryArgs(args, values) {
9480
9831
  return null;
9481
9832
  }
9482
9833
  }
9483
- var getFilename, __filename2, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, finance_default, identity_default, publishing_default, settings_default, branches_default, ACTION_TEMPLATES, ACTION_CHECKS, GAP, prog, httpTo, httpWrite, HTTP_WRITE, HTTP_WRITE_ANY, NOT_STRIPE_TEST, postTo, STRIPE_OBJECTS, APP_SHIELD_SPECS, APP_SHIELDS, APP_SHIELD_NAMES, APP_SERVER_GLOB, GENERAL_ACTION_RULES, BUILTIN_SHIELDS, SSRF_MAX_HOST, METADATA_ADDRESSES, STRICT_TIERS, METADATA_HOSTNAMES, v4Octets, TIER_REASON, CHECK_TEXT, LEVELS, MODE_VALUES, SOFT, STRICT, BLOCK_ALL, LEVEL_CHECKS, ASI, ASI_IDS, PACK_SHIELD_OWASP, PACK_RULE_OWASP, VERDICTS, CHECK_VERDICT_RANK, ALL, BUILTIN_DANGEROUS_WORDS, PRODUCT_CHECKS, SHIELD_RULE_NAME, SHIELD_RULES_FOLDED_INTO_PRODUCT, CHECKS, CHECK_BY_ID, LEGACY_TO_ID, PROJECT_JAIL_BLOCK_READ, OVERRIDE_PREFIX, LABEL_WRAPPERS, LEGACY_READERS, syntax, KINDS, STRUCTS, unknownKinds, INVISIBLE_RE, INVISIBLE_ONE, HOMOGLYPHS, HOMOGLYPH_RE, MAX_BLOBS, MIN_DECODED, BASE64_RUN_RE, HEX_RUN_RE, HEX_ESCAPE_RUN_RE, SYSTEM_AUTH_FILES, SYSTEM_AUTH_DIRS, PASSWORD_HASH_FILES, STARTUP_NAMES, SYSTEM_DIRS, SYSTEM_DIRS_EXEMPT, UPLOAD_SYSTEM_DIRS, AGENT_INSTRUCTION_FILES, AGENT_INSTRUCTION_DIRS, LEGACY_SYSTEM_PATH_RE, MAX_ALTERNATIVES, MAX_CALL_DEPTH, MAX_CALL_WALKS, MAX_XARGS_PAYLOAD, POSITIONAL, MAX_DEPTH, MAX_PAYLOAD_DEPTH, unique, clone, SENDING_STDIN, DOWNLOAD_SHORT_VALUE, DOWNLOAD_LONG_VALUE, DOWNLOAD_OUTPUT_FLAGS, DOWNLOAD_DIR_FLAGS, base, quoteGlob, asWord, ops, MAX_INLINE_CODE, WHAT, OUTSIDE, pyImport, either, PYTHON, NODE, PERL, RUBY, PHP, TABLES, STRING_LITERAL, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, codeBodiesCache, AST_CACHE_MAX, astCache, PARSE_FAIL, INTERPRETER_LINE, PROGRAM_DIR, ACCESS_VIEW_CACHE_MAX, accessViewCache, MAX_READING_PAYLOADS, STDIN_CODE_INTERPRETERS, RUNS_TEXT_ELSEWHERE, defaultExpansionOps, 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, BATCH_SEPARATOR, IMPERATIVE, SECRET_TARGET, GUARD, SIGNALS, DESTINATION_RE, REUSED_SIGNALS, NEVER_NEGATED, NEGATION_BEFORE, RDR_IN, COPY_VERBS2, LITERAL_VERBS, GIT_PRESCREEN_RE, MAX_PAYLOAD_DEPTH2, MAX_ALIAS_DEPTH, CACHE_MAX, cache, PAYLOAD_SHELLS, CONFIG_SCOPE_FLAGS, CONFIG_VALUE_FLAGS, GLOBAL_VALUE_OPTIONS, GLOBAL_FLAG_OPTIONS, MAX_LOOP_ITEMS, GitWalk, POSITIONAL2, 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, LOOP_MAX_RECORDS, CANARY_MIN_LENGTH, MAX_TEXT, MAX_DEPTH4, MAX_JSON_PARSE, URL_DEPTH, B64_DEPTH, MIN_SEGMENT, SEPARATORS, stripSeparators, looksText, CANARY_DECODERS, VIEWS, LONG_OUTPUT_THRESHOLD_BYTES;
9834
+ var getFilename, __filename2, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, finance_default, identity_default, publishing_default, settings_default, branches_default, ACTION_TEMPLATES, ACTION_CHECKS, GAP, prog, httpTo, httpWrite, HTTP_WRITE, HTTP_WRITE_ANY, NOT_STRIPE_TEST, postTo, STRIPE_OBJECTS, APP_SHIELD_SPECS, APP_SHIELDS, APP_SHIELD_NAMES, APP_SERVER_GLOB, GENERAL_ACTION_RULES, BUILTIN_SHIELDS, SSRF_MAX_HOST, METADATA_ADDRESSES, STRICT_TIERS, METADATA_HOSTNAMES, v4Octets, TIER_REASON, CHECK_TEXT, LEVELS, MODE_VALUES, SOFT, STRICT, BLOCK_ALL, LEVEL_CHECKS, ASI, ASI_IDS, PACK_SHIELD_OWASP, PACK_RULE_OWASP, VERDICTS, CHECK_VERDICT_RANK, ALL, BUILTIN_DANGEROUS_WORDS, PRODUCT_CHECKS, SHIELD_RULE_NAME, SHIELD_RULES_FOLDED_INTO_PRODUCT, CHECKS, CHECK_BY_ID, LEGACY_TO_ID, PROJECT_JAIL_BLOCK_READ, OVERRIDE_PREFIX, LABEL_WRAPPERS, LEGACY_READERS, syntax, KINDS, STRUCTS, unknownKinds, INVISIBLE_RE, INVISIBLE_ONE, HOMOGLYPHS, HOMOGLYPH_RE, MAX_BLOBS, MIN_DECODED, BASE64_RUN_RE, HEX_RUN_RE, HEX_ESCAPE_RUN_RE, SYSTEM_AUTH_FILES, SYSTEM_AUTH_DIRS, PASSWORD_HASH_FILES, STARTUP_NAMES, SYSTEM_DIRS, SYSTEM_DIRS_EXEMPT, UPLOAD_SYSTEM_DIRS, AGENT_INSTRUCTION_FILES, AGENT_INSTRUCTION_DIRS, LEGACY_SYSTEM_PATH_RE, MAX_ALTERNATIVES, MAX_CALL_DEPTH, MAX_CALL_WALKS, MAX_XARGS_PAYLOAD, POSITIONAL, MAX_DEPTH, MAX_PAYLOAD_DEPTH, unique, clone, SENDING_STDIN, DOWNLOAD_SHORT_VALUE, DOWNLOAD_LONG_VALUE, DOWNLOAD_OUTPUT_FLAGS, DOWNLOAD_DIR_FLAGS, base, quoteGlob, asWord, ops, MAX_INLINE_CODE, WHAT, OUTSIDE, pyImport, either, PYTHON, NODE, PERL, RUBY, PHP, TABLES, STRING_LITERAL, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, codeBodiesCache, AST_CACHE_MAX, astCache, PARSE_FAIL, INTERPRETER_LINE, PROGRAM_DIR, ACCESS_VIEW_CACHE_MAX, accessViewCache, MAX_READING_PAYLOADS, STDIN_CODE_INTERPRETERS, RUNS_TEXT_ELSEWHERE, defaultExpansionOps, EMPTY_NAMES, VARIABLE_READERS, PLAIN_PROGRAM_NAME, FS_READ_TOOLS, GREP_SHAPE, PATTERN_VERBS, PATTERN_VERB_NAMES, GREP_FILE_OPERANDS, READER_VALUE_LETTERS, FILE_OPERAND_FLAGS, SCP_VALUE_FLAGS, CURL_SOURCE_FLAGS, CURL_VALUE_LETTERS, WGET_VALUE_LETTERS, TAR_VALUE_LETTERS, ZIP_VALUE_LETTERS, RSYNC_VALUE_LETTERS, RSYNC_SKIP, CP_VALUE_LETTERS, INSTALL_VALUE_LETTERS, COPY_VERBS, TAR_MODE_WORD, COPY_VERB_HEADS, FS_OP_PRESCREEN_RE, HOME_CACHE_ALLOWLIST, SENSITIVE_PATH_RULES, AST_FS_REGEX_RULES, SQL_DB_CLIS, SQL_DDL_RE, CHMOD_OPEN_PERM_TOKENS, COMMAND_WRAPPERS, INLINE_INTERPRETER, RUNNER_WRAPPERS, _redirStdinOps, _listOps, WRAPPER_TAKES_TARGET, FIND_EXEC_FLAGS, FS_MUTATION_TOOLS, INTERP_LEADING_TARGET, MAX_PAYLOAD_NESTING, payloadNesting, MAX_INLINE_CALLS, INLINE_EXEC_FALLBACK, UNREADABLE_SOURCE, SCRIPT_RUNNERS, SCRIPT_RUNNER_VALUE_FLAGS, MAX_SCRIPT_OPERANDS, NET_BINARIES, VALUE_FLAGS, HOME_VARIABLES, assignmentTable, currentStmtOffset, PAYLOAD_BUDGET, payloadBudget, seenPayloads, UNANALYSABLE_NESTING, ASSIGNMENT_HEADS, andOrOps, ARRAY_MARK, ARRAY_SEP, MAX_CALL_DEPTH2, MAX_CALL_WALKS2, callDepth, callWalks, FS_OP_CACHE_MAX, fsOpCache, stripDotSlash, REDIR_TRUNCATE_OPS, REDIR_FILE_IN_OPS, REDIR_HEREDOC_OPS, FIND_OPTIONS, COPY_RULE_OF, INDIRECT_SHAPE_RE, SOURCES_FILE_RE, SHELL_SET_VARS, pipeOps, callWords, JAILED_GLOB_NAMES, JAILED_DIR_NAMES, shellGlob, GLOB_META, isReaderWord, positionalAfter, NONE, UNKNOWN, FORBIDDEN_PATH_SEGMENTS, BASH_TOOL_NAMES, MCP_EXECUTORS, BUILTIN_FIELDS, ARGV_FIELDS, BARE_WORD, MAX_ARGV_CHARACTERS, exempt, RANK, SEQUENCE_OPS, BIP39_ENGLISH, BIP39_PUBLIC_VECTORS, indices, lengths, hash, MAX_MNEMONIC_HASHES, SECRET_VIEW_LIMITS, IBAN_LENGTH, IBAN_BBAN_SHAPE, B58, B58_INDEX, XPRV_VERSIONS, B62, CRC32_TABLE, GITHUB_TOKEN_RE, B64URL, B64URL_INDEX, CASK_TWO_ZERO_SUFFIX, CASK_FOUR_ZERO_SUFFIX, CF_ODD, CF_EVEN, SPANISH_LETTERS, HETU_CHARS, VERHOEFF_D, VERHOEFF_P, MAX, UNTRUSTED_TOOLS, INJECTION_SIGNALS, MENTION_QUOTE_RE, MENTION_WORD_RE, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, REFERENCE_VALUE_RE, IDENTIFIER_VALUE_RE, PATH_VALUE_RE, DOTTED_REFERENCE_RE, CONCATENATION_RE, EMAIL_VALUE_RE, FIXTURE_NAME_RE, DERIVATIVE_NAME_RE, QUERY_CREDENTIAL_VALUE_RE, OTP_SECRET_RE, OTP_DOC_SECRETS, ASSIGNED_SECRET_VALUE_RE, ASSIGNED_SECRET_NAME_RE, NAME_KEYWORD_RE, ARN_CONTEXT_RE, CIPHER_KEY_NAME_RE, CIPHER_NAME_PARTS, CIPHER_KEY_VALUE_RE, LARAVEL_KEY_VALUE_RE, SQL_PASSWORD_VALUE_RE, GRAFANA_KEY_VALUE_RE, BASIC_VALUE_RE, BASIC_PAIR_RE, BASIC_HEADER_RE, BASIC_HEADER_PAIR_RE, BASIC_PLACEHOLDER_RE, BASIC_TEMPLATE_MARKERS, DROPBOX_SECRET_RE, OTP_CAPTURE_RE, DB_URL_PASSWORD_RE, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, SENSITIVE_PATH_REGEXES, MAX_DEPTH2, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, BATCH_SEPARATOR, IMPERATIVE, SECRET_TARGET, GUARD, SIGNALS, DESTINATION_RE, REUSED_SIGNALS, NEVER_NEGATED, NEGATION_BEFORE, RDR_IN, COPY_VERBS2, LITERAL_VERBS, GIT_PRESCREEN_RE, MAX_PAYLOAD_DEPTH2, MAX_ALIAS_DEPTH, CACHE_MAX, cache, PAYLOAD_SHELLS, CONFIG_SCOPE_FLAGS, CONFIG_VALUE_FLAGS, GLOBAL_VALUE_OPTIONS, GLOBAL_FLAG_OPTIONS, MAX_LOOP_ITEMS, GitWalk, POSITIONAL2, NO_VARS, NO_APPENDS, FORCE_PUSH, DESTRUCTIVE, UNREADABLE, PUSH_LONG_VALUE, BRANCH_LONG_VALUE, TAG_LONG_VALUE, CLEAN_LONG_VALUE, RESTORE_LONG_VALUE, CHECKOUT_LONG_VALUE, HOOKS_BYPASS, NO_VERIFY_SUBS, HOOK_KILL_SWITCHES, COMMIT_LONG_VALUE, CHMOD_OPTION_RE, _appendOp, STAGE_ALL, ADD_LONG_VALUE, CHECK_ID, RULE_NAME, INERT_LINE_RE, CHECK_ID2, INSTRUCTION_NAMES, SETTINGS_RE, HOOK_WORDS_RE, SHELLS, NETCATS, NET_CLIENTS, SENDERS, NETCAT_EXEC_LONG, DECL_DUMPS, PROC_ENVIRON_RE, DEV_NET_RE, SOCAT_PROGRAM_RE, SOCAT_NET_RE, PRESCREEN_RE, MAX_PAYLOAD_DEPTH3, CACHE_MAX2, cache2, dupInOp, REVERSE_SHELL_DESCRIPTION, ENV_EXFIL_DESCRIPTION, DYNAMIC_WORD, MULTICALL, SHELL_VALUE_OPTIONS, INTERPRETERS, OutboundWalk, NPM_INSTALL_VERBS, NPM_EXEC_VERBS, PNPM_INSTALL_VERBS, YARN_INSTALL_VERBS, BUN_INSTALL_VERBS, DLX_VERBS, NON_REGISTRY_RE, NPM_NAME_RE, EXACT_SEMVER_RE, NPM_VALUE_FLAGS, PNPM_VALUE_FLAGS, YARN_VALUE_FLAGS, BUN_VALUE_FLAGS, PIP_VALUE_FLAGS, UV_VALUE_FLAGS, POETRY_VALUE_FLAGS, PIPENV_VALUE_FLAGS, PIPX_VALUE_FLAGS, PY_SPEC_RE, PY_EXACT_RE, EXACT_VERSION_RE, GEM_NAME_RE, GEM_VALUE_FLAGS, BUNDLE_VALUE_FLAGS, CRATE_NAME_RE, CARGO_ADD_VALUE_FLAGS, CARGO_INSTALL_VALUE_FLAGS, GO_VALUE_FLAGS, GO_PATH_RE, NUGET_NAME_RE, DOTNET_ADD_VALUE_FLAGS, DOTNET_TOOL_VALUE_FLAGS, NUGET_VALUE_FLAGS, MAVEN_COORD_RE, COMPOSER_NAME_RE, COMPOSER_VALUE_FLAGS, EDITOR_HEADS, EXTENSION_ID_RE, MANAGER_HEADS, SHELLS2, MAX_NESTING, SHELL_VALUE_FLAGS, NPM_FAMILY, PIP_FAMILY, NPM_FETCH_VERBS, PIP_FETCH_VERBS, NPM_FLAGS, PIP_FLAGS, NPM_VARS, PIP_VARS, PRESCREEN_RE2, PIP_ATTACHED_RE, MAX_PAYLOAD_DEPTH4, PUBLIC_REGISTRY_HOSTS, TUNING_FIELDS, MAP_GOVERNED, KNOB_GOVERNED, CONFIGURABLE_CHECK_IDS, DEFAULT_EGRESS_ALLOWLIST, PRIVATE_HOST_SUFFIXES, SOURCE_COMMANDS, SINK_COMMANDS, OBFUSCATORS, SENSITIVE_PATTERNS, FLAGS_WITH_VALUES, DESTINATION_ARGS, MCP_DESTINATION_KEYS, ARGUMENT_SCAN_LIMITS, RUN_CHAR, runChar, VERDICT_RANK, SQL_DML_KEYWORDS, MAX_DESTINATIONS, MAX_SCAN_CHARS, EMAIL_RE, URL_RE, IBAN_RE, RECIPIENT_KEYS, DESTRUCTIVE_VERB_RE, PII_EMAIL_RE, PII_SSN_RE, PII_PHONE_RE, PII_CC16_RE, PII_CC15_RE, PII_CC14_RE, PII_CC13_RE, PII_CC19_RE, CARD_PATTERNS, PII_ROUTING_RE, ROUTING_LABEL_RE, ROUTING_LABEL_WINDOW, NATIONAL_ID_SHAPES, NATIONAL_ID_LABEL_RE, NATIONAL_ID_LABEL_CS_RE, idLabelIn, NATIONAL_ID_LABEL_WINDOW, NATIONAL_ID_GUARD_RE, PII_IBAN_RE, REALTIME_PII_PATTERNS, MAX_PII_SCAN_BYTES, IBAN_DESTINATION_KEYS, WHOLE_IBAN_RE, JSON_ESCAPE_RE, JSON_ESCAPES, MASKED_KINDS, ENTRY_RE, IBAN_TEXT_RE, EMAIL_TEXT_RE, ACCOUNT_KEY_RE, ACCOUNT_VALUE_RE, MAX_MASK_DEPTH, EMPTY, CURL_SEND_FLAGS, WGET_SEND_FLAGS, IWR_SEND_FLAGS, WRITE_METHODS, GH_READ, GH_WRITE, GH_HOSTNAME_FLAGS, GH_REPO_VALUE_FLAGS, GH_API_VALUE_FLAGS, GIT_FETCH, PUBLISH, SHELLS3, OWN_HEADS, MAX_DEPTH3, CWD_CHANGERS, TERMINAL_ESCAPE_RE, LOOP_MAX_RECORDS, CANARY_MIN_LENGTH, MAX_TEXT, MAX_DEPTH4, MAX_JSON_PARSE, URL_DEPTH, B64_DEPTH, MIN_SEGMENT, SEPARATORS, stripSeparators, looksText, CANARY_DECODERS, VIEWS, LONG_OUTPUT_THRESHOLD_BYTES;
9484
9835
  var init_dist = __esm({
9485
9836
  "packages/policy-engine/dist/index.mjs"() {
9486
9837
  "use strict";
@@ -12747,6 +13098,7 @@ var init_dist = __esm({
12747
13098
  "sqlite3"
12748
13099
  ]);
12749
13100
  defaultExpansionOps = null;
13101
+ EMPTY_NAMES = /* @__PURE__ */ new Set();
12750
13102
  VARIABLE_READERS = /* @__PURE__ */ new Set([
12751
13103
  "read",
12752
13104
  "mapfile",
@@ -12759,6 +13111,7 @@ var init_dist = __esm({
12759
13111
  "readonly",
12760
13112
  "printf"
12761
13113
  ]);
13114
+ PLAIN_PROGRAM_NAME = /^[A-Za-z_][\w.+-]*$/;
12762
13115
  FS_READ_TOOLS = /* @__PURE__ */ new Set([
12763
13116
  "cat",
12764
13117
  "less",
@@ -13653,6 +14006,8 @@ var init_dist = __esm({
13653
14006
  AND_OR_OPS: new Set([and, or].filter((o) => o !== null))
13654
14007
  };
13655
14008
  });
14009
+ ARRAY_MARK = "array";
14010
+ ARRAY_SEP = "";
13656
14011
  MAX_CALL_DEPTH2 = 4;
13657
14012
  MAX_CALL_WALKS2 = 256;
13658
14013
  callDepth = 0;
@@ -16982,7 +17337,22 @@ zoo`.split("\n");
16982
17337
  found;
16983
17338
  /** Aliases defined earlier in this command: name -> value (null = dynamic). */
16984
17339
  aliases = /* @__PURE__ */ new Map();
17340
+ /** node9Firewall#385: variables assigned a tree-top word (`scope="$PWD"`,
17341
+ * `root=$(git rev-parse --show-toplevel)`, `DIR=$(pwd)`), so the
17342
+ * variable reads as the top where it is used. */
17343
+ treeTopVars = /* @__PURE__ */ new Set();
17344
+ /** node9Firewall#385 (c): a string a script builds with `+=` from
17345
+ * literals (`mode=--ha; mode+=rd`). The jail's table does not follow an
17346
+ * append (JAIL-14a); the git rules read the built word, since a flag
17347
+ * spelled in two halves is still that flag. name -> value, null once a
17348
+ * half is unknown. */
17349
+ appended = /* @__PURE__ */ new Map();
16985
17350
  payloads = /* @__PURE__ */ new Set();
17351
+ /** node9Firewall#385 (a): the text this command writes to each path
17352
+ * (`cat > /tmp/x.sh <<'SH' ... SH`, `echo '...' > f`, `>>` appends), so
17353
+ * a later statement that runs the path (`bash f`, `sh f`, `./f`) is
17354
+ * judged on what it runs. null once a write to the path is unknown. */
17355
+ written = /* @__PURE__ */ new Map();
16986
17356
  /** `for NAME in a b c`: the literal items, or null when one is dynamic. */
16987
17357
  loopVars = /* @__PURE__ */ new Map();
16988
17358
  /** Walk one command string. Returns false when it did not parse. */
@@ -17001,6 +17371,25 @@ zoo`.split("\n");
17001
17371
  const type = n.kind;
17002
17372
  if (type === "Stmt") {
17003
17373
  this.visitHeredocShell(n, depth);
17374
+ this.visitPipeline(n, depth);
17375
+ this.visitHookWrite(n);
17376
+ this.recordScriptWrite(n);
17377
+ this.visitWrittenScript(n, depth);
17378
+ return true;
17379
+ }
17380
+ if (type === "Assign" && n.Name?.Value && n.Value) {
17381
+ const name = n.Name.Value;
17382
+ if (n.Append) {
17383
+ const prev = this.appended.has(name) ? this.appended.get(name) : resolveWordLiteral(n.Value) === null ? null : void 0;
17384
+ const base2 = prev === void 0 ? recordedExpansion(name) : prev;
17385
+ const piece = resolveWordLiteral(n.Value);
17386
+ this.appended.set(name, typeof base2 === "string" && piece !== null ? base2 + piece : null);
17387
+ this.treeTopVars.delete(name);
17388
+ return true;
17389
+ }
17390
+ this.appended.delete(name);
17391
+ if (treeTopWord(n.Value, this.treeTopVars)) this.treeTopVars.add(name);
17392
+ else this.treeTopVars.delete(name);
17004
17393
  return true;
17005
17394
  }
17006
17395
  if (type === "ForClause") {
@@ -17029,13 +17418,13 @@ zoo`.split("\n");
17029
17418
  const param = plainParamName(rawArgs[loopArg]);
17030
17419
  for (const item of this.loopVars.get(param.name)) {
17031
17420
  const words2 = rawArgs.flatMap(
17032
- (a, i) => i === loopArg ? param.quoted ? [item] : splitFields(item) : resolveGitWords(a)
17421
+ (a, i) => i === loopArg ? param.quoted ? [item] : splitFields(item) : resolveGitWords(a, this.treeTopVars, this.appended)
17033
17422
  );
17034
17423
  this.visitWords(words2, new Map(prefixEnv), depth, false);
17035
17424
  }
17036
17425
  return true;
17037
17426
  }
17038
- const words = rawArgs.flatMap((a) => resolveGitWords(a));
17427
+ const words = rawArgs.flatMap((a) => resolveGitWords(a, this.treeTopVars, this.appended));
17039
17428
  this.visitWords(words, prefixEnv, depth, false);
17040
17429
  return true;
17041
17430
  }
@@ -17060,6 +17449,23 @@ zoo`.split("\n");
17060
17449
  if (f) this.found(f);
17061
17450
  return;
17062
17451
  }
17452
+ if (headWord === "rm" || headWord === "unlink") {
17453
+ if (words.slice(head + 1).some((w) => w !== null && isLiveHookFile(w)))
17454
+ this.found(hooksBypassed(shownOf(words)));
17455
+ return;
17456
+ }
17457
+ if (headWord === "mv") {
17458
+ const operands2 = words.slice(head + 1).filter((w) => w === null || !w.startsWith("-"));
17459
+ const src = operands2[0];
17460
+ if (typeof src === "string" && isLiveHookFile(src)) this.found(hooksBypassed(shownOf(words)));
17461
+ return;
17462
+ }
17463
+ if (headWord === "perl") {
17464
+ const code = words.slice(head + 1).find((w) => w !== null && /\brename\b/.test(w));
17465
+ const m = code ? /rename\s*\(?\s*q[qw]?\s*[(\[{<'"]([^)\]}>'"]+)/.exec(code) : null;
17466
+ if (m && isLiveHookFile(m[1])) this.found(hooksBypassed(shownOf(words)));
17467
+ return;
17468
+ }
17063
17469
  if (depth < MAX_PAYLOAD_DEPTH2) {
17064
17470
  const payload = literalShellPayload(words, baseWord(words[0]));
17065
17471
  if (payload !== null) this.runPayload(payload, depth);
@@ -17070,6 +17476,127 @@ zoo`.split("\n");
17070
17476
  this.payloads.add(payload);
17071
17477
  this.run(payload, depth + 1);
17072
17478
  }
17479
+ /**
17480
+ * node9Firewall#385: a pipeline whose last stage reads its pathspec from
17481
+ * stdin (`git add --pathspec-from-file=-`) or runs git over the names it
17482
+ * is fed (`xargs -0 git add --`), with a producer that is one literal
17483
+ * (`git rev-parse --show-toplevel`, `printf '%s\0' .`): the literal is
17484
+ * the operand, and the call is judged with it.
17485
+ */
17486
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
17487
+ visitPipeline(stmt, depth) {
17488
+ const cmd = stmt.Cmd;
17489
+ if (!cmd || cmd.kind !== "BinaryCmd" || !isPipeOp(cmd.Op)) return;
17490
+ const stages = [];
17491
+ let cur = cmd;
17492
+ while (cur?.kind === "BinaryCmd" && isPipeOp(cur.Op)) {
17493
+ stages.unshift(cur.Y);
17494
+ const left = cur.X;
17495
+ if (left?.Cmd?.kind === "BinaryCmd" && isPipeOp(left.Cmd.Op)) cur = left.Cmd;
17496
+ else {
17497
+ stages.unshift(left);
17498
+ break;
17499
+ }
17500
+ }
17501
+ if (stages.length < 2) return;
17502
+ const producer = stages[stages.length - 2]?.Cmd;
17503
+ const consumer = stages[stages.length - 1]?.Cmd;
17504
+ if (!producer || !consumer || producer.kind !== "CallExpr" || consumer.kind !== "CallExpr")
17505
+ return;
17506
+ const fed = producedLiteral(producer);
17507
+ if (fed === null) return;
17508
+ const words = (consumer.Args || []).flatMap(
17509
+ (a) => resolveGitWords(a, this.treeTopVars, this.appended)
17510
+ );
17511
+ const head = unwrapCommandHead(words);
17512
+ const viaXargs = words.slice(0, head).some((w) => baseWord(w) === "xargs");
17513
+ const fromFile = words.findIndex(
17514
+ (w) => w === "--pathspec-from-file=-" || w === "--pathspec-from-file"
17515
+ );
17516
+ if (!viaXargs && fromFile < 0) return;
17517
+ let fedWords;
17518
+ if (fromFile >= 0) {
17519
+ fedWords = words.filter(
17520
+ (w, k) => !(k === fromFile || w === "-" && words[k - 1] === "--pathspec-from-file")
17521
+ );
17522
+ fedWords.push(fed);
17523
+ } else fedWords = [...words, fed];
17524
+ this.visitWords(fedWords, /* @__PURE__ */ new Map(), depth, false);
17525
+ }
17526
+ /**
17527
+ * node9Firewall#385 (e): a hook file written with a body that checks
17528
+ * nothing (`cat > .githooks/pre-commit <<'SH' exit 0 SH`, `echo 'exit 0'
17529
+ * > .git/hooks/pre-push`, `: > .githooks/pre-commit`). A real body (a
17530
+ * test, a linter) is ordinary work on the hooks.
17531
+ */
17532
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
17533
+ visitHookWrite(stmt) {
17534
+ const cmd = stmt.Cmd;
17535
+ if (!cmd || cmd.kind !== "CallExpr") return;
17536
+ const redirs = stmt.Redirs || [];
17537
+ const target = redirs.find((r) => r?.Word && !r.Hdoc && (r.N == null || r.N.Value === "1"));
17538
+ if (!target) return;
17539
+ const path210 = resolveWordLiteral(target.Word);
17540
+ if (path210 === null || !isLiveHookFile(path210)) return;
17541
+ const hdoc = redirs.find((r) => r?.Hdoc);
17542
+ const words = (cmd.Args || []).map((a) => resolveWordLiteral(a));
17543
+ const head = baseWord(words[0]);
17544
+ let body = null;
17545
+ if (hdoc) body = resolveWordLiteral(hdoc.Hdoc);
17546
+ else if (head === ":" || head === "true" || words.length === 0) body = "";
17547
+ else if (head === "echo" || head === "printf") {
17548
+ const rest = words.slice(1);
17549
+ body = rest.some((w) => w === null) ? null : rest.filter((w) => !/^-[neE]+$/.test(w)).join(" ");
17550
+ if (body !== null) body = body.replace(/\\n/g, "\n");
17551
+ }
17552
+ if (body !== null && hookBodyIsEmpty(body)) this.found(hooksBypassed(shownOf(words)));
17553
+ }
17554
+ /** A statement that writes a literal text to a path, by redirect. */
17555
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
17556
+ recordScriptWrite(stmt) {
17557
+ const cmd = stmt.Cmd;
17558
+ if (!cmd || cmd.kind !== "CallExpr") return;
17559
+ const redirs = stmt.Redirs || [];
17560
+ const target = redirs.find((r) => r?.Word && !r.Hdoc && (r.N == null || r.N.Value === "1"));
17561
+ if (!target) return;
17562
+ const path210 = resolveWordLiteral(target.Word);
17563
+ if (path210 === null) return;
17564
+ const append = /^>>/.test(target.Op?.toString() ?? "") || redirOpIsAppend(target);
17565
+ const hdoc = redirs.find((r) => r?.Hdoc);
17566
+ const words = (cmd.Args || []).map((a) => resolveWordLiteral(a));
17567
+ const head = baseWord(words[0]);
17568
+ let text = null;
17569
+ if (hdoc && (head === "cat" || head === "tee")) text = resolveWordLiteral(hdoc.Hdoc);
17570
+ else if (head === "echo" || head === "printf") {
17571
+ const rest = words.slice(1);
17572
+ text = rest.some((w) => w === null) ? null : rest.filter((w) => !/^-[neE]+$/.test(w)).join(" ");
17573
+ if (text !== null && head === "printf") text = text.replace(/\\n/g, "\n");
17574
+ } else return;
17575
+ const prev = this.written.get(path210);
17576
+ if (text === null || append && prev === null) this.written.set(path210, null);
17577
+ else this.written.set(path210, append && typeof prev === "string" ? `${prev}
17578
+ ${text}` : text);
17579
+ }
17580
+ /** A statement that runs a path this command wrote: the text is the script. */
17581
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
17582
+ visitWrittenScript(stmt, depth) {
17583
+ const cmd = stmt.Cmd;
17584
+ if (!cmd || cmd.kind !== "CallExpr" || this.written.size === 0 || depth >= MAX_PAYLOAD_DEPTH2)
17585
+ return;
17586
+ const words = (cmd.Args || []).map((a) => resolveWordLiteral(a));
17587
+ const head = unwrapCommandHead(words);
17588
+ const name = baseWord(words[head]);
17589
+ let ran = null;
17590
+ if (PAYLOAD_SHELLS.has(name) && name !== "eval") {
17591
+ const operand = words.slice(head + 1).find((w) => w === null || !w.startsWith("-"));
17592
+ if (typeof operand === "string") ran = operand;
17593
+ } else if (typeof words[head] === "string" && this.written.has(words[head])) {
17594
+ ran = words[head];
17595
+ }
17596
+ if (ran === null) return;
17597
+ const text = this.written.get(ran);
17598
+ if (typeof text === "string") this.runPayload(text, depth);
17599
+ }
17073
17600
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
17074
17601
  visitHeredocShell(stmt, depth) {
17075
17602
  const cmd = stmt.Cmd;
@@ -17221,6 +17748,8 @@ zoo`.split("\n");
17221
17748
  }
17222
17749
  };
17223
17750
  POSITIONAL2 = "\0positional";
17751
+ NO_VARS = /* @__PURE__ */ new Set();
17752
+ NO_APPENDS = /* @__PURE__ */ new Map();
17224
17753
  FORCE_PUSH = {
17225
17754
  checkId: "commands.git-destructive",
17226
17755
  ruleName: "review-force-push",
@@ -17279,6 +17808,8 @@ zoo`.split("\n");
17279
17808
  "--cleanup"
17280
17809
  ]);
17281
17810
  CLEAN_LONG_VALUE = /* @__PURE__ */ new Set(["--exclude"]);
17811
+ RESTORE_LONG_VALUE = /* @__PURE__ */ new Set(["--source", "--pathspec-from-file"]);
17812
+ CHECKOUT_LONG_VALUE = /* @__PURE__ */ new Set(["--pathspec-from-file", "--orphan", "--conflict"]);
17282
17813
  HOOKS_BYPASS = {
17283
17814
  checkId: "commands.git-hooks-bypass",
17284
17815
  ruleName: "review-git-hooks-bypass",
@@ -17304,6 +17835,7 @@ zoo`.split("\n");
17304
17835
  "--gpg-sign"
17305
17836
  ]);
17306
17837
  CHMOD_OPTION_RE = /^-[RvcfHLP]+$/;
17838
+ _appendOp = null;
17307
17839
  STAGE_ALL = {
17308
17840
  checkId: "commands.git-stage-all",
17309
17841
  ruleName: "review-git-stage-all",
@@ -17428,7 +17960,7 @@ zoo`.split("\n");
17428
17960
  this.visitStmt(n, depth);
17429
17961
  return true;
17430
17962
  }
17431
- if (type === "BinaryCmd" && isPipeOp(n.Op)) {
17963
+ if (type === "BinaryCmd" && isPipeOp2(n.Op)) {
17432
17964
  this.visitPipeline(n);
17433
17965
  return true;
17434
17966
  }
@@ -17529,7 +18061,7 @@ zoo`.split("\n");
17529
18061
  const stages = [];
17530
18062
  const flatten = (s) => {
17531
18063
  const c = s?.Cmd;
17532
- if (c && c.kind === "BinaryCmd" && isPipeOp(c.Op)) {
18064
+ if (c && c.kind === "BinaryCmd" && isPipeOp2(c.Op)) {
17533
18065
  flatten(c.X);
17534
18066
  flatten(c.Y);
17535
18067
  } else stages.push(s);
@@ -18942,9 +19474,9 @@ init_audit();
18942
19474
 
18943
19475
  // src/config/index.ts
18944
19476
  init_esm_shims();
18945
- import fs4 from "fs";
18946
- import path5 from "path";
18947
- import os4 from "os";
19477
+ import fs5 from "fs";
19478
+ import path6 from "path";
19479
+ import os5 from "os";
18948
19480
 
18949
19481
  // src/config-schema.ts
18950
19482
  init_esm_shims();
@@ -19237,11 +19769,18 @@ function slug(s) {
19237
19769
  var B = "[\\s/\\\\]";
19238
19770
  var SEP = "[/\\\\]";
19239
19771
  function pathToRegexFragment(rawPath) {
19240
- const tail = rawPath.trim().replace(/^~[\\/]?/, "").replace(/^\$\{?HOME\}?[\\/]?/, "").replace(/^\/(?:home|Users)\/[^\\/]+[\\/]?/, "").replace(/^[A-Za-z]:[\\/]Users[\\/][^\\/]+[\\/]?/, "").replace(/^[\\/]+/, "").replace(/[\\/]+$/, "");
19772
+ const trimmed = rawPath.trim();
19773
+ const tail = trimmed.replace(/^~[\\/]?/, "").replace(/^\$\{?HOME\}?[\\/]?/, "").replace(/^\/(?:home|Users)\/[^\\/]+[\\/]?/, "").replace(/^[A-Za-z]:[\\/]Users[\\/][^\\/]+[\\/]?/, "").replace(/^[\\/]+/, "").replace(/[\\/]+$/, "");
19241
19774
  const segments2 = tail.split(/[\\/]+/).filter(Boolean).map(escapeRegex);
19242
19775
  if (segments2.length === 0) return "";
19776
+ if (isAbsoluteOutsideHome(trimmed)) {
19777
+ return `(^|[^A-Za-z0-9_.~\\/-])/${segments2.join(SEP)}(${B}|$)`;
19778
+ }
19243
19779
  return `(^|${B})${segments2.join(SEP)}(${B}|$)`;
19244
19780
  }
19781
+ function isAbsoluteOutsideHome(p) {
19782
+ return p.startsWith("/") && !/^\/(?:home|Users|root)(?:\/|$)/.test(p);
19783
+ }
19245
19784
  function pathMatchesFragment(candidate, rawPath) {
19246
19785
  const value = pathToRegexFragment(rawPath);
19247
19786
  if (!value || !candidate) return false;
@@ -19507,17 +20046,57 @@ function applyManagedApprovers(local, managed) {
19507
20046
  };
19508
20047
  }
19509
20048
 
19510
- // src/auth/trusted-hosts.ts
20049
+ // src/shields/jail.ts
19511
20050
  init_esm_shims();
19512
20051
  import fs3 from "fs";
19513
- import path4 from "path";
19514
20052
  import os3 from "os";
20053
+ import path4 from "path";
20054
+ init_dist();
20055
+ var USER_JAIL_SHIELD = "user-jail";
20056
+ function jailStorePath() {
20057
+ return path4.join(os3.homedir(), ".node9", "jail-paths.json");
20058
+ }
20059
+ function readJailPaths() {
20060
+ let text;
20061
+ try {
20062
+ text = fs3.readFileSync(jailStorePath(), "utf8");
20063
+ } catch (err) {
20064
+ if (err.code === "ENOENT") return [];
20065
+ throw err;
20066
+ }
20067
+ let parsed;
20068
+ try {
20069
+ parsed = JSON.parse(text);
20070
+ } catch {
20071
+ throw new Error(`${jailStorePath()} is not valid JSON \u2014 fix it before changing the jail.`);
20072
+ }
20073
+ if (!Array.isArray(parsed.paths)) return [];
20074
+ return parsed.paths.filter(
20075
+ (p) => !!p && typeof p.path === "string" && (p.verdict === "block" || p.verdict === "review")
20076
+ );
20077
+ }
20078
+ function findJailedPath(candidate) {
20079
+ return findJailedPathIn(candidate, readJailPaths());
20080
+ }
20081
+ function findJailedPathIn(candidate, paths) {
20082
+ if (!candidate) return null;
20083
+ for (const entry of paths) {
20084
+ if (pathMatchesFragment(candidate, entry.path)) return entry;
20085
+ }
20086
+ return null;
20087
+ }
20088
+
20089
+ // src/auth/trusted-hosts.ts
20090
+ init_esm_shims();
20091
+ import fs4 from "fs";
20092
+ import path5 from "path";
20093
+ import os4 from "os";
19515
20094
  function getTrustedHostsPath() {
19516
- return path4.join(os3.homedir(), ".node9", "trusted-hosts.json");
20095
+ return path5.join(os4.homedir(), ".node9", "trusted-hosts.json");
19517
20096
  }
19518
20097
  function readTrustedHosts() {
19519
20098
  try {
19520
- const raw = fs3.readFileSync(getTrustedHostsPath(), "utf8");
20099
+ const raw = fs4.readFileSync(getTrustedHostsPath(), "utf8");
19521
20100
  const parsed = JSON.parse(raw);
19522
20101
  return Array.isArray(parsed.hosts) ? parsed.hosts : [];
19523
20102
  } catch {
@@ -19528,7 +20107,7 @@ var _cache = null;
19528
20107
  var CACHE_TTL_MS = 5e3;
19529
20108
  function getFileMtime() {
19530
20109
  try {
19531
- return fs3.statSync(getTrustedHostsPath()).mtimeMs;
20110
+ return fs4.statSync(getTrustedHostsPath()).mtimeMs;
19532
20111
  } catch {
19533
20112
  return 0;
19534
20113
  }
@@ -20013,7 +20592,8 @@ var DEFAULT_CONFIG = {
20013
20592
  trustedHostsManaged: false,
20014
20593
  appPermissions: {},
20015
20594
  appLinks: {},
20016
- managedJailPaths: []
20595
+ managedJailPaths: [],
20596
+ jailPaths: []
20017
20597
  },
20018
20598
  environments: {}
20019
20599
  };
@@ -20082,8 +20662,8 @@ function noteRejectedApiUrl(raw) {
20082
20662
  if (rejectedApiUrlsSeen.has(key)) return;
20083
20663
  rejectedApiUrlsSeen.add(key);
20084
20664
  try {
20085
- fs4.appendFileSync(
20086
- path5.join(os4.homedir(), ".node9", "hook-debug.log"),
20665
+ fs5.appendFileSync(
20666
+ path6.join(os5.homedir(), ".node9", "hook-debug.log"),
20087
20667
  `[${(/* @__PURE__ */ new Date()).toISOString()}] REFUSED apiUrl, using the default instead: ${String(raw).slice(0, 200).replace(/[\r\n]+/g, " ")}
20088
20668
  `
20089
20669
  );
@@ -20100,9 +20680,9 @@ function getCredentials() {
20100
20680
  };
20101
20681
  }
20102
20682
  try {
20103
- const credPath = path5.join(os4.homedir(), ".node9", "credentials.json");
20104
- if (fs4.existsSync(credPath)) {
20105
- const creds = JSON.parse(fs4.readFileSync(credPath, "utf-8"));
20683
+ const credPath = path6.join(os5.homedir(), ".node9", "credentials.json");
20684
+ if (fs5.existsSync(credPath)) {
20685
+ const creds = JSON.parse(fs5.readFileSync(credPath, "utf-8"));
20106
20686
  const profileName = process.env.NODE9_PROFILE || "default";
20107
20687
  const profile = creds[profileName];
20108
20688
  if (typeof profile?.apiKey === "string" && profile.apiKey.length > 0) {
@@ -20134,7 +20714,7 @@ function readRulesCacheResilient(cacheFile) {
20134
20714
  for (let attempt = 0; attempt < 3; attempt++) {
20135
20715
  let content;
20136
20716
  try {
20137
- content = fs4.readFileSync(cacheFile, "utf-8");
20717
+ content = fs5.readFileSync(cacheFile, "utf-8");
20138
20718
  existed = true;
20139
20719
  } catch (err) {
20140
20720
  if (err.code === "ENOENT") return {};
@@ -20149,10 +20729,10 @@ function readRulesCacheResilient(cacheFile) {
20149
20729
  }
20150
20730
  }
20151
20731
  if (existed || sawReadError) {
20152
- const backup = path5.join(path5.dirname(cacheFile), "rules-cache.last-good.json");
20732
+ const backup = path6.join(path6.dirname(cacheFile), "rules-cache.last-good.json");
20153
20733
  if (backup !== cacheFile) {
20154
20734
  try {
20155
- const raw = JSON.parse(fs4.readFileSync(backup, "utf-8"));
20735
+ const raw = JSON.parse(fs5.readFileSync(backup, "utf-8"));
20156
20736
  logCacheReadIssue(cacheFile, "RULES_CACHE_CORRUPT_USED_BACKUP");
20157
20737
  lastParsedRulesCache = raw;
20158
20738
  return raw;
@@ -20175,8 +20755,8 @@ function logCacheReadIssue(cacheFile, kind) {
20175
20755
  if (now - cacheReadLastLoggedAt < CACHE_LOG_REARM_MS) return;
20176
20756
  cacheReadLastLoggedAt = now;
20177
20757
  try {
20178
- fs4.appendFileSync(
20179
- path5.join(os4.homedir(), ".node9", "hook-debug.log"),
20758
+ fs5.appendFileSync(
20759
+ path6.join(os5.homedir(), ".node9", "hook-debug.log"),
20180
20760
  `[${(/* @__PURE__ */ new Date()).toISOString()}] ${kind} ${cacheFile}
20181
20761
  `
20182
20762
  );
@@ -20202,8 +20782,8 @@ function catalogSettingsFrom(settings, policy) {
20202
20782
  }
20203
20783
  function getConfig(cwd) {
20204
20784
  if (!cwd && cachedConfig) return cachedConfig;
20205
- const globalPath = path5.join(os4.homedir(), ".node9", "config.json");
20206
- const projectPath = path5.join(cwd ?? process.cwd(), "node9.config.json");
20785
+ const globalPath = path6.join(os5.homedir(), ".node9", "config.json");
20786
+ const projectPath = path6.join(cwd ?? process.cwd(), "node9.config.json");
20207
20787
  const globalConfig = tryLoadConfig(globalPath);
20208
20788
  const projectConfig = tryLoadConfig(projectPath, { project: true });
20209
20789
  const mergedSettings = {
@@ -20253,7 +20833,8 @@ function getConfig(cwd) {
20253
20833
  trustedHostsManaged: false,
20254
20834
  appPermissions: {},
20255
20835
  appLinks: {},
20256
- managedJailPaths: []
20836
+ managedJailPaths: [],
20837
+ jailPaths: []
20257
20838
  };
20258
20839
  const mergedEnvironments = { ...DEFAULT_CONFIG.environments };
20259
20840
  const rank = (v) => {
@@ -20448,7 +21029,7 @@ function getConfig(cwd) {
20448
21029
  let cloudMandatesEnforcement = false;
20449
21030
  let cloudMandatesAppPerm = false;
20450
21031
  {
20451
- const cacheFile = path5.join(os4.homedir(), ".node9", "rules-cache.json");
21032
+ const cacheFile = path6.join(os5.homedir(), ".node9", "rules-cache.json");
20452
21033
  try {
20453
21034
  const raw = readRulesCacheResilient(cacheFile);
20454
21035
  if (Array.isArray(raw.rules) && raw.rules.length > 0) {
@@ -20648,6 +21229,7 @@ function getConfig(cwd) {
20648
21229
  mergedPolicy.smartRules.push({ ...r, name: `org:${r.name}` });
20649
21230
  }
20650
21231
  mergedPolicy.managedJailPaths.push({ path: path33, verdict });
21232
+ mergedPolicy.jailPaths.push(path33);
20651
21233
  }
20652
21234
  }
20653
21235
  if (Array.isArray(mc.trustedHosts)) {
@@ -20723,6 +21305,11 @@ function getConfig(cwd) {
20723
21305
  }
20724
21306
  }
20725
21307
  mergedPolicy.appliedShields = appliedShields.sort();
21308
+ if (appliedShields.includes(USER_JAIL_SHIELD)) {
21309
+ for (const { path: path33 } of readJailPaths()) {
21310
+ if (!mergedPolicy.jailPaths.includes(path33)) mergedPolicy.jailPaths.push(path33);
21311
+ }
21312
+ }
20726
21313
  const existingAdvisoryNames = new Set(mergedPolicy.smartRules.map((r) => r.name));
20727
21314
  const cc = mergedPolicy.commandChecks ?? {};
20728
21315
  const advisoryKnobKey = (name) => {
@@ -20880,10 +21467,10 @@ function getConfig(cwd) {
20880
21467
  }
20881
21468
  function tryLoadConfig(filePath, options = {}) {
20882
21469
  v2ChecksByPath.delete(filePath);
20883
- if (!fs4.existsSync(filePath)) return null;
21470
+ if (!fs5.existsSync(filePath)) return null;
20884
21471
  let raw;
20885
21472
  try {
20886
- raw = JSON.parse(fs4.readFileSync(filePath, "utf-8"));
21473
+ raw = JSON.parse(fs5.readFileSync(filePath, "utf-8"));
20887
21474
  } catch (err) {
20888
21475
  const msg = err instanceof Error ? err.message : String(err);
20889
21476
  process.stderr.write(
@@ -20989,27 +21576,27 @@ init_dist();
20989
21576
  // src/policy/index.ts
20990
21577
  init_esm_shims();
20991
21578
  init_dist();
20992
- import fs13 from "fs";
21579
+ import fs14 from "fs";
20993
21580
 
20994
21581
  // src/utils/read-capped.ts
20995
21582
  init_esm_shims();
20996
- import fs5 from "fs";
21583
+ import fs6 from "fs";
20997
21584
  function readCapped(file2, maxBytes, openFlags = "r") {
20998
21585
  if (maxBytes < 0) return null;
20999
21586
  let fd;
21000
21587
  try {
21001
- fd = fs5.openSync(file2, openFlags);
21588
+ fd = fs6.openSync(file2, openFlags);
21002
21589
  } catch {
21003
21590
  return null;
21004
21591
  }
21005
21592
  try {
21006
- const st = fs5.fstatSync(fd);
21593
+ const st = fs6.fstatSync(fd);
21007
21594
  if (!st.isFile()) return null;
21008
21595
  const want = Math.min(st.size, maxBytes + 1);
21009
21596
  const buf = Buffer.alloc(want);
21010
21597
  let read2 = 0;
21011
21598
  while (read2 < want) {
21012
- const n = fs5.readSync(fd, buf, read2, want - read2, read2);
21599
+ const n = fs6.readSync(fd, buf, read2, want - read2, read2);
21013
21600
  if (n <= 0) break;
21014
21601
  read2 += n;
21015
21602
  }
@@ -21021,7 +21608,7 @@ function readCapped(file2, maxBytes, openFlags = "r") {
21021
21608
  return null;
21022
21609
  } finally {
21023
21610
  try {
21024
- fs5.closeSync(fd);
21611
+ fs6.closeSync(fd);
21025
21612
  } catch {
21026
21613
  }
21027
21614
  }
@@ -21032,24 +21619,24 @@ function readCappedText(file2, maxBytes, openFlags = "r") {
21032
21619
  }
21033
21620
 
21034
21621
  // src/policy/index.ts
21035
- import path14 from "path";
21622
+ import path15 from "path";
21036
21623
 
21037
21624
  // src/dlp.ts
21038
21625
  init_esm_shims();
21039
21626
  init_dist();
21040
21627
  init_dist();
21041
- import fs6 from "fs";
21042
- import path6 from "path";
21628
+ import fs7 from "fs";
21629
+ import path7 from "path";
21043
21630
  function scanFilePath(filePath, cwd = process.cwd(), options = {}) {
21044
21631
  if (!filePath) return null;
21045
21632
  let resolved;
21046
21633
  try {
21047
- const absolute2 = path6.resolve(cwd, filePath);
21048
- resolved = fs6.realpathSync.native(absolute2);
21634
+ const absolute2 = path7.resolve(cwd, filePath);
21635
+ resolved = fs7.realpathSync.native(absolute2);
21049
21636
  } catch (err) {
21050
21637
  const code = err.code;
21051
21638
  if (code === "ENOENT" || code === "ENOTDIR") {
21052
- resolved = path6.resolve(cwd, filePath);
21639
+ resolved = path7.resolve(cwd, filePath);
21053
21640
  } else {
21054
21641
  return sensitivePathMatch(filePath);
21055
21642
  }
@@ -21272,29 +21859,29 @@ function combineWithEngine(gates, engine, opts = { panicMode: false }) {
21272
21859
  // src/policy/file-access.ts
21273
21860
  init_esm_shims();
21274
21861
  init_dist();
21275
- import fs7 from "fs";
21276
- import os5 from "os";
21277
- import path7 from "path";
21862
+ import fs8 from "fs";
21863
+ import os6 from "os";
21864
+ import path8 from "path";
21278
21865
  function absoluteSpelling(p, cwd, home) {
21279
21866
  if (p === "~" || p.startsWith("~/")) p = home + p.slice(1);
21280
- return path7.isAbsolute(p) ? p : `${cwd}/${p}`;
21867
+ return path8.isAbsolute(p) ? p : `${cwd}/${p}`;
21281
21868
  }
21282
21869
  function physical(p, followFinal, links = 0) {
21283
21870
  if (links > 40) throw new Error("Too many symbolic links");
21284
21871
  if (!followFinal) {
21285
- const parent = physical(path7.dirname(p), true, links);
21286
- return { path: `${parent.path}/${path7.basename(p)}`, missing: parent.missing };
21872
+ const parent = physical(path8.dirname(p), true, links);
21873
+ return { path: `${parent.path}/${path8.basename(p)}`, missing: parent.missing };
21287
21874
  }
21288
21875
  try {
21289
- return { path: fs7.realpathSync.native(p), missing: false };
21876
+ return { path: fs8.realpathSync.native(p), missing: false };
21290
21877
  } catch (error) {
21291
21878
  const code = error.code;
21292
21879
  if (!["ENOENT", "ENOTDIR"].includes(code ?? "")) throw error;
21293
21880
  try {
21294
- if (fs7.lstatSync(p).isSymbolicLink()) {
21295
- const target = fs7.readlinkSync(p);
21881
+ if (fs8.lstatSync(p).isSymbolicLink()) {
21882
+ const target = fs8.readlinkSync(p);
21296
21883
  return physical(
21297
- path7.isAbsolute(target) ? target : `${path7.dirname(p)}/${target}`,
21884
+ path8.isAbsolute(target) ? target : `${path8.dirname(p)}/${target}`,
21298
21885
  true,
21299
21886
  links + 1
21300
21887
  );
@@ -21303,13 +21890,13 @@ function physical(p, followFinal, links = 0) {
21303
21890
  if (!["ENOENT", "ENOTDIR"].includes(linkError.code ?? ""))
21304
21891
  throw linkError;
21305
21892
  }
21306
- const parent = path7.dirname(p);
21893
+ const parent = path8.dirname(p);
21307
21894
  if (parent === p) return { path: p, missing: true };
21308
21895
  const resolved = physical(parent, true, links);
21309
- return { path: `${resolved.path}/${path7.basename(p)}`, missing: true };
21896
+ return { path: `${resolved.path}/${path8.basename(p)}`, missing: true };
21310
21897
  }
21311
21898
  }
21312
- function prepareFileAccesses(tool, args, cwd = process.cwd(), inspection = {}, home = os5.homedir()) {
21899
+ function prepareFileAccesses(tool, args, cwd = process.cwd(), inspection = {}, home = os6.homedir()) {
21313
21900
  const context = { cwd, homeDirs: [home, "/root"] };
21314
21901
  const accesses = collectFileAccesses(tool, args, context, inspection);
21315
21902
  const handledPaths = /* @__PURE__ */ new Set();
@@ -21332,14 +21919,14 @@ function prepareFileAccesses(tool, args, cwd = process.cwd(), inspection = {}, h
21332
21919
  const raw = absoluteSpelling(access.path, access.cwd ?? cwd, home);
21333
21920
  if (access.directoryTarget) {
21334
21921
  try {
21335
- if (!fs7.statSync(absoluteSpelling(access.directoryTarget, access.cwd ?? cwd, home)).isDirectory())
21922
+ if (!fs8.statSync(absoluteSpelling(access.directoryTarget, access.cwd ?? cwd, home)).isDirectory())
21336
21923
  continue;
21337
21924
  } catch {
21338
21925
  }
21339
21926
  }
21340
21927
  if (access.operation === "create") {
21341
21928
  try {
21342
- fs7.statSync(raw);
21929
+ fs8.statSync(raw);
21343
21930
  continue;
21344
21931
  } catch {
21345
21932
  }
@@ -21391,39 +21978,39 @@ function prepareFileAccesses(tool, args, cwd = process.cwd(), inspection = {}, h
21391
21978
  init_esm_shims();
21392
21979
  init_dist();
21393
21980
  import picomatch3 from "picomatch";
21394
- import path11 from "path";
21981
+ import path12 from "path";
21395
21982
 
21396
21983
  // src/supply-chain/osv-index.ts
21397
21984
  init_esm_shims();
21398
21985
  init_dist();
21399
- import fs9 from "fs";
21400
- import os6 from "os";
21401
- import path9 from "path";
21986
+ import fs10 from "fs";
21987
+ import os7 from "os";
21988
+ import path10 from "path";
21402
21989
  import { createHash as createHash4 } from "crypto";
21403
21990
 
21404
21991
  // src/utils/atomic-write.ts
21405
21992
  init_esm_shims();
21406
- import fs8 from "fs";
21407
- import path8 from "path";
21993
+ import fs9 from "fs";
21994
+ import path9 from "path";
21408
21995
  import { randomUUID } from "crypto";
21409
21996
  function atomicWriteSync(filePath, data, options) {
21410
- const dir2 = path8.dirname(filePath);
21411
- if (!fs8.existsSync(dir2)) fs8.mkdirSync(dir2, { recursive: true });
21997
+ const dir2 = path9.dirname(filePath);
21998
+ if (!fs9.existsSync(dir2)) fs9.mkdirSync(dir2, { recursive: true });
21412
21999
  const tmpPath = `${filePath}.${randomUUID()}.tmp`;
21413
22000
  try {
21414
- fs8.writeFileSync(tmpPath, data, options);
22001
+ fs9.writeFileSync(tmpPath, data, options);
21415
22002
  } catch (err) {
21416
22003
  try {
21417
- fs8.unlinkSync(tmpPath);
22004
+ fs9.unlinkSync(tmpPath);
21418
22005
  } catch {
21419
22006
  }
21420
22007
  throw err;
21421
22008
  }
21422
22009
  try {
21423
- fs8.renameSync(tmpPath, filePath);
22010
+ fs9.renameSync(tmpPath, filePath);
21424
22011
  } catch (err) {
21425
22012
  try {
21426
- fs8.unlinkSync(tmpPath);
22013
+ fs9.unlinkSync(tmpPath);
21427
22014
  } catch {
21428
22015
  }
21429
22016
  throw err;
@@ -21433,10 +22020,10 @@ function atomicWriteSync(filePath, data, options) {
21433
22020
  // src/supply-chain/osv-index.ts
21434
22021
  var INDEX_FRESH_MS = 72 * 60 * 60 * 1e3;
21435
22022
  function osvRoot() {
21436
- return path9.join(os6.homedir(), ".node9", "osv");
22023
+ return path10.join(os7.homedir(), ".node9", "osv");
21437
22024
  }
21438
22025
  function ecoDir(eco) {
21439
- return path9.join(osvRoot(), eco);
22026
+ return path10.join(osvRoot(), eco);
21440
22027
  }
21441
22028
  function indexKey(eco, name) {
21442
22029
  switch (eco) {
@@ -21455,11 +22042,11 @@ function shardOf(key) {
21455
22042
  return createHash4("sha1").update(key).digest("hex").slice(0, 2);
21456
22043
  }
21457
22044
  function shardPath(eco, shard) {
21458
- return path9.join(ecoDir(eco), "shards", `${shard}.json`);
22045
+ return path10.join(ecoDir(eco), "shards", `${shard}.json`);
21459
22046
  }
21460
22047
  function readMeta(eco) {
21461
22048
  try {
21462
- const m = JSON.parse(fs9.readFileSync(path9.join(ecoDir(eco), "meta.json"), "utf8"));
22049
+ const m = JSON.parse(fs10.readFileSync(path10.join(ecoDir(eco), "meta.json"), "utf8"));
21463
22050
  if (typeof m?.syncedAt !== "string" || typeof m?.lastModifiedMs !== "number") return null;
21464
22051
  return m;
21465
22052
  } catch {
@@ -21468,7 +22055,7 @@ function readMeta(eco) {
21468
22055
  }
21469
22056
  function readShard(eco, shard) {
21470
22057
  try {
21471
- const s = JSON.parse(fs9.readFileSync(shardPath(eco, shard), "utf8"));
22058
+ const s = JSON.parse(fs10.readFileSync(shardPath(eco, shard), "utf8"));
21472
22059
  return s && typeof s === "object" && !Array.isArray(s) ? s : {};
21473
22060
  } catch {
21474
22061
  return {};
@@ -21822,19 +22409,19 @@ async function registryInfo(eco, name, version, freshWindowMs, raw) {
21822
22409
 
21823
22410
  // src/supply-chain/local-resolve.ts
21824
22411
  init_esm_shims();
21825
- import fs10 from "fs";
21826
- import path10 from "path";
22412
+ import fs11 from "fs";
22413
+ import path11 from "path";
21827
22414
  var MAX_LEVELS = 32;
21828
22415
  var LOCAL_FIRST_MANAGERS = /* @__PURE__ */ new Set(["npx", "bunx", "npm exec"]);
21829
22416
  function resolveInstalled(name, cwd) {
21830
- if (!cwd || !path10.isAbsolute(cwd)) return null;
22417
+ if (!cwd || !path11.isAbsolute(cwd)) return null;
21831
22418
  if (!/^(?:@[^/\\]+\/)?[^/\\]+$/.test(name)) return null;
21832
- let dir2 = path10.resolve(cwd);
22419
+ let dir2 = path11.resolve(cwd);
21833
22420
  for (let i = 0; i < MAX_LEVELS; i++) {
21834
- const pkgDir = path10.join(dir2, "node_modules", ...name.split("/"));
21835
- const manifest = path10.join(pkgDir, "package.json");
22421
+ const pkgDir = path11.join(dir2, "node_modules", ...name.split("/"));
22422
+ const manifest = path11.join(pkgDir, "package.json");
21836
22423
  try {
21837
- const raw = fs10.readFileSync(manifest, "utf8");
22424
+ const raw = fs11.readFileSync(manifest, "utf8");
21838
22425
  const parsed = JSON.parse(raw);
21839
22426
  if (typeof parsed.version === "string" && parsed.version.length > 0) {
21840
22427
  return { version: parsed.version, dir: pkgDir };
@@ -21842,7 +22429,7 @@ function resolveInstalled(name, cwd) {
21842
22429
  return null;
21843
22430
  } catch {
21844
22431
  }
21845
- const parent = path10.dirname(dir2);
22432
+ const parent = path11.dirname(dir2);
21846
22433
  if (parent === dir2) break;
21847
22434
  dir2 = parent;
21848
22435
  }
@@ -21964,7 +22551,7 @@ function checkInstalled(p, version, misses) {
21964
22551
  }
21965
22552
  async function checkOne(p, cfg, misses, networkCapped, cwd) {
21966
22553
  if (p.ecosystem === "npm" && LOCAL_FIRST_MANAGERS.has(p.manager) && p.localCwd !== null && (p.raw === p.name || p.version)) {
21967
- const localCwd = cwd && path11.isAbsolute(cwd) ? (p.localCwd ?? []).reduce((dir2, step) => path11.resolve(dir2, step), cwd) : void 0;
22554
+ const localCwd = cwd && path12.isAbsolute(cwd) ? (p.localCwd ?? []).reduce((dir2, step) => path12.resolve(dir2, step), cwd) : void 0;
21968
22555
  const installed = resolveInstalled(p.name, localCwd);
21969
22556
  if (installed && (!p.version || p.version === installed.version))
21970
22557
  return checkInstalled(p, installed.version, misses);
@@ -22110,9 +22697,9 @@ async function runPackageCheck(toolName, args, cfg, cwd, opts = {}) {
22110
22697
  // src/supply-chain/registries.ts
22111
22698
  init_esm_shims();
22112
22699
  init_dist();
22113
- import os7 from "os";
22114
- import path12 from "path";
22115
- import fs11 from "fs";
22700
+ import os8 from "os";
22701
+ import path13 from "path";
22702
+ import fs12 from "fs";
22116
22703
  var MAX_CONFIG_BYTES = 256 * 1024;
22117
22704
  var MAX_PARENT_DIRS = 12;
22118
22705
  var TTL_MS = 5e3;
@@ -22153,29 +22740,29 @@ var REGISTRY_KEY_RE = /registry|index|find-links|mirror/i;
22153
22740
  var REGISTRY_SECTION_RE = /index|source|registr|scopes|mirror/i;
22154
22741
  var URL_RE2 = /https?:\/\/[^\s"'<>,;]+/gi;
22155
22742
  function userFiles(home, env) {
22156
- const xdg = env.XDG_CONFIG_HOME || path12.join(home, ".config");
22743
+ const xdg = env.XDG_CONFIG_HOME || path13.join(home, ".config");
22157
22744
  return [
22158
- env.NPM_CONFIG_USERCONFIG || env.npm_config_userconfig || path12.join(home, ".npmrc"),
22159
- path12.join(home, ".yarnrc"),
22160
- path12.join(home, ".yarnrc.yml"),
22161
- path12.join(home, ".bunfig.toml"),
22745
+ env.NPM_CONFIG_USERCONFIG || env.npm_config_userconfig || path13.join(home, ".npmrc"),
22746
+ path13.join(home, ".yarnrc"),
22747
+ path13.join(home, ".yarnrc.yml"),
22748
+ path13.join(home, ".bunfig.toml"),
22162
22749
  env.PIP_CONFIG_FILE || "",
22163
- path12.join(xdg, "pip", "pip.conf"),
22164
- path12.join(home, ".pip", "pip.conf"),
22750
+ path13.join(xdg, "pip", "pip.conf"),
22751
+ path13.join(home, ".pip", "pip.conf"),
22165
22752
  "/etc/pip.conf",
22166
- env.VIRTUAL_ENV ? path12.join(env.VIRTUAL_ENV, "pip.conf") : "",
22753
+ env.VIRTUAL_ENV ? path13.join(env.VIRTUAL_ENV, "pip.conf") : "",
22167
22754
  env.UV_CONFIG_FILE || "",
22168
- path12.join(xdg, "uv", "uv.toml"),
22755
+ path13.join(xdg, "uv", "uv.toml"),
22169
22756
  "/etc/uv/uv.toml"
22170
22757
  ].filter(Boolean);
22171
22758
  }
22172
22759
  function projectFiles(cwd) {
22173
- if (!cwd || !path12.isAbsolute(cwd)) return [];
22760
+ if (!cwd || !path13.isAbsolute(cwd)) return [];
22174
22761
  const out = [];
22175
22762
  let dir2 = cwd;
22176
22763
  for (let i = 0; i < MAX_PARENT_DIRS; i++) {
22177
- for (const f of PROJECT_FILES) out.push(path12.join(dir2, f));
22178
- const parent = path12.dirname(dir2);
22764
+ for (const f of PROJECT_FILES) out.push(path13.join(dir2, f));
22765
+ const parent = path13.dirname(dir2);
22179
22766
  if (parent === dir2) break;
22180
22767
  dir2 = parent;
22181
22768
  }
@@ -22215,7 +22802,7 @@ function hostsInText(text, out) {
22215
22802
  }
22216
22803
  }
22217
22804
  var cache3 = /* @__PURE__ */ new Map();
22218
- function configuredRegistryHosts(cwd, env = process.env, home = os7.homedir()) {
22805
+ function configuredRegistryHosts(cwd, env = process.env, home = os8.homedir()) {
22219
22806
  const key = [
22220
22807
  cwd ?? "",
22221
22808
  home,
@@ -22237,7 +22824,7 @@ function configuredRegistryHosts(cwd, env = process.env, home = os7.homedir()) {
22237
22824
  const r = readCappedText(
22238
22825
  file2,
22239
22826
  MAX_CONFIG_BYTES,
22240
- fs11.constants.O_RDONLY | (fs11.constants.O_NONBLOCK ?? 0)
22827
+ fs12.constants.O_RDONLY | (fs12.constants.O_NONBLOCK ?? 0)
22241
22828
  );
22242
22829
  if (r) hostsInText(r.text, hosts);
22243
22830
  }
@@ -22247,27 +22834,27 @@ function configuredRegistryHosts(cwd, env = process.env, home = os7.homedir()) {
22247
22834
 
22248
22835
  // src/utils/provenance.ts
22249
22836
  init_esm_shims();
22250
- import fs12 from "fs";
22251
- import path13 from "path";
22252
- import os8 from "os";
22837
+ import fs13 from "fs";
22838
+ import path14 from "path";
22839
+ import os9 from "os";
22253
22840
  var SYSTEM_PREFIXES = ["/usr/bin", "/usr/sbin", "/bin", "/sbin"];
22254
22841
  var MANAGED_PREFIXES = ["/usr/local/bin", "/opt/homebrew", "/home/linuxbrew", "/nix/store"];
22255
22842
  var USER_PREFIXES = [
22256
- path13.join(os8.homedir(), "bin"),
22257
- path13.join(os8.homedir(), ".local", "bin"),
22258
- path13.join(os8.homedir(), ".cargo", "bin"),
22259
- path13.join(os8.homedir(), ".npm-global", "bin"),
22260
- path13.join(os8.homedir(), ".volta", "bin")
22843
+ path14.join(os9.homedir(), "bin"),
22844
+ path14.join(os9.homedir(), ".local", "bin"),
22845
+ path14.join(os9.homedir(), ".cargo", "bin"),
22846
+ path14.join(os9.homedir(), ".npm-global", "bin"),
22847
+ path14.join(os9.homedir(), ".volta", "bin")
22261
22848
  ];
22262
22849
  var SUSPECT_PREFIXES = ["/tmp", "/var/tmp", "/dev/shm"];
22263
22850
  function findInPath(cmd) {
22264
- if (path13.posix.isAbsolute(cmd)) return cmd;
22851
+ if (path14.posix.isAbsolute(cmd)) return cmd;
22265
22852
  const pathEnv = process.env.PATH ?? "";
22266
- for (const dir2 of pathEnv.split(path13.delimiter)) {
22853
+ for (const dir2 of pathEnv.split(path14.delimiter)) {
22267
22854
  if (!dir2) continue;
22268
- const full = path13.join(dir2, cmd);
22855
+ const full = path14.join(dir2, cmd);
22269
22856
  try {
22270
- fs12.accessSync(full, fs12.constants.X_OK);
22857
+ fs13.accessSync(full, fs13.constants.X_OK);
22271
22858
  return full;
22272
22859
  } catch {
22273
22860
  }
@@ -22278,7 +22865,7 @@ function _classifyPath(resolved, cwd) {
22278
22865
  if (cwd && resolved.startsWith(cwd + "/")) {
22279
22866
  return { trustLevel: "user", reason: "binary in project directory" };
22280
22867
  }
22281
- const osTmp = os8.tmpdir();
22868
+ const osTmp = os9.tmpdir();
22282
22869
  const allSuspect = osTmp ? [...SUSPECT_PREFIXES, osTmp] : SUSPECT_PREFIXES;
22283
22870
  if (allSuspect.some((p) => resolved === p || resolved.startsWith(p + "/"))) {
22284
22871
  return { trustLevel: "suspect", reason: `binary in temp directory: ${resolved}` };
@@ -22296,7 +22883,7 @@ function _classifyPath(resolved, cwd) {
22296
22883
  }
22297
22884
  function checkProvenance(cmd, cwd) {
22298
22885
  const bare = cmd.startsWith("./") ? cmd.slice(2) : cmd;
22299
- if (path13.posix.isAbsolute(bare)) {
22886
+ if (path14.posix.isAbsolute(bare)) {
22300
22887
  const early = _classifyPath(bare, cwd);
22301
22888
  if (early.trustLevel === "suspect") {
22302
22889
  return { resolvedPath: bare, ...early };
@@ -22312,7 +22899,7 @@ function checkProvenance(cmd, cwd) {
22312
22899
  reason: "binary not found in PATH"
22313
22900
  };
22314
22901
  }
22315
- resolved = fs12.realpathSync(found);
22902
+ resolved = fs13.realpathSync(found);
22316
22903
  } catch {
22317
22904
  return {
22318
22905
  resolvedPath: cmd,
@@ -22321,7 +22908,7 @@ function checkProvenance(cmd, cwd) {
22321
22908
  };
22322
22909
  }
22323
22910
  try {
22324
- const stat = fs12.statSync(resolved);
22911
+ const stat = fs13.statSync(resolved);
22325
22912
  if (stat.mode & 2) {
22326
22913
  return {
22327
22914
  resolvedPath: resolved,
@@ -22375,9 +22962,9 @@ async function evaluatePolicy2(toolName, args, agent, cwd, opts) {
22375
22962
  );
22376
22963
  }
22377
22964
  function isGitWorkTreeTop(dir2, cwd) {
22378
- if (!path14.isAbsolute(cwd)) return true;
22965
+ if (!path15.isAbsolute(cwd)) return true;
22379
22966
  try {
22380
- return fs13.existsSync(path14.join(path14.resolve(cwd, dir2), ".git"));
22967
+ return fs14.existsSync(path15.join(path15.resolve(cwd, dir2), ".git"));
22381
22968
  } catch {
22382
22969
  return true;
22383
22970
  }
@@ -22387,7 +22974,7 @@ function readAgentSettings(absPath) {
22387
22974
  const r = readCappedText(
22388
22975
  absPath,
22389
22976
  MAX_AGENT_SETTINGS_BYTES,
22390
- fs13.constants.O_RDONLY | (fs13.constants.O_NONBLOCK ?? 0)
22977
+ fs14.constants.O_RDONLY | (fs14.constants.O_NONBLOCK ?? 0)
22391
22978
  );
22392
22979
  return r && !r.truncated ? r.text : null;
22393
22980
  }
@@ -22397,12 +22984,12 @@ function isIgnoredTool2(toolName) {
22397
22984
 
22398
22985
  // src/auth/state.ts
22399
22986
  init_esm_shims();
22400
- import fs14 from "fs";
22401
- import path15 from "path";
22402
- import os9 from "os";
22987
+ import fs15 from "fs";
22988
+ import path16 from "path";
22989
+ import os10 from "os";
22403
22990
  init_forecast_context();
22404
- var PAUSED_FILE = path15.join(os9.homedir(), ".node9", "PAUSED");
22405
- var TRUST_FILE = path15.join(os9.homedir(), ".node9", "trust.json");
22991
+ var PAUSED_FILE = path16.join(os10.homedir(), ".node9", "PAUSED");
22992
+ var TRUST_FILE = path16.join(os10.homedir(), ".node9", "trust.json");
22406
22993
  function extractCommandPattern(toolName, args) {
22407
22994
  const lower2 = toolName.toLowerCase();
22408
22995
  if (lower2 !== "bash" && lower2 !== "execute_bash" && lower2 !== "shell") return void 0;
@@ -22415,11 +23002,11 @@ function extractCommandPattern(toolName, args) {
22415
23002
  function checkPause() {
22416
23003
  if (forecastRun()) return { paused: false };
22417
23004
  try {
22418
- if (!fs14.existsSync(PAUSED_FILE)) return { paused: false };
22419
- const state = JSON.parse(fs14.readFileSync(PAUSED_FILE, "utf-8"));
23005
+ if (!fs15.existsSync(PAUSED_FILE)) return { paused: false };
23006
+ const state = JSON.parse(fs15.readFileSync(PAUSED_FILE, "utf-8"));
22420
23007
  if (state.expiry > 0 && Date.now() >= state.expiry) {
22421
23008
  try {
22422
- fs14.unlinkSync(PAUSED_FILE);
23009
+ fs15.unlinkSync(PAUSED_FILE);
22423
23010
  } catch {
22424
23011
  }
22425
23012
  return { paused: false };
@@ -22432,8 +23019,8 @@ function checkPause() {
22432
23019
  function getActiveTrustSession(toolName, args) {
22433
23020
  if (forecastRun()) return false;
22434
23021
  try {
22435
- if (!fs14.existsSync(TRUST_FILE)) return false;
22436
- const trust = JSON.parse(fs14.readFileSync(TRUST_FILE, "utf-8"));
23022
+ if (!fs15.existsSync(TRUST_FILE)) return false;
23023
+ const trust = JSON.parse(fs15.readFileSync(TRUST_FILE, "utf-8"));
22437
23024
  const now = Date.now();
22438
23025
  const active = trust.entries.filter((e) => e.expiry > now);
22439
23026
  if (active.length !== trust.entries.length) {
@@ -22456,8 +23043,8 @@ function writeTrustSession(toolName, durationMs, args) {
22456
23043
  try {
22457
23044
  let trust = { entries: [] };
22458
23045
  try {
22459
- if (fs14.existsSync(TRUST_FILE)) {
22460
- trust = JSON.parse(fs14.readFileSync(TRUST_FILE, "utf-8"));
23046
+ if (fs15.existsSync(TRUST_FILE)) {
23047
+ trust = JSON.parse(fs15.readFileSync(TRUST_FILE, "utf-8"));
22461
23048
  }
22462
23049
  } catch {
22463
23050
  }
@@ -22479,9 +23066,9 @@ function writeTrustSession(toolName, durationMs, args) {
22479
23066
  }
22480
23067
  function getPersistentDecision(toolName) {
22481
23068
  try {
22482
- const file2 = path15.join(os9.homedir(), ".node9", "decisions.json");
22483
- if (!fs14.existsSync(file2)) return null;
22484
- const decisions = JSON.parse(fs14.readFileSync(file2, "utf-8"));
23069
+ const file2 = path16.join(os10.homedir(), ".node9", "decisions.json");
23070
+ if (!fs15.existsSync(file2)) return null;
23071
+ const decisions = JSON.parse(fs15.readFileSync(file2, "utf-8"));
22485
23072
  const d = decisions[toolName];
22486
23073
  if (d === "allow" || d === "deny") return d;
22487
23074
  } catch {
@@ -22492,16 +23079,16 @@ function getPersistentDecision(toolName) {
22492
23079
  // src/auth/daemon.ts
22493
23080
  init_esm_shims();
22494
23081
  init_forecast_context();
22495
- import fs16 from "fs";
23082
+ import fs17 from "fs";
22496
23083
  import http from "http";
22497
23084
  import net from "net";
22498
- import path17 from "path";
22499
- import os10 from "os";
23085
+ import path18 from "path";
23086
+ import os11 from "os";
22500
23087
 
22501
23088
  // src/utils/activity-socket.ts
22502
23089
  init_esm_shims();
22503
- import fs15 from "fs";
22504
- import path16 from "path";
23090
+ import fs16 from "fs";
23091
+ import path17 from "path";
22505
23092
 
22506
23093
  // src/utils/own-dir.ts
22507
23094
  init_esm_shims();
@@ -22510,7 +23097,7 @@ init_esm_shims();
22510
23097
  var currentUid = () => typeof process.getuid === "function" ? process.getuid() : void 0;
22511
23098
  function isPrivateDir(dir2, uid = currentUid()) {
22512
23099
  try {
22513
- const st = fs15.lstatSync(dir2);
23100
+ const st = fs16.lstatSync(dir2);
22514
23101
  if (st.isSymbolicLink() || !st.isDirectory()) return false;
22515
23102
  if (uid !== void 0 && st.uid !== uid) return false;
22516
23103
  return (st.mode & 18) === 0;
@@ -22522,13 +23109,13 @@ var WINDOWS_PIPE = /^\\\\\.\\pipe\\node9-activity-[0-9a-f]{32}$/;
22522
23109
  function isTrustedActivitySocket(socketPath, uid = currentUid()) {
22523
23110
  if (process.platform === "win32") return WINDOWS_PIPE.test(socketPath);
22524
23111
  try {
22525
- const st = fs15.lstatSync(socketPath);
23112
+ const st = fs16.lstatSync(socketPath);
22526
23113
  if (!st.isSocket()) return false;
22527
23114
  if (uid !== void 0 && st.uid !== uid) return false;
22528
23115
  } catch {
22529
23116
  return false;
22530
23117
  }
22531
- return isPrivateDir(path16.dirname(socketPath), uid);
23118
+ return isPrivateDir(path17.dirname(socketPath), uid);
22532
23119
  }
22533
23120
 
22534
23121
  // src/auth/decision-record.ts
@@ -22680,9 +23267,9 @@ function recordFromLegacyWait(body, now = Date.now()) {
22680
23267
  }
22681
23268
 
22682
23269
  // src/auth/daemon.ts
22683
- function daemonActivitySocket(home = os10.homedir()) {
23270
+ function daemonActivitySocket(home = os11.homedir()) {
22684
23271
  try {
22685
- const raw = fs16.readFileSync(path17.join(home, ".node9", "daemon.pid"), "utf-8");
23272
+ const raw = fs17.readFileSync(path18.join(home, ".node9", "daemon.pid"), "utf-8");
22686
23273
  const p = JSON.parse(raw).activitySocket;
22687
23274
  return typeof p === "string" && isTrustedActivitySocket(p) ? p : null;
22688
23275
  } catch {
@@ -22746,9 +23333,9 @@ var DAEMON_HOST = "127.0.0.1";
22746
23333
  function getInternalToken() {
22747
23334
  if (forecastRun()) return null;
22748
23335
  try {
22749
- const pidFile = path17.join(os10.homedir(), ".node9", "daemon.pid");
22750
- if (!fs16.existsSync(pidFile)) return null;
22751
- const data = JSON.parse(fs16.readFileSync(pidFile, "utf-8"));
23336
+ const pidFile = path18.join(os11.homedir(), ".node9", "daemon.pid");
23337
+ if (!fs17.existsSync(pidFile)) return null;
23338
+ const data = JSON.parse(fs17.readFileSync(pidFile, "utf-8"));
22752
23339
  process.kill(data.pid, 0);
22753
23340
  return data.internalToken ?? null;
22754
23341
  } catch {
@@ -22757,12 +23344,12 @@ function getInternalToken() {
22757
23344
  }
22758
23345
  function isDaemonRunning() {
22759
23346
  if (forecastRun()) return false;
22760
- const pidFile = path17.join(os10.homedir(), ".node9", "daemon.pid");
22761
- if (fs16.existsSync(pidFile)) {
23347
+ const pidFile = path18.join(os11.homedir(), ".node9", "daemon.pid");
23348
+ if (fs17.existsSync(pidFile)) {
22762
23349
  let pid;
22763
23350
  let port;
22764
23351
  try {
22765
- const data = JSON.parse(fs16.readFileSync(pidFile, "utf-8"));
23352
+ const data = JSON.parse(fs17.readFileSync(pidFile, "utf-8"));
22766
23353
  pid = data.pid;
22767
23354
  port = data.port;
22768
23355
  } catch {
@@ -22780,7 +23367,7 @@ function isDaemonRunning() {
22780
23367
  } catch (err) {
22781
23368
  if (err instanceof Error && "code" in err && err.code === "ESRCH") {
22782
23369
  try {
22783
- fs16.unlinkSync(pidFile);
23370
+ fs17.unlinkSync(pidFile);
22784
23371
  } catch {
22785
23372
  }
22786
23373
  }
@@ -23120,9 +23707,9 @@ import { randomUUID as randomUUID2 } from "crypto";
23120
23707
 
23121
23708
  // src/apps/state.ts
23122
23709
  init_esm_shims();
23123
- import fs18 from "fs";
23124
- import os12 from "os";
23125
- import path19 from "path";
23710
+ import fs19 from "fs";
23711
+ import os13 from "os";
23712
+ import path20 from "path";
23126
23713
 
23127
23714
  // src/apps/catalog.ts
23128
23715
  init_esm_shims();
@@ -23256,22 +23843,22 @@ init_audit();
23256
23843
 
23257
23844
  // src/daemon/mcp-tools.ts
23258
23845
  init_esm_shims();
23259
- import fs17 from "fs";
23260
- import path18 from "path";
23261
- import os11 from "os";
23846
+ import fs18 from "fs";
23847
+ import path19 from "path";
23848
+ import os12 from "os";
23262
23849
 
23263
23850
  // src/mcp-cmd.ts
23264
23851
  init_esm_shims();
23265
23852
 
23266
23853
  // src/daemon/mcp-tools.ts
23267
23854
  function getMcpToolsFile() {
23268
- return path18.join(os11.homedir(), ".node9", "mcp-tools.json");
23855
+ return path19.join(os12.homedir(), ".node9", "mcp-tools.json");
23269
23856
  }
23270
23857
  function readMcpToolsConfig() {
23271
23858
  try {
23272
23859
  const file2 = getMcpToolsFile();
23273
- if (!fs17.existsSync(file2)) return {};
23274
- const raw = fs17.readFileSync(file2, "utf-8");
23860
+ if (!fs18.existsSync(file2)) return {};
23861
+ const raw = fs18.readFileSync(file2, "utf-8");
23275
23862
  return JSON.parse(raw);
23276
23863
  } catch {
23277
23864
  return {};
@@ -23279,8 +23866,8 @@ function readMcpToolsConfig() {
23279
23866
  }
23280
23867
 
23281
23868
  // src/apps/state.ts
23282
- function appsFilePath(home = os12.homedir()) {
23283
- return path19.join(home, ".node9", "apps.json");
23869
+ function appsFilePath(home = os13.homedir()) {
23870
+ return path20.join(home, ".node9", "apps.json");
23284
23871
  }
23285
23872
  var SIGNAL_KINDS = /* @__PURE__ */ new Set(["mcp-server", "command", "package", "env-name"]);
23286
23873
  function emptyFile() {
@@ -23328,7 +23915,7 @@ function readAppsFile(home) {
23328
23915
  function readAppsFileStrict(home) {
23329
23916
  let text;
23330
23917
  try {
23331
- text = fs18.readFileSync(appsFilePath(home), "utf8");
23918
+ text = fs19.readFileSync(appsFilePath(home), "utf8");
23332
23919
  } catch (e) {
23333
23920
  return e.code === "ENOENT" ? emptyFile() : null;
23334
23921
  }
@@ -23343,7 +23930,7 @@ var NO_LINKS = /* @__PURE__ */ Object.create(null);
23343
23930
  function localLinks() {
23344
23931
  const file2 = appsFilePath();
23345
23932
  try {
23346
- const mtimeMs = fs18.statSync(file2).mtimeMs;
23933
+ const mtimeMs = fs19.statSync(file2).mtimeMs;
23347
23934
  if (!linkCache || linkCache.file !== file2 || linkCache.mtimeMs !== mtimeMs) {
23348
23935
  linkCache = { mtimeMs, file: file2, links: readAppsFile().links };
23349
23936
  }
@@ -23389,11 +23976,11 @@ import path32 from "path";
23389
23976
  // src/ui/native.ts
23390
23977
  init_esm_shims();
23391
23978
  import { spawn } from "child_process";
23392
- import path21 from "path";
23979
+ import path22 from "path";
23393
23980
 
23394
23981
  // src/context-sniper.ts
23395
23982
  init_esm_shims();
23396
- import path20 from "path";
23983
+ import path21 from "path";
23397
23984
  function smartTruncate(str, maxLen = 500) {
23398
23985
  if (str.length <= maxLen) return str;
23399
23986
  const edge = Math.floor(maxLen / 2) - 3;
@@ -23461,7 +24048,7 @@ function computeRiskMetadata(args, tier, blockedByLabel, matchedField, matchedWo
23461
24048
  intent = "EDIT";
23462
24049
  if (obj.file_path) {
23463
24050
  editFilePath = String(obj.file_path);
23464
- editFileName = path20.basename(editFilePath);
24051
+ editFileName = path21.basename(editFilePath);
23465
24052
  }
23466
24053
  const result = extractContext(String(obj.new_string), matchedWord);
23467
24054
  contextSnippet = result.snippet;
@@ -23530,7 +24117,7 @@ function formatArgs(args, matchedField, matchedWord) {
23530
24117
  if (typeof parsed === "object" && !Array.isArray(parsed)) {
23531
24118
  const obj = parsed;
23532
24119
  if (obj.old_string !== void 0 && obj.new_string !== void 0) {
23533
- const file2 = obj.file_path ? path21.basename(String(obj.file_path)) : "file";
24120
+ const file2 = obj.file_path ? path22.basename(String(obj.file_path)) : "file";
23534
24121
  const oldPreview = smartTruncate(String(obj.old_string), 120);
23535
24122
  const newPreview = extractContext(String(obj.new_string), matchedWord).snippet;
23536
24123
  return {
@@ -23738,48 +24325,6 @@ import fs20 from "fs";
23738
24325
  import os14 from "os";
23739
24326
  import path23 from "path";
23740
24327
  init_dist();
23741
-
23742
- // src/shields/jail.ts
23743
- init_esm_shims();
23744
- import fs19 from "fs";
23745
- import os13 from "os";
23746
- import path22 from "path";
23747
- init_dist();
23748
- var USER_JAIL_SHIELD = "user-jail";
23749
- function jailStorePath() {
23750
- return path22.join(os13.homedir(), ".node9", "jail-paths.json");
23751
- }
23752
- function readJailPaths() {
23753
- let text;
23754
- try {
23755
- text = fs19.readFileSync(jailStorePath(), "utf8");
23756
- } catch (err) {
23757
- if (err.code === "ENOENT") return [];
23758
- throw err;
23759
- }
23760
- let parsed;
23761
- try {
23762
- parsed = JSON.parse(text);
23763
- } catch {
23764
- throw new Error(`${jailStorePath()} is not valid JSON \u2014 fix it before changing the jail.`);
23765
- }
23766
- if (!Array.isArray(parsed.paths)) return [];
23767
- return parsed.paths.filter(
23768
- (p) => !!p && typeof p.path === "string" && (p.verdict === "block" || p.verdict === "review")
23769
- );
23770
- }
23771
- function findJailedPath(candidate) {
23772
- return findJailedPathIn(candidate, readJailPaths());
23773
- }
23774
- function findJailedPathIn(candidate, paths) {
23775
- if (!candidate) return null;
23776
- for (const entry of paths) {
23777
- if (pathMatchesFragment(candidate, entry.path)) return entry;
23778
- }
23779
- return null;
23780
- }
23781
-
23782
- // src/canary/registry.ts
23783
24328
  function canaryStorePath() {
23784
24329
  return path23.join(os14.homedir(), ".node9", "canaries.json");
23785
24330
  }