@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.js CHANGED
@@ -2104,9 +2104,15 @@ function detectDangerousShellExec(command) {
2104
2104
  if (f === PARSE_FAIL) return null;
2105
2105
  let result = null;
2106
2106
  const assigned = assignedNames(f);
2107
- walk(f, (node) => {
2107
+ const known = plainlyNamedPrograms(f);
2108
+ const calledByName = literallyCalledFunctions(f);
2109
+ const bodies = [];
2110
+ const inBody = (pos) => bodies.some(([a, b]) => pos >= a && pos < b);
2111
+ walkWithCalls(f, (node) => {
2108
2112
  if (!node || result === "block") return false;
2109
2113
  const n = node;
2114
+ if (n.kind === "FuncDecl" && n.Body && callDepth === 0 && calledByName.has(n.Name?.Value))
2115
+ bodies.push([n.Body.pos, n.Body.end]);
2110
2116
  if (n.kind === "BinaryCmd" && n.Y && !listOps().has(n.Op)) {
2111
2117
  const call = n.Y.Cmd;
2112
2118
  if (call && call.kind === "CallExpr") {
@@ -2130,7 +2136,8 @@ function detectDangerousShellExec(command) {
2130
2136
  );
2131
2137
  const head = unwrapCommandHead(headWords, false, true);
2132
2138
  const lookup = headWords[0]?.toLowerCase() === "command" && headWords.slice(1).some((w) => w === "-v" || w === "-V");
2133
- if (!lookup && (opaqueExecutable(args[0], assigned) || opaqueExecutable(args[head], assigned)))
2139
+ const unboundBody = callDepth === 0 && inBody(n.pos ?? -1);
2140
+ if (!lookup && !unboundBody && (opaqueExecutable(args[0], assigned, known) || opaqueExecutable(args[head], assigned, known)))
2134
2141
  result = "review";
2135
2142
  const firstParts = args[0].Parts || [];
2136
2143
  if (firstParts.length !== 1 || firstParts[0].kind !== "Lit") return true;
@@ -2190,11 +2197,105 @@ function assignedNames(f) {
2190
2197
  });
2191
2198
  return names;
2192
2199
  }
2193
- function executableParts(word, assigned, quoted = false) {
2200
+ function literallyCalledFunctions(f) {
2201
+ const declared = /* @__PURE__ */ new Set();
2202
+ const called = /* @__PURE__ */ new Set();
2203
+ walk(f, (node) => {
2204
+ if (!node) return false;
2205
+ if (node.kind === "FuncDecl" && node.Name?.Value) declared.add(node.Name.Value);
2206
+ if (node.kind === "CallExpr") {
2207
+ const head = resolveWordLiteral(node.Args?.[0]);
2208
+ if (head) called.add(head);
2209
+ }
2210
+ return true;
2211
+ });
2212
+ return new Set([...declared].filter((name) => called.has(name)));
2213
+ }
2214
+ function plainlyNamedPrograms(f) {
2215
+ const values = /* @__PURE__ */ new Map();
2216
+ const arrays = /* @__PURE__ */ new Set();
2217
+ const give = (name, value) => {
2218
+ if (value === null || !PLAIN_PROGRAM_NAME.test(value)) {
2219
+ values.set(name, null);
2220
+ return;
2221
+ }
2222
+ const cur = values.get(name);
2223
+ if (cur === null) return;
2224
+ if (cur === void 0) values.set(name, /* @__PURE__ */ new Set([value]));
2225
+ else cur.add(value);
2226
+ };
2227
+ const literalOnly = (w) => {
2228
+ const parts = w?.Parts ?? [];
2229
+ if (parts.length !== 1) return null;
2230
+ const p = parts[0];
2231
+ if (p.kind === "Lit") return p.Value ?? null;
2232
+ if (p.kind === "ParamExp" && expansionName(p) === "FUNCNAME") return "FUNCNAME";
2233
+ if (p.kind === "SglQuoted" && !p.Dollar) return p.Value ?? null;
2234
+ if (p.kind === "DblQuoted") {
2235
+ const inner = p.Parts ?? [];
2236
+ return inner.length === 1 && inner[0].kind === "Lit" ? inner[0].Value ?? null : null;
2237
+ }
2238
+ return null;
2239
+ };
2240
+ let ifsTouched = false;
2241
+ walk(f, (node) => {
2242
+ if (!node) return false;
2243
+ const kind = node.kind;
2244
+ if (kind === "FuncDecl" && node.Name?.Value) give(node.Name.Value, node.Name.Value);
2245
+ if (kind === "Assign" && node.Name?.Value) {
2246
+ const name = node.Name.Value;
2247
+ if (name === "IFS") ifsTouched = true;
2248
+ if (node.Append) values.set(name, null);
2249
+ else if (node.Array) {
2250
+ arrays.add(name);
2251
+ for (const el of node.Array.Elems ?? []) give(name, literalOnly(el.Value));
2252
+ } else if (node.Index) {
2253
+ arrays.add(name);
2254
+ give(name, node.Value ? literalOnly(node.Value) : null);
2255
+ } else give(name, node.Value ? literalOnly(node.Value) : null);
2256
+ }
2257
+ if (kind === "ForClause" && node.Loop?.Name?.Value) values.set(node.Loop.Name.Value, null);
2258
+ if (kind === "CallExpr") {
2259
+ const words = (node.Args ?? []).map((a) => resolveWordLiteral(a));
2260
+ if (VARIABLE_READERS.has(words[0] ?? "")) {
2261
+ for (const w of words.slice(1)) if (w && /^[A-Za-z_]\w*$/.test(w)) values.set(w, null);
2262
+ }
2263
+ }
2264
+ return true;
2265
+ });
2266
+ if (ifsTouched) return /* @__PURE__ */ new Set();
2267
+ const functions = /* @__PURE__ */ new Set();
2268
+ walk(f, (node) => {
2269
+ if (node && node.kind === "FuncDecl" && node.Name?.Value) functions.add(node.Name.Value);
2270
+ return true;
2271
+ });
2272
+ const callable = (v) => functions.has(v) || v === "FUNCNAME" || v === "true" || v === "false" || v === ":";
2273
+ for (const [name, vals] of values) {
2274
+ if (vals && ![...vals].every(callable)) values.set(name, null);
2275
+ }
2276
+ const known = /* @__PURE__ */ new Set();
2277
+ for (const [name, vals] of values)
2278
+ if (vals && (vals.size === 1 || arrays.has(name) && vals.size > 0)) known.add(name);
2279
+ return known;
2280
+ }
2281
+ function expansionName(part) {
2282
+ if (part.kind !== "ParamExp") return null;
2283
+ if (part.Excl || part.Length || part.Width || part.Slice || part.Repl || part.Exp) return null;
2284
+ return part.Param?.Value ?? null;
2285
+ }
2286
+ function executableParts(word, assigned, quoted = false, known = EMPTY_NAMES) {
2194
2287
  return (word?.Parts ?? []).flatMap((part) => {
2195
2288
  const kind = part.kind;
2196
- if (kind === "DblQuoted") return executableParts(part, assigned, true);
2289
+ if (kind === "DblQuoted") return executableParts(part, assigned, true, known);
2197
2290
  if (kind === "Lit") return [{ kind: "Lit", value: part.Value ?? "", quoted }];
2291
+ const name = expansionName(part);
2292
+ if (name !== null && (known.has(name) || name === "FUNCNAME"))
2293
+ return [{ kind: "Lit", value: name, quoted }];
2294
+ if (name !== null) {
2295
+ const bound = recordedExpansion(name === "@" || name === "*" ? "1" : name);
2296
+ if (typeof bound === "string" && PLAIN_PROGRAM_NAME.test(bound))
2297
+ return [{ kind: "Lit", value: bound, quoted }];
2298
+ }
2198
2299
  if (kind === "SglQuoted")
2199
2300
  return [{ kind: part.Dollar ? "Dynamic" : "Lit", value: part.Value ?? "", quoted: true }];
2200
2301
  if (kind === "ParamExp" && isDefaultExpansion(part) && !assigned.has(part.Param?.Value)) {
@@ -2202,14 +2303,14 @@ function executableParts(word, assigned, quoted = false) {
2202
2303
  if (fallback !== null) return [{ kind: "Lit", value: fallback, quoted }];
2203
2304
  }
2204
2305
  if (kind === "CmdSubst") {
2205
- const name = lookupSubstitution(part);
2206
- if (name !== null) return [{ kind: "Lit", value: name, quoted }];
2306
+ const name2 = lookupSubstitution(part);
2307
+ if (name2 !== null) return [{ kind: "Lit", value: name2, quoted }];
2207
2308
  }
2208
2309
  return [{ kind: "Dynamic", value: "", quoted }];
2209
2310
  });
2210
2311
  }
2211
- function opaqueExecutable(word, assigned) {
2212
- const parts = executableParts(word, assigned);
2312
+ function opaqueExecutable(word, assigned, known = EMPTY_NAMES) {
2313
+ const parts = executableParts(word, assigned, false, known);
2213
2314
  let lastSlash = -1;
2214
2315
  parts.forEach((p, i) => {
2215
2316
  if (p.kind === "Lit" && p.value.includes("/")) lastSlash = i;
@@ -2331,6 +2432,9 @@ function deriveBinaryOp(sample) {
2331
2432
  function isListOp(op) {
2332
2433
  return listOps().has(op);
2333
2434
  }
2435
+ function isPipeOp(op) {
2436
+ return pipeOps().has(op);
2437
+ }
2334
2438
  function listOps() {
2335
2439
  if (_listOps) return _listOps;
2336
2440
  _listOps = new Set(
@@ -2575,6 +2679,33 @@ function isProtectedCodePath(p) {
2575
2679
  if (matchSensitivePath(p) !== null) return true;
2576
2680
  return SYSTEM_AUTH_FILES.includes(p) || SYSTEM_AUTH_DIRS.some((d) => p === d || p.startsWith(d + "/"));
2577
2681
  }
2682
+ function isJailedCodePath(literal, jailPaths) {
2683
+ const value = trimTrailingSlashes(literal.replace(/\\/g, "/"));
2684
+ if (!value) return false;
2685
+ for (const raw of jailPaths) {
2686
+ const jail = trimTrailingSlashes(raw.trim().replace(/\\/g, "/"));
2687
+ if (!jail) continue;
2688
+ const home = homeTail(jail);
2689
+ if (home === null) {
2690
+ if (value === jail || value.startsWith(jail + "/")) return true;
2691
+ continue;
2692
+ }
2693
+ if (!home) continue;
2694
+ const tail = homeTail(value);
2695
+ if (tail !== null && (tail === home || tail.startsWith(home + "/"))) return true;
2696
+ }
2697
+ return false;
2698
+ }
2699
+ function trimTrailingSlashes(s) {
2700
+ let end = s.length;
2701
+ while (end > 0 && s.charCodeAt(end - 1) === 47) end--;
2702
+ return s.slice(0, end);
2703
+ }
2704
+ function homeTail(p) {
2705
+ const m = /^(?:~|\$\{?HOME\}?|\/home\/[^/]+|\/Users\/[^/]+|\/root)(?:\/(.*))?$/.exec(p);
2706
+ if (!m) return null;
2707
+ return m[1] ?? "";
2708
+ }
2578
2709
  function inlineExecDescription(r) {
2579
2710
  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.";
2580
2711
  return `${lead} Review the full script below before allowing it to execute.`;
@@ -2583,7 +2714,8 @@ function inlineExecReason(r) {
2583
2714
  const who = r.interp ? `This inline ${r.interp} code` : "This inline code";
2584
2715
  return r.evidence ? `${who} ${r.what} (${r.evidence})` : `${who} ${r.what}`;
2585
2716
  }
2586
- function inlineExecReview(command) {
2717
+ function inlineExecReview(command, jailPaths = []) {
2718
+ const isProtected = jailPaths.length === 0 ? isProtectedCodePath : (p) => isProtectedCodePath(p) || isJailedCodePath(p, jailPaths);
2587
2719
  const calls = inlineExecCalls(command);
2588
2720
  if (calls === PARSE_FAIL) {
2589
2721
  return INLINE_EXEC_FALLBACK.test(command) ? { outcome: "unreadable", interp: "", what: "could not be parsed" } : null;
@@ -2606,7 +2738,7 @@ function inlineExecReview(command) {
2606
2738
  what: `is ${call.interp} code, which node9 does not read`
2607
2739
  };
2608
2740
  } else {
2609
- const judged = judgeInlineCode(language, code, isProtectedCodePath);
2741
+ const judged = judgeInlineCode(language, code, isProtected);
2610
2742
  if (judged) finding = { ...judged, interp: call.interp };
2611
2743
  }
2612
2744
  if (finding?.outcome === "dangerous") return finding;
@@ -2787,6 +2919,11 @@ function recordTopLevelStmt(stmt) {
2787
2919
  if (t === "DeclClause") return ASSIGNMENT_HEADS.has(stmt.Cmd.Variant?.Value ?? "");
2788
2920
  return (stmt.Cmd.Args || []).length === 0 && (stmt.Cmd.Assigns || []).length > 0;
2789
2921
  }
2922
+ function arrayElements(value) {
2923
+ if (typeof value !== "string" || !value.startsWith(ARRAY_MARK)) return null;
2924
+ const body = value.slice(ARRAY_MARK.length);
2925
+ return body === "" ? [] : body.split(ARRAY_SEP);
2926
+ }
2790
2927
  function recordAssignments(n, at) {
2791
2928
  if (!assignmentTable) return;
2792
2929
  const t = n.kind;
@@ -2800,7 +2937,19 @@ function recordAssignments(n, at) {
2800
2937
  } else return;
2801
2938
  for (const a of assigns) {
2802
2939
  const name = a?.Name?.Value;
2803
- if (!name || !a.Value || a.Append) continue;
2940
+ if (!name) continue;
2941
+ if (a.Array) {
2942
+ const elems = (a.Array.Elems || []).map(
2943
+ (e) => e?.Index ? null : resolveWordLiteral(e?.Value)
2944
+ );
2945
+ const literal = elems.every((v) => v !== null);
2946
+ assignmentTable.set(name, {
2947
+ value: literal && !a.Append ? ARRAY_MARK + elems.join(ARRAY_SEP) : null,
2948
+ at
2949
+ });
2950
+ continue;
2951
+ }
2952
+ if (!a.Value || a.Append) continue;
2804
2953
  assignmentTable.set(name, { value: resolveWordLiteral(a.Value), at });
2805
2954
  }
2806
2955
  }
@@ -2811,7 +2960,33 @@ function resolveTrivialSubst(part) {
2811
2960
  const st = stmts[0];
2812
2961
  if ((st.Redirs || []).length > 0 || st.Negated || st.Background) return void 0;
2813
2962
  const cmd = st.Cmd;
2814
- if (!cmd || cmd.kind !== "CallExpr") return void 0;
2963
+ if (!cmd) return void 0;
2964
+ if (cmd.kind === "BinaryCmd" && pipeOps().has(cmd.Op)) {
2965
+ const left = cmd.X?.Cmd;
2966
+ const right = cmd.Y?.Cmd;
2967
+ if (!left || !right || left.kind !== "CallExpr" || right.kind !== "CallExpr") return void 0;
2968
+ if ((cmd.X.Redirs || []).length > 0 || (cmd.Y.Redirs || []).length > 0) return void 0;
2969
+ const input = trivialCallOutput(left);
2970
+ if (input === void 0) return void 0;
2971
+ const filter = (right.Args || []).map((a) => resolveWordLiteral(a));
2972
+ if (filter.some((w) => w === null)) return void 0;
2973
+ const f = baseWord(filter[0]);
2974
+ const flags = filter.slice(1);
2975
+ if (f === "base64" && flags.length === 1 && /^(-d|-D|--decode)$/.test(flags[0])) {
2976
+ try {
2977
+ const out = Buffer.from(input, "base64").toString("utf8");
2978
+ return /^[\x20-\x7e]*$/.test(out) ? out : void 0;
2979
+ } catch {
2980
+ return void 0;
2981
+ }
2982
+ }
2983
+ if (f === "rev" && flags.length === 0) return [...input].reverse().join("");
2984
+ return void 0;
2985
+ }
2986
+ if (cmd.kind !== "CallExpr") return void 0;
2987
+ return trivialCallOutput(cmd);
2988
+ }
2989
+ function trivialCallOutput(cmd) {
2815
2990
  if ((cmd.Assigns || []).length > 0) return void 0;
2816
2991
  const words = (cmd.Args || []).map((a) => resolveWordLiteral(a));
2817
2992
  if (words.length === 0 || words.some((w) => w === null)) return void 0;
@@ -2823,9 +2998,16 @@ function resolveTrivialSubst(part) {
2823
2998
  return rest.slice(i).join(" ");
2824
2999
  }
2825
3000
  if (head === "printf") {
3001
+ if (rest.length === 1 && /^(?:\\(?:[0-7]{1,3}|x[0-9A-Fa-f]{1,2}))+$/.test(rest[0])) {
3002
+ return rest[0].replace(
3003
+ /\\(?:x([0-9A-Fa-f]{1,2})|([0-7]{1,3}))/g,
3004
+ (_, hex, oct) => String.fromCharCode(hex ? parseInt(hex, 16) : parseInt(oct, 8))
3005
+ );
3006
+ }
2826
3007
  if (rest.length !== 2) return void 0;
2827
- if (!/^%s(\\n)?$/.test(rest[0])) return void 0;
2828
- return rest[1];
3008
+ const m = /^([^%]*)%s([^%]*?)(\\n)?$/.exec(rest[0]);
3009
+ if (!m) return void 0;
3010
+ return m[1] + rest[1] + m[2];
2829
3011
  }
2830
3012
  return void 0;
2831
3013
  }
@@ -2839,11 +3021,28 @@ function isPlainParam(p) {
2839
3021
  if (p.kind !== "ParamExp") return false;
2840
3022
  return !(p.Excl || p.Length || p.Width || p.Index || p.Slice || p.Repl || p.Exp);
2841
3023
  }
3024
+ function indexedParam(p) {
3025
+ if (p.kind !== "ParamExp" || !p.Index) return null;
3026
+ if (p.Excl || p.Length || p.Width || p.Slice || p.Repl || p.Exp) return null;
3027
+ const index = resolveWordLiteral(p.Index);
3028
+ const name = p.Param?.Value;
3029
+ return name && index !== null ? { name, index } : null;
3030
+ }
2842
3031
  function expandPlainParam(p) {
2843
3032
  if (!assignmentTable) return void 0;
3033
+ const indexed = indexedParam(p);
3034
+ if (indexed) {
3035
+ const elems = arrayElements(recordedExpansion(indexed.name));
3036
+ if (elems === null) return void 0;
3037
+ if (indexed.index === "@" || indexed.index === "*") return elems.join(" ");
3038
+ return /^\d+$/.test(indexed.index) ? elems[Number(indexed.index)] ?? null : null;
3039
+ }
2844
3040
  if (!isPlainParam(p)) return void 0;
2845
3041
  const recorded = recordedExpansion(p.Param?.Value);
2846
- if (recorded !== void 0) return recorded;
3042
+ if (recorded !== void 0) {
3043
+ const elems = arrayElements(recorded);
3044
+ return elems === null ? recorded : elems[0] ?? "";
3045
+ }
2847
3046
  return HOME_VARIABLES.has(p.Param?.Value) ? "~" : void 0;
2848
3047
  }
2849
3048
  function functionBodies(f) {
@@ -5790,7 +5989,59 @@ function dynamicOptionWord(word) {
5790
5989
  if (/^--[a-z-]+=/.test(prefix) || /^-[cC]/.test(prefix)) return `${prefix}$\u2026`;
5791
5990
  return null;
5792
5991
  }
5793
- function resolveGitWords(word) {
5992
+ function arrayExpansionWords(word) {
5993
+ let parts = word?.Parts || [];
5994
+ if (parts.length === 1 && parts[0].kind === "DblQuoted") parts = parts[0].Parts || [];
5995
+ if (parts.length !== 1 || parts[0].kind !== "ParamExp" || !parts[0].Index) return null;
5996
+ const index = resolveWordLiteral(parts[0].Index);
5997
+ if (index !== "@" && index !== "*") return null;
5998
+ const elems = arrayElements(recordedExpansion(parts[0].Param?.Value));
5999
+ return elems;
6000
+ }
6001
+ function producedLiteral(call) {
6002
+ if ((call.Assigns || []).length > 0) return null;
6003
+ const words = (call.Args || []).map((a) => resolveWordLiteral(a));
6004
+ if (words.length === 0 || words.some((w) => w === null)) return null;
6005
+ const head = baseWord(words[0]);
6006
+ const rest = words.slice(1);
6007
+ if (head === "git" && rest[0] === "rev-parse" && rest[1] === "--show-toplevel" && rest.length === 2)
6008
+ return ":/";
6009
+ if (head === "echo") {
6010
+ let i = 0;
6011
+ while (i < rest.length && /^-[neE]+$/.test(rest[i])) i++;
6012
+ return rest.length - i === 1 ? rest[i] : null;
6013
+ }
6014
+ if (head === "printf" && rest.length === 2 && /^%s(\\[0n])?$/.test(rest[0])) return rest[1];
6015
+ return null;
6016
+ }
6017
+ function treeTopWord(word, treeTopVars = NO_VARS) {
6018
+ let parts = word?.Parts || [];
6019
+ if (parts.length === 1 && parts[0].kind === "DblQuoted") parts = parts[0].Parts || [];
6020
+ if (parts.length !== 1) return false;
6021
+ const p = parts[0];
6022
+ if (p.kind === "ParamExp") {
6023
+ const name = plainParamName(word)?.name;
6024
+ return name === "PWD" || name !== void 0 && treeTopVars.has(name);
6025
+ }
6026
+ if (p.kind !== "CmdSubst") return false;
6027
+ const stmts = p.Stmts || [];
6028
+ if (stmts.length !== 1 || !stmts[0].Cmd || stmts[0].Cmd.kind !== "CallExpr") return false;
6029
+ const words = (stmts[0].Cmd.Args || []).map((a) => resolveWordLiteral(a));
6030
+ if (words.some((w) => w === null)) return false;
6031
+ const head = baseWord(words[0]);
6032
+ if (head === "pwd") return words.length === 1 || words[1] === "-P" || words[1] === "-L";
6033
+ return head === "git" && words[1] === "rev-parse" && words[2] === "--show-toplevel";
6034
+ }
6035
+ function resolveGitWords(word, treeTopVars = NO_VARS, appended = NO_APPENDS) {
6036
+ if (treeTopWord(word, treeTopVars)) return [":/"];
6037
+ const plain = plainParamName(word);
6038
+ if (plain && appended.has(plain.name)) {
6039
+ const v = appended.get(plain.name);
6040
+ if (v === null || v === void 0) return [null];
6041
+ return plain.quoted ? [v] : splitFields(v);
6042
+ }
6043
+ const elems = arrayExpansionWords(word);
6044
+ if (elems !== null) return elems;
5794
6045
  const value = resolveWordLiteral(word);
5795
6046
  if (value === null) {
5796
6047
  if (isPositionalParam(word)) return boundPositionals() ?? [POSITIONAL2];
@@ -5898,6 +6149,8 @@ function classify(inv) {
5898
6149
  if (o.flags.has("-n") || o.flags.has("--dry-run")) break;
5899
6150
  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("+"));
5900
6151
  if (forced) found(FORCE_PUSH);
6152
+ const deletes = o.flags.has("-d") || o.flags.has("--delete") || o.operands.some((r) => /^:[^:]/.test(r));
6153
+ if (deletes) found(DESTRUCTIVE);
5901
6154
  break;
5902
6155
  }
5903
6156
  case "reset": {
@@ -5925,11 +6178,72 @@ function classify(inv) {
5925
6178
  if (o.flags.has("-d") || o.flags.has("--delete")) found(DESTRUCTIVE);
5926
6179
  break;
5927
6180
  }
6181
+ // node9Firewall#385: history and work discarded through plumbing. A ref
6182
+ // deleted by hand, a stash dropped, the reflog expired or pruned: the
6183
+ // commits behind them are gone the way `branch -D` loses them.
6184
+ case "update-ref": {
6185
+ const o = parseOptions(inv.args, "m", /* @__PURE__ */ new Set());
6186
+ if (o.flags.has("-d") && (o.dynamic || o.operands.some(isHistoryRef))) found(DESTRUCTIVE);
6187
+ break;
6188
+ }
6189
+ case "reflog": {
6190
+ const o = parseOptions(inv.args, "", /* @__PURE__ */ new Set());
6191
+ if (o.operands[0] === "expire" || o.operands[0] === "delete") found(DESTRUCTIVE);
6192
+ break;
6193
+ }
6194
+ case "gc": {
6195
+ if (inv.config.get("gc.reflogexpire") === "now") found(DESTRUCTIVE);
6196
+ break;
6197
+ }
6198
+ case "stash": {
6199
+ const o = parseOptions(inv.args, "", /* @__PURE__ */ new Set());
6200
+ if (o.operands[0] === "clear" || o.operands[0] === "drop") found(DESTRUCTIVE);
6201
+ break;
6202
+ }
6203
+ case "worktree": {
6204
+ const o = parseOptions(inv.args, "", /* @__PURE__ */ new Set());
6205
+ if (o.operands[0] === "remove" && (o.flags.has("-f") || o.flags.has("--force")))
6206
+ found(DESTRUCTIVE);
6207
+ break;
6208
+ }
6209
+ // The working tree overwritten: `restore` from another ref, a forced
6210
+ // checkout, or `checkout -- .` (every local change dropped). A bare
6211
+ // `restore .` or `checkout .` stays an ordinary undo, as pinned.
6212
+ case "restore": {
6213
+ const o = parseOptions(inv.args, "s", RESTORE_LONG_VALUE);
6214
+ const src = restoreSource(inv.args);
6215
+ const whole = o.dynamic || o.operands.some(wholeTree);
6216
+ if (src !== null && src !== "HEAD" && whole) found(DESTRUCTIVE);
6217
+ break;
6218
+ }
6219
+ case "checkout": {
6220
+ const o = parseOptions(inv.args, "bB", CHECKOUT_LONG_VALUE);
6221
+ const forced = o.flags.has("-f") || o.flags.has("--force");
6222
+ const dropAll = inv.args.includes("--") && o.operands.some(wholeTree);
6223
+ if (forced || dropAll) found(DESTRUCTIVE);
6224
+ break;
6225
+ }
5928
6226
  default:
5929
6227
  break;
5930
6228
  }
5931
6229
  return out;
5932
6230
  }
6231
+ function isExcludePathspec(operand) {
6232
+ return /^:[!^]/.test(operand) || /^:\([^)]*\bexclude\b[^)]*\)/.test(operand);
6233
+ }
6234
+ function restoreSource(args) {
6235
+ for (let i = 0; i < args.length; i++) {
6236
+ const w = args[i];
6237
+ if (w === null) continue;
6238
+ if (w.startsWith("--source=")) return w.slice("--source=".length);
6239
+ if (w === "--source" || w === "-s") return args[i + 1] ?? null;
6240
+ if (/^-s./.test(w)) return w.slice(2);
6241
+ }
6242
+ return null;
6243
+ }
6244
+ function isHistoryRef(ref) {
6245
+ return /^refs\/(heads|stash)(\/|$)/.test(ref);
6246
+ }
5933
6247
  function killSwitchSet(name, value) {
5934
6248
  if (value === null) return false;
5935
6249
  switch (name) {
@@ -5987,6 +6301,7 @@ function hooksBypass(inv) {
5987
6301
  const o = parseOptions(inv.args, "mFCcut", COMMIT_LONG_VALUE);
5988
6302
  return o.flags.has("-n") || o.flags.has("--no-verify");
5989
6303
  }
6304
+ if (sub === "commit-tree") return true;
5990
6305
  if (sub !== void 0 && NO_VERIFY_SUBS.has(sub)) {
5991
6306
  return inv.args.some((a) => a === "--no-verify");
5992
6307
  }
@@ -6024,7 +6339,20 @@ function configWritesHooksPath(args) {
6024
6339
  return write;
6025
6340
  }
6026
6341
  function isHookPath(word) {
6027
- return /(^|[\\/])(\.git[\\/]hooks|\.husky)([\\/]|$)/.test(word);
6342
+ return /(^|[\\/])(\.git[\\/]hooks|\.husky|\.githooks)([\\/]|$)/.test(word);
6343
+ }
6344
+ function isLiveHookFile(word) {
6345
+ if (!isHookPath(word)) return false;
6346
+ const m = /(^|[\\/])(\.git[\\/]hooks|\.husky|\.githooks)(?:[\\/](.*))?$/.exec(word);
6347
+ if (!m) return false;
6348
+ const rest = m[3] ?? "";
6349
+ if (rest === "" || rest === "_") return true;
6350
+ const leaf = rest.replace(/^_[\\/]/, "");
6351
+ return /^[a-z-]+$/.test(leaf);
6352
+ }
6353
+ function hookBodyIsEmpty(text) {
6354
+ const lines = text.split(/\r?\n/).map((l) => l.trim()).filter((l) => l !== "" && !l.startsWith("#"));
6355
+ return lines.every((l) => /^(exit(\s+0)?|true|:|set\s+-[a-zA-Z]+|set\s+-o\s+\w+)$/.test(l));
6028
6356
  }
6029
6357
  function modeRemovesExecute(mode) {
6030
6358
  if (/^[0-7]{3,4}$/.test(mode)) {
@@ -6033,6 +6361,22 @@ function modeRemovesExecute(mode) {
6033
6361
  }
6034
6362
  return mode.split(",").some((part) => /^[ugoa]*-[rwst]*x/.test(part));
6035
6363
  }
6364
+ function redirOpIsAppend(redir) {
6365
+ return redir?.Op === appendOp();
6366
+ }
6367
+ function appendOp() {
6368
+ if (_appendOp !== null) return _appendOp;
6369
+ try {
6370
+ const f = parseShared("echo x >> y");
6371
+ _appendOp = typeof f === "symbol" ? -1 : f.Stmts[0].Redirs[0].Op;
6372
+ } catch {
6373
+ _appendOp = -1;
6374
+ }
6375
+ return _appendOp;
6376
+ }
6377
+ function hooksBypassed(shown) {
6378
+ return { ...HOOKS_BYPASS, shown, reason: `${HOOKS_BYPASS.reason}: ${shown}` };
6379
+ }
6036
6380
  function chmodDisarmsHook(args, shown) {
6037
6381
  let i = 0;
6038
6382
  while (i < args.length) {
@@ -6075,6 +6419,7 @@ function stagesEverything(inv) {
6075
6419
  const sub = inv.sub?.toLowerCase();
6076
6420
  const prefix = chdirPrefix(inv);
6077
6421
  const judge = (operands2) => {
6422
+ if (operands2.length > 0 && operands2.every(isExcludePathspec)) return {};
6078
6423
  let conditional = false;
6079
6424
  for (const operand of operands2) {
6080
6425
  if (!wholeTree(operand)) continue;
@@ -6367,7 +6712,7 @@ function evaluateAgentFileWrite(args, accesses, context, settings, shell, hooks
6367
6712
  }
6368
6713
  return null;
6369
6714
  }
6370
- function isPipeOp(op) {
6715
+ function isPipeOp2(op) {
6371
6716
  return !isListOp(op);
6372
6717
  }
6373
6718
  function isDupInOp(op) {
@@ -8400,7 +8745,7 @@ async function evaluatePolicyCore(config, toolName, args, context = {}, hooks =
8400
8745
  if (v?.logged) noteLogged(v);
8401
8746
  else if (v) candidates.push(v);
8402
8747
  };
8403
- const inline = reviewsEveryInline(config.settings.mode) ? detectInlineExec(shellCommand) ? void 0 : null : inlineExecReview(shellCommand);
8748
+ const inline = reviewsEveryInline(config.settings.mode) ? detectInlineExec(shellCommand) ? void 0 : null : inlineExecReview(shellCommand, config.policy.jailPaths);
8404
8749
  if (inline !== null) {
8405
8750
  consider("commands.inline-exec", {
8406
8751
  decision: "review",
@@ -8743,7 +9088,7 @@ function* validCardRuns(text, base2, exemptFractions = true) {
8743
9088
  let m;
8744
9089
  while ((m = re.exec(text)) !== null) {
8745
9090
  const digits = m[0].replace(/\D/g, "");
8746
- const fraction = exemptFractions && isDecimalFraction(text, m.index, digits.length === m[0].length);
9091
+ const fraction = exemptFractions && (isDecimalFraction(text, m.index, digits.length === m[0].length) || isInteriorDotNumber(m[0]));
8747
9092
  if (!fraction && validateLuhn(digits)) {
8748
9093
  yield { index: m.index, length: m[0].length, digits };
8749
9094
  } else {
@@ -8770,6 +9115,12 @@ function replaceValidCards(text, replace, { exemptFractions = true } = {}) {
8770
9115
  }
8771
9116
  return out;
8772
9117
  }
9118
+ function isInteriorDotNumber(run) {
9119
+ const first = run.indexOf(".");
9120
+ if (first <= 0 || first === run.length - 1) return false;
9121
+ if (run.indexOf(".", first + 1) !== -1) return false;
9122
+ return !/[-\s]/.test(run);
9123
+ }
8773
9124
  function isDecimalFraction(text, index, unseparated) {
8774
9125
  if (!unseparated) return false;
8775
9126
  const sep = text[index - 1];
@@ -9493,7 +9844,7 @@ function matchCanaryArgs(args, values) {
9493
9844
  return null;
9494
9845
  }
9495
9846
  }
9496
- var import_url, import_picomatch, import_safe_regex2, import_picomatch2, import_module, import_picomatch3, import_safe_regex22, import_crypto2, import_crypto3, import_crypto4, import_crypto5, getFilename, __filename2, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, finance_default, identity_default, publishing_default, settings_default, branches_default, ACTION_TEMPLATES, ACTION_CHECKS, GAP, prog, httpTo, httpWrite, HTTP_WRITE, HTTP_WRITE_ANY, NOT_STRIPE_TEST, postTo, STRIPE_OBJECTS, APP_SHIELD_SPECS, APP_SHIELDS, APP_SHIELD_NAMES, APP_SERVER_GLOB, GENERAL_ACTION_RULES, BUILTIN_SHIELDS, SSRF_MAX_HOST, METADATA_ADDRESSES, STRICT_TIERS, METADATA_HOSTNAMES, v4Octets, TIER_REASON, CHECK_TEXT, LEVELS, MODE_VALUES, SOFT, STRICT, BLOCK_ALL, LEVEL_CHECKS, ASI, ASI_IDS, PACK_SHIELD_OWASP, PACK_RULE_OWASP, VERDICTS, CHECK_VERDICT_RANK, ALL, BUILTIN_DANGEROUS_WORDS, PRODUCT_CHECKS, SHIELD_RULE_NAME, SHIELD_RULES_FOLDED_INTO_PRODUCT, CHECKS, CHECK_BY_ID, LEGACY_TO_ID, PROJECT_JAIL_BLOCK_READ, OVERRIDE_PREFIX, LABEL_WRAPPERS, LEGACY_READERS, syntax, KINDS, STRUCTS, unknownKinds, INVISIBLE_RE, INVISIBLE_ONE, HOMOGLYPHS, HOMOGLYPH_RE, MAX_BLOBS, MIN_DECODED, BASE64_RUN_RE, HEX_RUN_RE, HEX_ESCAPE_RUN_RE, SYSTEM_AUTH_FILES, SYSTEM_AUTH_DIRS, PASSWORD_HASH_FILES, STARTUP_NAMES, SYSTEM_DIRS, SYSTEM_DIRS_EXEMPT, UPLOAD_SYSTEM_DIRS, AGENT_INSTRUCTION_FILES, AGENT_INSTRUCTION_DIRS, LEGACY_SYSTEM_PATH_RE, MAX_ALTERNATIVES, MAX_CALL_DEPTH, MAX_CALL_WALKS, MAX_XARGS_PAYLOAD, POSITIONAL, MAX_DEPTH, MAX_PAYLOAD_DEPTH, unique, clone, SENDING_STDIN, DOWNLOAD_SHORT_VALUE, DOWNLOAD_LONG_VALUE, DOWNLOAD_OUTPUT_FLAGS, DOWNLOAD_DIR_FLAGS, base, quoteGlob, asWord, ops, MAX_INLINE_CODE, WHAT, OUTSIDE, pyImport, either, PYTHON, NODE, PERL, RUBY, PHP, TABLES, STRING_LITERAL, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, codeBodiesCache, AST_CACHE_MAX, astCache, PARSE_FAIL, INTERPRETER_LINE, PROGRAM_DIR, ACCESS_VIEW_CACHE_MAX, accessViewCache, MAX_READING_PAYLOADS, STDIN_CODE_INTERPRETERS, RUNS_TEXT_ELSEWHERE, defaultExpansionOps, 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;
9847
+ var import_url, import_picomatch, import_safe_regex2, import_picomatch2, import_module, import_picomatch3, import_safe_regex22, import_crypto2, import_crypto3, import_crypto4, import_crypto5, getFilename, __filename2, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, finance_default, identity_default, publishing_default, settings_default, branches_default, ACTION_TEMPLATES, ACTION_CHECKS, GAP, prog, httpTo, httpWrite, HTTP_WRITE, HTTP_WRITE_ANY, NOT_STRIPE_TEST, postTo, STRIPE_OBJECTS, APP_SHIELD_SPECS, APP_SHIELDS, APP_SHIELD_NAMES, APP_SERVER_GLOB, GENERAL_ACTION_RULES, BUILTIN_SHIELDS, SSRF_MAX_HOST, METADATA_ADDRESSES, STRICT_TIERS, METADATA_HOSTNAMES, v4Octets, TIER_REASON, CHECK_TEXT, LEVELS, MODE_VALUES, SOFT, STRICT, BLOCK_ALL, LEVEL_CHECKS, ASI, ASI_IDS, PACK_SHIELD_OWASP, PACK_RULE_OWASP, VERDICTS, CHECK_VERDICT_RANK, ALL, BUILTIN_DANGEROUS_WORDS, PRODUCT_CHECKS, SHIELD_RULE_NAME, SHIELD_RULES_FOLDED_INTO_PRODUCT, CHECKS, CHECK_BY_ID, LEGACY_TO_ID, PROJECT_JAIL_BLOCK_READ, OVERRIDE_PREFIX, LABEL_WRAPPERS, LEGACY_READERS, syntax, KINDS, STRUCTS, unknownKinds, INVISIBLE_RE, INVISIBLE_ONE, HOMOGLYPHS, HOMOGLYPH_RE, MAX_BLOBS, MIN_DECODED, BASE64_RUN_RE, HEX_RUN_RE, HEX_ESCAPE_RUN_RE, SYSTEM_AUTH_FILES, SYSTEM_AUTH_DIRS, PASSWORD_HASH_FILES, STARTUP_NAMES, SYSTEM_DIRS, SYSTEM_DIRS_EXEMPT, UPLOAD_SYSTEM_DIRS, AGENT_INSTRUCTION_FILES, AGENT_INSTRUCTION_DIRS, LEGACY_SYSTEM_PATH_RE, MAX_ALTERNATIVES, MAX_CALL_DEPTH, MAX_CALL_WALKS, MAX_XARGS_PAYLOAD, POSITIONAL, MAX_DEPTH, MAX_PAYLOAD_DEPTH, unique, clone, SENDING_STDIN, DOWNLOAD_SHORT_VALUE, DOWNLOAD_LONG_VALUE, DOWNLOAD_OUTPUT_FLAGS, DOWNLOAD_DIR_FLAGS, base, quoteGlob, asWord, ops, MAX_INLINE_CODE, WHAT, OUTSIDE, pyImport, either, PYTHON, NODE, PERL, RUBY, PHP, TABLES, STRING_LITERAL, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, codeBodiesCache, AST_CACHE_MAX, astCache, PARSE_FAIL, INTERPRETER_LINE, PROGRAM_DIR, ACCESS_VIEW_CACHE_MAX, accessViewCache, MAX_READING_PAYLOADS, STDIN_CODE_INTERPRETERS, RUNS_TEXT_ELSEWHERE, defaultExpansionOps, EMPTY_NAMES, VARIABLE_READERS, PLAIN_PROGRAM_NAME, FS_READ_TOOLS, GREP_SHAPE, PATTERN_VERBS, PATTERN_VERB_NAMES, GREP_FILE_OPERANDS, READER_VALUE_LETTERS, FILE_OPERAND_FLAGS, SCP_VALUE_FLAGS, CURL_SOURCE_FLAGS, CURL_VALUE_LETTERS, WGET_VALUE_LETTERS, TAR_VALUE_LETTERS, ZIP_VALUE_LETTERS, RSYNC_VALUE_LETTERS, RSYNC_SKIP, CP_VALUE_LETTERS, INSTALL_VALUE_LETTERS, COPY_VERBS, TAR_MODE_WORD, COPY_VERB_HEADS, FS_OP_PRESCREEN_RE, HOME_CACHE_ALLOWLIST, SENSITIVE_PATH_RULES, AST_FS_REGEX_RULES, SQL_DB_CLIS, SQL_DDL_RE, CHMOD_OPEN_PERM_TOKENS, COMMAND_WRAPPERS, INLINE_INTERPRETER, RUNNER_WRAPPERS, _redirStdinOps, _listOps, WRAPPER_TAKES_TARGET, FIND_EXEC_FLAGS, FS_MUTATION_TOOLS, INTERP_LEADING_TARGET, MAX_PAYLOAD_NESTING, payloadNesting, MAX_INLINE_CALLS, INLINE_EXEC_FALLBACK, UNREADABLE_SOURCE, SCRIPT_RUNNERS, SCRIPT_RUNNER_VALUE_FLAGS, MAX_SCRIPT_OPERANDS, NET_BINARIES, VALUE_FLAGS, HOME_VARIABLES, assignmentTable, currentStmtOffset, PAYLOAD_BUDGET, payloadBudget, seenPayloads, UNANALYSABLE_NESTING, ASSIGNMENT_HEADS, andOrOps, ARRAY_MARK, ARRAY_SEP, MAX_CALL_DEPTH2, MAX_CALL_WALKS2, callDepth, callWalks, FS_OP_CACHE_MAX, fsOpCache, stripDotSlash, REDIR_TRUNCATE_OPS, REDIR_FILE_IN_OPS, REDIR_HEREDOC_OPS, FIND_OPTIONS, COPY_RULE_OF, INDIRECT_SHAPE_RE, SOURCES_FILE_RE, SHELL_SET_VARS, pipeOps, callWords, JAILED_GLOB_NAMES, JAILED_DIR_NAMES, shellGlob, GLOB_META, isReaderWord, positionalAfter, NONE, UNKNOWN, FORBIDDEN_PATH_SEGMENTS, BASH_TOOL_NAMES, MCP_EXECUTORS, BUILTIN_FIELDS, ARGV_FIELDS, BARE_WORD, MAX_ARGV_CHARACTERS, exempt, RANK, SEQUENCE_OPS, BIP39_ENGLISH, BIP39_PUBLIC_VECTORS, indices, lengths, hash, MAX_MNEMONIC_HASHES, SECRET_VIEW_LIMITS, IBAN_LENGTH, IBAN_BBAN_SHAPE, B58, B58_INDEX, XPRV_VERSIONS, B62, CRC32_TABLE, GITHUB_TOKEN_RE, B64URL, B64URL_INDEX, CASK_TWO_ZERO_SUFFIX, CASK_FOUR_ZERO_SUFFIX, CF_ODD, CF_EVEN, SPANISH_LETTERS, HETU_CHARS, VERHOEFF_D, VERHOEFF_P, MAX, UNTRUSTED_TOOLS, INJECTION_SIGNALS, MENTION_QUOTE_RE, MENTION_WORD_RE, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, REFERENCE_VALUE_RE, IDENTIFIER_VALUE_RE, PATH_VALUE_RE, DOTTED_REFERENCE_RE, CONCATENATION_RE, EMAIL_VALUE_RE, FIXTURE_NAME_RE, DERIVATIVE_NAME_RE, QUERY_CREDENTIAL_VALUE_RE, OTP_SECRET_RE, OTP_DOC_SECRETS, ASSIGNED_SECRET_VALUE_RE, ASSIGNED_SECRET_NAME_RE, NAME_KEYWORD_RE, ARN_CONTEXT_RE, CIPHER_KEY_NAME_RE, CIPHER_NAME_PARTS, CIPHER_KEY_VALUE_RE, LARAVEL_KEY_VALUE_RE, SQL_PASSWORD_VALUE_RE, GRAFANA_KEY_VALUE_RE, BASIC_VALUE_RE, BASIC_PAIR_RE, BASIC_HEADER_RE, BASIC_HEADER_PAIR_RE, BASIC_PLACEHOLDER_RE, BASIC_TEMPLATE_MARKERS, DROPBOX_SECRET_RE, OTP_CAPTURE_RE, DB_URL_PASSWORD_RE, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, SENSITIVE_PATH_REGEXES, MAX_DEPTH2, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, BATCH_SEPARATOR, IMPERATIVE, SECRET_TARGET, GUARD, SIGNALS, DESTINATION_RE, REUSED_SIGNALS, NEVER_NEGATED, NEGATION_BEFORE, RDR_IN, COPY_VERBS2, LITERAL_VERBS, GIT_PRESCREEN_RE, MAX_PAYLOAD_DEPTH2, MAX_ALIAS_DEPTH, CACHE_MAX, cache, PAYLOAD_SHELLS, CONFIG_SCOPE_FLAGS, CONFIG_VALUE_FLAGS, GLOBAL_VALUE_OPTIONS, GLOBAL_FLAG_OPTIONS, MAX_LOOP_ITEMS, GitWalk, POSITIONAL2, NO_VARS, NO_APPENDS, FORCE_PUSH, DESTRUCTIVE, UNREADABLE, PUSH_LONG_VALUE, BRANCH_LONG_VALUE, TAG_LONG_VALUE, CLEAN_LONG_VALUE, RESTORE_LONG_VALUE, CHECKOUT_LONG_VALUE, HOOKS_BYPASS, NO_VERIFY_SUBS, HOOK_KILL_SWITCHES, COMMIT_LONG_VALUE, CHMOD_OPTION_RE, _appendOp, STAGE_ALL, ADD_LONG_VALUE, CHECK_ID, RULE_NAME, INERT_LINE_RE, CHECK_ID2, INSTRUCTION_NAMES, SETTINGS_RE, HOOK_WORDS_RE, SHELLS, NETCATS, NET_CLIENTS, SENDERS, NETCAT_EXEC_LONG, DECL_DUMPS, PROC_ENVIRON_RE, DEV_NET_RE, SOCAT_PROGRAM_RE, SOCAT_NET_RE, PRESCREEN_RE, MAX_PAYLOAD_DEPTH3, CACHE_MAX2, cache2, dupInOp, REVERSE_SHELL_DESCRIPTION, ENV_EXFIL_DESCRIPTION, DYNAMIC_WORD, MULTICALL, SHELL_VALUE_OPTIONS, INTERPRETERS, OutboundWalk, NPM_INSTALL_VERBS, NPM_EXEC_VERBS, PNPM_INSTALL_VERBS, YARN_INSTALL_VERBS, BUN_INSTALL_VERBS, DLX_VERBS, NON_REGISTRY_RE, NPM_NAME_RE, EXACT_SEMVER_RE, NPM_VALUE_FLAGS, PNPM_VALUE_FLAGS, YARN_VALUE_FLAGS, BUN_VALUE_FLAGS, PIP_VALUE_FLAGS, UV_VALUE_FLAGS, POETRY_VALUE_FLAGS, PIPENV_VALUE_FLAGS, PIPX_VALUE_FLAGS, PY_SPEC_RE, PY_EXACT_RE, EXACT_VERSION_RE, GEM_NAME_RE, GEM_VALUE_FLAGS, BUNDLE_VALUE_FLAGS, CRATE_NAME_RE, CARGO_ADD_VALUE_FLAGS, CARGO_INSTALL_VALUE_FLAGS, GO_VALUE_FLAGS, GO_PATH_RE, NUGET_NAME_RE, DOTNET_ADD_VALUE_FLAGS, DOTNET_TOOL_VALUE_FLAGS, NUGET_VALUE_FLAGS, MAVEN_COORD_RE, COMPOSER_NAME_RE, COMPOSER_VALUE_FLAGS, EDITOR_HEADS, EXTENSION_ID_RE, MANAGER_HEADS, SHELLS2, MAX_NESTING, SHELL_VALUE_FLAGS, NPM_FAMILY, PIP_FAMILY, NPM_FETCH_VERBS, PIP_FETCH_VERBS, NPM_FLAGS, PIP_FLAGS, NPM_VARS, PIP_VARS, PRESCREEN_RE2, PIP_ATTACHED_RE, MAX_PAYLOAD_DEPTH4, PUBLIC_REGISTRY_HOSTS, TUNING_FIELDS, MAP_GOVERNED, KNOB_GOVERNED, CONFIGURABLE_CHECK_IDS, DEFAULT_EGRESS_ALLOWLIST, PRIVATE_HOST_SUFFIXES, SOURCE_COMMANDS, SINK_COMMANDS, OBFUSCATORS, SENSITIVE_PATTERNS, FLAGS_WITH_VALUES, DESTINATION_ARGS, MCP_DESTINATION_KEYS, ARGUMENT_SCAN_LIMITS, RUN_CHAR, runChar, VERDICT_RANK, SQL_DML_KEYWORDS, MAX_DESTINATIONS, MAX_SCAN_CHARS, EMAIL_RE, URL_RE, IBAN_RE, RECIPIENT_KEYS, DESTRUCTIVE_VERB_RE, PII_EMAIL_RE, PII_SSN_RE, PII_PHONE_RE, PII_CC16_RE, PII_CC15_RE, PII_CC14_RE, PII_CC13_RE, PII_CC19_RE, CARD_PATTERNS, PII_ROUTING_RE, ROUTING_LABEL_RE, ROUTING_LABEL_WINDOW, NATIONAL_ID_SHAPES, NATIONAL_ID_LABEL_RE, NATIONAL_ID_LABEL_CS_RE, idLabelIn, NATIONAL_ID_LABEL_WINDOW, NATIONAL_ID_GUARD_RE, PII_IBAN_RE, REALTIME_PII_PATTERNS, MAX_PII_SCAN_BYTES, IBAN_DESTINATION_KEYS, WHOLE_IBAN_RE, JSON_ESCAPE_RE, JSON_ESCAPES, MASKED_KINDS, ENTRY_RE, IBAN_TEXT_RE, EMAIL_TEXT_RE, ACCOUNT_KEY_RE, ACCOUNT_VALUE_RE, MAX_MASK_DEPTH, EMPTY, CURL_SEND_FLAGS, WGET_SEND_FLAGS, IWR_SEND_FLAGS, WRITE_METHODS, GH_READ, GH_WRITE, GH_HOSTNAME_FLAGS, GH_REPO_VALUE_FLAGS, GH_API_VALUE_FLAGS, GIT_FETCH, PUBLISH, SHELLS3, OWN_HEADS, MAX_DEPTH3, CWD_CHANGERS, TERMINAL_ESCAPE_RE, LOOP_MAX_RECORDS, CANARY_MIN_LENGTH, MAX_TEXT, MAX_DEPTH4, MAX_JSON_PARSE, URL_DEPTH, B64_DEPTH, MIN_SEGMENT, SEPARATORS, stripSeparators, looksText, CANARY_DECODERS, VIEWS, LONG_OUTPUT_THRESHOLD_BYTES;
9497
9848
  var init_dist = __esm({
9498
9849
  "packages/policy-engine/dist/index.mjs"() {
9499
9850
  "use strict";
@@ -12771,6 +13122,7 @@ var init_dist = __esm({
12771
13122
  "sqlite3"
12772
13123
  ]);
12773
13124
  defaultExpansionOps = null;
13125
+ EMPTY_NAMES = /* @__PURE__ */ new Set();
12774
13126
  VARIABLE_READERS = /* @__PURE__ */ new Set([
12775
13127
  "read",
12776
13128
  "mapfile",
@@ -12783,6 +13135,7 @@ var init_dist = __esm({
12783
13135
  "readonly",
12784
13136
  "printf"
12785
13137
  ]);
13138
+ PLAIN_PROGRAM_NAME = /^[A-Za-z_][\w.+-]*$/;
12786
13139
  FS_READ_TOOLS = /* @__PURE__ */ new Set([
12787
13140
  "cat",
12788
13141
  "less",
@@ -13677,6 +14030,8 @@ var init_dist = __esm({
13677
14030
  AND_OR_OPS: new Set([and, or].filter((o) => o !== null))
13678
14031
  };
13679
14032
  });
14033
+ ARRAY_MARK = "array";
14034
+ ARRAY_SEP = "";
13680
14035
  MAX_CALL_DEPTH2 = 4;
13681
14036
  MAX_CALL_WALKS2 = 256;
13682
14037
  callDepth = 0;
@@ -17006,7 +17361,22 @@ zoo`.split("\n");
17006
17361
  found;
17007
17362
  /** Aliases defined earlier in this command: name -> value (null = dynamic). */
17008
17363
  aliases = /* @__PURE__ */ new Map();
17364
+ /** node9Firewall#385: variables assigned a tree-top word (`scope="$PWD"`,
17365
+ * `root=$(git rev-parse --show-toplevel)`, `DIR=$(pwd)`), so the
17366
+ * variable reads as the top where it is used. */
17367
+ treeTopVars = /* @__PURE__ */ new Set();
17368
+ /** node9Firewall#385 (c): a string a script builds with `+=` from
17369
+ * literals (`mode=--ha; mode+=rd`). The jail's table does not follow an
17370
+ * append (JAIL-14a); the git rules read the built word, since a flag
17371
+ * spelled in two halves is still that flag. name -> value, null once a
17372
+ * half is unknown. */
17373
+ appended = /* @__PURE__ */ new Map();
17009
17374
  payloads = /* @__PURE__ */ new Set();
17375
+ /** node9Firewall#385 (a): the text this command writes to each path
17376
+ * (`cat > /tmp/x.sh <<'SH' ... SH`, `echo '...' > f`, `>>` appends), so
17377
+ * a later statement that runs the path (`bash f`, `sh f`, `./f`) is
17378
+ * judged on what it runs. null once a write to the path is unknown. */
17379
+ written = /* @__PURE__ */ new Map();
17010
17380
  /** `for NAME in a b c`: the literal items, or null when one is dynamic. */
17011
17381
  loopVars = /* @__PURE__ */ new Map();
17012
17382
  /** Walk one command string. Returns false when it did not parse. */
@@ -17025,6 +17395,25 @@ zoo`.split("\n");
17025
17395
  const type = n.kind;
17026
17396
  if (type === "Stmt") {
17027
17397
  this.visitHeredocShell(n, depth);
17398
+ this.visitPipeline(n, depth);
17399
+ this.visitHookWrite(n);
17400
+ this.recordScriptWrite(n);
17401
+ this.visitWrittenScript(n, depth);
17402
+ return true;
17403
+ }
17404
+ if (type === "Assign" && n.Name?.Value && n.Value) {
17405
+ const name = n.Name.Value;
17406
+ if (n.Append) {
17407
+ const prev = this.appended.has(name) ? this.appended.get(name) : resolveWordLiteral(n.Value) === null ? null : void 0;
17408
+ const base2 = prev === void 0 ? recordedExpansion(name) : prev;
17409
+ const piece = resolveWordLiteral(n.Value);
17410
+ this.appended.set(name, typeof base2 === "string" && piece !== null ? base2 + piece : null);
17411
+ this.treeTopVars.delete(name);
17412
+ return true;
17413
+ }
17414
+ this.appended.delete(name);
17415
+ if (treeTopWord(n.Value, this.treeTopVars)) this.treeTopVars.add(name);
17416
+ else this.treeTopVars.delete(name);
17028
17417
  return true;
17029
17418
  }
17030
17419
  if (type === "ForClause") {
@@ -17053,13 +17442,13 @@ zoo`.split("\n");
17053
17442
  const param = plainParamName(rawArgs[loopArg]);
17054
17443
  for (const item of this.loopVars.get(param.name)) {
17055
17444
  const words2 = rawArgs.flatMap(
17056
- (a, i) => i === loopArg ? param.quoted ? [item] : splitFields(item) : resolveGitWords(a)
17445
+ (a, i) => i === loopArg ? param.quoted ? [item] : splitFields(item) : resolveGitWords(a, this.treeTopVars, this.appended)
17057
17446
  );
17058
17447
  this.visitWords(words2, new Map(prefixEnv), depth, false);
17059
17448
  }
17060
17449
  return true;
17061
17450
  }
17062
- const words = rawArgs.flatMap((a) => resolveGitWords(a));
17451
+ const words = rawArgs.flatMap((a) => resolveGitWords(a, this.treeTopVars, this.appended));
17063
17452
  this.visitWords(words, prefixEnv, depth, false);
17064
17453
  return true;
17065
17454
  }
@@ -17084,6 +17473,23 @@ zoo`.split("\n");
17084
17473
  if (f) this.found(f);
17085
17474
  return;
17086
17475
  }
17476
+ if (headWord === "rm" || headWord === "unlink") {
17477
+ if (words.slice(head + 1).some((w) => w !== null && isLiveHookFile(w)))
17478
+ this.found(hooksBypassed(shownOf(words)));
17479
+ return;
17480
+ }
17481
+ if (headWord === "mv") {
17482
+ const operands2 = words.slice(head + 1).filter((w) => w === null || !w.startsWith("-"));
17483
+ const src = operands2[0];
17484
+ if (typeof src === "string" && isLiveHookFile(src)) this.found(hooksBypassed(shownOf(words)));
17485
+ return;
17486
+ }
17487
+ if (headWord === "perl") {
17488
+ const code = words.slice(head + 1).find((w) => w !== null && /\brename\b/.test(w));
17489
+ const m = code ? /rename\s*\(?\s*q[qw]?\s*[(\[{<'"]([^)\]}>'"]+)/.exec(code) : null;
17490
+ if (m && isLiveHookFile(m[1])) this.found(hooksBypassed(shownOf(words)));
17491
+ return;
17492
+ }
17087
17493
  if (depth < MAX_PAYLOAD_DEPTH2) {
17088
17494
  const payload = literalShellPayload(words, baseWord(words[0]));
17089
17495
  if (payload !== null) this.runPayload(payload, depth);
@@ -17094,6 +17500,127 @@ zoo`.split("\n");
17094
17500
  this.payloads.add(payload);
17095
17501
  this.run(payload, depth + 1);
17096
17502
  }
17503
+ /**
17504
+ * node9Firewall#385: a pipeline whose last stage reads its pathspec from
17505
+ * stdin (`git add --pathspec-from-file=-`) or runs git over the names it
17506
+ * is fed (`xargs -0 git add --`), with a producer that is one literal
17507
+ * (`git rev-parse --show-toplevel`, `printf '%s\0' .`): the literal is
17508
+ * the operand, and the call is judged with it.
17509
+ */
17510
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
17511
+ visitPipeline(stmt, depth) {
17512
+ const cmd = stmt.Cmd;
17513
+ if (!cmd || cmd.kind !== "BinaryCmd" || !isPipeOp(cmd.Op)) return;
17514
+ const stages = [];
17515
+ let cur = cmd;
17516
+ while (cur?.kind === "BinaryCmd" && isPipeOp(cur.Op)) {
17517
+ stages.unshift(cur.Y);
17518
+ const left = cur.X;
17519
+ if (left?.Cmd?.kind === "BinaryCmd" && isPipeOp(left.Cmd.Op)) cur = left.Cmd;
17520
+ else {
17521
+ stages.unshift(left);
17522
+ break;
17523
+ }
17524
+ }
17525
+ if (stages.length < 2) return;
17526
+ const producer = stages[stages.length - 2]?.Cmd;
17527
+ const consumer = stages[stages.length - 1]?.Cmd;
17528
+ if (!producer || !consumer || producer.kind !== "CallExpr" || consumer.kind !== "CallExpr")
17529
+ return;
17530
+ const fed = producedLiteral(producer);
17531
+ if (fed === null) return;
17532
+ const words = (consumer.Args || []).flatMap(
17533
+ (a) => resolveGitWords(a, this.treeTopVars, this.appended)
17534
+ );
17535
+ const head = unwrapCommandHead(words);
17536
+ const viaXargs = words.slice(0, head).some((w) => baseWord(w) === "xargs");
17537
+ const fromFile = words.findIndex(
17538
+ (w) => w === "--pathspec-from-file=-" || w === "--pathspec-from-file"
17539
+ );
17540
+ if (!viaXargs && fromFile < 0) return;
17541
+ let fedWords;
17542
+ if (fromFile >= 0) {
17543
+ fedWords = words.filter(
17544
+ (w, k) => !(k === fromFile || w === "-" && words[k - 1] === "--pathspec-from-file")
17545
+ );
17546
+ fedWords.push(fed);
17547
+ } else fedWords = [...words, fed];
17548
+ this.visitWords(fedWords, /* @__PURE__ */ new Map(), depth, false);
17549
+ }
17550
+ /**
17551
+ * node9Firewall#385 (e): a hook file written with a body that checks
17552
+ * nothing (`cat > .githooks/pre-commit <<'SH' exit 0 SH`, `echo 'exit 0'
17553
+ * > .git/hooks/pre-push`, `: > .githooks/pre-commit`). A real body (a
17554
+ * test, a linter) is ordinary work on the hooks.
17555
+ */
17556
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
17557
+ visitHookWrite(stmt) {
17558
+ const cmd = stmt.Cmd;
17559
+ if (!cmd || cmd.kind !== "CallExpr") return;
17560
+ const redirs = stmt.Redirs || [];
17561
+ const target = redirs.find((r) => r?.Word && !r.Hdoc && (r.N == null || r.N.Value === "1"));
17562
+ if (!target) return;
17563
+ const path210 = resolveWordLiteral(target.Word);
17564
+ if (path210 === null || !isLiveHookFile(path210)) return;
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 body = null;
17569
+ if (hdoc) body = resolveWordLiteral(hdoc.Hdoc);
17570
+ else if (head === ":" || head === "true" || words.length === 0) body = "";
17571
+ else if (head === "echo" || head === "printf") {
17572
+ const rest = words.slice(1);
17573
+ body = rest.some((w) => w === null) ? null : rest.filter((w) => !/^-[neE]+$/.test(w)).join(" ");
17574
+ if (body !== null) body = body.replace(/\\n/g, "\n");
17575
+ }
17576
+ if (body !== null && hookBodyIsEmpty(body)) this.found(hooksBypassed(shownOf(words)));
17577
+ }
17578
+ /** A statement that writes a literal text to a path, by redirect. */
17579
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
17580
+ recordScriptWrite(stmt) {
17581
+ const cmd = stmt.Cmd;
17582
+ if (!cmd || cmd.kind !== "CallExpr") return;
17583
+ const redirs = stmt.Redirs || [];
17584
+ const target = redirs.find((r) => r?.Word && !r.Hdoc && (r.N == null || r.N.Value === "1"));
17585
+ if (!target) return;
17586
+ const path210 = resolveWordLiteral(target.Word);
17587
+ if (path210 === null) return;
17588
+ const append = /^>>/.test(target.Op?.toString() ?? "") || redirOpIsAppend(target);
17589
+ const hdoc = redirs.find((r) => r?.Hdoc);
17590
+ const words = (cmd.Args || []).map((a) => resolveWordLiteral(a));
17591
+ const head = baseWord(words[0]);
17592
+ let text = null;
17593
+ if (hdoc && (head === "cat" || head === "tee")) text = resolveWordLiteral(hdoc.Hdoc);
17594
+ else if (head === "echo" || head === "printf") {
17595
+ const rest = words.slice(1);
17596
+ text = rest.some((w) => w === null) ? null : rest.filter((w) => !/^-[neE]+$/.test(w)).join(" ");
17597
+ if (text !== null && head === "printf") text = text.replace(/\\n/g, "\n");
17598
+ } else return;
17599
+ const prev = this.written.get(path210);
17600
+ if (text === null || append && prev === null) this.written.set(path210, null);
17601
+ else this.written.set(path210, append && typeof prev === "string" ? `${prev}
17602
+ ${text}` : text);
17603
+ }
17604
+ /** A statement that runs a path this command wrote: the text is the script. */
17605
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
17606
+ visitWrittenScript(stmt, depth) {
17607
+ const cmd = stmt.Cmd;
17608
+ if (!cmd || cmd.kind !== "CallExpr" || this.written.size === 0 || depth >= MAX_PAYLOAD_DEPTH2)
17609
+ return;
17610
+ const words = (cmd.Args || []).map((a) => resolveWordLiteral(a));
17611
+ const head = unwrapCommandHead(words);
17612
+ const name = baseWord(words[head]);
17613
+ let ran = null;
17614
+ if (PAYLOAD_SHELLS.has(name) && name !== "eval") {
17615
+ const operand = words.slice(head + 1).find((w) => w === null || !w.startsWith("-"));
17616
+ if (typeof operand === "string") ran = operand;
17617
+ } else if (typeof words[head] === "string" && this.written.has(words[head])) {
17618
+ ran = words[head];
17619
+ }
17620
+ if (ran === null) return;
17621
+ const text = this.written.get(ran);
17622
+ if (typeof text === "string") this.runPayload(text, depth);
17623
+ }
17097
17624
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
17098
17625
  visitHeredocShell(stmt, depth) {
17099
17626
  const cmd = stmt.Cmd;
@@ -17245,6 +17772,8 @@ zoo`.split("\n");
17245
17772
  }
17246
17773
  };
17247
17774
  POSITIONAL2 = "\0positional";
17775
+ NO_VARS = /* @__PURE__ */ new Set();
17776
+ NO_APPENDS = /* @__PURE__ */ new Map();
17248
17777
  FORCE_PUSH = {
17249
17778
  checkId: "commands.git-destructive",
17250
17779
  ruleName: "review-force-push",
@@ -17303,6 +17832,8 @@ zoo`.split("\n");
17303
17832
  "--cleanup"
17304
17833
  ]);
17305
17834
  CLEAN_LONG_VALUE = /* @__PURE__ */ new Set(["--exclude"]);
17835
+ RESTORE_LONG_VALUE = /* @__PURE__ */ new Set(["--source", "--pathspec-from-file"]);
17836
+ CHECKOUT_LONG_VALUE = /* @__PURE__ */ new Set(["--pathspec-from-file", "--orphan", "--conflict"]);
17306
17837
  HOOKS_BYPASS = {
17307
17838
  checkId: "commands.git-hooks-bypass",
17308
17839
  ruleName: "review-git-hooks-bypass",
@@ -17328,6 +17859,7 @@ zoo`.split("\n");
17328
17859
  "--gpg-sign"
17329
17860
  ]);
17330
17861
  CHMOD_OPTION_RE = /^-[RvcfHLP]+$/;
17862
+ _appendOp = null;
17331
17863
  STAGE_ALL = {
17332
17864
  checkId: "commands.git-stage-all",
17333
17865
  ruleName: "review-git-stage-all",
@@ -17452,7 +17984,7 @@ zoo`.split("\n");
17452
17984
  this.visitStmt(n, depth);
17453
17985
  return true;
17454
17986
  }
17455
- if (type === "BinaryCmd" && isPipeOp(n.Op)) {
17987
+ if (type === "BinaryCmd" && isPipeOp2(n.Op)) {
17456
17988
  this.visitPipeline(n);
17457
17989
  return true;
17458
17990
  }
@@ -17553,7 +18085,7 @@ zoo`.split("\n");
17553
18085
  const stages = [];
17554
18086
  const flatten = (s) => {
17555
18087
  const c = s?.Cmd;
17556
- if (c && c.kind === "BinaryCmd" && isPipeOp(c.Op)) {
18088
+ if (c && c.kind === "BinaryCmd" && isPipeOp2(c.Op)) {
17557
18089
  flatten(c.X);
17558
18090
  flatten(c.Y);
17559
18091
  } else stages.push(s);
@@ -18971,9 +19503,9 @@ init_audit();
18971
19503
 
18972
19504
  // src/config/index.ts
18973
19505
  init_cjs_shims();
18974
- var import_fs4 = __toESM(require("fs"));
18975
- var import_path4 = __toESM(require("path"));
18976
- var import_os4 = __toESM(require("os"));
19506
+ var import_fs5 = __toESM(require("fs"));
19507
+ var import_path5 = __toESM(require("path"));
19508
+ var import_os5 = __toESM(require("os"));
18977
19509
 
18978
19510
  // src/config-schema.ts
18979
19511
  init_cjs_shims();
@@ -19266,11 +19798,18 @@ function slug(s) {
19266
19798
  var B = "[\\s/\\\\]";
19267
19799
  var SEP = "[/\\\\]";
19268
19800
  function pathToRegexFragment(rawPath) {
19269
- const tail = rawPath.trim().replace(/^~[\\/]?/, "").replace(/^\$\{?HOME\}?[\\/]?/, "").replace(/^\/(?:home|Users)\/[^\\/]+[\\/]?/, "").replace(/^[A-Za-z]:[\\/]Users[\\/][^\\/]+[\\/]?/, "").replace(/^[\\/]+/, "").replace(/[\\/]+$/, "");
19801
+ const trimmed = rawPath.trim();
19802
+ const tail = trimmed.replace(/^~[\\/]?/, "").replace(/^\$\{?HOME\}?[\\/]?/, "").replace(/^\/(?:home|Users)\/[^\\/]+[\\/]?/, "").replace(/^[A-Za-z]:[\\/]Users[\\/][^\\/]+[\\/]?/, "").replace(/^[\\/]+/, "").replace(/[\\/]+$/, "");
19270
19803
  const segments2 = tail.split(/[\\/]+/).filter(Boolean).map(escapeRegex);
19271
19804
  if (segments2.length === 0) return "";
19805
+ if (isAbsoluteOutsideHome(trimmed)) {
19806
+ return `(^|[^A-Za-z0-9_.~\\/-])/${segments2.join(SEP)}(${B}|$)`;
19807
+ }
19272
19808
  return `(^|${B})${segments2.join(SEP)}(${B}|$)`;
19273
19809
  }
19810
+ function isAbsoluteOutsideHome(p) {
19811
+ return p.startsWith("/") && !/^\/(?:home|Users|root)(?:\/|$)/.test(p);
19812
+ }
19274
19813
  function pathMatchesFragment(candidate, rawPath) {
19275
19814
  const value = pathToRegexFragment(rawPath);
19276
19815
  if (!value || !candidate) return false;
@@ -19536,17 +20075,57 @@ function applyManagedApprovers(local, managed) {
19536
20075
  };
19537
20076
  }
19538
20077
 
19539
- // src/auth/trusted-hosts.ts
20078
+ // src/shields/jail.ts
19540
20079
  init_cjs_shims();
19541
20080
  var import_fs3 = __toESM(require("fs"));
19542
- var import_path3 = __toESM(require("path"));
19543
20081
  var import_os3 = __toESM(require("os"));
20082
+ var import_path3 = __toESM(require("path"));
20083
+ init_dist();
20084
+ var USER_JAIL_SHIELD = "user-jail";
20085
+ function jailStorePath() {
20086
+ return import_path3.default.join(import_os3.default.homedir(), ".node9", "jail-paths.json");
20087
+ }
20088
+ function readJailPaths() {
20089
+ let text;
20090
+ try {
20091
+ text = import_fs3.default.readFileSync(jailStorePath(), "utf8");
20092
+ } catch (err) {
20093
+ if (err.code === "ENOENT") return [];
20094
+ throw err;
20095
+ }
20096
+ let parsed;
20097
+ try {
20098
+ parsed = JSON.parse(text);
20099
+ } catch {
20100
+ throw new Error(`${jailStorePath()} is not valid JSON \u2014 fix it before changing the jail.`);
20101
+ }
20102
+ if (!Array.isArray(parsed.paths)) return [];
20103
+ return parsed.paths.filter(
20104
+ (p) => !!p && typeof p.path === "string" && (p.verdict === "block" || p.verdict === "review")
20105
+ );
20106
+ }
20107
+ function findJailedPath(candidate) {
20108
+ return findJailedPathIn(candidate, readJailPaths());
20109
+ }
20110
+ function findJailedPathIn(candidate, paths) {
20111
+ if (!candidate) return null;
20112
+ for (const entry of paths) {
20113
+ if (pathMatchesFragment(candidate, entry.path)) return entry;
20114
+ }
20115
+ return null;
20116
+ }
20117
+
20118
+ // src/auth/trusted-hosts.ts
20119
+ init_cjs_shims();
20120
+ var import_fs4 = __toESM(require("fs"));
20121
+ var import_path4 = __toESM(require("path"));
20122
+ var import_os4 = __toESM(require("os"));
19544
20123
  function getTrustedHostsPath() {
19545
- return import_path3.default.join(import_os3.default.homedir(), ".node9", "trusted-hosts.json");
20124
+ return import_path4.default.join(import_os4.default.homedir(), ".node9", "trusted-hosts.json");
19546
20125
  }
19547
20126
  function readTrustedHosts() {
19548
20127
  try {
19549
- const raw = import_fs3.default.readFileSync(getTrustedHostsPath(), "utf8");
20128
+ const raw = import_fs4.default.readFileSync(getTrustedHostsPath(), "utf8");
19550
20129
  const parsed = JSON.parse(raw);
19551
20130
  return Array.isArray(parsed.hosts) ? parsed.hosts : [];
19552
20131
  } catch {
@@ -19557,7 +20136,7 @@ var _cache = null;
19557
20136
  var CACHE_TTL_MS = 5e3;
19558
20137
  function getFileMtime() {
19559
20138
  try {
19560
- return import_fs3.default.statSync(getTrustedHostsPath()).mtimeMs;
20139
+ return import_fs4.default.statSync(getTrustedHostsPath()).mtimeMs;
19561
20140
  } catch {
19562
20141
  return 0;
19563
20142
  }
@@ -20042,7 +20621,8 @@ var DEFAULT_CONFIG = {
20042
20621
  trustedHostsManaged: false,
20043
20622
  appPermissions: {},
20044
20623
  appLinks: {},
20045
- managedJailPaths: []
20624
+ managedJailPaths: [],
20625
+ jailPaths: []
20046
20626
  },
20047
20627
  environments: {}
20048
20628
  };
@@ -20111,8 +20691,8 @@ function noteRejectedApiUrl(raw) {
20111
20691
  if (rejectedApiUrlsSeen.has(key)) return;
20112
20692
  rejectedApiUrlsSeen.add(key);
20113
20693
  try {
20114
- import_fs4.default.appendFileSync(
20115
- import_path4.default.join(import_os4.default.homedir(), ".node9", "hook-debug.log"),
20694
+ import_fs5.default.appendFileSync(
20695
+ import_path5.default.join(import_os5.default.homedir(), ".node9", "hook-debug.log"),
20116
20696
  `[${(/* @__PURE__ */ new Date()).toISOString()}] REFUSED apiUrl, using the default instead: ${String(raw).slice(0, 200).replace(/[\r\n]+/g, " ")}
20117
20697
  `
20118
20698
  );
@@ -20129,9 +20709,9 @@ function getCredentials() {
20129
20709
  };
20130
20710
  }
20131
20711
  try {
20132
- const credPath = import_path4.default.join(import_os4.default.homedir(), ".node9", "credentials.json");
20133
- if (import_fs4.default.existsSync(credPath)) {
20134
- const creds = JSON.parse(import_fs4.default.readFileSync(credPath, "utf-8"));
20712
+ const credPath = import_path5.default.join(import_os5.default.homedir(), ".node9", "credentials.json");
20713
+ if (import_fs5.default.existsSync(credPath)) {
20714
+ const creds = JSON.parse(import_fs5.default.readFileSync(credPath, "utf-8"));
20135
20715
  const profileName = process.env.NODE9_PROFILE || "default";
20136
20716
  const profile = creds[profileName];
20137
20717
  if (typeof profile?.apiKey === "string" && profile.apiKey.length > 0) {
@@ -20163,7 +20743,7 @@ function readRulesCacheResilient(cacheFile) {
20163
20743
  for (let attempt = 0; attempt < 3; attempt++) {
20164
20744
  let content;
20165
20745
  try {
20166
- content = import_fs4.default.readFileSync(cacheFile, "utf-8");
20746
+ content = import_fs5.default.readFileSync(cacheFile, "utf-8");
20167
20747
  existed = true;
20168
20748
  } catch (err) {
20169
20749
  if (err.code === "ENOENT") return {};
@@ -20178,10 +20758,10 @@ function readRulesCacheResilient(cacheFile) {
20178
20758
  }
20179
20759
  }
20180
20760
  if (existed || sawReadError) {
20181
- const backup = import_path4.default.join(import_path4.default.dirname(cacheFile), "rules-cache.last-good.json");
20761
+ const backup = import_path5.default.join(import_path5.default.dirname(cacheFile), "rules-cache.last-good.json");
20182
20762
  if (backup !== cacheFile) {
20183
20763
  try {
20184
- const raw = JSON.parse(import_fs4.default.readFileSync(backup, "utf-8"));
20764
+ const raw = JSON.parse(import_fs5.default.readFileSync(backup, "utf-8"));
20185
20765
  logCacheReadIssue(cacheFile, "RULES_CACHE_CORRUPT_USED_BACKUP");
20186
20766
  lastParsedRulesCache = raw;
20187
20767
  return raw;
@@ -20204,8 +20784,8 @@ function logCacheReadIssue(cacheFile, kind) {
20204
20784
  if (now - cacheReadLastLoggedAt < CACHE_LOG_REARM_MS) return;
20205
20785
  cacheReadLastLoggedAt = now;
20206
20786
  try {
20207
- import_fs4.default.appendFileSync(
20208
- import_path4.default.join(import_os4.default.homedir(), ".node9", "hook-debug.log"),
20787
+ import_fs5.default.appendFileSync(
20788
+ import_path5.default.join(import_os5.default.homedir(), ".node9", "hook-debug.log"),
20209
20789
  `[${(/* @__PURE__ */ new Date()).toISOString()}] ${kind} ${cacheFile}
20210
20790
  `
20211
20791
  );
@@ -20231,8 +20811,8 @@ function catalogSettingsFrom(settings, policy) {
20231
20811
  }
20232
20812
  function getConfig(cwd) {
20233
20813
  if (!cwd && cachedConfig) return cachedConfig;
20234
- const globalPath = import_path4.default.join(import_os4.default.homedir(), ".node9", "config.json");
20235
- const projectPath = import_path4.default.join(cwd ?? process.cwd(), "node9.config.json");
20814
+ const globalPath = import_path5.default.join(import_os5.default.homedir(), ".node9", "config.json");
20815
+ const projectPath = import_path5.default.join(cwd ?? process.cwd(), "node9.config.json");
20236
20816
  const globalConfig = tryLoadConfig(globalPath);
20237
20817
  const projectConfig = tryLoadConfig(projectPath, { project: true });
20238
20818
  const mergedSettings = {
@@ -20282,7 +20862,8 @@ function getConfig(cwd) {
20282
20862
  trustedHostsManaged: false,
20283
20863
  appPermissions: {},
20284
20864
  appLinks: {},
20285
- managedJailPaths: []
20865
+ managedJailPaths: [],
20866
+ jailPaths: []
20286
20867
  };
20287
20868
  const mergedEnvironments = { ...DEFAULT_CONFIG.environments };
20288
20869
  const rank = (v) => {
@@ -20477,7 +21058,7 @@ function getConfig(cwd) {
20477
21058
  let cloudMandatesEnforcement = false;
20478
21059
  let cloudMandatesAppPerm = false;
20479
21060
  {
20480
- const cacheFile = import_path4.default.join(import_os4.default.homedir(), ".node9", "rules-cache.json");
21061
+ const cacheFile = import_path5.default.join(import_os5.default.homedir(), ".node9", "rules-cache.json");
20481
21062
  try {
20482
21063
  const raw = readRulesCacheResilient(cacheFile);
20483
21064
  if (Array.isArray(raw.rules) && raw.rules.length > 0) {
@@ -20677,6 +21258,7 @@ function getConfig(cwd) {
20677
21258
  mergedPolicy.smartRules.push({ ...r, name: `org:${r.name}` });
20678
21259
  }
20679
21260
  mergedPolicy.managedJailPaths.push({ path: path32, verdict });
21261
+ mergedPolicy.jailPaths.push(path32);
20680
21262
  }
20681
21263
  }
20682
21264
  if (Array.isArray(mc.trustedHosts)) {
@@ -20752,6 +21334,11 @@ function getConfig(cwd) {
20752
21334
  }
20753
21335
  }
20754
21336
  mergedPolicy.appliedShields = appliedShields.sort();
21337
+ if (appliedShields.includes(USER_JAIL_SHIELD)) {
21338
+ for (const { path: path32 } of readJailPaths()) {
21339
+ if (!mergedPolicy.jailPaths.includes(path32)) mergedPolicy.jailPaths.push(path32);
21340
+ }
21341
+ }
20755
21342
  const existingAdvisoryNames = new Set(mergedPolicy.smartRules.map((r) => r.name));
20756
21343
  const cc = mergedPolicy.commandChecks ?? {};
20757
21344
  const advisoryKnobKey = (name) => {
@@ -20909,10 +21496,10 @@ function getConfig(cwd) {
20909
21496
  }
20910
21497
  function tryLoadConfig(filePath, options = {}) {
20911
21498
  v2ChecksByPath.delete(filePath);
20912
- if (!import_fs4.default.existsSync(filePath)) return null;
21499
+ if (!import_fs5.default.existsSync(filePath)) return null;
20913
21500
  let raw;
20914
21501
  try {
20915
- raw = JSON.parse(import_fs4.default.readFileSync(filePath, "utf-8"));
21502
+ raw = JSON.parse(import_fs5.default.readFileSync(filePath, "utf-8"));
20916
21503
  } catch (err) {
20917
21504
  const msg = err instanceof Error ? err.message : String(err);
20918
21505
  process.stderr.write(
@@ -21018,27 +21605,27 @@ init_dist();
21018
21605
  // src/policy/index.ts
21019
21606
  init_cjs_shims();
21020
21607
  init_dist();
21021
- var import_fs13 = __toESM(require("fs"));
21608
+ var import_fs14 = __toESM(require("fs"));
21022
21609
 
21023
21610
  // src/utils/read-capped.ts
21024
21611
  init_cjs_shims();
21025
- var import_fs5 = __toESM(require("fs"));
21612
+ var import_fs6 = __toESM(require("fs"));
21026
21613
  function readCapped(file2, maxBytes, openFlags = "r") {
21027
21614
  if (maxBytes < 0) return null;
21028
21615
  let fd;
21029
21616
  try {
21030
- fd = import_fs5.default.openSync(file2, openFlags);
21617
+ fd = import_fs6.default.openSync(file2, openFlags);
21031
21618
  } catch {
21032
21619
  return null;
21033
21620
  }
21034
21621
  try {
21035
- const st = import_fs5.default.fstatSync(fd);
21622
+ const st = import_fs6.default.fstatSync(fd);
21036
21623
  if (!st.isFile()) return null;
21037
21624
  const want = Math.min(st.size, maxBytes + 1);
21038
21625
  const buf = Buffer.alloc(want);
21039
21626
  let read2 = 0;
21040
21627
  while (read2 < want) {
21041
- const n = import_fs5.default.readSync(fd, buf, read2, want - read2, read2);
21628
+ const n = import_fs6.default.readSync(fd, buf, read2, want - read2, read2);
21042
21629
  if (n <= 0) break;
21043
21630
  read2 += n;
21044
21631
  }
@@ -21050,7 +21637,7 @@ function readCapped(file2, maxBytes, openFlags = "r") {
21050
21637
  return null;
21051
21638
  } finally {
21052
21639
  try {
21053
- import_fs5.default.closeSync(fd);
21640
+ import_fs6.default.closeSync(fd);
21054
21641
  } catch {
21055
21642
  }
21056
21643
  }
@@ -21061,24 +21648,24 @@ function readCappedText(file2, maxBytes, openFlags = "r") {
21061
21648
  }
21062
21649
 
21063
21650
  // src/policy/index.ts
21064
- var import_path13 = __toESM(require("path"));
21651
+ var import_path14 = __toESM(require("path"));
21065
21652
 
21066
21653
  // src/dlp.ts
21067
21654
  init_cjs_shims();
21068
- var import_fs6 = __toESM(require("fs"));
21069
- var import_path5 = __toESM(require("path"));
21655
+ var import_fs7 = __toESM(require("fs"));
21656
+ var import_path6 = __toESM(require("path"));
21070
21657
  init_dist();
21071
21658
  init_dist();
21072
21659
  function scanFilePath(filePath, cwd = process.cwd(), options = {}) {
21073
21660
  if (!filePath) return null;
21074
21661
  let resolved;
21075
21662
  try {
21076
- const absolute2 = import_path5.default.resolve(cwd, filePath);
21077
- resolved = import_fs6.default.realpathSync.native(absolute2);
21663
+ const absolute2 = import_path6.default.resolve(cwd, filePath);
21664
+ resolved = import_fs7.default.realpathSync.native(absolute2);
21078
21665
  } catch (err) {
21079
21666
  const code = err.code;
21080
21667
  if (code === "ENOENT" || code === "ENOTDIR") {
21081
- resolved = import_path5.default.resolve(cwd, filePath);
21668
+ resolved = import_path6.default.resolve(cwd, filePath);
21082
21669
  } else {
21083
21670
  return sensitivePathMatch(filePath);
21084
21671
  }
@@ -21300,30 +21887,30 @@ function combineWithEngine(gates, engine, opts = { panicMode: false }) {
21300
21887
 
21301
21888
  // src/policy/file-access.ts
21302
21889
  init_cjs_shims();
21303
- var import_fs7 = __toESM(require("fs"));
21304
- var import_os5 = __toESM(require("os"));
21305
- var import_path6 = __toESM(require("path"));
21890
+ var import_fs8 = __toESM(require("fs"));
21891
+ var import_os6 = __toESM(require("os"));
21892
+ var import_path7 = __toESM(require("path"));
21306
21893
  init_dist();
21307
21894
  function absoluteSpelling(p, cwd, home) {
21308
21895
  if (p === "~" || p.startsWith("~/")) p = home + p.slice(1);
21309
- return import_path6.default.isAbsolute(p) ? p : `${cwd}/${p}`;
21896
+ return import_path7.default.isAbsolute(p) ? p : `${cwd}/${p}`;
21310
21897
  }
21311
21898
  function physical(p, followFinal, links = 0) {
21312
21899
  if (links > 40) throw new Error("Too many symbolic links");
21313
21900
  if (!followFinal) {
21314
- const parent = physical(import_path6.default.dirname(p), true, links);
21315
- return { path: `${parent.path}/${import_path6.default.basename(p)}`, missing: parent.missing };
21901
+ const parent = physical(import_path7.default.dirname(p), true, links);
21902
+ return { path: `${parent.path}/${import_path7.default.basename(p)}`, missing: parent.missing };
21316
21903
  }
21317
21904
  try {
21318
- return { path: import_fs7.default.realpathSync.native(p), missing: false };
21905
+ return { path: import_fs8.default.realpathSync.native(p), missing: false };
21319
21906
  } catch (error) {
21320
21907
  const code = error.code;
21321
21908
  if (!["ENOENT", "ENOTDIR"].includes(code ?? "")) throw error;
21322
21909
  try {
21323
- if (import_fs7.default.lstatSync(p).isSymbolicLink()) {
21324
- const target = import_fs7.default.readlinkSync(p);
21910
+ if (import_fs8.default.lstatSync(p).isSymbolicLink()) {
21911
+ const target = import_fs8.default.readlinkSync(p);
21325
21912
  return physical(
21326
- import_path6.default.isAbsolute(target) ? target : `${import_path6.default.dirname(p)}/${target}`,
21913
+ import_path7.default.isAbsolute(target) ? target : `${import_path7.default.dirname(p)}/${target}`,
21327
21914
  true,
21328
21915
  links + 1
21329
21916
  );
@@ -21332,13 +21919,13 @@ function physical(p, followFinal, links = 0) {
21332
21919
  if (!["ENOENT", "ENOTDIR"].includes(linkError.code ?? ""))
21333
21920
  throw linkError;
21334
21921
  }
21335
- const parent = import_path6.default.dirname(p);
21922
+ const parent = import_path7.default.dirname(p);
21336
21923
  if (parent === p) return { path: p, missing: true };
21337
21924
  const resolved = physical(parent, true, links);
21338
- return { path: `${resolved.path}/${import_path6.default.basename(p)}`, missing: true };
21925
+ return { path: `${resolved.path}/${import_path7.default.basename(p)}`, missing: true };
21339
21926
  }
21340
21927
  }
21341
- function prepareFileAccesses(tool, args, cwd = process.cwd(), inspection = {}, home = import_os5.default.homedir()) {
21928
+ function prepareFileAccesses(tool, args, cwd = process.cwd(), inspection = {}, home = import_os6.default.homedir()) {
21342
21929
  const context = { cwd, homeDirs: [home, "/root"] };
21343
21930
  const accesses = collectFileAccesses(tool, args, context, inspection);
21344
21931
  const handledPaths = /* @__PURE__ */ new Set();
@@ -21361,14 +21948,14 @@ function prepareFileAccesses(tool, args, cwd = process.cwd(), inspection = {}, h
21361
21948
  const raw = absoluteSpelling(access.path, access.cwd ?? cwd, home);
21362
21949
  if (access.directoryTarget) {
21363
21950
  try {
21364
- if (!import_fs7.default.statSync(absoluteSpelling(access.directoryTarget, access.cwd ?? cwd, home)).isDirectory())
21951
+ if (!import_fs8.default.statSync(absoluteSpelling(access.directoryTarget, access.cwd ?? cwd, home)).isDirectory())
21365
21952
  continue;
21366
21953
  } catch {
21367
21954
  }
21368
21955
  }
21369
21956
  if (access.operation === "create") {
21370
21957
  try {
21371
- import_fs7.default.statSync(raw);
21958
+ import_fs8.default.statSync(raw);
21372
21959
  continue;
21373
21960
  } catch {
21374
21961
  }
@@ -21419,40 +22006,40 @@ function prepareFileAccesses(tool, args, cwd = process.cwd(), inspection = {}, h
21419
22006
  // src/supply-chain/check.ts
21420
22007
  init_cjs_shims();
21421
22008
  var import_picomatch4 = __toESM(require("picomatch"));
21422
- var import_path10 = __toESM(require("path"));
22009
+ var import_path11 = __toESM(require("path"));
21423
22010
  init_dist();
21424
22011
 
21425
22012
  // src/supply-chain/osv-index.ts
21426
22013
  init_cjs_shims();
21427
- var import_fs9 = __toESM(require("fs"));
21428
- var import_os6 = __toESM(require("os"));
21429
- var import_path8 = __toESM(require("path"));
22014
+ var import_fs10 = __toESM(require("fs"));
22015
+ var import_os7 = __toESM(require("os"));
22016
+ var import_path9 = __toESM(require("path"));
21430
22017
  var import_crypto8 = require("crypto");
21431
22018
  init_dist();
21432
22019
 
21433
22020
  // src/utils/atomic-write.ts
21434
22021
  init_cjs_shims();
21435
- var import_fs8 = __toESM(require("fs"));
21436
- var import_path7 = __toESM(require("path"));
22022
+ var import_fs9 = __toESM(require("fs"));
22023
+ var import_path8 = __toESM(require("path"));
21437
22024
  var import_crypto7 = require("crypto");
21438
22025
  function atomicWriteSync(filePath, data, options) {
21439
- const dir2 = import_path7.default.dirname(filePath);
21440
- if (!import_fs8.default.existsSync(dir2)) import_fs8.default.mkdirSync(dir2, { recursive: true });
22026
+ const dir2 = import_path8.default.dirname(filePath);
22027
+ if (!import_fs9.default.existsSync(dir2)) import_fs9.default.mkdirSync(dir2, { recursive: true });
21441
22028
  const tmpPath = `${filePath}.${(0, import_crypto7.randomUUID)()}.tmp`;
21442
22029
  try {
21443
- import_fs8.default.writeFileSync(tmpPath, data, options);
22030
+ import_fs9.default.writeFileSync(tmpPath, data, options);
21444
22031
  } catch (err) {
21445
22032
  try {
21446
- import_fs8.default.unlinkSync(tmpPath);
22033
+ import_fs9.default.unlinkSync(tmpPath);
21447
22034
  } catch {
21448
22035
  }
21449
22036
  throw err;
21450
22037
  }
21451
22038
  try {
21452
- import_fs8.default.renameSync(tmpPath, filePath);
22039
+ import_fs9.default.renameSync(tmpPath, filePath);
21453
22040
  } catch (err) {
21454
22041
  try {
21455
- import_fs8.default.unlinkSync(tmpPath);
22042
+ import_fs9.default.unlinkSync(tmpPath);
21456
22043
  } catch {
21457
22044
  }
21458
22045
  throw err;
@@ -21462,10 +22049,10 @@ function atomicWriteSync(filePath, data, options) {
21462
22049
  // src/supply-chain/osv-index.ts
21463
22050
  var INDEX_FRESH_MS = 72 * 60 * 60 * 1e3;
21464
22051
  function osvRoot() {
21465
- return import_path8.default.join(import_os6.default.homedir(), ".node9", "osv");
22052
+ return import_path9.default.join(import_os7.default.homedir(), ".node9", "osv");
21466
22053
  }
21467
22054
  function ecoDir(eco) {
21468
- return import_path8.default.join(osvRoot(), eco);
22055
+ return import_path9.default.join(osvRoot(), eco);
21469
22056
  }
21470
22057
  function indexKey(eco, name) {
21471
22058
  switch (eco) {
@@ -21484,11 +22071,11 @@ function shardOf(key) {
21484
22071
  return (0, import_crypto8.createHash)("sha1").update(key).digest("hex").slice(0, 2);
21485
22072
  }
21486
22073
  function shardPath(eco, shard) {
21487
- return import_path8.default.join(ecoDir(eco), "shards", `${shard}.json`);
22074
+ return import_path9.default.join(ecoDir(eco), "shards", `${shard}.json`);
21488
22075
  }
21489
22076
  function readMeta(eco) {
21490
22077
  try {
21491
- const m = JSON.parse(import_fs9.default.readFileSync(import_path8.default.join(ecoDir(eco), "meta.json"), "utf8"));
22078
+ const m = JSON.parse(import_fs10.default.readFileSync(import_path9.default.join(ecoDir(eco), "meta.json"), "utf8"));
21492
22079
  if (typeof m?.syncedAt !== "string" || typeof m?.lastModifiedMs !== "number") return null;
21493
22080
  return m;
21494
22081
  } catch {
@@ -21497,7 +22084,7 @@ function readMeta(eco) {
21497
22084
  }
21498
22085
  function readShard(eco, shard) {
21499
22086
  try {
21500
- const s = JSON.parse(import_fs9.default.readFileSync(shardPath(eco, shard), "utf8"));
22087
+ const s = JSON.parse(import_fs10.default.readFileSync(shardPath(eco, shard), "utf8"));
21501
22088
  return s && typeof s === "object" && !Array.isArray(s) ? s : {};
21502
22089
  } catch {
21503
22090
  return {};
@@ -21851,19 +22438,19 @@ async function registryInfo(eco, name, version, freshWindowMs, raw) {
21851
22438
 
21852
22439
  // src/supply-chain/local-resolve.ts
21853
22440
  init_cjs_shims();
21854
- var import_fs10 = __toESM(require("fs"));
21855
- var import_path9 = __toESM(require("path"));
22441
+ var import_fs11 = __toESM(require("fs"));
22442
+ var import_path10 = __toESM(require("path"));
21856
22443
  var MAX_LEVELS = 32;
21857
22444
  var LOCAL_FIRST_MANAGERS = /* @__PURE__ */ new Set(["npx", "bunx", "npm exec"]);
21858
22445
  function resolveInstalled(name, cwd) {
21859
- if (!cwd || !import_path9.default.isAbsolute(cwd)) return null;
22446
+ if (!cwd || !import_path10.default.isAbsolute(cwd)) return null;
21860
22447
  if (!/^(?:@[^/\\]+\/)?[^/\\]+$/.test(name)) return null;
21861
- let dir2 = import_path9.default.resolve(cwd);
22448
+ let dir2 = import_path10.default.resolve(cwd);
21862
22449
  for (let i = 0; i < MAX_LEVELS; i++) {
21863
- const pkgDir = import_path9.default.join(dir2, "node_modules", ...name.split("/"));
21864
- const manifest = import_path9.default.join(pkgDir, "package.json");
22450
+ const pkgDir = import_path10.default.join(dir2, "node_modules", ...name.split("/"));
22451
+ const manifest = import_path10.default.join(pkgDir, "package.json");
21865
22452
  try {
21866
- const raw = import_fs10.default.readFileSync(manifest, "utf8");
22453
+ const raw = import_fs11.default.readFileSync(manifest, "utf8");
21867
22454
  const parsed = JSON.parse(raw);
21868
22455
  if (typeof parsed.version === "string" && parsed.version.length > 0) {
21869
22456
  return { version: parsed.version, dir: pkgDir };
@@ -21871,7 +22458,7 @@ function resolveInstalled(name, cwd) {
21871
22458
  return null;
21872
22459
  } catch {
21873
22460
  }
21874
- const parent = import_path9.default.dirname(dir2);
22461
+ const parent = import_path10.default.dirname(dir2);
21875
22462
  if (parent === dir2) break;
21876
22463
  dir2 = parent;
21877
22464
  }
@@ -21993,7 +22580,7 @@ function checkInstalled(p, version, misses) {
21993
22580
  }
21994
22581
  async function checkOne(p, cfg, misses, networkCapped, cwd) {
21995
22582
  if (p.ecosystem === "npm" && LOCAL_FIRST_MANAGERS.has(p.manager) && p.localCwd !== null && (p.raw === p.name || p.version)) {
21996
- const localCwd = cwd && import_path10.default.isAbsolute(cwd) ? (p.localCwd ?? []).reduce((dir2, step) => import_path10.default.resolve(dir2, step), cwd) : void 0;
22583
+ const localCwd = cwd && import_path11.default.isAbsolute(cwd) ? (p.localCwd ?? []).reduce((dir2, step) => import_path11.default.resolve(dir2, step), cwd) : void 0;
21997
22584
  const installed = resolveInstalled(p.name, localCwd);
21998
22585
  if (installed && (!p.version || p.version === installed.version))
21999
22586
  return checkInstalled(p, installed.version, misses);
@@ -22138,9 +22725,9 @@ async function runPackageCheck(toolName, args, cfg, cwd, opts = {}) {
22138
22725
 
22139
22726
  // src/supply-chain/registries.ts
22140
22727
  init_cjs_shims();
22141
- var import_os7 = __toESM(require("os"));
22142
- var import_path11 = __toESM(require("path"));
22143
- var import_fs11 = __toESM(require("fs"));
22728
+ var import_os8 = __toESM(require("os"));
22729
+ var import_path12 = __toESM(require("path"));
22730
+ var import_fs12 = __toESM(require("fs"));
22144
22731
  init_dist();
22145
22732
  var MAX_CONFIG_BYTES = 256 * 1024;
22146
22733
  var MAX_PARENT_DIRS = 12;
@@ -22182,29 +22769,29 @@ var REGISTRY_KEY_RE = /registry|index|find-links|mirror/i;
22182
22769
  var REGISTRY_SECTION_RE = /index|source|registr|scopes|mirror/i;
22183
22770
  var URL_RE2 = /https?:\/\/[^\s"'<>,;]+/gi;
22184
22771
  function userFiles(home, env) {
22185
- const xdg = env.XDG_CONFIG_HOME || import_path11.default.join(home, ".config");
22772
+ const xdg = env.XDG_CONFIG_HOME || import_path12.default.join(home, ".config");
22186
22773
  return [
22187
- env.NPM_CONFIG_USERCONFIG || env.npm_config_userconfig || import_path11.default.join(home, ".npmrc"),
22188
- import_path11.default.join(home, ".yarnrc"),
22189
- import_path11.default.join(home, ".yarnrc.yml"),
22190
- import_path11.default.join(home, ".bunfig.toml"),
22774
+ env.NPM_CONFIG_USERCONFIG || env.npm_config_userconfig || import_path12.default.join(home, ".npmrc"),
22775
+ import_path12.default.join(home, ".yarnrc"),
22776
+ import_path12.default.join(home, ".yarnrc.yml"),
22777
+ import_path12.default.join(home, ".bunfig.toml"),
22191
22778
  env.PIP_CONFIG_FILE || "",
22192
- import_path11.default.join(xdg, "pip", "pip.conf"),
22193
- import_path11.default.join(home, ".pip", "pip.conf"),
22779
+ import_path12.default.join(xdg, "pip", "pip.conf"),
22780
+ import_path12.default.join(home, ".pip", "pip.conf"),
22194
22781
  "/etc/pip.conf",
22195
- env.VIRTUAL_ENV ? import_path11.default.join(env.VIRTUAL_ENV, "pip.conf") : "",
22782
+ env.VIRTUAL_ENV ? import_path12.default.join(env.VIRTUAL_ENV, "pip.conf") : "",
22196
22783
  env.UV_CONFIG_FILE || "",
22197
- import_path11.default.join(xdg, "uv", "uv.toml"),
22784
+ import_path12.default.join(xdg, "uv", "uv.toml"),
22198
22785
  "/etc/uv/uv.toml"
22199
22786
  ].filter(Boolean);
22200
22787
  }
22201
22788
  function projectFiles(cwd) {
22202
- if (!cwd || !import_path11.default.isAbsolute(cwd)) return [];
22789
+ if (!cwd || !import_path12.default.isAbsolute(cwd)) return [];
22203
22790
  const out = [];
22204
22791
  let dir2 = cwd;
22205
22792
  for (let i = 0; i < MAX_PARENT_DIRS; i++) {
22206
- for (const f of PROJECT_FILES) out.push(import_path11.default.join(dir2, f));
22207
- const parent = import_path11.default.dirname(dir2);
22793
+ for (const f of PROJECT_FILES) out.push(import_path12.default.join(dir2, f));
22794
+ const parent = import_path12.default.dirname(dir2);
22208
22795
  if (parent === dir2) break;
22209
22796
  dir2 = parent;
22210
22797
  }
@@ -22244,7 +22831,7 @@ function hostsInText(text, out) {
22244
22831
  }
22245
22832
  }
22246
22833
  var cache3 = /* @__PURE__ */ new Map();
22247
- function configuredRegistryHosts(cwd, env = process.env, home = import_os7.default.homedir()) {
22834
+ function configuredRegistryHosts(cwd, env = process.env, home = import_os8.default.homedir()) {
22248
22835
  const key = [
22249
22836
  cwd ?? "",
22250
22837
  home,
@@ -22266,7 +22853,7 @@ function configuredRegistryHosts(cwd, env = process.env, home = import_os7.defau
22266
22853
  const r = readCappedText(
22267
22854
  file2,
22268
22855
  MAX_CONFIG_BYTES,
22269
- import_fs11.default.constants.O_RDONLY | (import_fs11.default.constants.O_NONBLOCK ?? 0)
22856
+ import_fs12.default.constants.O_RDONLY | (import_fs12.default.constants.O_NONBLOCK ?? 0)
22270
22857
  );
22271
22858
  if (r) hostsInText(r.text, hosts);
22272
22859
  }
@@ -22276,27 +22863,27 @@ function configuredRegistryHosts(cwd, env = process.env, home = import_os7.defau
22276
22863
 
22277
22864
  // src/utils/provenance.ts
22278
22865
  init_cjs_shims();
22279
- var import_fs12 = __toESM(require("fs"));
22280
- var import_path12 = __toESM(require("path"));
22281
- var import_os8 = __toESM(require("os"));
22866
+ var import_fs13 = __toESM(require("fs"));
22867
+ var import_path13 = __toESM(require("path"));
22868
+ var import_os9 = __toESM(require("os"));
22282
22869
  var SYSTEM_PREFIXES = ["/usr/bin", "/usr/sbin", "/bin", "/sbin"];
22283
22870
  var MANAGED_PREFIXES = ["/usr/local/bin", "/opt/homebrew", "/home/linuxbrew", "/nix/store"];
22284
22871
  var USER_PREFIXES = [
22285
- import_path12.default.join(import_os8.default.homedir(), "bin"),
22286
- import_path12.default.join(import_os8.default.homedir(), ".local", "bin"),
22287
- import_path12.default.join(import_os8.default.homedir(), ".cargo", "bin"),
22288
- import_path12.default.join(import_os8.default.homedir(), ".npm-global", "bin"),
22289
- import_path12.default.join(import_os8.default.homedir(), ".volta", "bin")
22872
+ import_path13.default.join(import_os9.default.homedir(), "bin"),
22873
+ import_path13.default.join(import_os9.default.homedir(), ".local", "bin"),
22874
+ import_path13.default.join(import_os9.default.homedir(), ".cargo", "bin"),
22875
+ import_path13.default.join(import_os9.default.homedir(), ".npm-global", "bin"),
22876
+ import_path13.default.join(import_os9.default.homedir(), ".volta", "bin")
22290
22877
  ];
22291
22878
  var SUSPECT_PREFIXES = ["/tmp", "/var/tmp", "/dev/shm"];
22292
22879
  function findInPath(cmd) {
22293
- if (import_path12.default.posix.isAbsolute(cmd)) return cmd;
22880
+ if (import_path13.default.posix.isAbsolute(cmd)) return cmd;
22294
22881
  const pathEnv = process.env.PATH ?? "";
22295
- for (const dir2 of pathEnv.split(import_path12.default.delimiter)) {
22882
+ for (const dir2 of pathEnv.split(import_path13.default.delimiter)) {
22296
22883
  if (!dir2) continue;
22297
- const full = import_path12.default.join(dir2, cmd);
22884
+ const full = import_path13.default.join(dir2, cmd);
22298
22885
  try {
22299
- import_fs12.default.accessSync(full, import_fs12.default.constants.X_OK);
22886
+ import_fs13.default.accessSync(full, import_fs13.default.constants.X_OK);
22300
22887
  return full;
22301
22888
  } catch {
22302
22889
  }
@@ -22307,7 +22894,7 @@ function _classifyPath(resolved, cwd) {
22307
22894
  if (cwd && resolved.startsWith(cwd + "/")) {
22308
22895
  return { trustLevel: "user", reason: "binary in project directory" };
22309
22896
  }
22310
- const osTmp = import_os8.default.tmpdir();
22897
+ const osTmp = import_os9.default.tmpdir();
22311
22898
  const allSuspect = osTmp ? [...SUSPECT_PREFIXES, osTmp] : SUSPECT_PREFIXES;
22312
22899
  if (allSuspect.some((p) => resolved === p || resolved.startsWith(p + "/"))) {
22313
22900
  return { trustLevel: "suspect", reason: `binary in temp directory: ${resolved}` };
@@ -22325,7 +22912,7 @@ function _classifyPath(resolved, cwd) {
22325
22912
  }
22326
22913
  function checkProvenance(cmd, cwd) {
22327
22914
  const bare = cmd.startsWith("./") ? cmd.slice(2) : cmd;
22328
- if (import_path12.default.posix.isAbsolute(bare)) {
22915
+ if (import_path13.default.posix.isAbsolute(bare)) {
22329
22916
  const early = _classifyPath(bare, cwd);
22330
22917
  if (early.trustLevel === "suspect") {
22331
22918
  return { resolvedPath: bare, ...early };
@@ -22341,7 +22928,7 @@ function checkProvenance(cmd, cwd) {
22341
22928
  reason: "binary not found in PATH"
22342
22929
  };
22343
22930
  }
22344
- resolved = import_fs12.default.realpathSync(found);
22931
+ resolved = import_fs13.default.realpathSync(found);
22345
22932
  } catch {
22346
22933
  return {
22347
22934
  resolvedPath: cmd,
@@ -22350,7 +22937,7 @@ function checkProvenance(cmd, cwd) {
22350
22937
  };
22351
22938
  }
22352
22939
  try {
22353
- const stat = import_fs12.default.statSync(resolved);
22940
+ const stat = import_fs13.default.statSync(resolved);
22354
22941
  if (stat.mode & 2) {
22355
22942
  return {
22356
22943
  resolvedPath: resolved,
@@ -22404,9 +22991,9 @@ async function evaluatePolicy2(toolName, args, agent, cwd, opts) {
22404
22991
  );
22405
22992
  }
22406
22993
  function isGitWorkTreeTop(dir2, cwd) {
22407
- if (!import_path13.default.isAbsolute(cwd)) return true;
22994
+ if (!import_path14.default.isAbsolute(cwd)) return true;
22408
22995
  try {
22409
- return import_fs13.default.existsSync(import_path13.default.join(import_path13.default.resolve(cwd, dir2), ".git"));
22996
+ return import_fs14.default.existsSync(import_path14.default.join(import_path14.default.resolve(cwd, dir2), ".git"));
22410
22997
  } catch {
22411
22998
  return true;
22412
22999
  }
@@ -22416,7 +23003,7 @@ function readAgentSettings(absPath) {
22416
23003
  const r = readCappedText(
22417
23004
  absPath,
22418
23005
  MAX_AGENT_SETTINGS_BYTES,
22419
- import_fs13.default.constants.O_RDONLY | (import_fs13.default.constants.O_NONBLOCK ?? 0)
23006
+ import_fs14.default.constants.O_RDONLY | (import_fs14.default.constants.O_NONBLOCK ?? 0)
22420
23007
  );
22421
23008
  return r && !r.truncated ? r.text : null;
22422
23009
  }
@@ -22426,12 +23013,12 @@ function isIgnoredTool2(toolName) {
22426
23013
 
22427
23014
  // src/auth/state.ts
22428
23015
  init_cjs_shims();
22429
- var import_fs14 = __toESM(require("fs"));
22430
- var import_path14 = __toESM(require("path"));
22431
- var import_os9 = __toESM(require("os"));
23016
+ var import_fs15 = __toESM(require("fs"));
23017
+ var import_path15 = __toESM(require("path"));
23018
+ var import_os10 = __toESM(require("os"));
22432
23019
  init_forecast_context();
22433
- var PAUSED_FILE = import_path14.default.join(import_os9.default.homedir(), ".node9", "PAUSED");
22434
- var TRUST_FILE = import_path14.default.join(import_os9.default.homedir(), ".node9", "trust.json");
23020
+ var PAUSED_FILE = import_path15.default.join(import_os10.default.homedir(), ".node9", "PAUSED");
23021
+ var TRUST_FILE = import_path15.default.join(import_os10.default.homedir(), ".node9", "trust.json");
22435
23022
  function extractCommandPattern(toolName, args) {
22436
23023
  const lower2 = toolName.toLowerCase();
22437
23024
  if (lower2 !== "bash" && lower2 !== "execute_bash" && lower2 !== "shell") return void 0;
@@ -22444,11 +23031,11 @@ function extractCommandPattern(toolName, args) {
22444
23031
  function checkPause() {
22445
23032
  if (forecastRun()) return { paused: false };
22446
23033
  try {
22447
- if (!import_fs14.default.existsSync(PAUSED_FILE)) return { paused: false };
22448
- const state = JSON.parse(import_fs14.default.readFileSync(PAUSED_FILE, "utf-8"));
23034
+ if (!import_fs15.default.existsSync(PAUSED_FILE)) return { paused: false };
23035
+ const state = JSON.parse(import_fs15.default.readFileSync(PAUSED_FILE, "utf-8"));
22449
23036
  if (state.expiry > 0 && Date.now() >= state.expiry) {
22450
23037
  try {
22451
- import_fs14.default.unlinkSync(PAUSED_FILE);
23038
+ import_fs15.default.unlinkSync(PAUSED_FILE);
22452
23039
  } catch {
22453
23040
  }
22454
23041
  return { paused: false };
@@ -22461,8 +23048,8 @@ function checkPause() {
22461
23048
  function getActiveTrustSession(toolName, args) {
22462
23049
  if (forecastRun()) return false;
22463
23050
  try {
22464
- if (!import_fs14.default.existsSync(TRUST_FILE)) return false;
22465
- const trust = JSON.parse(import_fs14.default.readFileSync(TRUST_FILE, "utf-8"));
23051
+ if (!import_fs15.default.existsSync(TRUST_FILE)) return false;
23052
+ const trust = JSON.parse(import_fs15.default.readFileSync(TRUST_FILE, "utf-8"));
22466
23053
  const now = Date.now();
22467
23054
  const active = trust.entries.filter((e) => e.expiry > now);
22468
23055
  if (active.length !== trust.entries.length) {
@@ -22485,8 +23072,8 @@ function writeTrustSession(toolName, durationMs, args) {
22485
23072
  try {
22486
23073
  let trust = { entries: [] };
22487
23074
  try {
22488
- if (import_fs14.default.existsSync(TRUST_FILE)) {
22489
- trust = JSON.parse(import_fs14.default.readFileSync(TRUST_FILE, "utf-8"));
23075
+ if (import_fs15.default.existsSync(TRUST_FILE)) {
23076
+ trust = JSON.parse(import_fs15.default.readFileSync(TRUST_FILE, "utf-8"));
22490
23077
  }
22491
23078
  } catch {
22492
23079
  }
@@ -22508,9 +23095,9 @@ function writeTrustSession(toolName, durationMs, args) {
22508
23095
  }
22509
23096
  function getPersistentDecision(toolName) {
22510
23097
  try {
22511
- const file2 = import_path14.default.join(import_os9.default.homedir(), ".node9", "decisions.json");
22512
- if (!import_fs14.default.existsSync(file2)) return null;
22513
- const decisions = JSON.parse(import_fs14.default.readFileSync(file2, "utf-8"));
23098
+ const file2 = import_path15.default.join(import_os10.default.homedir(), ".node9", "decisions.json");
23099
+ if (!import_fs15.default.existsSync(file2)) return null;
23100
+ const decisions = JSON.parse(import_fs15.default.readFileSync(file2, "utf-8"));
22514
23101
  const d = decisions[toolName];
22515
23102
  if (d === "allow" || d === "deny") return d;
22516
23103
  } catch {
@@ -22520,17 +23107,17 @@ function getPersistentDecision(toolName) {
22520
23107
 
22521
23108
  // src/auth/daemon.ts
22522
23109
  init_cjs_shims();
22523
- var import_fs16 = __toESM(require("fs"));
23110
+ var import_fs17 = __toESM(require("fs"));
22524
23111
  var import_http = __toESM(require("http"));
22525
23112
  var import_net4 = __toESM(require("net"));
22526
- var import_path16 = __toESM(require("path"));
22527
- var import_os10 = __toESM(require("os"));
23113
+ var import_path17 = __toESM(require("path"));
23114
+ var import_os11 = __toESM(require("os"));
22528
23115
  init_forecast_context();
22529
23116
 
22530
23117
  // src/utils/activity-socket.ts
22531
23118
  init_cjs_shims();
22532
- var import_fs15 = __toESM(require("fs"));
22533
- var import_path15 = __toESM(require("path"));
23119
+ var import_fs16 = __toESM(require("fs"));
23120
+ var import_path16 = __toESM(require("path"));
22534
23121
 
22535
23122
  // src/utils/own-dir.ts
22536
23123
  init_cjs_shims();
@@ -22539,7 +23126,7 @@ init_cjs_shims();
22539
23126
  var currentUid = () => typeof process.getuid === "function" ? process.getuid() : void 0;
22540
23127
  function isPrivateDir(dir2, uid = currentUid()) {
22541
23128
  try {
22542
- const st = import_fs15.default.lstatSync(dir2);
23129
+ const st = import_fs16.default.lstatSync(dir2);
22543
23130
  if (st.isSymbolicLink() || !st.isDirectory()) return false;
22544
23131
  if (uid !== void 0 && st.uid !== uid) return false;
22545
23132
  return (st.mode & 18) === 0;
@@ -22551,13 +23138,13 @@ var WINDOWS_PIPE = /^\\\\\.\\pipe\\node9-activity-[0-9a-f]{32}$/;
22551
23138
  function isTrustedActivitySocket(socketPath, uid = currentUid()) {
22552
23139
  if (process.platform === "win32") return WINDOWS_PIPE.test(socketPath);
22553
23140
  try {
22554
- const st = import_fs15.default.lstatSync(socketPath);
23141
+ const st = import_fs16.default.lstatSync(socketPath);
22555
23142
  if (!st.isSocket()) return false;
22556
23143
  if (uid !== void 0 && st.uid !== uid) return false;
22557
23144
  } catch {
22558
23145
  return false;
22559
23146
  }
22560
- return isPrivateDir(import_path15.default.dirname(socketPath), uid);
23147
+ return isPrivateDir(import_path16.default.dirname(socketPath), uid);
22561
23148
  }
22562
23149
 
22563
23150
  // src/auth/decision-record.ts
@@ -22709,9 +23296,9 @@ function recordFromLegacyWait(body, now = Date.now()) {
22709
23296
  }
22710
23297
 
22711
23298
  // src/auth/daemon.ts
22712
- function daemonActivitySocket(home = import_os10.default.homedir()) {
23299
+ function daemonActivitySocket(home = import_os11.default.homedir()) {
22713
23300
  try {
22714
- const raw = import_fs16.default.readFileSync(import_path16.default.join(home, ".node9", "daemon.pid"), "utf-8");
23301
+ const raw = import_fs17.default.readFileSync(import_path17.default.join(home, ".node9", "daemon.pid"), "utf-8");
22715
23302
  const p = JSON.parse(raw).activitySocket;
22716
23303
  return typeof p === "string" && isTrustedActivitySocket(p) ? p : null;
22717
23304
  } catch {
@@ -22775,9 +23362,9 @@ var DAEMON_HOST = "127.0.0.1";
22775
23362
  function getInternalToken() {
22776
23363
  if (forecastRun()) return null;
22777
23364
  try {
22778
- const pidFile = import_path16.default.join(import_os10.default.homedir(), ".node9", "daemon.pid");
22779
- if (!import_fs16.default.existsSync(pidFile)) return null;
22780
- const data = JSON.parse(import_fs16.default.readFileSync(pidFile, "utf-8"));
23365
+ const pidFile = import_path17.default.join(import_os11.default.homedir(), ".node9", "daemon.pid");
23366
+ if (!import_fs17.default.existsSync(pidFile)) return null;
23367
+ const data = JSON.parse(import_fs17.default.readFileSync(pidFile, "utf-8"));
22781
23368
  process.kill(data.pid, 0);
22782
23369
  return data.internalToken ?? null;
22783
23370
  } catch {
@@ -22786,12 +23373,12 @@ function getInternalToken() {
22786
23373
  }
22787
23374
  function isDaemonRunning() {
22788
23375
  if (forecastRun()) return false;
22789
- const pidFile = import_path16.default.join(import_os10.default.homedir(), ".node9", "daemon.pid");
22790
- if (import_fs16.default.existsSync(pidFile)) {
23376
+ const pidFile = import_path17.default.join(import_os11.default.homedir(), ".node9", "daemon.pid");
23377
+ if (import_fs17.default.existsSync(pidFile)) {
22791
23378
  let pid;
22792
23379
  let port;
22793
23380
  try {
22794
- const data = JSON.parse(import_fs16.default.readFileSync(pidFile, "utf-8"));
23381
+ const data = JSON.parse(import_fs17.default.readFileSync(pidFile, "utf-8"));
22795
23382
  pid = data.pid;
22796
23383
  port = data.port;
22797
23384
  } catch {
@@ -22809,7 +23396,7 @@ function isDaemonRunning() {
22809
23396
  } catch (err) {
22810
23397
  if (err instanceof Error && "code" in err && err.code === "ESRCH") {
22811
23398
  try {
22812
- import_fs16.default.unlinkSync(pidFile);
23399
+ import_fs17.default.unlinkSync(pidFile);
22813
23400
  } catch {
22814
23401
  }
22815
23402
  }
@@ -23149,9 +23736,9 @@ var import_crypto10 = require("crypto");
23149
23736
 
23150
23737
  // src/apps/state.ts
23151
23738
  init_cjs_shims();
23152
- var import_fs18 = __toESM(require("fs"));
23153
- var import_os12 = __toESM(require("os"));
23154
- var import_path18 = __toESM(require("path"));
23739
+ var import_fs19 = __toESM(require("fs"));
23740
+ var import_os13 = __toESM(require("os"));
23741
+ var import_path19 = __toESM(require("path"));
23155
23742
 
23156
23743
  // src/apps/catalog.ts
23157
23744
  init_cjs_shims();
@@ -23285,22 +23872,22 @@ init_audit();
23285
23872
 
23286
23873
  // src/daemon/mcp-tools.ts
23287
23874
  init_cjs_shims();
23288
- var import_fs17 = __toESM(require("fs"));
23289
- var import_path17 = __toESM(require("path"));
23290
- var import_os11 = __toESM(require("os"));
23875
+ var import_fs18 = __toESM(require("fs"));
23876
+ var import_path18 = __toESM(require("path"));
23877
+ var import_os12 = __toESM(require("os"));
23291
23878
 
23292
23879
  // src/mcp-cmd.ts
23293
23880
  init_cjs_shims();
23294
23881
 
23295
23882
  // src/daemon/mcp-tools.ts
23296
23883
  function getMcpToolsFile() {
23297
- return import_path17.default.join(import_os11.default.homedir(), ".node9", "mcp-tools.json");
23884
+ return import_path18.default.join(import_os12.default.homedir(), ".node9", "mcp-tools.json");
23298
23885
  }
23299
23886
  function readMcpToolsConfig() {
23300
23887
  try {
23301
23888
  const file2 = getMcpToolsFile();
23302
- if (!import_fs17.default.existsSync(file2)) return {};
23303
- const raw = import_fs17.default.readFileSync(file2, "utf-8");
23889
+ if (!import_fs18.default.existsSync(file2)) return {};
23890
+ const raw = import_fs18.default.readFileSync(file2, "utf-8");
23304
23891
  return JSON.parse(raw);
23305
23892
  } catch {
23306
23893
  return {};
@@ -23308,8 +23895,8 @@ function readMcpToolsConfig() {
23308
23895
  }
23309
23896
 
23310
23897
  // src/apps/state.ts
23311
- function appsFilePath(home = import_os12.default.homedir()) {
23312
- return import_path18.default.join(home, ".node9", "apps.json");
23898
+ function appsFilePath(home = import_os13.default.homedir()) {
23899
+ return import_path19.default.join(home, ".node9", "apps.json");
23313
23900
  }
23314
23901
  var SIGNAL_KINDS = /* @__PURE__ */ new Set(["mcp-server", "command", "package", "env-name"]);
23315
23902
  function emptyFile() {
@@ -23357,7 +23944,7 @@ function readAppsFile(home) {
23357
23944
  function readAppsFileStrict(home) {
23358
23945
  let text;
23359
23946
  try {
23360
- text = import_fs18.default.readFileSync(appsFilePath(home), "utf8");
23947
+ text = import_fs19.default.readFileSync(appsFilePath(home), "utf8");
23361
23948
  } catch (e) {
23362
23949
  return e.code === "ENOENT" ? emptyFile() : null;
23363
23950
  }
@@ -23372,7 +23959,7 @@ var NO_LINKS = /* @__PURE__ */ Object.create(null);
23372
23959
  function localLinks() {
23373
23960
  const file2 = appsFilePath();
23374
23961
  try {
23375
- const mtimeMs = import_fs18.default.statSync(file2).mtimeMs;
23962
+ const mtimeMs = import_fs19.default.statSync(file2).mtimeMs;
23376
23963
  if (!linkCache || linkCache.file !== file2 || linkCache.mtimeMs !== mtimeMs) {
23377
23964
  linkCache = { mtimeMs, file: file2, links: readAppsFile().links };
23378
23965
  }
@@ -23418,11 +24005,11 @@ var import_path31 = __toESM(require("path"));
23418
24005
  // src/ui/native.ts
23419
24006
  init_cjs_shims();
23420
24007
  var import_child_process = require("child_process");
23421
- var import_path20 = __toESM(require("path"));
24008
+ var import_path21 = __toESM(require("path"));
23422
24009
 
23423
24010
  // src/context-sniper.ts
23424
24011
  init_cjs_shims();
23425
- var import_path19 = __toESM(require("path"));
24012
+ var import_path20 = __toESM(require("path"));
23426
24013
  function smartTruncate(str, maxLen = 500) {
23427
24014
  if (str.length <= maxLen) return str;
23428
24015
  const edge = Math.floor(maxLen / 2) - 3;
@@ -23490,7 +24077,7 @@ function computeRiskMetadata(args, tier, blockedByLabel, matchedField, matchedWo
23490
24077
  intent = "EDIT";
23491
24078
  if (obj.file_path) {
23492
24079
  editFilePath = String(obj.file_path);
23493
- editFileName = import_path19.default.basename(editFilePath);
24080
+ editFileName = import_path20.default.basename(editFilePath);
23494
24081
  }
23495
24082
  const result = extractContext(String(obj.new_string), matchedWord);
23496
24083
  contextSnippet = result.snippet;
@@ -23559,7 +24146,7 @@ function formatArgs(args, matchedField, matchedWord) {
23559
24146
  if (typeof parsed === "object" && !Array.isArray(parsed)) {
23560
24147
  const obj = parsed;
23561
24148
  if (obj.old_string !== void 0 && obj.new_string !== void 0) {
23562
- const file2 = obj.file_path ? import_path20.default.basename(String(obj.file_path)) : "file";
24149
+ const file2 = obj.file_path ? import_path21.default.basename(String(obj.file_path)) : "file";
23563
24150
  const oldPreview = smartTruncate(String(obj.old_string), 120);
23564
24151
  const newPreview = extractContext(String(obj.new_string), matchedWord).snippet;
23565
24152
  return {
@@ -23767,48 +24354,6 @@ var import_fs20 = __toESM(require("fs"));
23767
24354
  var import_os14 = __toESM(require("os"));
23768
24355
  var import_path22 = __toESM(require("path"));
23769
24356
  init_dist();
23770
-
23771
- // src/shields/jail.ts
23772
- init_cjs_shims();
23773
- var import_fs19 = __toESM(require("fs"));
23774
- var import_os13 = __toESM(require("os"));
23775
- var import_path21 = __toESM(require("path"));
23776
- init_dist();
23777
- var USER_JAIL_SHIELD = "user-jail";
23778
- function jailStorePath() {
23779
- return import_path21.default.join(import_os13.default.homedir(), ".node9", "jail-paths.json");
23780
- }
23781
- function readJailPaths() {
23782
- let text;
23783
- try {
23784
- text = import_fs19.default.readFileSync(jailStorePath(), "utf8");
23785
- } catch (err) {
23786
- if (err.code === "ENOENT") return [];
23787
- throw err;
23788
- }
23789
- let parsed;
23790
- try {
23791
- parsed = JSON.parse(text);
23792
- } catch {
23793
- throw new Error(`${jailStorePath()} is not valid JSON \u2014 fix it before changing the jail.`);
23794
- }
23795
- if (!Array.isArray(parsed.paths)) return [];
23796
- return parsed.paths.filter(
23797
- (p) => !!p && typeof p.path === "string" && (p.verdict === "block" || p.verdict === "review")
23798
- );
23799
- }
23800
- function findJailedPath(candidate) {
23801
- return findJailedPathIn(candidate, readJailPaths());
23802
- }
23803
- function findJailedPathIn(candidate, paths) {
23804
- if (!candidate) return null;
23805
- for (const entry of paths) {
23806
- if (pathMatchesFragment(candidate, entry.path)) return entry;
23807
- }
23808
- return null;
23809
- }
23810
-
23811
- // src/canary/registry.ts
23812
24357
  function canaryStorePath() {
23813
24358
  return import_path22.default.join(import_os14.default.homedir(), ".node9", "canaries.json");
23814
24359
  }