@node9/proxy 2.45.0 → 2.47.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +8948 -8935
- package/dist/cli.mjs +8923 -8910
- package/dist/dashboard.mjs +685 -506
- package/dist/index.js +278 -180
- package/dist/index.mjs +279 -181
- package/dist/scan-ink.mjs +123 -25
- package/package.json +1 -1
package/dist/dashboard.mjs
CHANGED
|
@@ -106,22 +106,16 @@ var init_forecast_context = __esm({
|
|
|
106
106
|
});
|
|
107
107
|
|
|
108
108
|
// packages/policy-engine/dist/index.mjs
|
|
109
|
-
import mvdanSh3 from "mvdan-sh";
|
|
110
109
|
import picomatch from "picomatch";
|
|
111
110
|
import safeRegex from "safe-regex2";
|
|
112
111
|
import pm from "picomatch";
|
|
113
|
-
import mvdanSh from "mvdan-sh";
|
|
114
112
|
import mvdanSh2 from "mvdan-sh";
|
|
113
|
+
import mvdanSh from "mvdan-sh";
|
|
114
|
+
import mvdanSh3 from "mvdan-sh";
|
|
115
115
|
import safeRegex2 from "safe-regex2";
|
|
116
116
|
import { createHash as createHash2 } from "crypto";
|
|
117
117
|
import { createHash as createHash22 } from "crypto";
|
|
118
|
-
import mvdanSh4 from "mvdan-sh";
|
|
119
|
-
import mvdanSh5 from "mvdan-sh";
|
|
120
|
-
import mvdanSh6 from "mvdan-sh";
|
|
121
|
-
import mvdanSh8 from "mvdan-sh";
|
|
122
|
-
import mvdanSh7 from "mvdan-sh";
|
|
123
118
|
import { createHash as createHash3 } from "crypto";
|
|
124
|
-
import mvdanSh9 from "mvdan-sh";
|
|
125
119
|
import crypto from "crypto";
|
|
126
120
|
function validateRegex(pattern) {
|
|
127
121
|
if (!pattern) return "Pattern is required";
|
|
@@ -703,6 +697,62 @@ function resolveCheckMap(settings) {
|
|
|
703
697
|
for (const c of CHECKS) out[c.id] = checkValue(settings, c.id);
|
|
704
698
|
return out;
|
|
705
699
|
}
|
|
700
|
+
function isGoNode(v) {
|
|
701
|
+
return v !== null && typeof v === "object" && typeof v.Pos === "function";
|
|
702
|
+
}
|
|
703
|
+
function toPlainTree(goNode) {
|
|
704
|
+
const kind = syntax.NodeType(goNode);
|
|
705
|
+
const out = { kind, pos: goNode.Pos().Offset(), end: goNode.End().Offset() };
|
|
706
|
+
const spec = KINDS[kind];
|
|
707
|
+
if (!spec) {
|
|
708
|
+
unknownKinds.add(kind);
|
|
709
|
+
return out;
|
|
710
|
+
}
|
|
711
|
+
convertFields(goNode, spec, out, kind);
|
|
712
|
+
return out;
|
|
713
|
+
}
|
|
714
|
+
function convertFields(goNode, spec, out, kind) {
|
|
715
|
+
for (const f of spec.scalars ?? []) out[f] = goNode[f];
|
|
716
|
+
for (const f of spec.nodes ?? []) {
|
|
717
|
+
const v = goNode[f];
|
|
718
|
+
if (v === void 0 || v === null) {
|
|
719
|
+
out[f] = null;
|
|
720
|
+
} else if (Array.isArray(v)) {
|
|
721
|
+
out[f] = v.map((c) => isGoNode(c) ? toPlainTree(c) : c);
|
|
722
|
+
} else if (isGoNode(v)) {
|
|
723
|
+
out[f] = toPlainTree(v);
|
|
724
|
+
} else if (typeof v === "object" && STRUCTS[`${kind}.${f}`]) {
|
|
725
|
+
const sub = { struct: `${kind}.${f}` };
|
|
726
|
+
convertFields(v, STRUCTS[`${kind}.${f}`], sub, `${kind}.${f}`);
|
|
727
|
+
out[f] = sub;
|
|
728
|
+
} else {
|
|
729
|
+
out[f] = v;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
function childrenOf(n) {
|
|
734
|
+
const spec = n.kind ? KINDS[n.kind] : STRUCTS[n.struct];
|
|
735
|
+
if (!spec?.nodes) return [];
|
|
736
|
+
const out = [];
|
|
737
|
+
for (const f of spec.nodes) {
|
|
738
|
+
if (spec.notWalked?.includes(f)) continue;
|
|
739
|
+
const v = n[f];
|
|
740
|
+
if (!v) continue;
|
|
741
|
+
if (Array.isArray(v)) {
|
|
742
|
+
for (const c of v) if (c) out.push(c);
|
|
743
|
+
} else if (v.kind) {
|
|
744
|
+
out.push(v);
|
|
745
|
+
} else if (v.struct) {
|
|
746
|
+
out.push(...childrenOf(v));
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
return out;
|
|
750
|
+
}
|
|
751
|
+
function walk(root, fn) {
|
|
752
|
+
if (!root) return;
|
|
753
|
+
if (!fn(root)) return;
|
|
754
|
+
for (const c of childrenOf(root)) walk(c, fn);
|
|
755
|
+
}
|
|
706
756
|
function isInvisibleChar(char) {
|
|
707
757
|
return INVISIBLE_ONE.test(char);
|
|
708
758
|
}
|
|
@@ -858,7 +908,7 @@ function judgeInlineCode(language, code, isProtected) {
|
|
|
858
908
|
}
|
|
859
909
|
function isCatHeredocOrLit(part) {
|
|
860
910
|
if (!part) return false;
|
|
861
|
-
const t =
|
|
911
|
+
const t = part.kind;
|
|
862
912
|
if (t === "Lit") return true;
|
|
863
913
|
if (t !== "CmdSubst") return false;
|
|
864
914
|
const stmts = part.Stmts || [];
|
|
@@ -868,9 +918,9 @@ function isCatHeredocOrLit(part) {
|
|
|
868
918
|
const hasHeredoc = redirs.some((r) => r && r.Hdoc);
|
|
869
919
|
if (!hasHeredoc) return false;
|
|
870
920
|
const cmd = stmt.Cmd;
|
|
871
|
-
if (!cmd ||
|
|
921
|
+
if (!cmd || cmd.kind !== "CallExpr") return false;
|
|
872
922
|
const firstArg = cmd.Args?.[0]?.Parts || [];
|
|
873
|
-
if (firstArg.length !== 1 ||
|
|
923
|
+
if (firstArg.length !== 1 || firstArg[0].kind !== "Lit") return false;
|
|
874
924
|
return (firstArg[0].Value || "").toLowerCase() === "cat";
|
|
875
925
|
}
|
|
876
926
|
function parseShared(command) {
|
|
@@ -882,7 +932,7 @@ function parseShared(command) {
|
|
|
882
932
|
}
|
|
883
933
|
let parsed;
|
|
884
934
|
try {
|
|
885
|
-
parsed = sharedParser.Parse(command, "cmd");
|
|
935
|
+
parsed = toPlainTree(sharedParser.Parse(command, "cmd"));
|
|
886
936
|
} catch {
|
|
887
937
|
parsed = PARSE_FAIL;
|
|
888
938
|
}
|
|
@@ -943,10 +993,10 @@ function fileAccessViewImpl(command) {
|
|
|
943
993
|
const at = (byteOffset) => toCharIndex === null ? byteOffset : toCharIndex(byteOffset);
|
|
944
994
|
const edits = [];
|
|
945
995
|
try {
|
|
946
|
-
|
|
996
|
+
walk(f, (node) => {
|
|
947
997
|
if (!node) return false;
|
|
948
998
|
const n = node;
|
|
949
|
-
if (
|
|
999
|
+
if (n.kind !== "CallExpr") return true;
|
|
950
1000
|
const args = n.Args || [];
|
|
951
1001
|
const words = args.map((a) => resolveWordLiteral(a));
|
|
952
1002
|
const head = unwrapCommandHead(words);
|
|
@@ -958,8 +1008,8 @@ function fileAccessViewImpl(command) {
|
|
|
958
1008
|
if (!PROGRAM_DIR.test(word)) continue;
|
|
959
1009
|
const name = word.split(/[\\/]/).pop() ?? "";
|
|
960
1010
|
if (!name) continue;
|
|
961
|
-
const s = at(args[i].
|
|
962
|
-
const e = at(args[i].
|
|
1011
|
+
const s = at(args[i].pos);
|
|
1012
|
+
const e = at(args[i].end);
|
|
963
1013
|
if (s < 0 || e < 0) continue;
|
|
964
1014
|
edits.push([s, e, name]);
|
|
965
1015
|
}
|
|
@@ -987,10 +1037,10 @@ function literalPayloadsOf(command, depth) {
|
|
|
987
1037
|
if (f === PARSE_FAIL) return [];
|
|
988
1038
|
const out = [];
|
|
989
1039
|
try {
|
|
990
|
-
|
|
1040
|
+
walk(f, (node) => {
|
|
991
1041
|
if (!node || out.length >= MAX_READING_PAYLOADS) return false;
|
|
992
1042
|
const n = node;
|
|
993
|
-
if (
|
|
1043
|
+
if (n.kind !== "CallExpr") return true;
|
|
994
1044
|
const words = (n.Args || []).map((a) => resolveWordLiteral(a));
|
|
995
1045
|
if (words.length === 0) return true;
|
|
996
1046
|
const payload = literalShellPayload(words, baseWord(words[0]));
|
|
@@ -1010,23 +1060,23 @@ function foldCommandWords(reading) {
|
|
|
1010
1060
|
const at = (byteOffset) => toCharIndex === null ? byteOffset : toCharIndex(byteOffset);
|
|
1011
1061
|
const edits = [];
|
|
1012
1062
|
try {
|
|
1013
|
-
|
|
1063
|
+
walk(f, (node) => {
|
|
1014
1064
|
if (!node) return false;
|
|
1015
1065
|
const n = node;
|
|
1016
|
-
if (
|
|
1066
|
+
if (n.kind !== "CallExpr") return true;
|
|
1017
1067
|
const args = n.Args || [];
|
|
1018
1068
|
const words = args.map((a) => resolveWordLiteral(a));
|
|
1019
1069
|
const folded = words.map((w) => w === null ? null : foldHomoglyphs(w));
|
|
1020
1070
|
const head = unwrapCommandHead(folded);
|
|
1021
1071
|
for (let i = 0; i <= head && i < args.length; i++) {
|
|
1022
1072
|
const parts = args[i].Parts || [];
|
|
1023
|
-
if (parts.length !== 1 ||
|
|
1073
|
+
if (parts.length !== 1 || parts[0].kind !== "Lit") continue;
|
|
1024
1074
|
const value = parts[0].Value ?? "";
|
|
1025
1075
|
const word = folded[i];
|
|
1026
1076
|
if (word === null || word === value || !/[^\x00-\x7f]/.test(value)) continue;
|
|
1027
1077
|
if (!/^[A-Za-z0-9._/-]+$/.test(word)) continue;
|
|
1028
|
-
const s = at(args[i].
|
|
1029
|
-
const e = at(args[i].
|
|
1078
|
+
const s = at(args[i].pos);
|
|
1079
|
+
const e = at(args[i].end);
|
|
1030
1080
|
if (s < 0 || e < 0) continue;
|
|
1031
1081
|
edits.push([s, e, word]);
|
|
1032
1082
|
}
|
|
@@ -1063,10 +1113,10 @@ function interpreterHeredocSpans(f, at) {
|
|
|
1063
1113
|
const stmts = f?.Stmts || [];
|
|
1064
1114
|
const candidates = [];
|
|
1065
1115
|
let unsafe = false;
|
|
1066
|
-
|
|
1116
|
+
walk(f, (node) => {
|
|
1067
1117
|
if (!node || unsafe) return false;
|
|
1068
1118
|
const n = node;
|
|
1069
|
-
const t =
|
|
1119
|
+
const t = n.kind;
|
|
1070
1120
|
if (t === "FuncDecl") {
|
|
1071
1121
|
unsafe = true;
|
|
1072
1122
|
return false;
|
|
@@ -1086,7 +1136,7 @@ function interpreterHeredocSpans(f, at) {
|
|
|
1086
1136
|
});
|
|
1087
1137
|
if (unsafe) return [];
|
|
1088
1138
|
for (const st of stmts) {
|
|
1089
|
-
if (!st?.Cmd ||
|
|
1139
|
+
if (!st?.Cmd || st.Cmd.kind !== "CallExpr") continue;
|
|
1090
1140
|
const words = (st.Cmd.Args || []).map((a) => resolveWordLiteral(a));
|
|
1091
1141
|
const name = (words[0] ?? "").toLowerCase();
|
|
1092
1142
|
if (!STDIN_CODE_INTERPRETERS.test(name)) continue;
|
|
@@ -1101,8 +1151,8 @@ function interpreterHeredocSpans(f, at) {
|
|
|
1101
1151
|
if (stdin.length !== 1 || others > 0) continue;
|
|
1102
1152
|
const r = stdin[0];
|
|
1103
1153
|
if (heredocText(r.Hdoc) === null) continue;
|
|
1104
|
-
const hs = at(r.Hdoc.
|
|
1105
|
-
const he = at(r.Hdoc.
|
|
1154
|
+
const hs = at(r.Hdoc.pos);
|
|
1155
|
+
const he = at(r.Hdoc.end);
|
|
1106
1156
|
if (hs < 0 || he < 0 || he <= hs) continue;
|
|
1107
1157
|
spans.push([hs, he]);
|
|
1108
1158
|
}
|
|
@@ -1122,24 +1172,24 @@ function normalizeCommandForPolicyImpl(command, codeBodies) {
|
|
|
1122
1172
|
strips.push([hs, he]);
|
|
1123
1173
|
msgSpans.add(`${hs}:${he}`);
|
|
1124
1174
|
}
|
|
1125
|
-
|
|
1175
|
+
walk(f, (node) => {
|
|
1126
1176
|
if (!node) return false;
|
|
1127
1177
|
const n = node;
|
|
1128
|
-
if (
|
|
1178
|
+
if (n.kind !== "CallExpr") return true;
|
|
1129
1179
|
const args = n.Args || [];
|
|
1130
1180
|
for (let i = 0; i < args.length - 1; i++) {
|
|
1131
1181
|
const argParts = args[i].Parts || [];
|
|
1132
|
-
if (argParts.length !== 1 ||
|
|
1182
|
+
if (argParts.length !== 1 || argParts[0].kind !== "Lit") continue;
|
|
1133
1183
|
const flagVal = argParts[0].Value || "";
|
|
1134
1184
|
if (!MESSAGE_FLAGS.has(flagVal.toLowerCase())) continue;
|
|
1135
1185
|
const next = args[i + 1];
|
|
1136
1186
|
const nextParts = next.Parts || [];
|
|
1137
1187
|
if (nextParts.length !== 1) continue;
|
|
1138
1188
|
const quotedNode = nextParts[0];
|
|
1139
|
-
const nt =
|
|
1189
|
+
const nt = quotedNode.kind;
|
|
1140
1190
|
const markStrip = () => {
|
|
1141
|
-
const s = at(next.
|
|
1142
|
-
const e = at(next.
|
|
1191
|
+
const s = at(next.pos);
|
|
1192
|
+
const e = at(next.end);
|
|
1143
1193
|
if (s < 0 || e < 0) return;
|
|
1144
1194
|
strips.push([s, e]);
|
|
1145
1195
|
msgSpans.add(`${s}:${e}`);
|
|
@@ -1148,7 +1198,7 @@ function normalizeCommandForPolicyImpl(command, codeBodies) {
|
|
|
1148
1198
|
markStrip();
|
|
1149
1199
|
} else if (nt === "DblQuoted") {
|
|
1150
1200
|
const innerParts = quotedNode.Parts || [];
|
|
1151
|
-
const allLit = innerParts.length === 0 || innerParts.every((p) =>
|
|
1201
|
+
const allLit = innerParts.length === 0 || innerParts.every((p) => p.kind === "Lit");
|
|
1152
1202
|
if (allLit) {
|
|
1153
1203
|
markStrip();
|
|
1154
1204
|
} else if (innerParts.every((p) => isCatHeredocOrLit(p))) {
|
|
@@ -1157,8 +1207,8 @@ function normalizeCommandForPolicyImpl(command, codeBodies) {
|
|
|
1157
1207
|
}
|
|
1158
1208
|
}
|
|
1159
1209
|
for (const arg of args) {
|
|
1160
|
-
const s = at(arg.
|
|
1161
|
-
const e = at(arg.
|
|
1210
|
+
const s = at(arg.pos);
|
|
1211
|
+
const e = at(arg.end);
|
|
1162
1212
|
if (s < 0 || e < 0) continue;
|
|
1163
1213
|
if (msgSpans.has(`${s}:${e}`)) continue;
|
|
1164
1214
|
const resolved = resolveWordLiteral(arg);
|
|
@@ -1198,10 +1248,10 @@ function scanArgsForDynamicExec(args, startIdx) {
|
|
|
1198
1248
|
let hasParamExp = false;
|
|
1199
1249
|
let hasCurl = false;
|
|
1200
1250
|
for (let i = startIdx; i < args.length; i++) {
|
|
1201
|
-
|
|
1251
|
+
walk(args[i], (inner) => {
|
|
1202
1252
|
if (!inner) return false;
|
|
1203
1253
|
const inn = inner;
|
|
1204
|
-
const it =
|
|
1254
|
+
const it = inn.kind;
|
|
1205
1255
|
if (it === "CmdSubst") hasCmdSubst = true;
|
|
1206
1256
|
if (it === "ParamExp") hasParamExp = true;
|
|
1207
1257
|
if (it === "Lit" && DOWNLOAD_CMDS.has(inn.Value?.toLowerCase())) hasCurl = true;
|
|
@@ -1214,15 +1264,16 @@ function scanArgsForDynamicExec(args, startIdx) {
|
|
|
1214
1264
|
}
|
|
1215
1265
|
function detectDangerousShellExec(command) {
|
|
1216
1266
|
try {
|
|
1217
|
-
const f =
|
|
1267
|
+
const f = parseShared(command);
|
|
1268
|
+
if (f === PARSE_FAIL) return null;
|
|
1218
1269
|
let result = null;
|
|
1219
1270
|
const assigned = assignedNames(f);
|
|
1220
|
-
|
|
1271
|
+
walk(f, (node) => {
|
|
1221
1272
|
if (!node || result === "block") return false;
|
|
1222
1273
|
const n = node;
|
|
1223
|
-
if (
|
|
1274
|
+
if (n.kind === "BinaryCmd" && n.Y && !listOps().has(n.Op)) {
|
|
1224
1275
|
const call = n.Y.Cmd;
|
|
1225
|
-
if (call &&
|
|
1276
|
+
if (call && call.kind === "CallExpr") {
|
|
1226
1277
|
const words = (call.Args || []).map((a) => resolveWordLiteral(a));
|
|
1227
1278
|
const head2 = unwrapCommandHead(words);
|
|
1228
1279
|
const name = (words[head2] ?? "").split("/").pop() ?? "";
|
|
@@ -1235,7 +1286,7 @@ function detectDangerousShellExec(command) {
|
|
|
1235
1286
|
result = "review";
|
|
1236
1287
|
}
|
|
1237
1288
|
}
|
|
1238
|
-
if (
|
|
1289
|
+
if (n.kind !== "CallExpr") return true;
|
|
1239
1290
|
const args = n.Args || [];
|
|
1240
1291
|
if (args.length === 0) return true;
|
|
1241
1292
|
const headWords = args.map(
|
|
@@ -1246,7 +1297,7 @@ function detectDangerousShellExec(command) {
|
|
|
1246
1297
|
if (!lookup && (opaqueExecutable(args[0], assigned) || opaqueExecutable(args[head], assigned)))
|
|
1247
1298
|
result = "review";
|
|
1248
1299
|
const firstParts = args[0].Parts || [];
|
|
1249
|
-
if (firstParts.length !== 1 ||
|
|
1300
|
+
if (firstParts.length !== 1 || firstParts[0].kind !== "Lit") return true;
|
|
1250
1301
|
const cmdName = firstParts[0].Value?.toLowerCase() ?? "";
|
|
1251
1302
|
if (cmdName === "eval") {
|
|
1252
1303
|
const v = scanArgsForDynamicExec(args, 1);
|
|
@@ -1254,7 +1305,7 @@ function detectDangerousShellExec(command) {
|
|
|
1254
1305
|
} else if (SHELL_INTERPRETERS.has(cmdName)) {
|
|
1255
1306
|
for (let i = 1; i < args.length - 1; i++) {
|
|
1256
1307
|
const flagParts = args[i].Parts || [];
|
|
1257
|
-
if (flagParts.length !== 1 ||
|
|
1308
|
+
if (flagParts.length !== 1 || flagParts[0].kind !== "Lit" || flagParts[0].Value !== "-c")
|
|
1258
1309
|
continue;
|
|
1259
1310
|
const v = scanArgsForDynamicExec(args, i + 1);
|
|
1260
1311
|
if (v === "block" || v === "review" && result === null) result = v;
|
|
@@ -1278,8 +1329,7 @@ function isDefaultExpansion(part) {
|
|
|
1278
1329
|
}
|
|
1279
1330
|
function lookupSubstitution(part) {
|
|
1280
1331
|
const stmts = part.Stmts ?? [];
|
|
1281
|
-
if (stmts.length !== 1 || !stmts[0].Cmd ||
|
|
1282
|
-
return null;
|
|
1332
|
+
if (stmts.length !== 1 || !stmts[0].Cmd || stmts[0].Cmd.kind !== "CallExpr") return null;
|
|
1283
1333
|
const words = (stmts[0].Cmd.Args ?? []).map((a) => resolveWordLiteral(a));
|
|
1284
1334
|
if (words.some((w) => w === null)) return null;
|
|
1285
1335
|
if (words.length === 2 && words[0] === "which") return words[1];
|
|
@@ -1289,9 +1339,9 @@ function lookupSubstitution(part) {
|
|
|
1289
1339
|
}
|
|
1290
1340
|
function assignedNames(f) {
|
|
1291
1341
|
const names = /* @__PURE__ */ new Set();
|
|
1292
|
-
|
|
1342
|
+
walk(f, (node) => {
|
|
1293
1343
|
if (!node) return false;
|
|
1294
|
-
const kind =
|
|
1344
|
+
const kind = node.kind;
|
|
1295
1345
|
if (kind === "Assign" && node.Name?.Value) names.add(node.Name.Value);
|
|
1296
1346
|
if (kind === "ForClause" && node.Loop?.Name?.Value) names.add(node.Loop.Name.Value);
|
|
1297
1347
|
if (kind === "CallExpr") {
|
|
@@ -1306,7 +1356,7 @@ function assignedNames(f) {
|
|
|
1306
1356
|
}
|
|
1307
1357
|
function executableParts(word, assigned, quoted = false) {
|
|
1308
1358
|
return (word?.Parts ?? []).flatMap((part) => {
|
|
1309
|
-
const kind =
|
|
1359
|
+
const kind = part.kind;
|
|
1310
1360
|
if (kind === "DblQuoted") return executableParts(part, assigned, true);
|
|
1311
1361
|
if (kind === "Lit") return [{ kind: "Lit", value: part.Value ?? "", quoted }];
|
|
1312
1362
|
if (kind === "SglQuoted")
|
|
@@ -1374,10 +1424,10 @@ function chmodHasOpenPermMode(command) {
|
|
|
1374
1424
|
if (f === PARSE_FAIL) return false;
|
|
1375
1425
|
let found = false;
|
|
1376
1426
|
try {
|
|
1377
|
-
|
|
1427
|
+
walk(f, (node) => {
|
|
1378
1428
|
if (!node || found) return false;
|
|
1379
1429
|
const n = node;
|
|
1380
|
-
if (
|
|
1430
|
+
if (n.kind !== "CallExpr") return true;
|
|
1381
1431
|
const words = (n.Args || []).map((a) => resolveWordLiteral(a));
|
|
1382
1432
|
if (words.length === 0) return true;
|
|
1383
1433
|
const name = (words[0] ?? "").toLowerCase();
|
|
@@ -1430,11 +1480,11 @@ function redirStdinOps() {
|
|
|
1430
1480
|
}
|
|
1431
1481
|
function deriveBinaryOp(sample) {
|
|
1432
1482
|
try {
|
|
1433
|
-
const f = sharedParser.Parse(sample, "cmd");
|
|
1483
|
+
const f = toPlainTree(sharedParser.Parse(sample, "cmd"));
|
|
1434
1484
|
let op = -1;
|
|
1435
|
-
|
|
1485
|
+
walk(f, (node) => {
|
|
1436
1486
|
const n = node;
|
|
1437
|
-
if (n &&
|
|
1487
|
+
if (n && n.kind === "BinaryCmd" && op < 0) op = n.Op;
|
|
1438
1488
|
return true;
|
|
1439
1489
|
});
|
|
1440
1490
|
return op;
|
|
@@ -1508,7 +1558,7 @@ function heredocText(word) {
|
|
|
1508
1558
|
const parts = word?.Parts ?? [];
|
|
1509
1559
|
let out = "";
|
|
1510
1560
|
for (const p of parts) {
|
|
1511
|
-
if (
|
|
1561
|
+
if (p.kind !== "Lit") return null;
|
|
1512
1562
|
out += p.Value ?? "";
|
|
1513
1563
|
}
|
|
1514
1564
|
return out;
|
|
@@ -1544,7 +1594,7 @@ function attachedCode(interp, flag) {
|
|
|
1544
1594
|
}
|
|
1545
1595
|
function inlineExecCall(stmt, pipeFed) {
|
|
1546
1596
|
const cmd = stmt?.Cmd;
|
|
1547
|
-
if (!cmd ||
|
|
1597
|
+
if (!cmd || cmd.kind !== "CallExpr") return null;
|
|
1548
1598
|
const words = (cmd.Args || []).map((a) => resolveWordLiteral(a));
|
|
1549
1599
|
if (words.length === 0) return null;
|
|
1550
1600
|
const headIdx = unwrapCommandHead(words);
|
|
@@ -1625,10 +1675,10 @@ function shellPayloadNeedsReview(payload) {
|
|
|
1625
1675
|
let review = false;
|
|
1626
1676
|
payloadNesting++;
|
|
1627
1677
|
try {
|
|
1628
|
-
|
|
1678
|
+
walk(f, (node) => {
|
|
1629
1679
|
if (!node || review) return false;
|
|
1630
1680
|
const n = node;
|
|
1631
|
-
if (
|
|
1681
|
+
if (n.kind !== "CallExpr") return true;
|
|
1632
1682
|
const words = (n.Args || []).map((a) => resolveWordLiteral(a));
|
|
1633
1683
|
if (words.length === 0) return true;
|
|
1634
1684
|
const h = unwrapCommandHead(words);
|
|
@@ -1662,10 +1712,10 @@ function inlineExecCalls(command) {
|
|
|
1662
1712
|
if (f === PARSE_FAIL) return PARSE_FAIL;
|
|
1663
1713
|
const calls = [];
|
|
1664
1714
|
try {
|
|
1665
|
-
|
|
1715
|
+
walk(f, (node) => {
|
|
1666
1716
|
if (!node || calls.length >= MAX_INLINE_CALLS) return false;
|
|
1667
1717
|
const n = node;
|
|
1668
|
-
const t =
|
|
1718
|
+
const t = n.kind;
|
|
1669
1719
|
if (t === "BinaryCmd" && n.Y && !listOps().has(n.Op)) {
|
|
1670
1720
|
const call = inlineExecCall(n.Y, true);
|
|
1671
1721
|
if (call) calls.push(call);
|
|
@@ -1800,7 +1850,7 @@ function shellScriptOperands(command) {
|
|
|
1800
1850
|
walkWithCalls(f, (node) => {
|
|
1801
1851
|
if (!node) return false;
|
|
1802
1852
|
const n = node;
|
|
1803
|
-
if (
|
|
1853
|
+
if (n.kind !== "CallExpr") return true;
|
|
1804
1854
|
const callArgs = n.Args || [];
|
|
1805
1855
|
if (callArgs.length === 0) return true;
|
|
1806
1856
|
const words = callArgs.map((a) => resolveWordLiteral(a));
|
|
@@ -1836,7 +1886,7 @@ function shellScriptOperands(command) {
|
|
|
1836
1886
|
}
|
|
1837
1887
|
function windowsDrivePathSpelling(word) {
|
|
1838
1888
|
const parts = word?.Parts || [];
|
|
1839
|
-
if (parts.length === 0 || !parts.every((p) =>
|
|
1889
|
+
if (parts.length === 0 || !parts.every((p) => p.kind === "Lit")) return null;
|
|
1840
1890
|
const raw = parts.map((p) => p.Value ?? "").join("");
|
|
1841
1891
|
return /^[A-Za-z]:\\[^\s'"$`]*$/.test(raw) ? raw : null;
|
|
1842
1892
|
}
|
|
@@ -1874,15 +1924,15 @@ function recordTopLevelAssignments(f) {
|
|
|
1874
1924
|
}
|
|
1875
1925
|
function probeBinOp(src) {
|
|
1876
1926
|
try {
|
|
1877
|
-
const cmd =
|
|
1878
|
-
if (cmd &&
|
|
1927
|
+
const cmd = toPlainTree(syntax2.NewParser().Parse(src, "probe"))?.Stmts?.[0]?.Cmd;
|
|
1928
|
+
if (cmd && cmd.kind === "BinaryCmd") return cmd.Op;
|
|
1879
1929
|
} catch {
|
|
1880
1930
|
}
|
|
1881
1931
|
return null;
|
|
1882
1932
|
}
|
|
1883
1933
|
function recordTopLevelStmt(stmt) {
|
|
1884
1934
|
if (!stmt || !stmt.Cmd) return false;
|
|
1885
|
-
const t =
|
|
1935
|
+
const t = stmt.Cmd.kind;
|
|
1886
1936
|
if (t === "BinaryCmd") {
|
|
1887
1937
|
if (!AND_OR_OPS.has(stmt.Cmd.Op)) return false;
|
|
1888
1938
|
if (!recordTopLevelStmt(stmt.Cmd.X)) return false;
|
|
@@ -1892,7 +1942,7 @@ function recordTopLevelStmt(stmt) {
|
|
|
1892
1942
|
if (t !== "CallExpr" && t !== "DeclClause") return false;
|
|
1893
1943
|
let at = 0;
|
|
1894
1944
|
try {
|
|
1895
|
-
at = stmt.
|
|
1945
|
+
at = stmt.pos;
|
|
1896
1946
|
} catch {
|
|
1897
1947
|
at = 0;
|
|
1898
1948
|
}
|
|
@@ -1903,14 +1953,14 @@ function recordTopLevelStmt(stmt) {
|
|
|
1903
1953
|
}
|
|
1904
1954
|
function recordAssignments(n, at) {
|
|
1905
1955
|
if (!assignmentTable) return;
|
|
1906
|
-
const t =
|
|
1956
|
+
const t = n.kind;
|
|
1907
1957
|
let assigns = [];
|
|
1908
1958
|
if (t === "CallExpr") {
|
|
1909
1959
|
if ((n.Args || []).length > 0) return;
|
|
1910
1960
|
assigns = n.Assigns || [];
|
|
1911
1961
|
} else if (t === "DeclClause") {
|
|
1912
1962
|
if (!ASSIGNMENT_HEADS.has(n.Variant?.Value ?? "")) return;
|
|
1913
|
-
assigns = (n.Args || []).filter((a) =>
|
|
1963
|
+
assigns = (n.Args || []).filter((a) => a.kind === "Assign");
|
|
1914
1964
|
} else return;
|
|
1915
1965
|
for (const a of assigns) {
|
|
1916
1966
|
const name = a?.Name?.Value;
|
|
@@ -1919,13 +1969,13 @@ function recordAssignments(n, at) {
|
|
|
1919
1969
|
}
|
|
1920
1970
|
}
|
|
1921
1971
|
function resolveTrivialSubst(part) {
|
|
1922
|
-
if (
|
|
1972
|
+
if (part.kind !== "CmdSubst") return void 0;
|
|
1923
1973
|
const stmts = part.Stmts || [];
|
|
1924
1974
|
if (stmts.length !== 1) return void 0;
|
|
1925
1975
|
const st = stmts[0];
|
|
1926
1976
|
if ((st.Redirs || []).length > 0 || st.Negated || st.Background) return void 0;
|
|
1927
1977
|
const cmd = st.Cmd;
|
|
1928
|
-
if (!cmd ||
|
|
1978
|
+
if (!cmd || cmd.kind !== "CallExpr") return void 0;
|
|
1929
1979
|
if ((cmd.Assigns || []).length > 0) return void 0;
|
|
1930
1980
|
const words = (cmd.Args || []).map((a) => resolveWordLiteral(a));
|
|
1931
1981
|
if (words.length === 0 || words.some((w) => w === null)) return void 0;
|
|
@@ -1950,7 +2000,7 @@ function recordedExpansion(name) {
|
|
|
1950
2000
|
return rec.value;
|
|
1951
2001
|
}
|
|
1952
2002
|
function isPlainParam(p) {
|
|
1953
|
-
if (
|
|
2003
|
+
if (p.kind !== "ParamExp") return false;
|
|
1954
2004
|
return !(p.Excl || p.Length || p.Width || p.Index || p.Slice || p.Repl || p.Exp);
|
|
1955
2005
|
}
|
|
1956
2006
|
function expandPlainParam(p) {
|
|
@@ -1962,9 +2012,9 @@ function expandPlainParam(p) {
|
|
|
1962
2012
|
}
|
|
1963
2013
|
function functionBodies(f) {
|
|
1964
2014
|
const out = /* @__PURE__ */ new Map();
|
|
1965
|
-
|
|
2015
|
+
walk(f, (node) => {
|
|
1966
2016
|
const n = node;
|
|
1967
|
-
if (n &&
|
|
2017
|
+
if (n && n.kind === "FuncDecl" && n.Name?.Value && n.Body) {
|
|
1968
2018
|
const list = out.get(n.Name.Value) ?? [];
|
|
1969
2019
|
list.push(n.Body);
|
|
1970
2020
|
out.set(n.Name.Value, list);
|
|
@@ -1975,10 +2025,10 @@ function functionBodies(f) {
|
|
|
1975
2025
|
}
|
|
1976
2026
|
function rebindsPositionals(body) {
|
|
1977
2027
|
let found = false;
|
|
1978
|
-
|
|
2028
|
+
walk(body, (node) => {
|
|
1979
2029
|
const n = node;
|
|
1980
2030
|
if (found || !n) return false;
|
|
1981
|
-
if (
|
|
2031
|
+
if (n.kind === "CallExpr") {
|
|
1982
2032
|
const words = (n.Args || []).map((a) => resolveWordLiteral(a));
|
|
1983
2033
|
if (words[0] === "shift") found = true;
|
|
1984
2034
|
else if (words[0] === "set" && words.slice(1).some((w) => w === null || w === "--" || !/^[-+]/.test(w)))
|
|
@@ -1998,7 +2048,7 @@ function tableBindings() {
|
|
|
1998
2048
|
function walkWithCalls(f, visit) {
|
|
1999
2049
|
const bodies = functionBodies(f);
|
|
2000
2050
|
if (bodies.size === 0) {
|
|
2001
|
-
|
|
2051
|
+
walk(f, visit);
|
|
2002
2052
|
return;
|
|
2003
2053
|
}
|
|
2004
2054
|
const seenCalls = /* @__PURE__ */ new Set();
|
|
@@ -2032,8 +2082,8 @@ function walkWithCalls(f, visit) {
|
|
|
2032
2082
|
currentStmtOffset = Number.MAX_SAFE_INTEGER;
|
|
2033
2083
|
callDepth++;
|
|
2034
2084
|
try {
|
|
2035
|
-
if (body.Cmd &&
|
|
2036
|
-
|
|
2085
|
+
if (body.Cmd && body.Cmd.kind === "Block") recordTopLevelAssignments(body.Cmd);
|
|
2086
|
+
walk(body, step);
|
|
2037
2087
|
} finally {
|
|
2038
2088
|
callDepth--;
|
|
2039
2089
|
assignmentTable = outerTable;
|
|
@@ -2044,10 +2094,10 @@ function walkWithCalls(f, visit) {
|
|
|
2044
2094
|
const step = (node) => {
|
|
2045
2095
|
if (!node) return false;
|
|
2046
2096
|
const go = visit(node);
|
|
2047
|
-
if (go !== false &&
|
|
2097
|
+
if (go !== false && node.kind === "CallExpr") inline(node);
|
|
2048
2098
|
return go;
|
|
2049
2099
|
};
|
|
2050
|
-
|
|
2100
|
+
walk(f, step);
|
|
2051
2101
|
}
|
|
2052
2102
|
function withAssignments(f, body) {
|
|
2053
2103
|
const outerTable = assignmentTable;
|
|
@@ -2072,7 +2122,7 @@ function walkCommandScope(command, visit) {
|
|
|
2072
2122
|
if (f === PARSE_FAIL) return "parse-fail";
|
|
2073
2123
|
withAssignments(f, () => {
|
|
2074
2124
|
walkWithCalls(f, (node) => {
|
|
2075
|
-
if (node &&
|
|
2125
|
+
if (node && node.kind === "Stmt") trackStatement(node);
|
|
2076
2126
|
return visit(node);
|
|
2077
2127
|
});
|
|
2078
2128
|
});
|
|
@@ -2080,7 +2130,7 @@ function walkCommandScope(command, visit) {
|
|
|
2080
2130
|
}
|
|
2081
2131
|
function trackStatement(n) {
|
|
2082
2132
|
try {
|
|
2083
|
-
currentStmtOffset = n.
|
|
2133
|
+
currentStmtOffset = n.pos;
|
|
2084
2134
|
} catch {
|
|
2085
2135
|
currentStmtOffset = Number.MAX_SAFE_INTEGER;
|
|
2086
2136
|
}
|
|
@@ -2096,11 +2146,11 @@ function resolveWordLiteral(w) {
|
|
|
2096
2146
|
return s;
|
|
2097
2147
|
}
|
|
2098
2148
|
function resolveWordPrefix(w) {
|
|
2099
|
-
const
|
|
2149
|
+
const walk3 = (parts) => {
|
|
2100
2150
|
let s = "";
|
|
2101
2151
|
for (const p of parts) {
|
|
2102
2152
|
if (p && typeof p === "object" && Array.isArray(p.Parts) && p.Dollar !== void 0) {
|
|
2103
|
-
const inner =
|
|
2153
|
+
const inner = walk3(p.Parts);
|
|
2104
2154
|
s += inner.prefix;
|
|
2105
2155
|
if (inner.dynamic) return { prefix: s, dynamic: true };
|
|
2106
2156
|
continue;
|
|
@@ -2111,10 +2161,10 @@ function resolveWordPrefix(w) {
|
|
|
2111
2161
|
}
|
|
2112
2162
|
return { prefix: s, dynamic: false };
|
|
2113
2163
|
};
|
|
2114
|
-
return
|
|
2164
|
+
return walk3(w?.Parts || []);
|
|
2115
2165
|
}
|
|
2116
2166
|
function resolvePart(p, inQuotes) {
|
|
2117
|
-
const t =
|
|
2167
|
+
const t = p.kind;
|
|
2118
2168
|
if (t === "Lit") {
|
|
2119
2169
|
const raw = p.Value ?? "";
|
|
2120
2170
|
if (!inQuotes) return raw.replace(/\\(.)/g, "$1");
|
|
@@ -2210,7 +2260,7 @@ function extractShellDestinations(command) {
|
|
|
2210
2260
|
() => walkWithCalls(f, (node) => {
|
|
2211
2261
|
if (!node) return false;
|
|
2212
2262
|
const n = node;
|
|
2213
|
-
if (
|
|
2263
|
+
if (n.kind === "Stmt") {
|
|
2214
2264
|
trackStatement(n);
|
|
2215
2265
|
for (const raw of redirectWords(n)) {
|
|
2216
2266
|
const host2 = devNetRedirectHost(raw);
|
|
@@ -2222,7 +2272,7 @@ function extractShellDestinations(command) {
|
|
|
2222
2272
|
out.push({ host: host2, binary, raw });
|
|
2223
2273
|
}
|
|
2224
2274
|
}
|
|
2225
|
-
if (
|
|
2275
|
+
if (n.kind !== "CallExpr") return true;
|
|
2226
2276
|
const callArgs = n.Args || [];
|
|
2227
2277
|
if (callArgs.length === 0) return true;
|
|
2228
2278
|
const words = callArgs.map((a) => resolveWordLiteral(a));
|
|
@@ -2257,7 +2307,7 @@ function extractShellDestTokens(command) {
|
|
|
2257
2307
|
() => walkWithCalls(f, (node) => {
|
|
2258
2308
|
if (!node) return false;
|
|
2259
2309
|
const n = node;
|
|
2260
|
-
if (
|
|
2310
|
+
if (n.kind === "Stmt") {
|
|
2261
2311
|
trackStatement(n);
|
|
2262
2312
|
for (const raw of redirectWords(n)) {
|
|
2263
2313
|
const host2 = devNetRedirectHost(raw);
|
|
@@ -2269,7 +2319,7 @@ function extractShellDestTokens(command) {
|
|
|
2269
2319
|
out.push({ token: host2, binary });
|
|
2270
2320
|
}
|
|
2271
2321
|
}
|
|
2272
|
-
if (
|
|
2322
|
+
if (n.kind !== "CallExpr") return true;
|
|
2273
2323
|
const callArgs = n.Args || [];
|
|
2274
2324
|
if (callArgs.length === 0) return true;
|
|
2275
2325
|
const words = callArgs.map((a) => resolveWordLiteral(a));
|
|
@@ -2337,11 +2387,11 @@ function isWaivableCleanupTarget(p) {
|
|
|
2337
2387
|
}
|
|
2338
2388
|
function deriveRedirOp(sample) {
|
|
2339
2389
|
try {
|
|
2340
|
-
const f = sharedParser.Parse(sample, "cmd");
|
|
2390
|
+
const f = toPlainTree(sharedParser.Parse(sample, "cmd"));
|
|
2341
2391
|
let op = -1;
|
|
2342
|
-
|
|
2392
|
+
walk(f, (node) => {
|
|
2343
2393
|
const n = node;
|
|
2344
|
-
if (n &&
|
|
2394
|
+
if (n && n.kind === "Redirect" && op < 0) op = n.Op;
|
|
2345
2395
|
return true;
|
|
2346
2396
|
});
|
|
2347
2397
|
return op;
|
|
@@ -2354,7 +2404,7 @@ function collectSameCommandCreations(f) {
|
|
|
2354
2404
|
try {
|
|
2355
2405
|
const stmts = Array.isArray(f?.Stmts) ? f.Stmts : [];
|
|
2356
2406
|
for (const stmt of stmts) {
|
|
2357
|
-
if (!stmt || !stmt.Cmd ||
|
|
2407
|
+
if (!stmt || !stmt.Cmd || stmt.Cmd.kind !== "CallExpr") continue;
|
|
2358
2408
|
const redirs = stmt.Redirs || [];
|
|
2359
2409
|
if (!redirs.some((r) => r && REDIR_HEREDOC_OPS.has(r.Op))) continue;
|
|
2360
2410
|
for (const r of redirs) {
|
|
@@ -2378,10 +2428,10 @@ function isRmCreatedInCommandCleanup(command) {
|
|
|
2378
2428
|
let sawRm = false;
|
|
2379
2429
|
let ok = true;
|
|
2380
2430
|
try {
|
|
2381
|
-
|
|
2431
|
+
walk(f, (node) => {
|
|
2382
2432
|
if (!node || !ok) return false;
|
|
2383
2433
|
const n = node;
|
|
2384
|
-
if (
|
|
2434
|
+
if (n.kind !== "CallExpr") return true;
|
|
2385
2435
|
const args = n.Args || [];
|
|
2386
2436
|
const name = (resolveWordLiteral(args[0]) ?? "").toLowerCase();
|
|
2387
2437
|
if (name !== "rm") return true;
|
|
@@ -2431,10 +2481,10 @@ function analyzeFsOperationImpl(command, depth = 0) {
|
|
|
2431
2481
|
walkWithCalls(f, (node) => {
|
|
2432
2482
|
if (!node || result?.verdict === "block") return false;
|
|
2433
2483
|
const n = node;
|
|
2434
|
-
const nodeType =
|
|
2484
|
+
const nodeType = n.kind;
|
|
2435
2485
|
if (nodeType === "Stmt") {
|
|
2436
2486
|
try {
|
|
2437
|
-
currentStmtOffset = n.
|
|
2487
|
+
currentStmtOffset = n.pos;
|
|
2438
2488
|
} catch {
|
|
2439
2489
|
currentStmtOffset = Number.MAX_SAFE_INTEGER;
|
|
2440
2490
|
}
|
|
@@ -2947,11 +2997,12 @@ function analyzeShellCommand(command) {
|
|
|
2947
2997
|
if (lower2.startsWith("-")) allTokens.push(lower2.replace(/^-+/, ""));
|
|
2948
2998
|
};
|
|
2949
2999
|
try {
|
|
2950
|
-
const f =
|
|
2951
|
-
|
|
3000
|
+
const f = parseShared(command);
|
|
3001
|
+
if (f === PARSE_FAIL) throw new Error("parse failed");
|
|
3002
|
+
walk(f, (node) => {
|
|
2952
3003
|
if (!node) return false;
|
|
2953
3004
|
const n = node;
|
|
2954
|
-
if (
|
|
3005
|
+
if (n.kind !== "CallExpr") return true;
|
|
2955
3006
|
const wordValues2 = (n.Args || []).map((arg) => {
|
|
2956
3007
|
return (arg.Parts || []).map((p) => (p.Value ?? "").replace(/\\(.)/g, "$1")).join("");
|
|
2957
3008
|
}).filter((s) => s.length > 0);
|
|
@@ -3223,7 +3274,7 @@ function runnablePrefix(command) {
|
|
|
3223
3274
|
let text = command;
|
|
3224
3275
|
for (let attempt = 0; attempt < 32; attempt++) {
|
|
3225
3276
|
try {
|
|
3226
|
-
return
|
|
3277
|
+
return toPlainTree(syntax3.NewParser().Parse(text, ""));
|
|
3227
3278
|
} catch (error) {
|
|
3228
3279
|
const message = String(typeof error?.Error === "function" ? error.Error() : error?.message);
|
|
3229
3280
|
const line = Number(/^(\d+):/.exec(message)?.[1]);
|
|
@@ -3291,7 +3342,7 @@ function wordValues(word, state) {
|
|
|
3291
3342
|
out2 = next.slice(0, MAX_ALTERNATIVES);
|
|
3292
3343
|
};
|
|
3293
3344
|
for (const node of nodes) {
|
|
3294
|
-
const kind =
|
|
3345
|
+
const kind = node.kind;
|
|
3295
3346
|
let pieces;
|
|
3296
3347
|
if (kind === "ParamExp") pieces = paramValues(node, quoted);
|
|
3297
3348
|
else if (kind === "DblQuoted") {
|
|
@@ -3359,9 +3410,9 @@ function collectShellFileAccesses(command, context = {}) {
|
|
|
3359
3410
|
}
|
|
3360
3411
|
function substitutions(word, state, depth) {
|
|
3361
3412
|
if (!word) return;
|
|
3362
|
-
|
|
3413
|
+
walk(word, (node) => {
|
|
3363
3414
|
if (!node) return false;
|
|
3364
|
-
if (["CmdSubst", "ProcSubst"].includes(
|
|
3415
|
+
if (["CmdSubst", "ProcSubst"].includes(node.kind)) {
|
|
3365
3416
|
statements(node.Stmts ?? [], clone(state), depth + 1);
|
|
3366
3417
|
return false;
|
|
3367
3418
|
}
|
|
@@ -3785,7 +3836,7 @@ function collectShellFileAccesses(command, context = {}) {
|
|
|
3785
3836
|
}
|
|
3786
3837
|
function readsScriptFromStdin(stmt) {
|
|
3787
3838
|
const cmd = stmt?.Cmd;
|
|
3788
|
-
if (!cmd ||
|
|
3839
|
+
if (!cmd || cmd.kind !== "CallExpr") return false;
|
|
3789
3840
|
const words = (cmd.Args ?? []).map((a) => resolveWordLiteral(a));
|
|
3790
3841
|
const head = unwrapCommandHead(words);
|
|
3791
3842
|
return SHELL_INTERPRETERS.has(base(words[head])) && shellConsumesPipe(words.slice(head + 1));
|
|
@@ -3799,7 +3850,7 @@ function collectShellFileAccesses(command, context = {}) {
|
|
|
3799
3850
|
}
|
|
3800
3851
|
function producedText(stmt) {
|
|
3801
3852
|
const cmd = stmt?.Cmd;
|
|
3802
|
-
if (!cmd ||
|
|
3853
|
+
if (!cmd || cmd.kind !== "CallExpr") return null;
|
|
3803
3854
|
const words = (cmd.Args ?? []).map((a) => resolveWordLiteral(a));
|
|
3804
3855
|
if (words.some((w) => w === null)) return null;
|
|
3805
3856
|
const head = unwrapCommandHead(words);
|
|
@@ -3818,7 +3869,7 @@ function collectShellFileAccesses(command, context = {}) {
|
|
|
3818
3869
|
if (!stmt || depth > MAX_DEPTH) return;
|
|
3819
3870
|
state.onSuccess = void 0;
|
|
3820
3871
|
if (stmt.Background) state = clone(state);
|
|
3821
|
-
const headWord = stmt.Cmd &&
|
|
3872
|
+
const headWord = stmt.Cmd && stmt.Cmd.kind === "CallExpr" ? base(resolveWordLiteral(stmt.Cmd.Args?.[0])) : "";
|
|
3822
3873
|
const sends = SENDING_STDIN.has(headWord);
|
|
3823
3874
|
for (const redir of stmt.Redirs ?? []) {
|
|
3824
3875
|
substitutions(redir.Word, state, depth);
|
|
@@ -3834,7 +3885,7 @@ function collectShellFileAccesses(command, context = {}) {
|
|
|
3834
3885
|
const cmd = stmt.Cmd;
|
|
3835
3886
|
if (!cmd) return;
|
|
3836
3887
|
stdinScript(stmt, state, depth);
|
|
3837
|
-
switch (
|
|
3888
|
+
switch (cmd.kind) {
|
|
3838
3889
|
case "CallExpr":
|
|
3839
3890
|
call(cmd, state, depth);
|
|
3840
3891
|
break;
|
|
@@ -3848,7 +3899,7 @@ function collectShellFileAccesses(command, context = {}) {
|
|
|
3848
3899
|
for (; ; ) {
|
|
3849
3900
|
segments2.unshift({ op: node.Op, part: node.Y });
|
|
3850
3901
|
const left = node.X;
|
|
3851
|
-
if (left?.Cmd &&
|
|
3902
|
+
if (left?.Cmd && left.Cmd.kind === "BinaryCmd" && !left.Redirs?.length && !left.Negated && !left.Background)
|
|
3852
3903
|
node = left.Cmd;
|
|
3853
3904
|
else {
|
|
3854
3905
|
segments2.unshift({ op: node.Op, part: left });
|
|
@@ -5218,11 +5269,10 @@ function shellWriteUnseen(command, target) {
|
|
|
5218
5269
|
let unseen = false;
|
|
5219
5270
|
let seen = false;
|
|
5220
5271
|
try {
|
|
5221
|
-
|
|
5272
|
+
walk(f, (node) => {
|
|
5222
5273
|
if (!node || unseen) return false;
|
|
5223
5274
|
const n = node;
|
|
5224
|
-
if (
|
|
5225
|
-
return true;
|
|
5275
|
+
if (n.kind !== "Stmt" || !n.Cmd || n.Cmd.kind !== "CallExpr") return true;
|
|
5226
5276
|
const words = (n.Cmd.Args || []).map(
|
|
5227
5277
|
(a) => resolveWordLiteral(a)
|
|
5228
5278
|
);
|
|
@@ -5277,14 +5327,14 @@ function analyzeGitImpl(command) {
|
|
|
5277
5327
|
seen.add(key);
|
|
5278
5328
|
findings.push(f);
|
|
5279
5329
|
};
|
|
5280
|
-
const
|
|
5281
|
-
const parsed =
|
|
5330
|
+
const walk3 = new GitWalk((inv) => classify(inv).forEach(found), found);
|
|
5331
|
+
const parsed = walk3.run(command, 0);
|
|
5282
5332
|
return { parsed, findings };
|
|
5283
5333
|
}
|
|
5284
5334
|
function isPositionalParam(word) {
|
|
5285
5335
|
let parts = word?.Parts || [];
|
|
5286
|
-
if (parts.length === 1 &&
|
|
5287
|
-
if (parts.length !== 1 ||
|
|
5336
|
+
if (parts.length === 1 && parts[0].kind === "DblQuoted") parts = parts[0].Parts || [];
|
|
5337
|
+
if (parts.length !== 1 || parts[0].kind !== "ParamExp") return false;
|
|
5288
5338
|
return /^(\d+|@|\*)$/.test(parts[0].Param?.Value ?? "");
|
|
5289
5339
|
}
|
|
5290
5340
|
function boundPositionals() {
|
|
@@ -5298,11 +5348,11 @@ function boundPositionals() {
|
|
|
5298
5348
|
function plainParamName(word) {
|
|
5299
5349
|
let parts = word?.Parts || [];
|
|
5300
5350
|
let quoted = false;
|
|
5301
|
-
if (parts.length === 1 &&
|
|
5351
|
+
if (parts.length === 1 && parts[0].kind === "DblQuoted") {
|
|
5302
5352
|
parts = parts[0].Parts || [];
|
|
5303
5353
|
quoted = true;
|
|
5304
5354
|
}
|
|
5305
|
-
if (parts.length !== 1 ||
|
|
5355
|
+
if (parts.length !== 1 || parts[0].kind !== "ParamExp") return null;
|
|
5306
5356
|
const p = parts[0];
|
|
5307
5357
|
if (p.Excl || p.Length || p.Width || p.Index || p.Slice || p.Repl || p.Exp) return null;
|
|
5308
5358
|
const name = p.Param?.Value;
|
|
@@ -5327,7 +5377,7 @@ function resolveGitWords(word) {
|
|
|
5327
5377
|
}
|
|
5328
5378
|
const parts = word?.Parts || [];
|
|
5329
5379
|
const expansion = parts.some((p) => {
|
|
5330
|
-
const t =
|
|
5380
|
+
const t = p.kind;
|
|
5331
5381
|
return t === "ParamExp" || t === "CmdSubst";
|
|
5332
5382
|
});
|
|
5333
5383
|
return expansion ? splitFields(value) : [value];
|
|
@@ -5728,14 +5778,14 @@ function hookCommands(text) {
|
|
|
5728
5778
|
const root = parsed?.hooks;
|
|
5729
5779
|
if (!root || typeof root !== "object") return null;
|
|
5730
5780
|
const out = [];
|
|
5731
|
-
const
|
|
5781
|
+
const walk3 = (v, depth) => {
|
|
5732
5782
|
if (depth > 8 || !v || typeof v !== "object") return;
|
|
5733
5783
|
for (const [k, x] of Object.entries(v)) {
|
|
5734
5784
|
if (k === "command" && typeof x === "string") out.push(x);
|
|
5735
|
-
else
|
|
5785
|
+
else walk3(x, depth + 1);
|
|
5736
5786
|
}
|
|
5737
5787
|
};
|
|
5738
|
-
|
|
5788
|
+
walk3(root, 0);
|
|
5739
5789
|
return out;
|
|
5740
5790
|
}
|
|
5741
5791
|
function hooksDisarmed(text) {
|
|
@@ -5902,9 +5952,9 @@ function isDupInOp(op) {
|
|
|
5902
5952
|
if (dupInOp === null) {
|
|
5903
5953
|
let found = -1;
|
|
5904
5954
|
try {
|
|
5905
|
-
|
|
5955
|
+
walk(parseShared("cat <&3"), (node) => {
|
|
5906
5956
|
const n = node;
|
|
5907
|
-
if (found === -1 && n &&
|
|
5957
|
+
if (found === -1 && n && n.kind === "Stmt" && n.Redirs?.[0]) {
|
|
5908
5958
|
found = n.Redirs[0].Op;
|
|
5909
5959
|
}
|
|
5910
5960
|
return true;
|
|
@@ -5925,10 +5975,10 @@ function analyzeOutbound(command) {
|
|
|
5925
5975
|
return hit;
|
|
5926
5976
|
}
|
|
5927
5977
|
const findings = [];
|
|
5928
|
-
const
|
|
5929
|
-
const parsed =
|
|
5978
|
+
const walk3 = new OutboundWalk(findings);
|
|
5979
|
+
const parsed = walk3.run(command, 0);
|
|
5930
5980
|
const envOut = findings.find((f) => f.checkId === "data.env-exfil");
|
|
5931
|
-
if (envOut) envOut.destinations =
|
|
5981
|
+
if (envOut) envOut.destinations = walk3.envDestinations();
|
|
5932
5982
|
const computed = { parsed, findings };
|
|
5933
5983
|
if (cache2.size >= CACHE_MAX2) {
|
|
5934
5984
|
const oldest = cache2.keys().next().value;
|
|
@@ -5944,7 +5994,7 @@ function wordShape(word) {
|
|
|
5944
5994
|
const parts = word?.Parts || [];
|
|
5945
5995
|
let out = "";
|
|
5946
5996
|
for (const p of parts) {
|
|
5947
|
-
const t =
|
|
5997
|
+
const t = p.kind;
|
|
5948
5998
|
if (t === "Lit" || t === "SglQuoted") out += p.Value ?? "";
|
|
5949
5999
|
else if (t === "DblQuoted") out += wordShape(p);
|
|
5950
6000
|
else out += "X";
|
|
@@ -6079,16 +6129,16 @@ function declDumpsEnv(decl) {
|
|
|
6079
6129
|
}
|
|
6080
6130
|
function paramName(word) {
|
|
6081
6131
|
let parts = word?.Parts || [];
|
|
6082
|
-
if (parts.length === 1 &&
|
|
6083
|
-
if (parts.length !== 1 ||
|
|
6132
|
+
if (parts.length === 1 && parts[0].kind === "DblQuoted") parts = parts[0].Parts || [];
|
|
6133
|
+
if (parts.length !== 1 || parts[0].kind !== "ParamExp") return null;
|
|
6084
6134
|
return parts[0].Param?.Value ?? null;
|
|
6085
6135
|
}
|
|
6086
6136
|
function refersTo(word, names) {
|
|
6087
6137
|
let hit = false;
|
|
6088
|
-
|
|
6138
|
+
walk(word, (node) => {
|
|
6089
6139
|
if (hit || !node) return false;
|
|
6090
6140
|
const n = node;
|
|
6091
|
-
if (
|
|
6141
|
+
if (n.kind === "ParamExp" && names.has(n.Param?.Value)) hit = true;
|
|
6092
6142
|
return !hit;
|
|
6093
6143
|
});
|
|
6094
6144
|
return hit;
|
|
@@ -6096,10 +6146,10 @@ function refersTo(word, names) {
|
|
|
6096
6146
|
function runsCommandsIn(stmt) {
|
|
6097
6147
|
const readVars = /* @__PURE__ */ new Set();
|
|
6098
6148
|
let hit = false;
|
|
6099
|
-
|
|
6149
|
+
walk(stmt, (node) => {
|
|
6100
6150
|
if (hit || !node) return false;
|
|
6101
6151
|
const n = node;
|
|
6102
|
-
if (
|
|
6152
|
+
if (n.kind !== "CallExpr") return true;
|
|
6103
6153
|
const words = wordsOf(n);
|
|
6104
6154
|
if (words.length === 0) return true;
|
|
6105
6155
|
const { head, at } = headOf(words);
|
|
@@ -6676,7 +6726,7 @@ function localContexts(file2) {
|
|
|
6676
6726
|
if (depth > 64) return null;
|
|
6677
6727
|
if (!stmt || stmt.Background || stmt.Negated || stmt.Redirs?.length) return null;
|
|
6678
6728
|
const cmd = stmt.Cmd;
|
|
6679
|
-
const kind =
|
|
6729
|
+
const kind = cmd.kind;
|
|
6680
6730
|
if (kind === "BinaryCmd" && cmd.Op === andOp) {
|
|
6681
6731
|
const next = visit(cmd.X, steps, depth + 1);
|
|
6682
6732
|
return next === null ? null : visit(cmd.Y, next, depth + 1);
|
|
@@ -6692,7 +6742,7 @@ function localContexts(file2) {
|
|
|
6692
6742
|
)
|
|
6693
6743
|
))
|
|
6694
6744
|
return null;
|
|
6695
|
-
contexts.set(cmd.
|
|
6745
|
+
contexts.set(cmd.pos, steps);
|
|
6696
6746
|
return null;
|
|
6697
6747
|
}
|
|
6698
6748
|
if (file2.Stmts?.length === 1) visit(file2.Stmts[0], []);
|
|
@@ -6703,12 +6753,12 @@ function collect(command, out, depth) {
|
|
|
6703
6753
|
const f = parseShared(command);
|
|
6704
6754
|
if (typeof f === "symbol") return;
|
|
6705
6755
|
const contexts = depth === 0 ? localContexts(f) : /* @__PURE__ */ new Map();
|
|
6706
|
-
|
|
6707
|
-
if (node &&
|
|
6756
|
+
walk(f, (node) => {
|
|
6757
|
+
if (node && node.kind === "CallExpr") {
|
|
6708
6758
|
const words = (node.Args || []).map((a) => resolveWordLiteral(a));
|
|
6709
6759
|
const start = out.length;
|
|
6710
6760
|
if (words.length > 0) fromCall(words, out, depth);
|
|
6711
|
-
const steps = contexts.get(node.
|
|
6761
|
+
const steps = contexts.get(node.pos);
|
|
6712
6762
|
for (let i = start; i < out.length; i++) {
|
|
6713
6763
|
if (["npx", "bunx", "npm exec"].includes(out[i].manager)) {
|
|
6714
6764
|
if (!steps) out[i].localCwd = null;
|
|
@@ -6755,7 +6805,7 @@ function collect2(command, out, depth) {
|
|
|
6755
6805
|
try {
|
|
6756
6806
|
walkCommandScope(command, (node) => {
|
|
6757
6807
|
const n = node;
|
|
6758
|
-
if (!n ||
|
|
6808
|
+
if (!n || n.kind !== "CallExpr") return true;
|
|
6759
6809
|
const words = (n.Args || []).map((a) => resolveWordLiteral(a));
|
|
6760
6810
|
if (words.length === 0) return true;
|
|
6761
6811
|
const prefix = (n.Assigns || []).filter((a) => a?.Name?.Value).map(
|
|
@@ -8140,12 +8190,12 @@ function extractContentDestinations(text) {
|
|
|
8140
8190
|
function extractArgDestinations(args) {
|
|
8141
8191
|
const out = /* @__PURE__ */ new Set();
|
|
8142
8192
|
try {
|
|
8143
|
-
|
|
8193
|
+
walk2(args, void 0, out, 0);
|
|
8144
8194
|
} catch {
|
|
8145
8195
|
}
|
|
8146
8196
|
return [...out];
|
|
8147
8197
|
}
|
|
8148
|
-
function
|
|
8198
|
+
function walk2(value, key, out, depth) {
|
|
8149
8199
|
if (out.size >= MAX_DESTINATIONS || depth > 12) return;
|
|
8150
8200
|
if (typeof value === "string") {
|
|
8151
8201
|
const s = value.length > MAX_SCAN_CHARS ? value.slice(0, MAX_SCAN_CHARS) : value;
|
|
@@ -8164,12 +8214,12 @@ function walk(value, key, out, depth) {
|
|
|
8164
8214
|
return;
|
|
8165
8215
|
}
|
|
8166
8216
|
if (Array.isArray(value)) {
|
|
8167
|
-
for (const v of value)
|
|
8217
|
+
for (const v of value) walk2(v, key, out, depth + 1);
|
|
8168
8218
|
return;
|
|
8169
8219
|
}
|
|
8170
8220
|
if (value && typeof value === "object") {
|
|
8171
8221
|
for (const [k, v] of Object.entries(value))
|
|
8172
|
-
|
|
8222
|
+
walk2(v, k, out, depth + 1);
|
|
8173
8223
|
}
|
|
8174
8224
|
}
|
|
8175
8225
|
function destinationDomain(dest) {
|
|
@@ -8821,9 +8871,9 @@ function classifyCall(head, args, prefixes, env, state, out) {
|
|
|
8821
8871
|
function walkCalls(command, out, depth, state) {
|
|
8822
8872
|
const f = parseShared(command);
|
|
8823
8873
|
if (typeof f === "symbol" || !f) return;
|
|
8824
|
-
|
|
8874
|
+
walk(f, (node) => {
|
|
8825
8875
|
if (!node) return false;
|
|
8826
|
-
const type =
|
|
8876
|
+
const type = node.kind;
|
|
8827
8877
|
if (type === "DeclClause") {
|
|
8828
8878
|
for (const a of node.Args || []) if (a?.Name?.Value) state.env.add(a.Name.Value);
|
|
8829
8879
|
return true;
|
|
@@ -8975,7 +9025,7 @@ function matchCanaryArgs(args, values) {
|
|
|
8975
9025
|
if (values.length === 0) return null;
|
|
8976
9026
|
const needles = prepare(values);
|
|
8977
9027
|
if (needles.length === 0) return null;
|
|
8978
|
-
const
|
|
9028
|
+
const walk3 = (v, depth, fieldPath) => {
|
|
8979
9029
|
if (depth > MAX_DEPTH4) return null;
|
|
8980
9030
|
if (typeof v === "string") {
|
|
8981
9031
|
const hit = matchPrepared(v, needles);
|
|
@@ -8984,31 +9034,31 @@ function matchCanaryArgs(args, values) {
|
|
|
8984
9034
|
const trimmed = v.trim();
|
|
8985
9035
|
if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
|
8986
9036
|
try {
|
|
8987
|
-
return
|
|
9037
|
+
return walk3(JSON.parse(v), depth + 1, fieldPath);
|
|
8988
9038
|
} catch {
|
|
8989
9039
|
}
|
|
8990
9040
|
}
|
|
8991
9041
|
}
|
|
8992
9042
|
return null;
|
|
8993
9043
|
}
|
|
8994
|
-
if (typeof v === "number" && Number.isFinite(v)) return
|
|
9044
|
+
if (typeof v === "number" && Number.isFinite(v)) return walk3(String(v), depth, fieldPath);
|
|
8995
9045
|
if (Array.isArray(v)) {
|
|
8996
9046
|
for (let i = 0; i < v.length; i++) {
|
|
8997
|
-
const h =
|
|
9047
|
+
const h = walk3(v[i], depth + 1, `${fieldPath}[${i}]`);
|
|
8998
9048
|
if (h) return h;
|
|
8999
9049
|
}
|
|
9000
9050
|
return null;
|
|
9001
9051
|
}
|
|
9002
9052
|
if (v && typeof v === "object") {
|
|
9003
9053
|
for (const [k, child] of Object.entries(v)) {
|
|
9004
|
-
const h =
|
|
9054
|
+
const h = walk3(child, depth + 1, fieldPath ? `${fieldPath}.${k}` : k);
|
|
9005
9055
|
if (h) return h;
|
|
9006
9056
|
}
|
|
9007
9057
|
}
|
|
9008
9058
|
return null;
|
|
9009
9059
|
};
|
|
9010
9060
|
try {
|
|
9011
|
-
return
|
|
9061
|
+
return walk3(args, 0, "");
|
|
9012
9062
|
} catch (e) {
|
|
9013
9063
|
console.error(
|
|
9014
9064
|
"[node9 engine] canary args walk failed:",
|
|
@@ -9017,7 +9067,7 @@ function matchCanaryArgs(args, values) {
|
|
|
9017
9067
|
return null;
|
|
9018
9068
|
}
|
|
9019
9069
|
}
|
|
9020
|
-
var 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, 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, 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_INLINE_CODE, WHAT, OUTSIDE, pyImport, either, PYTHON, NODE, PERL, RUBY, PHP, TABLES, STRING_LITERAL,
|
|
9070
|
+
var 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, 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_INLINE_CODE, WHAT, OUTSIDE, pyImport, either, PYTHON, NODE, PERL, RUBY, PHP, TABLES, STRING_LITERAL, syntax2, 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, AND_OP, OR_OP, AND_OR_OPS, MAX_CALL_DEPTH, MAX_CALL_WALKS, callDepth, callWalks, FS_OP_CACHE_MAX, fsOpCache, stripDotSlash, REDIR_TRUNCATE_OPS, REDIR_FILE_IN_OPS, REDIR_HEREDOC_OPS, FIND_OPTIONS, COPY_RULE_OF, isReaderWord, positionalAfter, NONE, UNKNOWN, FORBIDDEN_PATH_SEGMENTS, BASH_TOOL_NAMES, MCP_EXECUTORS, BUILTIN_FIELDS, ARGV_FIELDS, BARE_WORD, MAX_ARGV_CHARACTERS, syntax3, MAX_ALTERNATIVES, MAX_CALL_DEPTH2, MAX_CALL_WALKS2, 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, WRITES, APPENDS, READS, DUP_OUT, HERE_STRING, AND, OR, DEFAULT_OPS, exempt, RANK, SEQUENCE_OPS, BIP39_ENGLISH, BIP39_PUBLIC_VECTORS, indices, lengths, hash, MAX_MNEMONIC_HASHES, SECRET_VIEW_LIMITS, IBAN_LENGTH, IBAN_BBAN_SHAPE, B58, B58_INDEX, XPRV_VERSIONS, B62, CRC32_TABLE, GITHUB_TOKEN_RE, B64URL, B64URL_INDEX, CASK_TWO_ZERO_SUFFIX, CASK_FOUR_ZERO_SUFFIX, CF_ODD, CF_EVEN, SPANISH_LETTERS, HETU_CHARS, VERHOEFF_D, VERHOEFF_P, MAX, UNTRUSTED_TOOLS, INJECTION_SIGNALS, MENTION_QUOTE_RE, MENTION_WORD_RE, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, REFERENCE_VALUE_RE, IDENTIFIER_VALUE_RE, PATH_VALUE_RE, DOTTED_REFERENCE_RE, CONCATENATION_RE, EMAIL_VALUE_RE, FIXTURE_NAME_RE, DERIVATIVE_NAME_RE, QUERY_CREDENTIAL_VALUE_RE, OTP_SECRET_RE, OTP_DOC_SECRETS, ASSIGNED_SECRET_VALUE_RE, ASSIGNED_SECRET_NAME_RE, NAME_KEYWORD_RE, ARN_CONTEXT_RE, CIPHER_KEY_NAME_RE, CIPHER_NAME_PARTS, CIPHER_KEY_VALUE_RE, LARAVEL_KEY_VALUE_RE, SQL_PASSWORD_VALUE_RE, GRAFANA_KEY_VALUE_RE, BASIC_VALUE_RE, BASIC_PAIR_RE, BASIC_HEADER_RE, BASIC_HEADER_PAIR_RE, BASIC_PLACEHOLDER_RE, BASIC_TEMPLATE_MARKERS, DROPBOX_SECRET_RE, OTP_CAPTURE_RE, DB_URL_PASSWORD_RE, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, SENSITIVE_PATH_REGEXES, MAX_DEPTH2, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, BATCH_SEPARATOR, ENCODED_RUN_RE, IMPERATIVE, SECRET_TARGET, GUARD, SIGNALS, DESTINATION_RE, REUSED_SIGNALS, NEVER_NEGATED, NEGATION_BEFORE, RDR_IN, COPY_VERBS2, LITERAL_VERBS, GIT_PRESCREEN_RE, MAX_PAYLOAD_DEPTH2, MAX_ALIAS_DEPTH, CACHE_MAX, cache, PAYLOAD_SHELLS, CONFIG_SCOPE_FLAGS, CONFIG_VALUE_FLAGS, GLOBAL_VALUE_OPTIONS, GLOBAL_FLAG_OPTIONS, MAX_LOOP_ITEMS, GitWalk, POSITIONAL2, 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, CANONICAL_EXTRACTOR_VERSION;
|
|
9021
9071
|
var init_dist = __esm({
|
|
9022
9072
|
"packages/policy-engine/dist/index.mjs"() {
|
|
9023
9073
|
"use strict";
|
|
@@ -11665,6 +11715,61 @@ var init_dist = __esm({
|
|
|
11665
11715
|
return s.untrustedContent.afterExposure === "log-only" ? "log" : "review";
|
|
11666
11716
|
}
|
|
11667
11717
|
};
|
|
11718
|
+
({ syntax } = mvdanSh);
|
|
11719
|
+
KINDS = {
|
|
11720
|
+
File: { nodes: ["Stmts"] },
|
|
11721
|
+
Stmt: { nodes: ["Cmd", "Redirs"], scalars: ["Negated", "Background", "Coprocess"] },
|
|
11722
|
+
CallExpr: { nodes: ["Assigns", "Args"] },
|
|
11723
|
+
Assign: { nodes: ["Name", "Value", "Index", "Array"], scalars: ["Append", "Naked"] },
|
|
11724
|
+
Word: { nodes: ["Parts"] },
|
|
11725
|
+
Lit: { scalars: ["Value"] },
|
|
11726
|
+
SglQuoted: { scalars: ["Value", "Dollar"] },
|
|
11727
|
+
DblQuoted: { nodes: ["Parts"], scalars: ["Dollar"] },
|
|
11728
|
+
ParamExp: {
|
|
11729
|
+
nodes: ["Param", "Index", "Slice", "Repl", "Exp"],
|
|
11730
|
+
scalars: ["Short", "Excl", "Length", "Width"]
|
|
11731
|
+
},
|
|
11732
|
+
CmdSubst: { nodes: ["Stmts"], scalars: ["Backquotes", "TempFile", "ReplyVar"] },
|
|
11733
|
+
ArithmExp: { nodes: ["X"], scalars: ["Bracket", "Unsigned"] },
|
|
11734
|
+
ArithmCmd: { nodes: ["X"], scalars: ["Unsigned"] },
|
|
11735
|
+
ProcSubst: { nodes: ["Stmts"], scalars: ["Op"] },
|
|
11736
|
+
ExtGlob: { nodes: ["Pattern"], scalars: ["Op"] },
|
|
11737
|
+
BraceExp: { nodes: ["Elems"], scalars: ["Sequence"] },
|
|
11738
|
+
// only with SplitBraces, unused here
|
|
11739
|
+
BinaryCmd: { nodes: ["X", "Y"], scalars: ["Op"] },
|
|
11740
|
+
BinaryArithm: { nodes: ["X", "Y"], scalars: ["Op"] },
|
|
11741
|
+
UnaryArithm: { nodes: ["X"], scalars: ["Op", "Post"] },
|
|
11742
|
+
ParenArithm: { nodes: ["X"] },
|
|
11743
|
+
BinaryTest: { nodes: ["X", "Y"], scalars: ["Op"] },
|
|
11744
|
+
UnaryTest: { nodes: ["X"], scalars: ["Op"] },
|
|
11745
|
+
ParenTest: { nodes: ["X"] },
|
|
11746
|
+
Redirect: { nodes: ["N", "Word", "Hdoc"], scalars: ["Op"] },
|
|
11747
|
+
Block: { nodes: ["Stmts"] },
|
|
11748
|
+
Subshell: { nodes: ["Stmts"] },
|
|
11749
|
+
IfClause: { nodes: ["Cond", "Then", "Else"] },
|
|
11750
|
+
WhileClause: { nodes: ["Cond", "Do"], scalars: ["Until"] },
|
|
11751
|
+
ForClause: { nodes: ["Loop", "Do"], scalars: ["Select"] },
|
|
11752
|
+
WordIter: { nodes: ["Name", "Items"] },
|
|
11753
|
+
CStyleLoop: { nodes: ["Init", "Cond", "Post"] },
|
|
11754
|
+
CaseClause: { nodes: ["Word", "Items"] },
|
|
11755
|
+
CaseItem: { nodes: ["Patterns", "Stmts"], scalars: ["Op"] },
|
|
11756
|
+
FuncDecl: { nodes: ["Name", "Body"], scalars: ["RsrvWord"] },
|
|
11757
|
+
DeclClause: { nodes: ["Variant", "Args"], notWalked: ["Variant"] },
|
|
11758
|
+
LetClause: { nodes: ["Exprs"] },
|
|
11759
|
+
TimeClause: { nodes: ["Stmt"], scalars: ["PosixFormat"] },
|
|
11760
|
+
CoprocClause: { nodes: ["Name", "Stmt"] },
|
|
11761
|
+
TestClause: { nodes: ["X"] },
|
|
11762
|
+
TestDecl: { nodes: ["Description", "Body"] },
|
|
11763
|
+
ArrayExpr: { nodes: ["Elems"] },
|
|
11764
|
+
ArrayElem: { nodes: ["Index", "Value"] },
|
|
11765
|
+
Comment: { scalars: ["Text"] }
|
|
11766
|
+
};
|
|
11767
|
+
STRUCTS = {
|
|
11768
|
+
"ParamExp.Slice": { nodes: ["Offset", "Length"], notWalked: ["Offset", "Length"] },
|
|
11769
|
+
"ParamExp.Repl": { nodes: ["Orig", "With"], scalars: ["All"] },
|
|
11770
|
+
"ParamExp.Exp": { nodes: ["Word"], scalars: ["Op"] }
|
|
11771
|
+
};
|
|
11772
|
+
unknownKinds = /* @__PURE__ */ new Set();
|
|
11668
11773
|
INVISIBLE_RE = /[͏----︀-️]|[\u{E0000}-\u{E007F}]/gu;
|
|
11669
11774
|
INVISIBLE_ONE = new RegExp(`^(?:${INVISIBLE_RE.source.split("|")[0]})$`, "u");
|
|
11670
11775
|
HOMOGLYPHS = {
|
|
@@ -11950,8 +12055,8 @@ var init_dist = __esm({
|
|
|
11950
12055
|
php: PHP
|
|
11951
12056
|
};
|
|
11952
12057
|
STRING_LITERAL = /(['"`])((?:\\.|(?!\1)[^\\\n]){1,4096})\1/g;
|
|
11953
|
-
({ syntax } =
|
|
11954
|
-
sharedParser =
|
|
12058
|
+
({ syntax: syntax2 } = mvdanSh2);
|
|
12059
|
+
sharedParser = syntax2.NewParser();
|
|
11955
12060
|
MESSAGE_FLAGS = /* @__PURE__ */ new Set([
|
|
11956
12061
|
"-m",
|
|
11957
12062
|
"--message",
|
|
@@ -11985,7 +12090,7 @@ var init_dist = __esm({
|
|
|
11985
12090
|
NORMALIZE_CACHE_MAX = 5e3;
|
|
11986
12091
|
normalizeCache = /* @__PURE__ */ new Map();
|
|
11987
12092
|
codeBodiesCache = /* @__PURE__ */ new Map();
|
|
11988
|
-
AST_CACHE_MAX =
|
|
12093
|
+
AST_CACHE_MAX = 256;
|
|
11989
12094
|
astCache = /* @__PURE__ */ new Map();
|
|
11990
12095
|
PARSE_FAIL = /* @__PURE__ */ Symbol("parse-fail");
|
|
11991
12096
|
INTERPRETER_LINE = /^#![ \t]*[\w./+-]+(?:[ \t]+[\w./+=:-]+)*[ \t]*\r?(?=\n|$)/;
|
|
@@ -12970,7 +13075,7 @@ var init_dist = __esm({
|
|
|
12970
13075
|
ARGV_FIELDS = ["args", "argv"];
|
|
12971
13076
|
BARE_WORD = /^[A-Za-z0-9_./+:@%,-]+$/;
|
|
12972
13077
|
MAX_ARGV_CHARACTERS = 262144;
|
|
12973
|
-
({ syntax:
|
|
13078
|
+
({ syntax: syntax3 } = mvdanSh3);
|
|
12974
13079
|
MAX_ALTERNATIVES = 64;
|
|
12975
13080
|
MAX_CALL_DEPTH2 = 4;
|
|
12976
13081
|
MAX_CALL_WALKS2 = 256;
|
|
@@ -13062,7 +13167,6 @@ var init_dist = __esm({
|
|
|
13062
13167
|
);
|
|
13063
13168
|
exempt = (p) => SYSTEM_DIRS_EXEMPT.some((d) => within(p, d));
|
|
13064
13169
|
RANK = { allow: 0, review: 1, block: 2 };
|
|
13065
|
-
({ syntax: syntax3 } = mvdanSh3);
|
|
13066
13170
|
SEQUENCE_OPS = (() => {
|
|
13067
13171
|
const ops = /* @__PURE__ */ new Set();
|
|
13068
13172
|
for (const text of ["x && y", "x || y"]) {
|
|
@@ -16224,7 +16328,6 @@ zoo`.split("\n");
|
|
|
16224
16328
|
);
|
|
16225
16329
|
NEVER_NEGATED = /* @__PURE__ */ new Set([SIGNALS[1].any[0]]);
|
|
16226
16330
|
NEGATION_BEFORE = /\b(never|do not|don'?t|must not|mustn'?t|should not|shouldn'?t|not allowed to|forbidden to|avoid|without|no|not)\b[^.!?;:\n]{0,24}$/i;
|
|
16227
|
-
({ syntax: syntax4 } = mvdanSh4);
|
|
16228
16331
|
RDR_IN = (() => {
|
|
16229
16332
|
try {
|
|
16230
16333
|
return parseShared("x <y").Stmts[0].Redirs[0].Op;
|
|
@@ -16253,7 +16356,6 @@ zoo`.split("\n");
|
|
|
16253
16356
|
"tar"
|
|
16254
16357
|
]);
|
|
16255
16358
|
LITERAL_VERBS = /* @__PURE__ */ new Set(["echo", "printf", ":", "true", "touch", "sed"]);
|
|
16256
|
-
({ syntax: syntax5 } = mvdanSh5);
|
|
16257
16359
|
GIT_PRESCREEN_RE = /git|husky/i;
|
|
16258
16360
|
MAX_PAYLOAD_DEPTH2 = 8;
|
|
16259
16361
|
MAX_ALIAS_DEPTH = 5;
|
|
@@ -16322,7 +16424,7 @@ zoo`.split("\n");
|
|
|
16322
16424
|
visit(node, depth) {
|
|
16323
16425
|
if (!node) return false;
|
|
16324
16426
|
const n = node;
|
|
16325
|
-
const type =
|
|
16427
|
+
const type = n.kind;
|
|
16326
16428
|
if (type === "Stmt") {
|
|
16327
16429
|
this.visitHeredocShell(n, depth);
|
|
16328
16430
|
return true;
|
|
@@ -16330,7 +16432,7 @@ zoo`.split("\n");
|
|
|
16330
16432
|
if (type === "ForClause") {
|
|
16331
16433
|
const loop = n.Loop;
|
|
16332
16434
|
const name = loop?.Name?.Value;
|
|
16333
|
-
if (name &&
|
|
16435
|
+
if (name && loop.kind === "WordIter") {
|
|
16334
16436
|
const items = (loop.Items || []).map((w) => resolveWordLiteral(w));
|
|
16335
16437
|
const literal = items.length > 0 && items.every((v) => v !== null);
|
|
16336
16438
|
this.loopVars.set(name, literal ? items.slice(0, MAX_LOOP_ITEMS) : null);
|
|
@@ -16397,7 +16499,7 @@ zoo`.split("\n");
|
|
|
16397
16499
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
16398
16500
|
visitHeredocShell(stmt, depth) {
|
|
16399
16501
|
const cmd = stmt.Cmd;
|
|
16400
|
-
if (!cmd ||
|
|
16502
|
+
if (!cmd || cmd.kind !== "CallExpr" || depth >= MAX_PAYLOAD_DEPTH2) return;
|
|
16401
16503
|
const redirs = stmt.Redirs || [];
|
|
16402
16504
|
const hdoc = redirs.find((r) => r?.Hdoc && (r.N == null || r.N.Value === "0"));
|
|
16403
16505
|
if (!hdoc) return;
|
|
@@ -16651,7 +16753,6 @@ zoo`.split("\n");
|
|
|
16651
16753
|
]);
|
|
16652
16754
|
SETTINGS_RE = /\/(?:\.claude\/settings(?:\.local)?\.json|\.gemini\/settings\.json|\.codex\/hooks\.json|\.cursor\/hooks\.json)$/i;
|
|
16653
16755
|
HOOK_WORDS_RE = /"hooks"|\b(PreToolUse|PostToolUse|UserPromptSubmit|Notification|Stop|SubagentStop|SessionStart|SessionEnd|PreCompact|BeforeTool|AfterTool)\b|"command"\s*:/;
|
|
16654
|
-
({ syntax: syntax6 } = mvdanSh6);
|
|
16655
16756
|
SHELLS = /* @__PURE__ */ new Set([...SHELL_INTERPRETERS, "mksh", "ash", "csh", "tcsh"]);
|
|
16656
16757
|
NETCATS = /* @__PURE__ */ new Set(["nc", "ncat", "netcat", "nc.traditional", "nc.openbsd"]);
|
|
16657
16758
|
NET_CLIENTS = /* @__PURE__ */ new Set([...NETCATS, "telnet", "socat"]);
|
|
@@ -16709,7 +16810,7 @@ zoo`.split("\n");
|
|
|
16709
16810
|
const f = parseShared(command);
|
|
16710
16811
|
if (!f || typeof f !== "object" || !f.Stmts) return false;
|
|
16711
16812
|
try {
|
|
16712
|
-
|
|
16813
|
+
walk(f, (node) => this.visit(node, depth));
|
|
16713
16814
|
} catch {
|
|
16714
16815
|
return false;
|
|
16715
16816
|
}
|
|
@@ -16748,7 +16849,7 @@ zoo`.split("\n");
|
|
|
16748
16849
|
}
|
|
16749
16850
|
visit(n, depth) {
|
|
16750
16851
|
if (!n) return false;
|
|
16751
|
-
const type =
|
|
16852
|
+
const type = n.kind;
|
|
16752
16853
|
if (type === "Stmt") {
|
|
16753
16854
|
this.visitStmt(n, depth);
|
|
16754
16855
|
return true;
|
|
@@ -16768,7 +16869,7 @@ zoo`.split("\n");
|
|
|
16768
16869
|
const redirs = redirectWords(stmt);
|
|
16769
16870
|
const toSocket = redirs.some((w) => w !== null && DEV_NET_RE.test(w));
|
|
16770
16871
|
if (!cmd) return;
|
|
16771
|
-
const type =
|
|
16872
|
+
const type = cmd.kind;
|
|
16772
16873
|
if (type !== "CallExpr" || wordsOf(cmd).length > 0) {
|
|
16773
16874
|
if (this.readsNetFd(stmt) && runsCommandsIn(stmt)) {
|
|
16774
16875
|
this.reverseShell("commands are read from a network socket opened with exec");
|
|
@@ -16854,7 +16955,7 @@ zoo`.split("\n");
|
|
|
16854
16955
|
const stages = [];
|
|
16855
16956
|
const flatten = (s) => {
|
|
16856
16957
|
const c = s?.Cmd;
|
|
16857
|
-
if (c &&
|
|
16958
|
+
if (c && c.kind === "BinaryCmd" && isPipeOp(c.Op)) {
|
|
16858
16959
|
flatten(c.X);
|
|
16859
16960
|
flatten(c.Y);
|
|
16860
16961
|
} else stages.push(s);
|
|
@@ -16876,8 +16977,8 @@ zoo`.split("\n");
|
|
|
16876
16977
|
sinks.set(i, socketHosts(st));
|
|
16877
16978
|
}
|
|
16878
16979
|
if (!c) return;
|
|
16879
|
-
if (
|
|
16880
|
-
if (
|
|
16980
|
+
if (c.kind === "DeclClause" && declDumpsEnv(c) && dump < 0) dump = i;
|
|
16981
|
+
if (c.kind !== "CallExpr") {
|
|
16881
16982
|
if (runsCommandsIn(st)) shell = i;
|
|
16882
16983
|
return;
|
|
16883
16984
|
}
|
|
@@ -16918,10 +17019,10 @@ zoo`.split("\n");
|
|
|
16918
17019
|
/** A word (or assignment value) whose text includes an env dump's output. */
|
|
16919
17020
|
carriesEnv(word) {
|
|
16920
17021
|
let hit = false;
|
|
16921
|
-
|
|
17022
|
+
walk(word, (node) => {
|
|
16922
17023
|
if (hit || !node) return false;
|
|
16923
17024
|
const n = node;
|
|
16924
|
-
const t =
|
|
17025
|
+
const t = n.kind;
|
|
16925
17026
|
if (t === "ParamExp" && n.Param?.Value && this.envVars.has(n.Param.Value)) {
|
|
16926
17027
|
hit = true;
|
|
16927
17028
|
return false;
|
|
@@ -16938,11 +17039,11 @@ zoo`.split("\n");
|
|
|
16938
17039
|
stmtsDumpEnv(stmts) {
|
|
16939
17040
|
let hit = false;
|
|
16940
17041
|
for (const s of stmts) {
|
|
16941
|
-
|
|
17042
|
+
walk(s, (node) => {
|
|
16942
17043
|
if (hit || !node) return false;
|
|
16943
17044
|
const n = node;
|
|
16944
|
-
const t =
|
|
16945
|
-
if (t === "Stmt" && n.Cmd &&
|
|
17045
|
+
const t = n.kind;
|
|
17046
|
+
if (t === "Stmt" && n.Cmd && n.Cmd.kind === "CallExpr") {
|
|
16946
17047
|
const words = wordsOf(n.Cmd);
|
|
16947
17048
|
if (words.length > 0 && dumpsEnv(words, n)) hit = true;
|
|
16948
17049
|
}
|
|
@@ -16959,7 +17060,6 @@ zoo`.split("\n");
|
|
|
16959
17060
|
this.run(payload, depth + 1);
|
|
16960
17061
|
}
|
|
16961
17062
|
};
|
|
16962
|
-
({ syntax: syntax7 } = mvdanSh7);
|
|
16963
17063
|
NPM_INSTALL_VERBS = /* @__PURE__ */ new Set([
|
|
16964
17064
|
"install",
|
|
16965
17065
|
"i",
|
|
@@ -17341,7 +17441,6 @@ zoo`.split("\n");
|
|
|
17341
17441
|
SHELLS2 = /* @__PURE__ */ new Set(["sh", "bash", "zsh", "dash", "ksh", "ash"]);
|
|
17342
17442
|
MAX_NESTING = 3;
|
|
17343
17443
|
SHELL_VALUE_FLAGS = /* @__PURE__ */ new Set(["--rcfile", "--init-file", "-O", "+O", "-o", "+o"]);
|
|
17344
|
-
({ syntax: syntax8 } = mvdanSh8);
|
|
17345
17444
|
NPM_FAMILY = /* @__PURE__ */ new Set(["npm", "pnpm", "yarn", "bun", "npx", "pnpx", "bunx"]);
|
|
17346
17445
|
PIP_FAMILY = /* @__PURE__ */ new Set(["pip", "pip3", "uv", "uvx", "pipx", "pipenv"]);
|
|
17347
17446
|
NPM_FETCH_VERBS = /* @__PURE__ */ new Set([
|
|
@@ -17873,7 +17972,6 @@ zoo`.split("\n");
|
|
|
17873
17972
|
ACCOUNT_KEY_RE = /("(?:iban|account|account_number|accountNumber)"\s*:\s*")([A-Za-z0-9 .-]{6,40})(")/g;
|
|
17874
17973
|
ACCOUNT_VALUE_RE = /"([A-Z]{2}\d{2}[A-Z0-9]{11,30})"/g;
|
|
17875
17974
|
MAX_MASK_DEPTH = 64;
|
|
17876
|
-
({ syntax: syntax9 } = mvdanSh9);
|
|
17877
17975
|
EMPTY = {
|
|
17878
17976
|
fetches: false,
|
|
17879
17977
|
sends: false,
|
|
@@ -18520,18 +18618,18 @@ function statAndFirstLine(file2) {
|
|
|
18520
18618
|
}
|
|
18521
18619
|
function listCodexSessionFiles(base2 = codexSessionsDir()) {
|
|
18522
18620
|
const files = [];
|
|
18523
|
-
const
|
|
18621
|
+
const walk3 = (dir2) => {
|
|
18524
18622
|
try {
|
|
18525
18623
|
for (const entry of fs3.readdirSync(dir2, { withFileTypes: true })) {
|
|
18526
18624
|
const file2 = path3.join(dir2, entry.name);
|
|
18527
|
-
if (entry.isDirectory())
|
|
18625
|
+
if (entry.isDirectory()) walk3(file2);
|
|
18528
18626
|
else if (entry.isFile() && entry.name.endsWith(".jsonl")) files.push(file2);
|
|
18529
18627
|
}
|
|
18530
18628
|
} catch {
|
|
18531
18629
|
}
|
|
18532
18630
|
};
|
|
18533
|
-
|
|
18534
|
-
if (path3.basename(base2) === "sessions")
|
|
18631
|
+
walk3(base2);
|
|
18632
|
+
if (path3.basename(base2) === "sessions") walk3(path3.join(path3.dirname(base2), "archived_sessions"));
|
|
18535
18633
|
const sessions = /* @__PURE__ */ new Map();
|
|
18536
18634
|
for (const file2 of files.sort()) {
|
|
18537
18635
|
try {
|
|
@@ -18747,79 +18845,6 @@ var init_credential_files = __esm({
|
|
|
18747
18845
|
}
|
|
18748
18846
|
});
|
|
18749
18847
|
|
|
18750
|
-
// src/cli/commands/blast.ts
|
|
18751
|
-
import chalk from "chalk";
|
|
18752
|
-
import fs5 from "fs";
|
|
18753
|
-
import path6 from "path";
|
|
18754
|
-
import os4 from "os";
|
|
18755
|
-
function buildSensitivePaths(home, cwd) {
|
|
18756
|
-
const homeFiles = homeCredentialFiles(home).map(({ full, label: label2, description, score }) => ({
|
|
18757
|
-
full,
|
|
18758
|
-
label: label2,
|
|
18759
|
-
description,
|
|
18760
|
-
score
|
|
18761
|
-
}));
|
|
18762
|
-
return [
|
|
18763
|
-
...homeFiles,
|
|
18764
|
-
{
|
|
18765
|
-
full: path6.join(cwd, ".env"),
|
|
18766
|
-
label: ".env (current folder)",
|
|
18767
|
-
description: "App secrets: database passwords, API keys",
|
|
18768
|
-
score: 20
|
|
18769
|
-
},
|
|
18770
|
-
{
|
|
18771
|
-
full: path6.join(cwd, ".env.local"),
|
|
18772
|
-
label: ".env.local (current folder)",
|
|
18773
|
-
description: "Local overrides: often contains real credentials",
|
|
18774
|
-
score: 15
|
|
18775
|
-
},
|
|
18776
|
-
{
|
|
18777
|
-
full: path6.join(cwd, ".env.production"),
|
|
18778
|
-
label: ".env.production (current folder)",
|
|
18779
|
-
description: "Production secrets",
|
|
18780
|
-
score: 20
|
|
18781
|
-
}
|
|
18782
|
-
];
|
|
18783
|
-
}
|
|
18784
|
-
function isReadable(filePath) {
|
|
18785
|
-
try {
|
|
18786
|
-
fs5.accessSync(filePath, fs5.constants.R_OK);
|
|
18787
|
-
return true;
|
|
18788
|
-
} catch {
|
|
18789
|
-
return false;
|
|
18790
|
-
}
|
|
18791
|
-
}
|
|
18792
|
-
function runBlast() {
|
|
18793
|
-
const home = os4.homedir();
|
|
18794
|
-
const cwd = process.cwd();
|
|
18795
|
-
const paths = buildSensitivePaths(home, cwd);
|
|
18796
|
-
let scoreDeduction = 0;
|
|
18797
|
-
const reachable = [];
|
|
18798
|
-
for (const p of paths) {
|
|
18799
|
-
if (fs5.existsSync(p.full) && isReadable(p.full)) {
|
|
18800
|
-
reachable.push(p);
|
|
18801
|
-
scoreDeduction += p.score;
|
|
18802
|
-
}
|
|
18803
|
-
}
|
|
18804
|
-
const envFindings = [];
|
|
18805
|
-
for (const [key, value] of Object.entries(process.env)) {
|
|
18806
|
-
if (!value) continue;
|
|
18807
|
-
const match = scanArgs({ [key]: value });
|
|
18808
|
-
if (match) {
|
|
18809
|
-
envFindings.push({ key, patternName: match.patternName });
|
|
18810
|
-
scoreDeduction += 10;
|
|
18811
|
-
}
|
|
18812
|
-
}
|
|
18813
|
-
return { reachable, envFindings, score: Math.max(0, 100 - scoreDeduction) };
|
|
18814
|
-
}
|
|
18815
|
-
var init_blast = __esm({
|
|
18816
|
-
"src/cli/commands/blast.ts"() {
|
|
18817
|
-
"use strict";
|
|
18818
|
-
init_dlp();
|
|
18819
|
-
init_credential_files();
|
|
18820
|
-
}
|
|
18821
|
-
});
|
|
18822
|
-
|
|
18823
18848
|
// src/mcp-cmd.ts
|
|
18824
18849
|
var init_mcp_cmd = __esm({
|
|
18825
18850
|
"src/mcp-cmd.ts"() {
|
|
@@ -18872,14 +18897,14 @@ var init_codex_trust = __esm({
|
|
|
18872
18897
|
});
|
|
18873
18898
|
|
|
18874
18899
|
// src/daemon/hook-baseline.ts
|
|
18875
|
-
import
|
|
18876
|
-
import
|
|
18900
|
+
import path6 from "path";
|
|
18901
|
+
import os4 from "os";
|
|
18877
18902
|
var BASELINE_FILE, NOTIFIED_FILE;
|
|
18878
18903
|
var init_hook_baseline = __esm({
|
|
18879
18904
|
"src/daemon/hook-baseline.ts"() {
|
|
18880
18905
|
"use strict";
|
|
18881
|
-
BASELINE_FILE =
|
|
18882
|
-
NOTIFIED_FILE =
|
|
18906
|
+
BASELINE_FILE = path6.join(os4.homedir(), ".node9", "hooks-baseline.json");
|
|
18907
|
+
NOTIFIED_FILE = path6.join(os4.homedir(), ".node9", "hook-heal-notified.json");
|
|
18883
18908
|
}
|
|
18884
18909
|
});
|
|
18885
18910
|
|
|
@@ -18898,27 +18923,27 @@ var init_setup_pi_shim = __esm({
|
|
|
18898
18923
|
});
|
|
18899
18924
|
|
|
18900
18925
|
// src/utils/atomic-write.ts
|
|
18901
|
-
import
|
|
18902
|
-
import
|
|
18926
|
+
import fs5 from "fs";
|
|
18927
|
+
import path7 from "path";
|
|
18903
18928
|
import { randomUUID } from "crypto";
|
|
18904
18929
|
function atomicWriteSync(filePath, data, options) {
|
|
18905
|
-
const dir2 =
|
|
18906
|
-
if (!
|
|
18930
|
+
const dir2 = path7.dirname(filePath);
|
|
18931
|
+
if (!fs5.existsSync(dir2)) fs5.mkdirSync(dir2, { recursive: true });
|
|
18907
18932
|
const tmpPath = `${filePath}.${randomUUID()}.tmp`;
|
|
18908
18933
|
try {
|
|
18909
|
-
|
|
18934
|
+
fs5.writeFileSync(tmpPath, data, options);
|
|
18910
18935
|
} catch (err) {
|
|
18911
18936
|
try {
|
|
18912
|
-
|
|
18937
|
+
fs5.unlinkSync(tmpPath);
|
|
18913
18938
|
} catch {
|
|
18914
18939
|
}
|
|
18915
18940
|
throw err;
|
|
18916
18941
|
}
|
|
18917
18942
|
try {
|
|
18918
|
-
|
|
18943
|
+
fs5.renameSync(tmpPath, filePath);
|
|
18919
18944
|
} catch (err) {
|
|
18920
18945
|
try {
|
|
18921
|
-
|
|
18946
|
+
fs5.unlinkSync(tmpPath);
|
|
18922
18947
|
} catch {
|
|
18923
18948
|
}
|
|
18924
18949
|
throw err;
|
|
@@ -18931,10 +18956,10 @@ var init_atomic_write = __esm({
|
|
|
18931
18956
|
});
|
|
18932
18957
|
|
|
18933
18958
|
// src/setup.ts
|
|
18934
|
-
import
|
|
18935
|
-
import
|
|
18936
|
-
import
|
|
18937
|
-
import
|
|
18959
|
+
import fs6 from "fs";
|
|
18960
|
+
import path8 from "path";
|
|
18961
|
+
import os5 from "os";
|
|
18962
|
+
import chalk from "chalk";
|
|
18938
18963
|
import { confirm as rawConfirm } from "@inquirer/prompts";
|
|
18939
18964
|
import { parse as parseToml4, stringify as stringifyToml2 } from "smol-toml";
|
|
18940
18965
|
import * as yaml from "yaml";
|
|
@@ -18946,9 +18971,9 @@ function isNode9Hook(cmd) {
|
|
|
18946
18971
|
// fell into after #185.
|
|
18947
18972
|
/[/\\]\.node9[/\\]bin[/\\]hook"? (?:check|log)/.test(cmd);
|
|
18948
18973
|
}
|
|
18949
|
-
function claudeDesktopConfigPath(homeDir =
|
|
18974
|
+
function claudeDesktopConfigPath(homeDir = os5.homedir()) {
|
|
18950
18975
|
if (process.platform === "darwin") {
|
|
18951
|
-
return
|
|
18976
|
+
return path8.join(
|
|
18952
18977
|
homeDir,
|
|
18953
18978
|
"Library",
|
|
18954
18979
|
"Application Support",
|
|
@@ -18957,47 +18982,47 @@ function claudeDesktopConfigPath(homeDir = os6.homedir()) {
|
|
|
18957
18982
|
);
|
|
18958
18983
|
}
|
|
18959
18984
|
if (process.platform === "linux") {
|
|
18960
|
-
return
|
|
18985
|
+
return path8.join(homeDir, ".config", "Claude", "claude_desktop_config.json");
|
|
18961
18986
|
}
|
|
18962
18987
|
if (process.platform === "win32") {
|
|
18963
|
-
const appData = process.env.APPDATA ||
|
|
18964
|
-
return
|
|
18988
|
+
const appData = process.env.APPDATA || path8.join(homeDir, "AppData", "Roaming");
|
|
18989
|
+
return path8.join(appData, "Claude", "claude_desktop_config.json");
|
|
18965
18990
|
}
|
|
18966
18991
|
return null;
|
|
18967
18992
|
}
|
|
18968
|
-
function opencodeConfigDir(home =
|
|
18993
|
+
function opencodeConfigDir(home = os5.homedir()) {
|
|
18969
18994
|
const xdg = process.env.XDG_CONFIG_HOME;
|
|
18970
|
-
const base2 = xdg &&
|
|
18971
|
-
return
|
|
18995
|
+
const base2 = xdg && path8.isAbsolute(xdg) ? xdg : path8.join(home, ".config");
|
|
18996
|
+
return path8.join(base2, "opencode");
|
|
18972
18997
|
}
|
|
18973
18998
|
function commonBinDirs(home) {
|
|
18974
18999
|
return [
|
|
18975
|
-
|
|
18976
|
-
|
|
18977
|
-
|
|
19000
|
+
path8.join(home, ".local", "bin"),
|
|
19001
|
+
path8.join(home, ".bun", "bin"),
|
|
19002
|
+
path8.join(home, ".npm-global", "bin"),
|
|
18978
19003
|
"/usr/local/bin",
|
|
18979
19004
|
"/opt/homebrew/bin"
|
|
18980
19005
|
];
|
|
18981
19006
|
}
|
|
18982
|
-
function binaryInPath(binary, home =
|
|
19007
|
+
function binaryInPath(binary, home = os5.homedir()) {
|
|
18983
19008
|
const pathEnv = process.env.PATH ?? "";
|
|
18984
|
-
const dirs = [...pathEnv.split(
|
|
19009
|
+
const dirs = [...pathEnv.split(path8.delimiter), ...commonBinDirs(home)];
|
|
18985
19010
|
const seen = /* @__PURE__ */ new Set();
|
|
18986
19011
|
for (const dir2 of dirs) {
|
|
18987
19012
|
if (!dir2 || seen.has(dir2)) continue;
|
|
18988
19013
|
seen.add(dir2);
|
|
18989
19014
|
try {
|
|
18990
|
-
|
|
19015
|
+
fs6.accessSync(path8.join(dir2, binary), fs6.constants.X_OK);
|
|
18991
19016
|
return true;
|
|
18992
19017
|
} catch {
|
|
18993
19018
|
}
|
|
18994
19019
|
}
|
|
18995
19020
|
return false;
|
|
18996
19021
|
}
|
|
18997
|
-
function detectAgents(homeDir =
|
|
19022
|
+
function detectAgents(homeDir = os5.homedir()) {
|
|
18998
19023
|
const exists2 = (p) => {
|
|
18999
19024
|
try {
|
|
19000
|
-
return
|
|
19025
|
+
return fs6.existsSync(p);
|
|
19001
19026
|
} catch (err) {
|
|
19002
19027
|
const code = err.code;
|
|
19003
19028
|
if (code !== "ENOENT") {
|
|
@@ -19009,7 +19034,7 @@ function detectAgents(homeDir = os6.homedir()) {
|
|
|
19009
19034
|
};
|
|
19010
19035
|
const desktopPath = claudeDesktopConfigPath(homeDir);
|
|
19011
19036
|
return {
|
|
19012
|
-
claude: exists2(
|
|
19037
|
+
claude: exists2(path8.join(homeDir, ".claude")) || exists2(path8.join(homeDir, ".claude.json")),
|
|
19013
19038
|
// Antigravity (agy) shares the ~/.gemini root, so a bare
|
|
19014
19039
|
// `exists(~/.gemini)` would report the (EOL'd) Gemini CLI as
|
|
19015
19040
|
// installed on every agy machine — and `node9 init` would then
|
|
@@ -19019,19 +19044,19 @@ function detectAgents(homeDir = os6.homedir()) {
|
|
|
19019
19044
|
// creates ~/.gemini/settings.json on first run; agy does not touch
|
|
19020
19045
|
// it (it uses antigravity-cli/settings.json) — that file is the
|
|
19021
19046
|
// legacy-CLI discriminator.
|
|
19022
|
-
gemini: exists2(
|
|
19047
|
+
gemini: exists2(path8.join(homeDir, ".gemini", "settings.json")) || binaryInPath("gemini", homeDir),
|
|
19023
19048
|
// agy creates ~/.gemini/antigravity-cli/ on first launch; the IDE
|
|
19024
19049
|
// creates antigravity-ide/. PATH fallback covers installed-but-
|
|
19025
19050
|
// never-launched (same class as opencode #186).
|
|
19026
|
-
antigravity: exists2(
|
|
19051
|
+
antigravity: exists2(path8.join(homeDir, ".gemini", "antigravity-cli")) || exists2(path8.join(homeDir, ".gemini", "antigravity-ide")) || binaryInPath("agy", homeDir),
|
|
19027
19052
|
// GitHub Copilot CLI creates ~/.copilot on first launch; PATH
|
|
19028
19053
|
// fallback covers installed-but-never-launched (same as opencode #186).
|
|
19029
|
-
copilot: exists2(
|
|
19030
|
-
cursor: exists2(
|
|
19031
|
-
codex: exists2(
|
|
19032
|
-
windsurf: exists2(
|
|
19033
|
-
vscode: exists2(
|
|
19034
|
-
claudeDesktop: desktopPath !== null && exists2(
|
|
19054
|
+
copilot: exists2(path8.join(homeDir, ".copilot")) || binaryInPath("copilot", homeDir),
|
|
19055
|
+
cursor: exists2(path8.join(homeDir, ".cursor")),
|
|
19056
|
+
codex: exists2(path8.join(homeDir, ".codex")),
|
|
19057
|
+
windsurf: exists2(path8.join(homeDir, ".codeium", "windsurf")),
|
|
19058
|
+
vscode: exists2(path8.join(homeDir, ".vscode")),
|
|
19059
|
+
claudeDesktop: desktopPath !== null && exists2(path8.dirname(desktopPath)),
|
|
19035
19060
|
// Opencode creates its config dir lazily on first launch — fall back to a
|
|
19036
19061
|
// PATH lookup so installed-but-never-launched CLIs are still wired. Config
|
|
19037
19062
|
// dir honors $XDG_CONFIG_HOME via opencodeConfigDir (#186, custom-XDG).
|
|
@@ -19042,7 +19067,7 @@ function detectAgents(homeDir = os6.homedir()) {
|
|
|
19042
19067
|
// dir lazily on first launch — same class of bug as opencode's #186
|
|
19043
19068
|
// (design R6) — so fall back to PATH lookup for installed-but-never-
|
|
19044
19069
|
// launched pi.
|
|
19045
|
-
pi: exists2(
|
|
19070
|
+
pi: exists2(path8.join(homeDir, ".pi", "agent")) || binaryInPath("pi", homeDir),
|
|
19046
19071
|
// Hermes Agent (https://github.com/NousResearch/hermes-agent): home dir
|
|
19047
19072
|
// is $HERMES_HOME (default ~/.hermes) per hermes_constants.py:30. config.yaml
|
|
19048
19073
|
// appears after `hermes setup` has run; the directory alone exists from
|
|
@@ -19051,13 +19076,13 @@ function detectAgents(homeDir = os6.homedir()) {
|
|
|
19051
19076
|
hermes: exists2(hermesHomeDir(homeDir)) || binaryInPath("hermes", homeDir)
|
|
19052
19077
|
};
|
|
19053
19078
|
}
|
|
19054
|
-
function hermesHomeDir(homeDir =
|
|
19079
|
+
function hermesHomeDir(homeDir = os5.homedir()) {
|
|
19055
19080
|
const env = process.env.HERMES_HOME?.trim();
|
|
19056
|
-
if (env &&
|
|
19057
|
-
return
|
|
19081
|
+
if (env && path8.isAbsolute(env)) return env;
|
|
19082
|
+
return path8.join(homeDir, ".hermes");
|
|
19058
19083
|
}
|
|
19059
|
-
function hermesConfigPath(homeDir =
|
|
19060
|
-
return
|
|
19084
|
+
function hermesConfigPath(homeDir = os5.homedir()) {
|
|
19085
|
+
return path8.join(hermesHomeDir(homeDir), HERMES_CONFIG_FILENAME);
|
|
19061
19086
|
}
|
|
19062
19087
|
var HERMES_CONFIG_FILENAME;
|
|
19063
19088
|
var init_setup = __esm({
|
|
@@ -19076,15 +19101,15 @@ var init_setup = __esm({
|
|
|
19076
19101
|
});
|
|
19077
19102
|
|
|
19078
19103
|
// src/agent-wiring.ts
|
|
19079
|
-
import
|
|
19080
|
-
import
|
|
19081
|
-
import
|
|
19104
|
+
import fs7 from "fs";
|
|
19105
|
+
import path9 from "path";
|
|
19106
|
+
import os6 from "os";
|
|
19082
19107
|
import * as yaml2 from "yaml";
|
|
19083
19108
|
import { parse as parseToml5 } from "smol-toml";
|
|
19084
19109
|
function readJson(filePath) {
|
|
19085
|
-
if (!
|
|
19110
|
+
if (!fs7.existsSync(filePath)) return null;
|
|
19086
19111
|
try {
|
|
19087
|
-
return JSON.parse(
|
|
19112
|
+
return JSON.parse(fs7.readFileSync(filePath, "utf-8"));
|
|
19088
19113
|
} catch {
|
|
19089
19114
|
return "invalid";
|
|
19090
19115
|
}
|
|
@@ -19096,10 +19121,10 @@ function flatHaveNode9Hook(entries) {
|
|
|
19096
19121
|
return (Array.isArray(entries) ? entries : []).some((h) => isNode9Hook(h.command));
|
|
19097
19122
|
}
|
|
19098
19123
|
function readHookRoot(filePath, format) {
|
|
19099
|
-
if (!
|
|
19124
|
+
if (!fs7.existsSync(filePath)) return "absent";
|
|
19100
19125
|
let raw;
|
|
19101
19126
|
try {
|
|
19102
|
-
raw =
|
|
19127
|
+
raw = fs7.readFileSync(filePath, "utf-8");
|
|
19103
19128
|
} catch {
|
|
19104
19129
|
return "absent";
|
|
19105
19130
|
}
|
|
@@ -19122,20 +19147,20 @@ function detectMcp(servers) {
|
|
|
19122
19147
|
return { wrapped, present };
|
|
19123
19148
|
}
|
|
19124
19149
|
function readMcp(filePath, format) {
|
|
19125
|
-
if (!
|
|
19150
|
+
if (!fs7.existsSync(filePath)) return { wrapped: [], present: false };
|
|
19126
19151
|
try {
|
|
19127
19152
|
if (format === "toml") {
|
|
19128
|
-
const parsed2 = parseToml5(
|
|
19153
|
+
const parsed2 = parseToml5(fs7.readFileSync(filePath, "utf-8"));
|
|
19129
19154
|
return detectMcp(parsed2?.mcp_servers);
|
|
19130
19155
|
}
|
|
19131
19156
|
const parsed = readJson(filePath);
|
|
19132
19157
|
if (parsed === null || parsed === "invalid") return { wrapped: [], present: false };
|
|
19133
|
-
return detectMcp(parsed.mcpServers);
|
|
19158
|
+
return detectMcp(format === "vscode" ? parsed.servers : parsed.mcpServers);
|
|
19134
19159
|
} catch {
|
|
19135
19160
|
return { wrapped: [], present: false };
|
|
19136
19161
|
}
|
|
19137
19162
|
}
|
|
19138
|
-
function getAgentWiring(home =
|
|
19163
|
+
function getAgentWiring(home = os6.homedir()) {
|
|
19139
19164
|
const detected = detectAgents(home);
|
|
19140
19165
|
return AGENT_SPECS.map((spec) => {
|
|
19141
19166
|
const present = spec.present(home);
|
|
@@ -19179,7 +19204,8 @@ function getAgentWiring(home = os7.homedir()) {
|
|
|
19179
19204
|
configFormat: spec.hookFormat === "yaml" ? "YAML" : "JSON",
|
|
19180
19205
|
mcpServers: mcp ? mcp.wrapped : null,
|
|
19181
19206
|
mcpProtected: mcp ? mcp.present : false,
|
|
19182
|
-
isProtected: anyHookWired || (mcp?.present ?? false)
|
|
19207
|
+
isProtected: anyHookWired || (mcp?.present ?? false),
|
|
19208
|
+
protection: spec.hookEvents.length > 0 || spec.shimFile ? "hooks" : "mcp"
|
|
19183
19209
|
};
|
|
19184
19210
|
});
|
|
19185
19211
|
}
|
|
@@ -19190,7 +19216,7 @@ var init_agent_wiring = __esm({
|
|
|
19190
19216
|
init_setup();
|
|
19191
19217
|
exists = (p) => {
|
|
19192
19218
|
try {
|
|
19193
|
-
return
|
|
19219
|
+
return fs7.existsSync(p);
|
|
19194
19220
|
} catch {
|
|
19195
19221
|
return false;
|
|
19196
19222
|
}
|
|
@@ -19204,55 +19230,55 @@ var init_agent_wiring = __esm({
|
|
|
19204
19230
|
id: "claude",
|
|
19205
19231
|
label: "Claude Code",
|
|
19206
19232
|
setupCommand: "node9 agents add claude",
|
|
19207
|
-
hookFile: (h) =>
|
|
19233
|
+
hookFile: (h) => path9.join(h, ".claude", "settings.json"),
|
|
19208
19234
|
hookFormat: "matcher",
|
|
19209
19235
|
// UserPromptSubmit is prompt DLP. setup.ts has written it for Claude since
|
|
19210
19236
|
// that shipped; the spec must name it too, or status/doctor never show the
|
|
19211
19237
|
// row and heal (which repairs via setupAgent) has no signal it is missing.
|
|
19212
19238
|
hookEvents: [ck("PreToolUse"), lg("PostToolUse"), ck("UserPromptSubmit")],
|
|
19213
|
-
mcpFile: (h) =>
|
|
19214
|
-
present: (h) => exists(
|
|
19239
|
+
mcpFile: (h) => path9.join(h, ".claude.json"),
|
|
19240
|
+
present: (h) => exists(path9.join(h, ".claude", "settings.json")) || exists(path9.join(h, ".claude.json"))
|
|
19215
19241
|
},
|
|
19216
19242
|
{
|
|
19217
19243
|
id: "gemini",
|
|
19218
19244
|
label: "Gemini CLI",
|
|
19219
19245
|
setupCommand: "node9 agents add gemini",
|
|
19220
|
-
hookFile: (h) =>
|
|
19246
|
+
hookFile: (h) => path9.join(h, ".gemini", "settings.json"),
|
|
19221
19247
|
hookFormat: "matcher",
|
|
19222
19248
|
hookEvents: [ck("BeforeTool"), lg("AfterTool")],
|
|
19223
|
-
mcpFile: (h) =>
|
|
19224
|
-
present: (h) => exists(
|
|
19249
|
+
mcpFile: (h) => path9.join(h, ".gemini", "settings.json"),
|
|
19250
|
+
present: (h) => exists(path9.join(h, ".gemini", "settings.json"))
|
|
19225
19251
|
},
|
|
19226
19252
|
{
|
|
19227
19253
|
id: "codex",
|
|
19228
19254
|
label: "Codex",
|
|
19229
19255
|
setupCommand: "node9 agents add codex",
|
|
19230
|
-
hookFile: (h) =>
|
|
19256
|
+
hookFile: (h) => path9.join(h, ".codex", "hooks.json"),
|
|
19231
19257
|
hookFormat: "matcher",
|
|
19232
19258
|
hookEvents: [ck("PreToolUse"), ck("UserPromptSubmit")],
|
|
19233
|
-
mcpFile: (h) =>
|
|
19259
|
+
mcpFile: (h) => path9.join(h, ".codex", "config.toml"),
|
|
19234
19260
|
mcpFormat: "toml",
|
|
19235
|
-
present: (h) => exists(
|
|
19261
|
+
present: (h) => exists(path9.join(h, ".codex"))
|
|
19236
19262
|
},
|
|
19237
19263
|
{
|
|
19238
19264
|
id: "antigravity",
|
|
19239
19265
|
label: "Antigravity",
|
|
19240
19266
|
setupCommand: "node9 agents add antigravity",
|
|
19241
|
-
hookFile: (h) =>
|
|
19267
|
+
hookFile: (h) => path9.join(h, ".gemini", "config", "hooks.json"),
|
|
19242
19268
|
hookFormat: "matcher",
|
|
19243
19269
|
hookEvents: [ck("PreToolUse"), lg("PostToolUse")],
|
|
19244
|
-
mcpFile: (h) =>
|
|
19245
|
-
present: (h) => exists(
|
|
19270
|
+
mcpFile: (h) => path9.join(h, ".gemini", "config", "mcp_config.json"),
|
|
19271
|
+
present: (h) => exists(path9.join(h, ".gemini", "config", "hooks.json")) || exists(path9.join(h, ".gemini", "antigravity-cli")) || exists(path9.join(h, ".gemini", "antigravity-ide"))
|
|
19246
19272
|
},
|
|
19247
19273
|
{
|
|
19248
19274
|
id: "copilot",
|
|
19249
19275
|
label: "GitHub Copilot",
|
|
19250
19276
|
setupCommand: "node9 agents add copilot",
|
|
19251
|
-
hookFile: (h) =>
|
|
19277
|
+
hookFile: (h) => path9.join(h, ".copilot", "hooks", "node9.json"),
|
|
19252
19278
|
hookFormat: "flat",
|
|
19253
19279
|
hookEvents: [ck("PreToolUse"), lg("PostToolUse"), ck("UserPromptSubmit")],
|
|
19254
|
-
mcpFile: (h) =>
|
|
19255
|
-
present: (h) => exists(
|
|
19280
|
+
mcpFile: (h) => path9.join(h, ".copilot", "mcp-config.json"),
|
|
19281
|
+
present: (h) => exists(path9.join(h, ".copilot"))
|
|
19256
19282
|
},
|
|
19257
19283
|
{
|
|
19258
19284
|
id: "cursor",
|
|
@@ -19261,8 +19287,45 @@ var init_agent_wiring = __esm({
|
|
|
19261
19287
|
// MCP-only — no hook file (see note above).
|
|
19262
19288
|
hookFormat: "flat",
|
|
19263
19289
|
hookEvents: [],
|
|
19264
|
-
mcpFile: (h) =>
|
|
19265
|
-
present: (h) => exists(
|
|
19290
|
+
mcpFile: (h) => path9.join(h, ".cursor", "mcp.json"),
|
|
19291
|
+
present: (h) => exists(path9.join(h, ".cursor", "mcp.json"))
|
|
19292
|
+
},
|
|
19293
|
+
// The three MCP-only agents below came over from `getAgentsStatus` (fw#421
|
|
19294
|
+
// P3), so one registry answers every "is it wired" question.
|
|
19295
|
+
{
|
|
19296
|
+
id: "windsurf",
|
|
19297
|
+
label: "Windsurf",
|
|
19298
|
+
setupCommand: "node9 agents add windsurf",
|
|
19299
|
+
hookFormat: "flat",
|
|
19300
|
+
hookEvents: [],
|
|
19301
|
+
mcpFile: (h) => path9.join(h, ".codeium", "windsurf", "mcp_config.json"),
|
|
19302
|
+
mcpInventory: false,
|
|
19303
|
+
present: (h) => exists(path9.join(h, ".codeium", "windsurf"))
|
|
19304
|
+
},
|
|
19305
|
+
{
|
|
19306
|
+
id: "vscode",
|
|
19307
|
+
label: "VSCode",
|
|
19308
|
+
setupCommand: "node9 agents add vscode",
|
|
19309
|
+
hookFormat: "flat",
|
|
19310
|
+
hookEvents: [],
|
|
19311
|
+
mcpFile: (h) => path9.join(h, ".vscode", "mcp.json"),
|
|
19312
|
+
mcpFormat: "vscode",
|
|
19313
|
+
mcpInventory: false,
|
|
19314
|
+
present: (h) => exists(path9.join(h, ".vscode"))
|
|
19315
|
+
},
|
|
19316
|
+
{
|
|
19317
|
+
id: "claudeDesktop",
|
|
19318
|
+
label: "Claude Desktop",
|
|
19319
|
+
setupCommand: "node9 agents add claudeDesktop",
|
|
19320
|
+
hookFormat: "flat",
|
|
19321
|
+
hookEvents: [],
|
|
19322
|
+
// The per-platform config path; its parent directory is the footprint.
|
|
19323
|
+
mcpFile: (h) => claudeDesktopConfigPath(h) ?? path9.join(h, ".config", "Claude", "claude_desktop_config.json"),
|
|
19324
|
+
mcpInventory: false,
|
|
19325
|
+
present: (h) => {
|
|
19326
|
+
const p = claudeDesktopConfigPath(h);
|
|
19327
|
+
return p !== null && exists(path9.dirname(p));
|
|
19328
|
+
}
|
|
19266
19329
|
},
|
|
19267
19330
|
{
|
|
19268
19331
|
id: "hermes",
|
|
@@ -19283,8 +19346,8 @@ var init_agent_wiring = __esm({
|
|
|
19283
19346
|
setupCommand: "node9 agents add opencode",
|
|
19284
19347
|
hookFormat: "flat",
|
|
19285
19348
|
hookEvents: [],
|
|
19286
|
-
shimFile: (h) =>
|
|
19287
|
-
present: (h) => exists(opencodeConfigDir(h)) || exists(
|
|
19349
|
+
shimFile: (h) => path9.join(opencodeConfigDir(h), "plugins", "node9.js"),
|
|
19350
|
+
present: (h) => exists(opencodeConfigDir(h)) || exists(path9.join(opencodeConfigDir(h), "plugins", "node9.js"))
|
|
19288
19351
|
},
|
|
19289
19352
|
{
|
|
19290
19353
|
id: "pi",
|
|
@@ -19292,8 +19355,8 @@ var init_agent_wiring = __esm({
|
|
|
19292
19355
|
setupCommand: "node9 agents add pi",
|
|
19293
19356
|
hookFormat: "flat",
|
|
19294
19357
|
hookEvents: [],
|
|
19295
|
-
shimFile: (h) =>
|
|
19296
|
-
present: (h) => exists(
|
|
19358
|
+
shimFile: (h) => path9.join(h, ".pi", "agent", "extensions", "node9.js"),
|
|
19359
|
+
present: (h) => exists(path9.join(h, ".pi", "agent")) || exists(path9.join(h, ".pi", "agent", "extensions", "node9.js"))
|
|
19297
19360
|
}
|
|
19298
19361
|
];
|
|
19299
19362
|
}
|
|
@@ -19383,14 +19446,14 @@ var init_build = __esm({
|
|
|
19383
19446
|
});
|
|
19384
19447
|
|
|
19385
19448
|
// src/shields.ts
|
|
19386
|
-
import
|
|
19387
|
-
import
|
|
19388
|
-
import
|
|
19449
|
+
import fs8 from "fs";
|
|
19450
|
+
import path10 from "path";
|
|
19451
|
+
import os7 from "os";
|
|
19389
19452
|
function loadUserShields() {
|
|
19390
19453
|
const result = {};
|
|
19391
19454
|
let entries;
|
|
19392
19455
|
try {
|
|
19393
|
-
entries =
|
|
19456
|
+
entries = fs8.readdirSync(USER_SHIELDS_DIR).filter((f) => f.endsWith(".json"));
|
|
19394
19457
|
} catch (err) {
|
|
19395
19458
|
if (err.code !== "ENOENT") {
|
|
19396
19459
|
process.stderr.write(
|
|
@@ -19401,9 +19464,9 @@ function loadUserShields() {
|
|
|
19401
19464
|
return result;
|
|
19402
19465
|
}
|
|
19403
19466
|
for (const file2 of entries) {
|
|
19404
|
-
const filePath =
|
|
19467
|
+
const filePath = path10.join(USER_SHIELDS_DIR, file2);
|
|
19405
19468
|
try {
|
|
19406
|
-
const raw = JSON.parse(
|
|
19469
|
+
const raw = JSON.parse(fs8.readFileSync(filePath, "utf-8"));
|
|
19407
19470
|
const v = validateShieldDefinition(raw);
|
|
19408
19471
|
if ("error" in v) {
|
|
19409
19472
|
process.stderr.write(`[node9] ${v.error}: ${filePath}
|
|
@@ -19435,7 +19498,7 @@ function getShield(name) {
|
|
|
19435
19498
|
}
|
|
19436
19499
|
function readShieldsFile() {
|
|
19437
19500
|
try {
|
|
19438
|
-
const raw =
|
|
19501
|
+
const raw = fs8.readFileSync(SHIELDS_STATE_FILE, "utf-8");
|
|
19439
19502
|
if (!raw.trim()) return { active: [] };
|
|
19440
19503
|
const parsed = JSON.parse(raw);
|
|
19441
19504
|
const active = Array.isArray(parsed.active) ? parsed.active.filter(
|
|
@@ -19468,23 +19531,23 @@ var init_shields = __esm({
|
|
|
19468
19531
|
init_dist();
|
|
19469
19532
|
init_build();
|
|
19470
19533
|
init_dist();
|
|
19471
|
-
USER_SHIELDS_DIR =
|
|
19534
|
+
USER_SHIELDS_DIR = path10.join(os7.homedir(), ".node9", "shields");
|
|
19472
19535
|
SHIELDS = buildSHIELDS();
|
|
19473
|
-
SHIELDS_STATE_FILE =
|
|
19536
|
+
SHIELDS_STATE_FILE = path10.join(os7.homedir(), ".node9", "shields.json");
|
|
19474
19537
|
}
|
|
19475
19538
|
});
|
|
19476
19539
|
|
|
19477
19540
|
// src/shields/jail.ts
|
|
19478
|
-
import
|
|
19479
|
-
import
|
|
19480
|
-
import
|
|
19541
|
+
import fs9 from "fs";
|
|
19542
|
+
import os8 from "os";
|
|
19543
|
+
import path11 from "path";
|
|
19481
19544
|
function jailStorePath() {
|
|
19482
|
-
return
|
|
19545
|
+
return path11.join(os8.homedir(), ".node9", "jail-paths.json");
|
|
19483
19546
|
}
|
|
19484
19547
|
function readJailPaths() {
|
|
19485
19548
|
let text;
|
|
19486
19549
|
try {
|
|
19487
|
-
text =
|
|
19550
|
+
text = fs9.readFileSync(jailStorePath(), "utf8");
|
|
19488
19551
|
} catch (err) {
|
|
19489
19552
|
if (err.code === "ENOENT") return [];
|
|
19490
19553
|
throw err;
|
|
@@ -19522,11 +19585,11 @@ var init_jail = __esm({
|
|
|
19522
19585
|
});
|
|
19523
19586
|
|
|
19524
19587
|
// src/canary/registry.ts
|
|
19525
|
-
import
|
|
19526
|
-
import
|
|
19527
|
-
import
|
|
19588
|
+
import fs10 from "fs";
|
|
19589
|
+
import os9 from "os";
|
|
19590
|
+
import path12 from "path";
|
|
19528
19591
|
function canaryStorePath() {
|
|
19529
|
-
return
|
|
19592
|
+
return path12.join(os9.homedir(), ".node9", "canaries.json");
|
|
19530
19593
|
}
|
|
19531
19594
|
function isRecord(x) {
|
|
19532
19595
|
if (!x || typeof x !== "object") return false;
|
|
@@ -19537,7 +19600,7 @@ function loadCanaries(opts) {
|
|
|
19537
19600
|
const p = canaryStorePath();
|
|
19538
19601
|
let raw;
|
|
19539
19602
|
try {
|
|
19540
|
-
raw =
|
|
19603
|
+
raw = fs10.readFileSync(p, "utf-8");
|
|
19541
19604
|
} catch (e) {
|
|
19542
19605
|
if (e.code === "ENOENT") return [];
|
|
19543
19606
|
throw e;
|
|
@@ -19576,23 +19639,23 @@ var init_registry = __esm({
|
|
|
19576
19639
|
});
|
|
19577
19640
|
|
|
19578
19641
|
// src/utils/read-capped.ts
|
|
19579
|
-
import
|
|
19642
|
+
import fs11 from "fs";
|
|
19580
19643
|
function readCapped(file2, maxBytes, openFlags = "r") {
|
|
19581
19644
|
if (maxBytes < 0) return null;
|
|
19582
19645
|
let fd;
|
|
19583
19646
|
try {
|
|
19584
|
-
fd =
|
|
19647
|
+
fd = fs11.openSync(file2, openFlags);
|
|
19585
19648
|
} catch {
|
|
19586
19649
|
return null;
|
|
19587
19650
|
}
|
|
19588
19651
|
try {
|
|
19589
|
-
const st =
|
|
19652
|
+
const st = fs11.fstatSync(fd);
|
|
19590
19653
|
if (!st.isFile()) return null;
|
|
19591
19654
|
const want = Math.min(st.size, maxBytes + 1);
|
|
19592
19655
|
const buf = Buffer.alloc(want);
|
|
19593
19656
|
let read2 = 0;
|
|
19594
19657
|
while (read2 < want) {
|
|
19595
|
-
const n =
|
|
19658
|
+
const n = fs11.readSync(fd, buf, read2, want - read2, read2);
|
|
19596
19659
|
if (n <= 0) break;
|
|
19597
19660
|
read2 += n;
|
|
19598
19661
|
}
|
|
@@ -19604,7 +19667,7 @@ function readCapped(file2, maxBytes, openFlags = "r") {
|
|
|
19604
19667
|
return null;
|
|
19605
19668
|
} finally {
|
|
19606
19669
|
try {
|
|
19607
|
-
|
|
19670
|
+
fs11.closeSync(fd);
|
|
19608
19671
|
} catch {
|
|
19609
19672
|
}
|
|
19610
19673
|
}
|
|
@@ -19620,13 +19683,13 @@ var init_read_capped = __esm({
|
|
|
19620
19683
|
});
|
|
19621
19684
|
|
|
19622
19685
|
// src/posture/secrets.ts
|
|
19623
|
-
import
|
|
19624
|
-
import
|
|
19625
|
-
import
|
|
19686
|
+
import fs12 from "fs";
|
|
19687
|
+
import path13 from "path";
|
|
19688
|
+
import os10 from "os";
|
|
19626
19689
|
function displayPath(p, home) {
|
|
19627
19690
|
if (p === home) return "~";
|
|
19628
|
-
const prefix = home.endsWith(
|
|
19629
|
-
if (p.startsWith(prefix)) return "~" +
|
|
19691
|
+
const prefix = home.endsWith(path13.sep) ? home : home + path13.sep;
|
|
19692
|
+
if (p.startsWith(prefix)) return "~" + path13.sep + p.slice(prefix.length);
|
|
19630
19693
|
return p;
|
|
19631
19694
|
}
|
|
19632
19695
|
function safeRead(file2) {
|
|
@@ -19637,8 +19700,8 @@ function safeRead(file2) {
|
|
|
19637
19700
|
function candidateFiles(home, cwd) {
|
|
19638
19701
|
const files = /* @__PURE__ */ new Set();
|
|
19639
19702
|
try {
|
|
19640
|
-
for (const name of
|
|
19641
|
-
if (name === ".env" || name.startsWith(".env.")) files.add(
|
|
19703
|
+
for (const name of fs12.readdirSync(cwd)) {
|
|
19704
|
+
if (name === ".env" || name.startsWith(".env.")) files.add(path13.join(cwd, name));
|
|
19642
19705
|
}
|
|
19643
19706
|
} catch {
|
|
19644
19707
|
}
|
|
@@ -19646,7 +19709,7 @@ function candidateFiles(home, cwd) {
|
|
|
19646
19709
|
if (spec.hookFile) files.add(spec.hookFile(home));
|
|
19647
19710
|
if (spec.mcpFile) files.add(spec.mcpFile(home));
|
|
19648
19711
|
}
|
|
19649
|
-
files.add(
|
|
19712
|
+
files.add(path13.join(home, ".env"));
|
|
19650
19713
|
return [...files];
|
|
19651
19714
|
}
|
|
19652
19715
|
function credentialMaterial(home) {
|
|
@@ -19660,7 +19723,7 @@ function plantedDecoyPaths() {
|
|
|
19660
19723
|
}
|
|
19661
19724
|
}
|
|
19662
19725
|
function checkSecrets(ctx) {
|
|
19663
|
-
const home = ctx.home ||
|
|
19726
|
+
const home = ctx.home || os10.homedir();
|
|
19664
19727
|
const findings = [];
|
|
19665
19728
|
const planted = plantedDecoyPaths();
|
|
19666
19729
|
const plaintext = [];
|
|
@@ -19696,7 +19759,7 @@ function checkSecrets(ctx) {
|
|
|
19696
19759
|
for (const file2 of credentialMaterial(home)) {
|
|
19697
19760
|
if (planted.has(file2)) continue;
|
|
19698
19761
|
try {
|
|
19699
|
-
if (
|
|
19762
|
+
if (fs12.statSync(file2).isFile()) {
|
|
19700
19763
|
creds.push(displayPath(file2, home));
|
|
19701
19764
|
credPaths.push(file2);
|
|
19702
19765
|
}
|
|
@@ -19717,6 +19780,25 @@ function checkSecrets(ctx) {
|
|
|
19717
19780
|
coverageProbe: { kind: "fileRead", paths: credPaths }
|
|
19718
19781
|
});
|
|
19719
19782
|
}
|
|
19783
|
+
const envSecrets = [];
|
|
19784
|
+
for (const [key, value] of Object.entries(ctx.env ?? process.env)) {
|
|
19785
|
+
if (!value) continue;
|
|
19786
|
+
const match = scanArgs({ [key]: value });
|
|
19787
|
+
if (match) envSecrets.push(`${key} (${match.patternName})`);
|
|
19788
|
+
}
|
|
19789
|
+
if (envSecrets.length > 0) {
|
|
19790
|
+
findings.push({
|
|
19791
|
+
category: "Secrets",
|
|
19792
|
+
severity: "high",
|
|
19793
|
+
title: `${envSecrets.length} secret environment variable${envSecrets.length === 1 ? "" : "s"} visible to the agent`,
|
|
19794
|
+
what: "A key or token is exported in the shell the agent runs from.",
|
|
19795
|
+
why: "Every program the agent starts inherits the environment, so the value is one `env` away.",
|
|
19796
|
+
who: "A tricked agent can read it and send it anywhere a command can reach.",
|
|
19797
|
+
detail: envSecrets,
|
|
19798
|
+
fix: "Unset it in the shell that starts the agent, or pass it to the one command that needs it (`env KEY=... cmd`) or a secrets manager.",
|
|
19799
|
+
owner: "os"
|
|
19800
|
+
});
|
|
19801
|
+
}
|
|
19720
19802
|
if (planted.size > 0) {
|
|
19721
19803
|
findings.push({
|
|
19722
19804
|
category: "Secrets",
|
|
@@ -19739,6 +19821,7 @@ var init_secrets = __esm({
|
|
|
19739
19821
|
init_registry();
|
|
19740
19822
|
init_read_capped();
|
|
19741
19823
|
init_credential_files();
|
|
19824
|
+
init_dlp();
|
|
19742
19825
|
MAX_FILE_BYTES = 256 * 1024;
|
|
19743
19826
|
}
|
|
19744
19827
|
});
|
|
@@ -20149,15 +20232,15 @@ var init_managed = __esm({
|
|
|
20149
20232
|
});
|
|
20150
20233
|
|
|
20151
20234
|
// src/auth/trusted-hosts.ts
|
|
20152
|
-
import
|
|
20153
|
-
import
|
|
20154
|
-
import
|
|
20235
|
+
import fs13 from "fs";
|
|
20236
|
+
import path14 from "path";
|
|
20237
|
+
import os11 from "os";
|
|
20155
20238
|
function getTrustedHostsPath() {
|
|
20156
|
-
return
|
|
20239
|
+
return path14.join(os11.homedir(), ".node9", "trusted-hosts.json");
|
|
20157
20240
|
}
|
|
20158
20241
|
function readTrustedHosts() {
|
|
20159
20242
|
try {
|
|
20160
|
-
const raw =
|
|
20243
|
+
const raw = fs13.readFileSync(getTrustedHostsPath(), "utf8");
|
|
20161
20244
|
const parsed = JSON.parse(raw);
|
|
20162
20245
|
return Array.isArray(parsed.hosts) ? parsed.hosts : [];
|
|
20163
20246
|
} catch {
|
|
@@ -20166,7 +20249,7 @@ function readTrustedHosts() {
|
|
|
20166
20249
|
}
|
|
20167
20250
|
function getFileMtime() {
|
|
20168
20251
|
try {
|
|
20169
|
-
return
|
|
20252
|
+
return fs13.statSync(getTrustedHostsPath()).mtimeMs;
|
|
20170
20253
|
} catch {
|
|
20171
20254
|
return 0;
|
|
20172
20255
|
}
|
|
@@ -20385,9 +20468,9 @@ var init_api_url = __esm({
|
|
|
20385
20468
|
});
|
|
20386
20469
|
|
|
20387
20470
|
// src/config/index.ts
|
|
20388
|
-
import
|
|
20389
|
-
import
|
|
20390
|
-
import
|
|
20471
|
+
import fs14 from "fs";
|
|
20472
|
+
import path15 from "path";
|
|
20473
|
+
import os12 from "os";
|
|
20391
20474
|
function sanitizeSsrfAllow(entries, source) {
|
|
20392
20475
|
const kept = [];
|
|
20393
20476
|
for (const entry of entries) {
|
|
@@ -20409,8 +20492,8 @@ function noteRejectedApiUrl(raw) {
|
|
|
20409
20492
|
if (rejectedApiUrlsSeen.has(key)) return;
|
|
20410
20493
|
rejectedApiUrlsSeen.add(key);
|
|
20411
20494
|
try {
|
|
20412
|
-
|
|
20413
|
-
|
|
20495
|
+
fs14.appendFileSync(
|
|
20496
|
+
path15.join(os12.homedir(), ".node9", "hook-debug.log"),
|
|
20414
20497
|
`[${(/* @__PURE__ */ new Date()).toISOString()}] REFUSED apiUrl, using the default instead: ${String(raw).slice(0, 200).replace(/[\r\n]+/g, " ")}
|
|
20415
20498
|
`
|
|
20416
20499
|
);
|
|
@@ -20427,9 +20510,9 @@ function getCredentials() {
|
|
|
20427
20510
|
};
|
|
20428
20511
|
}
|
|
20429
20512
|
try {
|
|
20430
|
-
const credPath =
|
|
20431
|
-
if (
|
|
20432
|
-
const creds = JSON.parse(
|
|
20513
|
+
const credPath = path15.join(os12.homedir(), ".node9", "credentials.json");
|
|
20514
|
+
if (fs14.existsSync(credPath)) {
|
|
20515
|
+
const creds = JSON.parse(fs14.readFileSync(credPath, "utf-8"));
|
|
20433
20516
|
const profileName = process.env.NODE9_PROFILE || "default";
|
|
20434
20517
|
const profile = creds[profileName];
|
|
20435
20518
|
if (typeof profile?.apiKey === "string" && profile.apiKey.length > 0) {
|
|
@@ -20461,7 +20544,7 @@ function readRulesCacheResilient(cacheFile) {
|
|
|
20461
20544
|
for (let attempt = 0; attempt < 3; attempt++) {
|
|
20462
20545
|
let content;
|
|
20463
20546
|
try {
|
|
20464
|
-
content =
|
|
20547
|
+
content = fs14.readFileSync(cacheFile, "utf-8");
|
|
20465
20548
|
existed = true;
|
|
20466
20549
|
} catch (err) {
|
|
20467
20550
|
if (err.code === "ENOENT") return {};
|
|
@@ -20476,10 +20559,10 @@ function readRulesCacheResilient(cacheFile) {
|
|
|
20476
20559
|
}
|
|
20477
20560
|
}
|
|
20478
20561
|
if (existed || sawReadError) {
|
|
20479
|
-
const backup =
|
|
20562
|
+
const backup = path15.join(path15.dirname(cacheFile), "rules-cache.last-good.json");
|
|
20480
20563
|
if (backup !== cacheFile) {
|
|
20481
20564
|
try {
|
|
20482
|
-
const raw = JSON.parse(
|
|
20565
|
+
const raw = JSON.parse(fs14.readFileSync(backup, "utf-8"));
|
|
20483
20566
|
logCacheReadIssue(cacheFile, "RULES_CACHE_CORRUPT_USED_BACKUP");
|
|
20484
20567
|
lastParsedRulesCache = raw;
|
|
20485
20568
|
return raw;
|
|
@@ -20499,8 +20582,8 @@ function logCacheReadIssue(cacheFile, kind) {
|
|
|
20499
20582
|
if (now - cacheReadLastLoggedAt < CACHE_LOG_REARM_MS) return;
|
|
20500
20583
|
cacheReadLastLoggedAt = now;
|
|
20501
20584
|
try {
|
|
20502
|
-
|
|
20503
|
-
|
|
20585
|
+
fs14.appendFileSync(
|
|
20586
|
+
path15.join(os12.homedir(), ".node9", "hook-debug.log"),
|
|
20504
20587
|
`[${(/* @__PURE__ */ new Date()).toISOString()}] ${kind} ${cacheFile}
|
|
20505
20588
|
`
|
|
20506
20589
|
);
|
|
@@ -20517,8 +20600,8 @@ function catalogSettingsFrom(settings, policy) {
|
|
|
20517
20600
|
}
|
|
20518
20601
|
function getConfig(cwd) {
|
|
20519
20602
|
if (!cwd && cachedConfig) return cachedConfig;
|
|
20520
|
-
const globalPath =
|
|
20521
|
-
const projectPath =
|
|
20603
|
+
const globalPath = path15.join(os12.homedir(), ".node9", "config.json");
|
|
20604
|
+
const projectPath = path15.join(cwd ?? process.cwd(), "node9.config.json");
|
|
20522
20605
|
const globalConfig = tryLoadConfig(globalPath);
|
|
20523
20606
|
const projectConfig = tryLoadConfig(projectPath, { project: true });
|
|
20524
20607
|
const mergedSettings = {
|
|
@@ -20763,7 +20846,7 @@ function getConfig(cwd) {
|
|
|
20763
20846
|
let cloudMandatesEnforcement = false;
|
|
20764
20847
|
let cloudMandatesAppPerm = false;
|
|
20765
20848
|
{
|
|
20766
|
-
const cacheFile =
|
|
20849
|
+
const cacheFile = path15.join(os12.homedir(), ".node9", "rules-cache.json");
|
|
20767
20850
|
try {
|
|
20768
20851
|
const raw = readRulesCacheResilient(cacheFile);
|
|
20769
20852
|
if (Array.isArray(raw.rules) && raw.rules.length > 0) {
|
|
@@ -21195,10 +21278,10 @@ function getConfig(cwd) {
|
|
|
21195
21278
|
}
|
|
21196
21279
|
function tryLoadConfig(filePath, options = {}) {
|
|
21197
21280
|
v2ChecksByPath.delete(filePath);
|
|
21198
|
-
if (!
|
|
21281
|
+
if (!fs14.existsSync(filePath)) return null;
|
|
21199
21282
|
let raw;
|
|
21200
21283
|
try {
|
|
21201
|
-
raw = JSON.parse(
|
|
21284
|
+
raw = JSON.parse(fs14.readFileSync(filePath, "utf-8"));
|
|
21202
21285
|
} catch (err) {
|
|
21203
21286
|
const msg = err instanceof Error ? err.message : String(err);
|
|
21204
21287
|
process.stderr.write(
|
|
@@ -21661,10 +21744,10 @@ var init_templates = __esm({
|
|
|
21661
21744
|
});
|
|
21662
21745
|
|
|
21663
21746
|
// src/posture/egress.ts
|
|
21664
|
-
import
|
|
21747
|
+
import fs15 from "fs";
|
|
21665
21748
|
function sandboxEgressWallActive() {
|
|
21666
21749
|
try {
|
|
21667
|
-
return
|
|
21750
|
+
return fs15.existsSync(ALLOWED_DOMAINS_PATH);
|
|
21668
21751
|
} catch {
|
|
21669
21752
|
return false;
|
|
21670
21753
|
}
|
|
@@ -22011,29 +22094,29 @@ var init_gate_order = __esm({
|
|
|
22011
22094
|
});
|
|
22012
22095
|
|
|
22013
22096
|
// src/policy/file-access.ts
|
|
22014
|
-
import
|
|
22015
|
-
import
|
|
22016
|
-
import
|
|
22097
|
+
import fs16 from "fs";
|
|
22098
|
+
import os13 from "os";
|
|
22099
|
+
import path16 from "path";
|
|
22017
22100
|
function absoluteSpelling(p, cwd, home) {
|
|
22018
22101
|
if (p === "~" || p.startsWith("~/")) p = home + p.slice(1);
|
|
22019
|
-
return
|
|
22102
|
+
return path16.isAbsolute(p) ? p : `${cwd}/${p}`;
|
|
22020
22103
|
}
|
|
22021
22104
|
function physical(p, followFinal, links = 0) {
|
|
22022
22105
|
if (links > 40) throw new Error("Too many symbolic links");
|
|
22023
22106
|
if (!followFinal) {
|
|
22024
|
-
const parent = physical(
|
|
22025
|
-
return { path: `${parent.path}/${
|
|
22107
|
+
const parent = physical(path16.dirname(p), true, links);
|
|
22108
|
+
return { path: `${parent.path}/${path16.basename(p)}`, missing: parent.missing };
|
|
22026
22109
|
}
|
|
22027
22110
|
try {
|
|
22028
|
-
return { path:
|
|
22111
|
+
return { path: fs16.realpathSync.native(p), missing: false };
|
|
22029
22112
|
} catch (error) {
|
|
22030
22113
|
const code = error.code;
|
|
22031
22114
|
if (!["ENOENT", "ENOTDIR"].includes(code ?? "")) throw error;
|
|
22032
22115
|
try {
|
|
22033
|
-
if (
|
|
22034
|
-
const target =
|
|
22116
|
+
if (fs16.lstatSync(p).isSymbolicLink()) {
|
|
22117
|
+
const target = fs16.readlinkSync(p);
|
|
22035
22118
|
return physical(
|
|
22036
|
-
|
|
22119
|
+
path16.isAbsolute(target) ? target : `${path16.dirname(p)}/${target}`,
|
|
22037
22120
|
true,
|
|
22038
22121
|
links + 1
|
|
22039
22122
|
);
|
|
@@ -22042,13 +22125,13 @@ function physical(p, followFinal, links = 0) {
|
|
|
22042
22125
|
if (!["ENOENT", "ENOTDIR"].includes(linkError.code ?? ""))
|
|
22043
22126
|
throw linkError;
|
|
22044
22127
|
}
|
|
22045
|
-
const parent =
|
|
22128
|
+
const parent = path16.dirname(p);
|
|
22046
22129
|
if (parent === p) return { path: p, missing: true };
|
|
22047
22130
|
const resolved = physical(parent, true, links);
|
|
22048
|
-
return { path: `${resolved.path}/${
|
|
22131
|
+
return { path: `${resolved.path}/${path16.basename(p)}`, missing: true };
|
|
22049
22132
|
}
|
|
22050
22133
|
}
|
|
22051
|
-
function prepareFileAccesses(tool, args, cwd = process.cwd(), inspection = {}, home =
|
|
22134
|
+
function prepareFileAccesses(tool, args, cwd = process.cwd(), inspection = {}, home = os13.homedir()) {
|
|
22052
22135
|
const context = { cwd, homeDirs: [home, "/root"] };
|
|
22053
22136
|
const accesses = collectFileAccesses(tool, args, context, inspection);
|
|
22054
22137
|
const handledPaths = /* @__PURE__ */ new Set();
|
|
@@ -22071,14 +22154,14 @@ function prepareFileAccesses(tool, args, cwd = process.cwd(), inspection = {}, h
|
|
|
22071
22154
|
const raw = absoluteSpelling(access.path, access.cwd ?? cwd, home);
|
|
22072
22155
|
if (access.directoryTarget) {
|
|
22073
22156
|
try {
|
|
22074
|
-
if (!
|
|
22157
|
+
if (!fs16.statSync(absoluteSpelling(access.directoryTarget, access.cwd ?? cwd, home)).isDirectory())
|
|
22075
22158
|
continue;
|
|
22076
22159
|
} catch {
|
|
22077
22160
|
}
|
|
22078
22161
|
}
|
|
22079
22162
|
if (access.operation === "create") {
|
|
22080
22163
|
try {
|
|
22081
|
-
|
|
22164
|
+
fs16.statSync(raw);
|
|
22082
22165
|
continue;
|
|
22083
22166
|
} catch {
|
|
22084
22167
|
}
|
|
@@ -22133,15 +22216,15 @@ var init_file_access = __esm({
|
|
|
22133
22216
|
});
|
|
22134
22217
|
|
|
22135
22218
|
// src/supply-chain/osv-index.ts
|
|
22136
|
-
import
|
|
22137
|
-
import
|
|
22138
|
-
import
|
|
22219
|
+
import fs17 from "fs";
|
|
22220
|
+
import os14 from "os";
|
|
22221
|
+
import path17 from "path";
|
|
22139
22222
|
import { createHash as createHash4 } from "crypto";
|
|
22140
22223
|
function osvRoot() {
|
|
22141
|
-
return
|
|
22224
|
+
return path17.join(os14.homedir(), ".node9", "osv");
|
|
22142
22225
|
}
|
|
22143
22226
|
function ecoDir(eco) {
|
|
22144
|
-
return
|
|
22227
|
+
return path17.join(osvRoot(), eco);
|
|
22145
22228
|
}
|
|
22146
22229
|
function indexKey(eco, name) {
|
|
22147
22230
|
switch (eco) {
|
|
@@ -22160,11 +22243,11 @@ function shardOf(key) {
|
|
|
22160
22243
|
return createHash4("sha1").update(key).digest("hex").slice(0, 2);
|
|
22161
22244
|
}
|
|
22162
22245
|
function shardPath(eco, shard) {
|
|
22163
|
-
return
|
|
22246
|
+
return path17.join(ecoDir(eco), "shards", `${shard}.json`);
|
|
22164
22247
|
}
|
|
22165
22248
|
function readMeta(eco) {
|
|
22166
22249
|
try {
|
|
22167
|
-
const m = JSON.parse(
|
|
22250
|
+
const m = JSON.parse(fs17.readFileSync(path17.join(ecoDir(eco), "meta.json"), "utf8"));
|
|
22168
22251
|
if (typeof m?.syncedAt !== "string" || typeof m?.lastModifiedMs !== "number") return null;
|
|
22169
22252
|
return m;
|
|
22170
22253
|
} catch {
|
|
@@ -22173,7 +22256,7 @@ function readMeta(eco) {
|
|
|
22173
22256
|
}
|
|
22174
22257
|
function readShard(eco, shard) {
|
|
22175
22258
|
try {
|
|
22176
|
-
const s = JSON.parse(
|
|
22259
|
+
const s = JSON.parse(fs17.readFileSync(shardPath(eco, shard), "utf8"));
|
|
22177
22260
|
return s && typeof s === "object" && !Array.isArray(s) ? s : {};
|
|
22178
22261
|
} catch {
|
|
22179
22262
|
return {};
|
|
@@ -22555,17 +22638,17 @@ var init_registry2 = __esm({
|
|
|
22555
22638
|
});
|
|
22556
22639
|
|
|
22557
22640
|
// src/supply-chain/local-resolve.ts
|
|
22558
|
-
import
|
|
22559
|
-
import
|
|
22641
|
+
import fs18 from "fs";
|
|
22642
|
+
import path18 from "path";
|
|
22560
22643
|
function resolveInstalled(name, cwd) {
|
|
22561
|
-
if (!cwd || !
|
|
22644
|
+
if (!cwd || !path18.isAbsolute(cwd)) return null;
|
|
22562
22645
|
if (!/^(?:@[^/\\]+\/)?[^/\\]+$/.test(name)) return null;
|
|
22563
|
-
let dir2 =
|
|
22646
|
+
let dir2 = path18.resolve(cwd);
|
|
22564
22647
|
for (let i = 0; i < MAX_LEVELS; i++) {
|
|
22565
|
-
const pkgDir =
|
|
22566
|
-
const manifest =
|
|
22648
|
+
const pkgDir = path18.join(dir2, "node_modules", ...name.split("/"));
|
|
22649
|
+
const manifest = path18.join(pkgDir, "package.json");
|
|
22567
22650
|
try {
|
|
22568
|
-
const raw =
|
|
22651
|
+
const raw = fs18.readFileSync(manifest, "utf8");
|
|
22569
22652
|
const parsed = JSON.parse(raw);
|
|
22570
22653
|
if (typeof parsed.version === "string" && parsed.version.length > 0) {
|
|
22571
22654
|
return { version: parsed.version, dir: pkgDir };
|
|
@@ -22573,7 +22656,7 @@ function resolveInstalled(name, cwd) {
|
|
|
22573
22656
|
return null;
|
|
22574
22657
|
} catch {
|
|
22575
22658
|
}
|
|
22576
|
-
const parent =
|
|
22659
|
+
const parent = path18.dirname(dir2);
|
|
22577
22660
|
if (parent === dir2) break;
|
|
22578
22661
|
dir2 = parent;
|
|
22579
22662
|
}
|
|
@@ -22590,7 +22673,7 @@ var init_local_resolve = __esm({
|
|
|
22590
22673
|
|
|
22591
22674
|
// src/supply-chain/check.ts
|
|
22592
22675
|
import picomatch2 from "picomatch";
|
|
22593
|
-
import
|
|
22676
|
+
import path19 from "path";
|
|
22594
22677
|
function commandOf(toolName, args) {
|
|
22595
22678
|
if (!isBashTool(toolName) || !args || typeof args !== "object") return null;
|
|
22596
22679
|
const a = args;
|
|
@@ -22692,7 +22775,7 @@ function checkInstalled(p, version, misses) {
|
|
|
22692
22775
|
}
|
|
22693
22776
|
async function checkOne(p, cfg, misses, networkCapped, cwd) {
|
|
22694
22777
|
if (p.ecosystem === "npm" && LOCAL_FIRST_MANAGERS.has(p.manager) && p.localCwd !== null && (p.raw === p.name || p.version)) {
|
|
22695
|
-
const localCwd = cwd &&
|
|
22778
|
+
const localCwd = cwd && path19.isAbsolute(cwd) ? (p.localCwd ?? []).reduce((dir2, step) => path19.resolve(dir2, step), cwd) : void 0;
|
|
22696
22779
|
const installed = resolveInstalled(p.name, localCwd);
|
|
22697
22780
|
if (installed && (!p.version || p.version === installed.version))
|
|
22698
22781
|
return checkInstalled(p, installed.version, misses);
|
|
@@ -22861,33 +22944,33 @@ var init_check = __esm({
|
|
|
22861
22944
|
});
|
|
22862
22945
|
|
|
22863
22946
|
// src/supply-chain/registries.ts
|
|
22864
|
-
import
|
|
22865
|
-
import
|
|
22866
|
-
import
|
|
22947
|
+
import os15 from "os";
|
|
22948
|
+
import path20 from "path";
|
|
22949
|
+
import fs19 from "fs";
|
|
22867
22950
|
function userFiles(home, env) {
|
|
22868
|
-
const xdg = env.XDG_CONFIG_HOME ||
|
|
22951
|
+
const xdg = env.XDG_CONFIG_HOME || path20.join(home, ".config");
|
|
22869
22952
|
return [
|
|
22870
|
-
env.NPM_CONFIG_USERCONFIG || env.npm_config_userconfig ||
|
|
22871
|
-
|
|
22872
|
-
|
|
22873
|
-
|
|
22953
|
+
env.NPM_CONFIG_USERCONFIG || env.npm_config_userconfig || path20.join(home, ".npmrc"),
|
|
22954
|
+
path20.join(home, ".yarnrc"),
|
|
22955
|
+
path20.join(home, ".yarnrc.yml"),
|
|
22956
|
+
path20.join(home, ".bunfig.toml"),
|
|
22874
22957
|
env.PIP_CONFIG_FILE || "",
|
|
22875
|
-
|
|
22876
|
-
|
|
22958
|
+
path20.join(xdg, "pip", "pip.conf"),
|
|
22959
|
+
path20.join(home, ".pip", "pip.conf"),
|
|
22877
22960
|
"/etc/pip.conf",
|
|
22878
|
-
env.VIRTUAL_ENV ?
|
|
22961
|
+
env.VIRTUAL_ENV ? path20.join(env.VIRTUAL_ENV, "pip.conf") : "",
|
|
22879
22962
|
env.UV_CONFIG_FILE || "",
|
|
22880
|
-
|
|
22963
|
+
path20.join(xdg, "uv", "uv.toml"),
|
|
22881
22964
|
"/etc/uv/uv.toml"
|
|
22882
22965
|
].filter(Boolean);
|
|
22883
22966
|
}
|
|
22884
22967
|
function projectFiles(cwd) {
|
|
22885
|
-
if (!cwd || !
|
|
22968
|
+
if (!cwd || !path20.isAbsolute(cwd)) return [];
|
|
22886
22969
|
const out = [];
|
|
22887
22970
|
let dir2 = cwd;
|
|
22888
22971
|
for (let i = 0; i < MAX_PARENT_DIRS; i++) {
|
|
22889
|
-
for (const f of PROJECT_FILES) out.push(
|
|
22890
|
-
const parent =
|
|
22972
|
+
for (const f of PROJECT_FILES) out.push(path20.join(dir2, f));
|
|
22973
|
+
const parent = path20.dirname(dir2);
|
|
22891
22974
|
if (parent === dir2) break;
|
|
22892
22975
|
dir2 = parent;
|
|
22893
22976
|
}
|
|
@@ -22926,7 +23009,7 @@ function hostsInText(text, out) {
|
|
|
22926
23009
|
}
|
|
22927
23010
|
}
|
|
22928
23011
|
}
|
|
22929
|
-
function configuredRegistryHosts(cwd, env = process.env, home =
|
|
23012
|
+
function configuredRegistryHosts(cwd, env = process.env, home = os15.homedir()) {
|
|
22930
23013
|
const key = [
|
|
22931
23014
|
cwd ?? "",
|
|
22932
23015
|
home,
|
|
@@ -22948,7 +23031,7 @@ function configuredRegistryHosts(cwd, env = process.env, home = os16.homedir())
|
|
|
22948
23031
|
const r = readCappedText(
|
|
22949
23032
|
file2,
|
|
22950
23033
|
MAX_CONFIG_BYTES,
|
|
22951
|
-
|
|
23034
|
+
fs19.constants.O_RDONLY | (fs19.constants.O_NONBLOCK ?? 0)
|
|
22952
23035
|
);
|
|
22953
23036
|
if (r) hostsInText(r.text, hosts);
|
|
22954
23037
|
}
|
|
@@ -23005,17 +23088,17 @@ var init_registries = __esm({
|
|
|
23005
23088
|
});
|
|
23006
23089
|
|
|
23007
23090
|
// src/utils/provenance.ts
|
|
23008
|
-
import
|
|
23009
|
-
import
|
|
23010
|
-
import
|
|
23091
|
+
import fs20 from "fs";
|
|
23092
|
+
import path21 from "path";
|
|
23093
|
+
import os16 from "os";
|
|
23011
23094
|
function findInPath(cmd) {
|
|
23012
|
-
if (
|
|
23095
|
+
if (path21.posix.isAbsolute(cmd)) return cmd;
|
|
23013
23096
|
const pathEnv = process.env.PATH ?? "";
|
|
23014
|
-
for (const dir2 of pathEnv.split(
|
|
23097
|
+
for (const dir2 of pathEnv.split(path21.delimiter)) {
|
|
23015
23098
|
if (!dir2) continue;
|
|
23016
|
-
const full =
|
|
23099
|
+
const full = path21.join(dir2, cmd);
|
|
23017
23100
|
try {
|
|
23018
|
-
|
|
23101
|
+
fs20.accessSync(full, fs20.constants.X_OK);
|
|
23019
23102
|
return full;
|
|
23020
23103
|
} catch {
|
|
23021
23104
|
}
|
|
@@ -23026,7 +23109,7 @@ function _classifyPath(resolved, cwd) {
|
|
|
23026
23109
|
if (cwd && resolved.startsWith(cwd + "/")) {
|
|
23027
23110
|
return { trustLevel: "user", reason: "binary in project directory" };
|
|
23028
23111
|
}
|
|
23029
|
-
const osTmp =
|
|
23112
|
+
const osTmp = os16.tmpdir();
|
|
23030
23113
|
const allSuspect = osTmp ? [...SUSPECT_PREFIXES, osTmp] : SUSPECT_PREFIXES;
|
|
23031
23114
|
if (allSuspect.some((p) => resolved === p || resolved.startsWith(p + "/"))) {
|
|
23032
23115
|
return { trustLevel: "suspect", reason: `binary in temp directory: ${resolved}` };
|
|
@@ -23044,7 +23127,7 @@ function _classifyPath(resolved, cwd) {
|
|
|
23044
23127
|
}
|
|
23045
23128
|
function checkProvenance(cmd, cwd) {
|
|
23046
23129
|
const bare = cmd.startsWith("./") ? cmd.slice(2) : cmd;
|
|
23047
|
-
if (
|
|
23130
|
+
if (path21.posix.isAbsolute(bare)) {
|
|
23048
23131
|
const early = _classifyPath(bare, cwd);
|
|
23049
23132
|
if (early.trustLevel === "suspect") {
|
|
23050
23133
|
return { resolvedPath: bare, ...early };
|
|
@@ -23060,7 +23143,7 @@ function checkProvenance(cmd, cwd) {
|
|
|
23060
23143
|
reason: "binary not found in PATH"
|
|
23061
23144
|
};
|
|
23062
23145
|
}
|
|
23063
|
-
resolved =
|
|
23146
|
+
resolved = fs20.realpathSync(found);
|
|
23064
23147
|
} catch {
|
|
23065
23148
|
return {
|
|
23066
23149
|
resolvedPath: cmd,
|
|
@@ -23069,7 +23152,7 @@ function checkProvenance(cmd, cwd) {
|
|
|
23069
23152
|
};
|
|
23070
23153
|
}
|
|
23071
23154
|
try {
|
|
23072
|
-
const stat =
|
|
23155
|
+
const stat = fs20.statSync(resolved);
|
|
23073
23156
|
if (stat.mode & 2) {
|
|
23074
23157
|
return {
|
|
23075
23158
|
resolvedPath: resolved,
|
|
@@ -23094,19 +23177,19 @@ var init_provenance2 = __esm({
|
|
|
23094
23177
|
SYSTEM_PREFIXES = ["/usr/bin", "/usr/sbin", "/bin", "/sbin"];
|
|
23095
23178
|
MANAGED_PREFIXES = ["/usr/local/bin", "/opt/homebrew", "/home/linuxbrew", "/nix/store"];
|
|
23096
23179
|
USER_PREFIXES = [
|
|
23097
|
-
|
|
23098
|
-
|
|
23099
|
-
|
|
23100
|
-
|
|
23101
|
-
|
|
23180
|
+
path21.join(os16.homedir(), "bin"),
|
|
23181
|
+
path21.join(os16.homedir(), ".local", "bin"),
|
|
23182
|
+
path21.join(os16.homedir(), ".cargo", "bin"),
|
|
23183
|
+
path21.join(os16.homedir(), ".npm-global", "bin"),
|
|
23184
|
+
path21.join(os16.homedir(), ".volta", "bin")
|
|
23102
23185
|
];
|
|
23103
23186
|
SUSPECT_PREFIXES = ["/tmp", "/var/tmp", "/dev/shm"];
|
|
23104
23187
|
}
|
|
23105
23188
|
});
|
|
23106
23189
|
|
|
23107
23190
|
// src/policy/index.ts
|
|
23108
|
-
import
|
|
23109
|
-
import
|
|
23191
|
+
import fs21 from "fs";
|
|
23192
|
+
import path22 from "path";
|
|
23110
23193
|
async function evaluatePolicy2(toolName, args, agent, cwd, opts) {
|
|
23111
23194
|
const config = opts?.config ?? getConfig(cwd);
|
|
23112
23195
|
const prepared = opts?.fileAccess ?? prepareFileAccesses(toolName, args, cwd, inspectionLayers(config.policy));
|
|
@@ -23138,9 +23221,9 @@ async function evaluatePolicy2(toolName, args, agent, cwd, opts) {
|
|
|
23138
23221
|
);
|
|
23139
23222
|
}
|
|
23140
23223
|
function isGitWorkTreeTop(dir2, cwd) {
|
|
23141
|
-
if (!
|
|
23224
|
+
if (!path22.isAbsolute(cwd)) return true;
|
|
23142
23225
|
try {
|
|
23143
|
-
return
|
|
23226
|
+
return fs21.existsSync(path22.join(path22.resolve(cwd, dir2), ".git"));
|
|
23144
23227
|
} catch {
|
|
23145
23228
|
return true;
|
|
23146
23229
|
}
|
|
@@ -23149,7 +23232,7 @@ function readAgentSettings(absPath) {
|
|
|
23149
23232
|
const r = readCappedText(
|
|
23150
23233
|
absPath,
|
|
23151
23234
|
MAX_AGENT_SETTINGS_BYTES,
|
|
23152
|
-
|
|
23235
|
+
fs21.constants.O_RDONLY | (fs21.constants.O_NONBLOCK ?? 0)
|
|
23153
23236
|
);
|
|
23154
23237
|
return r && !r.truncated ? r.text : null;
|
|
23155
23238
|
}
|
|
@@ -23218,14 +23301,14 @@ var init_gate = __esm({
|
|
|
23218
23301
|
});
|
|
23219
23302
|
|
|
23220
23303
|
// src/posture/supply-chain.ts
|
|
23221
|
-
import
|
|
23222
|
-
import
|
|
23304
|
+
import os17 from "os";
|
|
23305
|
+
import path23 from "path";
|
|
23223
23306
|
import { parse as parseToml6 } from "smol-toml";
|
|
23224
23307
|
function isNode9Managed(command, args = []) {
|
|
23225
23308
|
if (!command) return false;
|
|
23226
|
-
if (
|
|
23227
|
-
if (PACKAGE_RUNNERS.has(
|
|
23228
|
-
return args.some((a) => a === "node9" ||
|
|
23309
|
+
if (path23.basename(command).toLowerCase() === "node9") return true;
|
|
23310
|
+
if (PACKAGE_RUNNERS.has(path23.basename(command).toLowerCase())) {
|
|
23311
|
+
return args.some((a) => a === "node9" || path23.basename(a).toLowerCase() === "node9");
|
|
23229
23312
|
}
|
|
23230
23313
|
return false;
|
|
23231
23314
|
}
|
|
@@ -23247,10 +23330,10 @@ function readServers(file2, format, agent) {
|
|
|
23247
23330
|
}
|
|
23248
23331
|
}
|
|
23249
23332
|
function checkSupplyChain(ctx) {
|
|
23250
|
-
const home = ctx.home ||
|
|
23333
|
+
const home = ctx.home || os17.homedir();
|
|
23251
23334
|
const servers = [];
|
|
23252
23335
|
for (const spec of AGENT_SPECS) {
|
|
23253
|
-
if (!spec.mcpFile) continue;
|
|
23336
|
+
if (!spec.mcpFile || spec.mcpInventory === false) continue;
|
|
23254
23337
|
servers.push(...readServers(spec.mcpFile(home), spec.mcpFormat ?? "json", spec.label));
|
|
23255
23338
|
}
|
|
23256
23339
|
if (servers.length === 0) return [];
|
|
@@ -23346,11 +23429,11 @@ var init_privilege = __esm({
|
|
|
23346
23429
|
});
|
|
23347
23430
|
|
|
23348
23431
|
// src/posture/containment.ts
|
|
23349
|
-
import
|
|
23432
|
+
import fs22 from "fs";
|
|
23350
23433
|
function inContainer() {
|
|
23351
|
-
if (
|
|
23434
|
+
if (fs22.existsSync("/.dockerenv") || fs22.existsSync("/run/.containerenv")) return true;
|
|
23352
23435
|
try {
|
|
23353
|
-
const cgroup =
|
|
23436
|
+
const cgroup = fs22.readFileSync("/proc/1/cgroup", "utf8");
|
|
23354
23437
|
if (/docker|kubepods|containerd|lxc|libpod/.test(cgroup)) return true;
|
|
23355
23438
|
} catch {
|
|
23356
23439
|
}
|
|
@@ -23396,7 +23479,7 @@ var init_containment = __esm({
|
|
|
23396
23479
|
});
|
|
23397
23480
|
|
|
23398
23481
|
// src/posture/inbound.ts
|
|
23399
|
-
import
|
|
23482
|
+
import fs23 from "fs";
|
|
23400
23483
|
function buildNetworkFix(labels) {
|
|
23401
23484
|
const shielded = [
|
|
23402
23485
|
...new Map(
|
|
@@ -23454,7 +23537,7 @@ function collectListeners() {
|
|
|
23454
23537
|
const byPort = /* @__PURE__ */ new Map();
|
|
23455
23538
|
for (const file2 of ["/proc/net/tcp", "/proc/net/tcp6"]) {
|
|
23456
23539
|
try {
|
|
23457
|
-
for (const l of parseListeners(
|
|
23540
|
+
for (const l of parseListeners(fs23.readFileSync(file2, "utf8"))) {
|
|
23458
23541
|
if (!byPort.has(l.port)) byPort.set(l.port, l);
|
|
23459
23542
|
}
|
|
23460
23543
|
} catch {
|
|
@@ -23466,11 +23549,11 @@ function readProc(pid) {
|
|
|
23466
23549
|
let comm = "unknown";
|
|
23467
23550
|
let cmdline = "";
|
|
23468
23551
|
try {
|
|
23469
|
-
comm =
|
|
23552
|
+
comm = fs23.readFileSync(`/proc/${pid}/comm`, "utf8").trim().replace(/-MainThread$/, "") || "unknown";
|
|
23470
23553
|
} catch {
|
|
23471
23554
|
}
|
|
23472
23555
|
try {
|
|
23473
|
-
cmdline =
|
|
23556
|
+
cmdline = fs23.readFileSync(`/proc/${pid}/cmdline`).toString().replace(/\0/g, " ").trim();
|
|
23474
23557
|
} catch {
|
|
23475
23558
|
}
|
|
23476
23559
|
return { comm, cmdline };
|
|
@@ -23480,21 +23563,21 @@ function resolveProcesses(inodes) {
|
|
|
23480
23563
|
if (inodes.size === 0) return map;
|
|
23481
23564
|
let pids;
|
|
23482
23565
|
try {
|
|
23483
|
-
pids =
|
|
23566
|
+
pids = fs23.readdirSync("/proc").filter((d) => /^\d+$/.test(d));
|
|
23484
23567
|
} catch {
|
|
23485
23568
|
return map;
|
|
23486
23569
|
}
|
|
23487
23570
|
for (const pid of pids) {
|
|
23488
23571
|
let fds;
|
|
23489
23572
|
try {
|
|
23490
|
-
fds =
|
|
23573
|
+
fds = fs23.readdirSync(`/proc/${pid}/fd`);
|
|
23491
23574
|
} catch {
|
|
23492
23575
|
continue;
|
|
23493
23576
|
}
|
|
23494
23577
|
for (const fd of fds) {
|
|
23495
23578
|
let link;
|
|
23496
23579
|
try {
|
|
23497
|
-
link =
|
|
23580
|
+
link = fs23.readlinkSync(`/proc/${pid}/fd/${fd}`);
|
|
23498
23581
|
} catch {
|
|
23499
23582
|
continue;
|
|
23500
23583
|
}
|
|
@@ -23599,9 +23682,9 @@ var init_inbound = __esm({
|
|
|
23599
23682
|
});
|
|
23600
23683
|
|
|
23601
23684
|
// src/posture/coverage.ts
|
|
23602
|
-
import
|
|
23685
|
+
import os18 from "os";
|
|
23603
23686
|
function checkCoverage(ctx) {
|
|
23604
|
-
const home = ctx.home ||
|
|
23687
|
+
const home = ctx.home || os18.homedir();
|
|
23605
23688
|
const findings = [];
|
|
23606
23689
|
const protectedAgents = getAgentWiring(home).filter((r) => r.isProtected);
|
|
23607
23690
|
if (protectedAgents.length === 0) {
|
|
@@ -23645,7 +23728,7 @@ var init_coverage = __esm({
|
|
|
23645
23728
|
});
|
|
23646
23729
|
|
|
23647
23730
|
// src/posture/governance.ts
|
|
23648
|
-
import
|
|
23731
|
+
import path24 from "path";
|
|
23649
23732
|
function checkData(ctx) {
|
|
23650
23733
|
const dlp = getConfig(ctx.cwd).policy.dlp;
|
|
23651
23734
|
if (!dlp?.enabled) {
|
|
@@ -23789,7 +23872,7 @@ var init_governance = __esm({
|
|
|
23789
23872
|
"src/posture/governance.ts"() {
|
|
23790
23873
|
"use strict";
|
|
23791
23874
|
init_config();
|
|
23792
|
-
jailProbePath = (home) =>
|
|
23875
|
+
jailProbePath = (home) => path24.join(home, ".aws", "credentials");
|
|
23793
23876
|
}
|
|
23794
23877
|
});
|
|
23795
23878
|
|
|
@@ -23968,7 +24051,7 @@ var init_enforcement = __esm({
|
|
|
23968
24051
|
});
|
|
23969
24052
|
|
|
23970
24053
|
// src/posture/index.ts
|
|
23971
|
-
import
|
|
24054
|
+
import os19 from "os";
|
|
23972
24055
|
function dropEnforcementRedundant(findings) {
|
|
23973
24056
|
const coveragePresent = findings.some((f) => f.category === "Coverage");
|
|
23974
24057
|
if (!coveragePresent) return findings;
|
|
@@ -23994,9 +24077,10 @@ async function runChecks(checks, ctx) {
|
|
|
23994
24077
|
}
|
|
23995
24078
|
async function runPosture(opts = {}) {
|
|
23996
24079
|
const ctx = {
|
|
23997
|
-
home: opts.home ??
|
|
24080
|
+
home: opts.home ?? os19.homedir(),
|
|
23998
24081
|
cwd: opts.cwd ?? process.cwd(),
|
|
23999
|
-
agent: opts.agent
|
|
24082
|
+
agent: opts.agent,
|
|
24083
|
+
env: opts.env
|
|
24000
24084
|
};
|
|
24001
24085
|
const {
|
|
24002
24086
|
findings: rawFindings,
|
|
@@ -24055,6 +24139,102 @@ var init_posture = __esm({
|
|
|
24055
24139
|
}
|
|
24056
24140
|
});
|
|
24057
24141
|
|
|
24142
|
+
// src/posture/render.ts
|
|
24143
|
+
import chalk2 from "chalk";
|
|
24144
|
+
var ICON, TIER_LABEL;
|
|
24145
|
+
var init_render = __esm({
|
|
24146
|
+
"src/posture/render.ts"() {
|
|
24147
|
+
"use strict";
|
|
24148
|
+
init_score();
|
|
24149
|
+
ICON = {
|
|
24150
|
+
critical: chalk2.red("\u274C"),
|
|
24151
|
+
high: chalk2.red("\u274C"),
|
|
24152
|
+
medium: chalk2.yellow("\u26A0\uFE0F "),
|
|
24153
|
+
advisory: chalk2.gray("\u26A0\uFE0F ")
|
|
24154
|
+
};
|
|
24155
|
+
TIER_LABEL = {
|
|
24156
|
+
good: chalk2.green("Good"),
|
|
24157
|
+
"at-risk": chalk2.yellow("At risk"),
|
|
24158
|
+
critical: chalk2.red("Critical")
|
|
24159
|
+
};
|
|
24160
|
+
}
|
|
24161
|
+
});
|
|
24162
|
+
|
|
24163
|
+
// src/cli/commands/blast.ts
|
|
24164
|
+
import chalk3 from "chalk";
|
|
24165
|
+
import fs24 from "fs";
|
|
24166
|
+
import path25 from "path";
|
|
24167
|
+
import os20 from "os";
|
|
24168
|
+
function buildSensitivePaths(home, cwd) {
|
|
24169
|
+
const homeFiles = homeCredentialFiles(home).map(({ full, label: label2, description, score }) => ({
|
|
24170
|
+
full,
|
|
24171
|
+
label: label2,
|
|
24172
|
+
description,
|
|
24173
|
+
score
|
|
24174
|
+
}));
|
|
24175
|
+
return [
|
|
24176
|
+
...homeFiles,
|
|
24177
|
+
{
|
|
24178
|
+
full: path25.join(cwd, ".env"),
|
|
24179
|
+
label: ".env (current folder)",
|
|
24180
|
+
description: "App secrets: database passwords, API keys",
|
|
24181
|
+
score: 20
|
|
24182
|
+
},
|
|
24183
|
+
{
|
|
24184
|
+
full: path25.join(cwd, ".env.local"),
|
|
24185
|
+
label: ".env.local (current folder)",
|
|
24186
|
+
description: "Local overrides: often contains real credentials",
|
|
24187
|
+
score: 15
|
|
24188
|
+
},
|
|
24189
|
+
{
|
|
24190
|
+
full: path25.join(cwd, ".env.production"),
|
|
24191
|
+
label: ".env.production (current folder)",
|
|
24192
|
+
description: "Production secrets",
|
|
24193
|
+
score: 20
|
|
24194
|
+
}
|
|
24195
|
+
];
|
|
24196
|
+
}
|
|
24197
|
+
function isReadable(filePath) {
|
|
24198
|
+
try {
|
|
24199
|
+
fs24.accessSync(filePath, fs24.constants.R_OK);
|
|
24200
|
+
return true;
|
|
24201
|
+
} catch {
|
|
24202
|
+
return false;
|
|
24203
|
+
}
|
|
24204
|
+
}
|
|
24205
|
+
function runBlast() {
|
|
24206
|
+
const home = os20.homedir();
|
|
24207
|
+
const cwd = process.cwd();
|
|
24208
|
+
const paths = buildSensitivePaths(home, cwd);
|
|
24209
|
+
let scoreDeduction = 0;
|
|
24210
|
+
const reachable = [];
|
|
24211
|
+
for (const p of paths) {
|
|
24212
|
+
if (fs24.existsSync(p.full) && isReadable(p.full)) {
|
|
24213
|
+
reachable.push(p);
|
|
24214
|
+
scoreDeduction += p.score;
|
|
24215
|
+
}
|
|
24216
|
+
}
|
|
24217
|
+
const envFindings = [];
|
|
24218
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
24219
|
+
if (!value) continue;
|
|
24220
|
+
const match = scanArgs({ [key]: value });
|
|
24221
|
+
if (match) {
|
|
24222
|
+
envFindings.push({ key, patternName: match.patternName });
|
|
24223
|
+
scoreDeduction += 10;
|
|
24224
|
+
}
|
|
24225
|
+
}
|
|
24226
|
+
return { reachable, envFindings, score: Math.max(0, 100 - scoreDeduction) };
|
|
24227
|
+
}
|
|
24228
|
+
var init_blast = __esm({
|
|
24229
|
+
"src/cli/commands/blast.ts"() {
|
|
24230
|
+
"use strict";
|
|
24231
|
+
init_dlp();
|
|
24232
|
+
init_credential_files();
|
|
24233
|
+
init_posture();
|
|
24234
|
+
init_render();
|
|
24235
|
+
}
|
|
24236
|
+
});
|
|
24237
|
+
|
|
24058
24238
|
// src/auth/decision-record.ts
|
|
24059
24239
|
function personRecord(channel, decision, extra = {}, now = Date.now()) {
|
|
24060
24240
|
return {
|
|
@@ -24714,7 +24894,7 @@ import * as fs26 from "fs";
|
|
|
24714
24894
|
import * as path27 from "path";
|
|
24715
24895
|
function listSessionFiles(dir2, maxDepth = 6) {
|
|
24716
24896
|
const out = [];
|
|
24717
|
-
const
|
|
24897
|
+
const walk3 = (d, rel, depth) => {
|
|
24718
24898
|
if (depth > maxDepth) return;
|
|
24719
24899
|
let entries;
|
|
24720
24900
|
try {
|
|
@@ -24724,11 +24904,11 @@ function listSessionFiles(dir2, maxDepth = 6) {
|
|
|
24724
24904
|
}
|
|
24725
24905
|
for (const e of entries) {
|
|
24726
24906
|
const childRel = rel ? path27.join(rel, e.name) : e.name;
|
|
24727
|
-
if (e.isDirectory())
|
|
24907
|
+
if (e.isDirectory()) walk3(path27.join(d, e.name), childRel, depth + 1);
|
|
24728
24908
|
else if (e.name.endsWith(".jsonl")) out.push(childRel);
|
|
24729
24909
|
}
|
|
24730
24910
|
};
|
|
24731
|
-
|
|
24911
|
+
walk3(dir2, "", 0);
|
|
24732
24912
|
return out;
|
|
24733
24913
|
}
|
|
24734
24914
|
function sessionIdOf(relPath) {
|
|
@@ -25535,7 +25715,7 @@ var init_report_audit = __esm({
|
|
|
25535
25715
|
});
|
|
25536
25716
|
|
|
25537
25717
|
// src/config/keyed-guard.ts
|
|
25538
|
-
import
|
|
25718
|
+
import chalk4 from "chalk";
|
|
25539
25719
|
function isKeyedForPolicy() {
|
|
25540
25720
|
try {
|
|
25541
25721
|
return getConfig().policySource === "workspace";
|
|
@@ -29344,7 +29524,7 @@ var init_scan_summary = __esm({
|
|
|
29344
29524
|
});
|
|
29345
29525
|
|
|
29346
29526
|
// src/cli/render/scan-derive.ts
|
|
29347
|
-
import
|
|
29527
|
+
import chalk5 from "chalk";
|
|
29348
29528
|
import stringWidth from "string-width";
|
|
29349
29529
|
var init_scan_derive = __esm({
|
|
29350
29530
|
"src/cli/render/scan-derive.ts"() {
|
|
@@ -29363,7 +29543,6 @@ var init_protection = __esm({
|
|
|
29363
29543
|
var init_scan_json = __esm({
|
|
29364
29544
|
"src/cli/render/scan-json.ts"() {
|
|
29365
29545
|
"use strict";
|
|
29366
|
-
init_scan_derive();
|
|
29367
29546
|
}
|
|
29368
29547
|
});
|
|
29369
29548
|
|
|
@@ -29376,7 +29555,7 @@ var init_scan_history = __esm({
|
|
|
29376
29555
|
});
|
|
29377
29556
|
|
|
29378
29557
|
// src/cli/commands/scan.ts
|
|
29379
|
-
import
|
|
29558
|
+
import chalk6 from "chalk";
|
|
29380
29559
|
import fs43 from "fs";
|
|
29381
29560
|
import path46 from "path";
|
|
29382
29561
|
import os35 from "os";
|
|
@@ -29522,19 +29701,19 @@ function scrubDecoys(subject, values) {
|
|
|
29522
29701
|
for (const v of values) if (v) out = out.split(v).join("[node9-decoy]");
|
|
29523
29702
|
return out;
|
|
29524
29703
|
};
|
|
29525
|
-
const
|
|
29704
|
+
const walk3 = (v, depth) => {
|
|
29526
29705
|
if (depth > 6) return v;
|
|
29527
29706
|
if (typeof v === "string") return scrubText(v);
|
|
29528
|
-
if (Array.isArray(v)) return v.map((x) =>
|
|
29707
|
+
if (Array.isArray(v)) return v.map((x) => walk3(x, depth + 1));
|
|
29529
29708
|
if (v && typeof v === "object") {
|
|
29530
29709
|
const out = {};
|
|
29531
29710
|
for (const [k, child] of Object.entries(v))
|
|
29532
|
-
out[k] =
|
|
29711
|
+
out[k] = walk3(child, depth + 1);
|
|
29533
29712
|
return out;
|
|
29534
29713
|
}
|
|
29535
29714
|
return v;
|
|
29536
29715
|
};
|
|
29537
|
-
return
|
|
29716
|
+
return walk3(subject, 0);
|
|
29538
29717
|
}
|
|
29539
29718
|
function detectLoops(calls, project, sessionId, agent) {
|
|
29540
29719
|
const counts = /* @__PURE__ */ new Map();
|
|
@@ -30721,7 +30900,7 @@ var init_scan = __esm({
|
|
|
30721
30900
|
init_hook_payload();
|
|
30722
30901
|
init_dist();
|
|
30723
30902
|
init_scan_summary();
|
|
30724
|
-
|
|
30903
|
+
init_agent_wiring();
|
|
30725
30904
|
init_blast();
|
|
30726
30905
|
init_scan_derive();
|
|
30727
30906
|
init_protection();
|