@node9/proxy 2.46.1 → 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 +299 -201
- package/dist/cli.mjs +300 -202
- package/dist/dashboard.mjs +290 -192
- 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/cli.js
CHANGED
|
@@ -680,6 +680,62 @@ function resolveCheckMap(settings) {
|
|
|
680
680
|
for (const c of CHECKS) out[c.id] = checkValue(settings, c.id);
|
|
681
681
|
return out;
|
|
682
682
|
}
|
|
683
|
+
function isGoNode(v) {
|
|
684
|
+
return v !== null && typeof v === "object" && typeof v.Pos === "function";
|
|
685
|
+
}
|
|
686
|
+
function toPlainTree(goNode) {
|
|
687
|
+
const kind = syntax.NodeType(goNode);
|
|
688
|
+
const out = { kind, pos: goNode.Pos().Offset(), end: goNode.End().Offset() };
|
|
689
|
+
const spec = KINDS[kind];
|
|
690
|
+
if (!spec) {
|
|
691
|
+
unknownKinds.add(kind);
|
|
692
|
+
return out;
|
|
693
|
+
}
|
|
694
|
+
convertFields(goNode, spec, out, kind);
|
|
695
|
+
return out;
|
|
696
|
+
}
|
|
697
|
+
function convertFields(goNode, spec, out, kind) {
|
|
698
|
+
for (const f of spec.scalars ?? []) out[f] = goNode[f];
|
|
699
|
+
for (const f of spec.nodes ?? []) {
|
|
700
|
+
const v = goNode[f];
|
|
701
|
+
if (v === void 0 || v === null) {
|
|
702
|
+
out[f] = null;
|
|
703
|
+
} else if (Array.isArray(v)) {
|
|
704
|
+
out[f] = v.map((c) => isGoNode(c) ? toPlainTree(c) : c);
|
|
705
|
+
} else if (isGoNode(v)) {
|
|
706
|
+
out[f] = toPlainTree(v);
|
|
707
|
+
} else if (typeof v === "object" && STRUCTS[`${kind}.${f}`]) {
|
|
708
|
+
const sub = { struct: `${kind}.${f}` };
|
|
709
|
+
convertFields(v, STRUCTS[`${kind}.${f}`], sub, `${kind}.${f}`);
|
|
710
|
+
out[f] = sub;
|
|
711
|
+
} else {
|
|
712
|
+
out[f] = v;
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
function childrenOf(n) {
|
|
717
|
+
const spec = n.kind ? KINDS[n.kind] : STRUCTS[n.struct];
|
|
718
|
+
if (!spec?.nodes) return [];
|
|
719
|
+
const out = [];
|
|
720
|
+
for (const f of spec.nodes) {
|
|
721
|
+
if (spec.notWalked?.includes(f)) continue;
|
|
722
|
+
const v = n[f];
|
|
723
|
+
if (!v) continue;
|
|
724
|
+
if (Array.isArray(v)) {
|
|
725
|
+
for (const c of v) if (c) out.push(c);
|
|
726
|
+
} else if (v.kind) {
|
|
727
|
+
out.push(v);
|
|
728
|
+
} else if (v.struct) {
|
|
729
|
+
out.push(...childrenOf(v));
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
return out;
|
|
733
|
+
}
|
|
734
|
+
function walk(root, fn) {
|
|
735
|
+
if (!root) return;
|
|
736
|
+
if (!fn(root)) return;
|
|
737
|
+
for (const c of childrenOf(root)) walk(c, fn);
|
|
738
|
+
}
|
|
683
739
|
function isInvisibleChar(char) {
|
|
684
740
|
return INVISIBLE_ONE.test(char);
|
|
685
741
|
}
|
|
@@ -835,7 +891,7 @@ function judgeInlineCode(language, code2, isProtected) {
|
|
|
835
891
|
}
|
|
836
892
|
function isCatHeredocOrLit(part) {
|
|
837
893
|
if (!part) return false;
|
|
838
|
-
const t =
|
|
894
|
+
const t = part.kind;
|
|
839
895
|
if (t === "Lit") return true;
|
|
840
896
|
if (t !== "CmdSubst") return false;
|
|
841
897
|
const stmts = part.Stmts || [];
|
|
@@ -845,9 +901,9 @@ function isCatHeredocOrLit(part) {
|
|
|
845
901
|
const hasHeredoc = redirs.some((r) => r && r.Hdoc);
|
|
846
902
|
if (!hasHeredoc) return false;
|
|
847
903
|
const cmd = stmt.Cmd;
|
|
848
|
-
if (!cmd ||
|
|
904
|
+
if (!cmd || cmd.kind !== "CallExpr") return false;
|
|
849
905
|
const firstArg2 = cmd.Args?.[0]?.Parts || [];
|
|
850
|
-
if (firstArg2.length !== 1 ||
|
|
906
|
+
if (firstArg2.length !== 1 || firstArg2[0].kind !== "Lit") return false;
|
|
851
907
|
return (firstArg2[0].Value || "").toLowerCase() === "cat";
|
|
852
908
|
}
|
|
853
909
|
function parseShared(command) {
|
|
@@ -859,7 +915,7 @@ function parseShared(command) {
|
|
|
859
915
|
}
|
|
860
916
|
let parsed;
|
|
861
917
|
try {
|
|
862
|
-
parsed = sharedParser.Parse(command, "cmd");
|
|
918
|
+
parsed = toPlainTree(sharedParser.Parse(command, "cmd"));
|
|
863
919
|
} catch {
|
|
864
920
|
parsed = PARSE_FAIL;
|
|
865
921
|
}
|
|
@@ -920,10 +976,10 @@ function fileAccessViewImpl(command) {
|
|
|
920
976
|
const at = (byteOffset) => toCharIndex === null ? byteOffset : toCharIndex(byteOffset);
|
|
921
977
|
const edits = [];
|
|
922
978
|
try {
|
|
923
|
-
|
|
979
|
+
walk(f, (node) => {
|
|
924
980
|
if (!node) return false;
|
|
925
981
|
const n = node;
|
|
926
|
-
if (
|
|
982
|
+
if (n.kind !== "CallExpr") return true;
|
|
927
983
|
const args = n.Args || [];
|
|
928
984
|
const words = args.map((a) => resolveWordLiteral(a));
|
|
929
985
|
const head = unwrapCommandHead(words);
|
|
@@ -935,8 +991,8 @@ function fileAccessViewImpl(command) {
|
|
|
935
991
|
if (!PROGRAM_DIR.test(word)) continue;
|
|
936
992
|
const name = word.split(/[\\/]/).pop() ?? "";
|
|
937
993
|
if (!name) continue;
|
|
938
|
-
const s = at(args[i].
|
|
939
|
-
const e = at(args[i].
|
|
994
|
+
const s = at(args[i].pos);
|
|
995
|
+
const e = at(args[i].end);
|
|
940
996
|
if (s < 0 || e < 0) continue;
|
|
941
997
|
edits.push([s, e, name]);
|
|
942
998
|
}
|
|
@@ -964,10 +1020,10 @@ function literalPayloadsOf(command, depth) {
|
|
|
964
1020
|
if (f === PARSE_FAIL) return [];
|
|
965
1021
|
const out = [];
|
|
966
1022
|
try {
|
|
967
|
-
|
|
1023
|
+
walk(f, (node) => {
|
|
968
1024
|
if (!node || out.length >= MAX_READING_PAYLOADS) return false;
|
|
969
1025
|
const n = node;
|
|
970
|
-
if (
|
|
1026
|
+
if (n.kind !== "CallExpr") return true;
|
|
971
1027
|
const words = (n.Args || []).map((a) => resolveWordLiteral(a));
|
|
972
1028
|
if (words.length === 0) return true;
|
|
973
1029
|
const payload = literalShellPayload(words, baseWord(words[0]));
|
|
@@ -987,23 +1043,23 @@ function foldCommandWords(reading) {
|
|
|
987
1043
|
const at = (byteOffset) => toCharIndex === null ? byteOffset : toCharIndex(byteOffset);
|
|
988
1044
|
const edits = [];
|
|
989
1045
|
try {
|
|
990
|
-
|
|
1046
|
+
walk(f, (node) => {
|
|
991
1047
|
if (!node) return false;
|
|
992
1048
|
const n = node;
|
|
993
|
-
if (
|
|
1049
|
+
if (n.kind !== "CallExpr") return true;
|
|
994
1050
|
const args = n.Args || [];
|
|
995
1051
|
const words = args.map((a) => resolveWordLiteral(a));
|
|
996
1052
|
const folded = words.map((w) => w === null ? null : foldHomoglyphs(w));
|
|
997
1053
|
const head = unwrapCommandHead(folded);
|
|
998
1054
|
for (let i = 0; i <= head && i < args.length; i++) {
|
|
999
1055
|
const parts = args[i].Parts || [];
|
|
1000
|
-
if (parts.length !== 1 ||
|
|
1056
|
+
if (parts.length !== 1 || parts[0].kind !== "Lit") continue;
|
|
1001
1057
|
const value = parts[0].Value ?? "";
|
|
1002
1058
|
const word = folded[i];
|
|
1003
1059
|
if (word === null || word === value || !/[^\x00-\x7f]/.test(value)) continue;
|
|
1004
1060
|
if (!/^[A-Za-z0-9._/-]+$/.test(word)) continue;
|
|
1005
|
-
const s = at(args[i].
|
|
1006
|
-
const e = at(args[i].
|
|
1061
|
+
const s = at(args[i].pos);
|
|
1062
|
+
const e = at(args[i].end);
|
|
1007
1063
|
if (s < 0 || e < 0) continue;
|
|
1008
1064
|
edits.push([s, e, word]);
|
|
1009
1065
|
}
|
|
@@ -1040,10 +1096,10 @@ function interpreterHeredocSpans(f, at) {
|
|
|
1040
1096
|
const stmts = f?.Stmts || [];
|
|
1041
1097
|
const candidates2 = [];
|
|
1042
1098
|
let unsafe = false;
|
|
1043
|
-
|
|
1099
|
+
walk(f, (node) => {
|
|
1044
1100
|
if (!node || unsafe) return false;
|
|
1045
1101
|
const n = node;
|
|
1046
|
-
const t =
|
|
1102
|
+
const t = n.kind;
|
|
1047
1103
|
if (t === "FuncDecl") {
|
|
1048
1104
|
unsafe = true;
|
|
1049
1105
|
return false;
|
|
@@ -1063,7 +1119,7 @@ function interpreterHeredocSpans(f, at) {
|
|
|
1063
1119
|
});
|
|
1064
1120
|
if (unsafe) return [];
|
|
1065
1121
|
for (const st of stmts) {
|
|
1066
|
-
if (!st?.Cmd ||
|
|
1122
|
+
if (!st?.Cmd || st.Cmd.kind !== "CallExpr") continue;
|
|
1067
1123
|
const words = (st.Cmd.Args || []).map((a) => resolveWordLiteral(a));
|
|
1068
1124
|
const name = (words[0] ?? "").toLowerCase();
|
|
1069
1125
|
if (!STDIN_CODE_INTERPRETERS.test(name)) continue;
|
|
@@ -1078,8 +1134,8 @@ function interpreterHeredocSpans(f, at) {
|
|
|
1078
1134
|
if (stdin.length !== 1 || others > 0) continue;
|
|
1079
1135
|
const r = stdin[0];
|
|
1080
1136
|
if (heredocText(r.Hdoc) === null) continue;
|
|
1081
|
-
const hs = at(r.Hdoc.
|
|
1082
|
-
const he = at(r.Hdoc.
|
|
1137
|
+
const hs = at(r.Hdoc.pos);
|
|
1138
|
+
const he = at(r.Hdoc.end);
|
|
1083
1139
|
if (hs < 0 || he < 0 || he <= hs) continue;
|
|
1084
1140
|
spans.push([hs, he]);
|
|
1085
1141
|
}
|
|
@@ -1099,24 +1155,24 @@ function normalizeCommandForPolicyImpl(command, codeBodies) {
|
|
|
1099
1155
|
strips.push([hs, he]);
|
|
1100
1156
|
msgSpans.add(`${hs}:${he}`);
|
|
1101
1157
|
}
|
|
1102
|
-
|
|
1158
|
+
walk(f, (node) => {
|
|
1103
1159
|
if (!node) return false;
|
|
1104
1160
|
const n = node;
|
|
1105
|
-
if (
|
|
1161
|
+
if (n.kind !== "CallExpr") return true;
|
|
1106
1162
|
const args = n.Args || [];
|
|
1107
1163
|
for (let i = 0; i < args.length - 1; i++) {
|
|
1108
1164
|
const argParts = args[i].Parts || [];
|
|
1109
|
-
if (argParts.length !== 1 ||
|
|
1165
|
+
if (argParts.length !== 1 || argParts[0].kind !== "Lit") continue;
|
|
1110
1166
|
const flagVal = argParts[0].Value || "";
|
|
1111
1167
|
if (!MESSAGE_FLAGS.has(flagVal.toLowerCase())) continue;
|
|
1112
1168
|
const next = args[i + 1];
|
|
1113
1169
|
const nextParts = next.Parts || [];
|
|
1114
1170
|
if (nextParts.length !== 1) continue;
|
|
1115
1171
|
const quotedNode = nextParts[0];
|
|
1116
|
-
const nt =
|
|
1172
|
+
const nt = quotedNode.kind;
|
|
1117
1173
|
const markStrip = () => {
|
|
1118
|
-
const s = at(next.
|
|
1119
|
-
const e = at(next.
|
|
1174
|
+
const s = at(next.pos);
|
|
1175
|
+
const e = at(next.end);
|
|
1120
1176
|
if (s < 0 || e < 0) return;
|
|
1121
1177
|
strips.push([s, e]);
|
|
1122
1178
|
msgSpans.add(`${s}:${e}`);
|
|
@@ -1125,7 +1181,7 @@ function normalizeCommandForPolicyImpl(command, codeBodies) {
|
|
|
1125
1181
|
markStrip();
|
|
1126
1182
|
} else if (nt === "DblQuoted") {
|
|
1127
1183
|
const innerParts = quotedNode.Parts || [];
|
|
1128
|
-
const allLit = innerParts.length === 0 || innerParts.every((p) =>
|
|
1184
|
+
const allLit = innerParts.length === 0 || innerParts.every((p) => p.kind === "Lit");
|
|
1129
1185
|
if (allLit) {
|
|
1130
1186
|
markStrip();
|
|
1131
1187
|
} else if (innerParts.every((p) => isCatHeredocOrLit(p))) {
|
|
@@ -1134,8 +1190,8 @@ function normalizeCommandForPolicyImpl(command, codeBodies) {
|
|
|
1134
1190
|
}
|
|
1135
1191
|
}
|
|
1136
1192
|
for (const arg of args) {
|
|
1137
|
-
const s = at(arg.
|
|
1138
|
-
const e = at(arg.
|
|
1193
|
+
const s = at(arg.pos);
|
|
1194
|
+
const e = at(arg.end);
|
|
1139
1195
|
if (s < 0 || e < 0) continue;
|
|
1140
1196
|
if (msgSpans.has(`${s}:${e}`)) continue;
|
|
1141
1197
|
const resolved = resolveWordLiteral(arg);
|
|
@@ -1175,10 +1231,10 @@ function scanArgsForDynamicExec(args, startIdx) {
|
|
|
1175
1231
|
let hasParamExp = false;
|
|
1176
1232
|
let hasCurl = false;
|
|
1177
1233
|
for (let i = startIdx; i < args.length; i++) {
|
|
1178
|
-
|
|
1234
|
+
walk(args[i], (inner) => {
|
|
1179
1235
|
if (!inner) return false;
|
|
1180
1236
|
const inn = inner;
|
|
1181
|
-
const it =
|
|
1237
|
+
const it = inn.kind;
|
|
1182
1238
|
if (it === "CmdSubst") hasCmdSubst = true;
|
|
1183
1239
|
if (it === "ParamExp") hasParamExp = true;
|
|
1184
1240
|
if (it === "Lit" && DOWNLOAD_CMDS.has(inn.Value?.toLowerCase())) hasCurl = true;
|
|
@@ -1191,15 +1247,16 @@ function scanArgsForDynamicExec(args, startIdx) {
|
|
|
1191
1247
|
}
|
|
1192
1248
|
function detectDangerousShellExec(command) {
|
|
1193
1249
|
try {
|
|
1194
|
-
const f =
|
|
1250
|
+
const f = parseShared(command);
|
|
1251
|
+
if (f === PARSE_FAIL) return null;
|
|
1195
1252
|
let result = null;
|
|
1196
1253
|
const assigned = assignedNames(f);
|
|
1197
|
-
|
|
1254
|
+
walk(f, (node) => {
|
|
1198
1255
|
if (!node || result === "block") return false;
|
|
1199
1256
|
const n = node;
|
|
1200
|
-
if (
|
|
1257
|
+
if (n.kind === "BinaryCmd" && n.Y && !listOps().has(n.Op)) {
|
|
1201
1258
|
const call = n.Y.Cmd;
|
|
1202
|
-
if (call &&
|
|
1259
|
+
if (call && call.kind === "CallExpr") {
|
|
1203
1260
|
const words = (call.Args || []).map((a) => resolveWordLiteral(a));
|
|
1204
1261
|
const head2 = unwrapCommandHead(words);
|
|
1205
1262
|
const name = (words[head2] ?? "").split("/").pop() ?? "";
|
|
@@ -1212,7 +1269,7 @@ function detectDangerousShellExec(command) {
|
|
|
1212
1269
|
result = "review";
|
|
1213
1270
|
}
|
|
1214
1271
|
}
|
|
1215
|
-
if (
|
|
1272
|
+
if (n.kind !== "CallExpr") return true;
|
|
1216
1273
|
const args = n.Args || [];
|
|
1217
1274
|
if (args.length === 0) return true;
|
|
1218
1275
|
const headWords = args.map(
|
|
@@ -1223,7 +1280,7 @@ function detectDangerousShellExec(command) {
|
|
|
1223
1280
|
if (!lookup && (opaqueExecutable(args[0], assigned) || opaqueExecutable(args[head], assigned)))
|
|
1224
1281
|
result = "review";
|
|
1225
1282
|
const firstParts = args[0].Parts || [];
|
|
1226
|
-
if (firstParts.length !== 1 ||
|
|
1283
|
+
if (firstParts.length !== 1 || firstParts[0].kind !== "Lit") return true;
|
|
1227
1284
|
const cmdName = firstParts[0].Value?.toLowerCase() ?? "";
|
|
1228
1285
|
if (cmdName === "eval") {
|
|
1229
1286
|
const v = scanArgsForDynamicExec(args, 1);
|
|
@@ -1231,7 +1288,7 @@ function detectDangerousShellExec(command) {
|
|
|
1231
1288
|
} else if (SHELL_INTERPRETERS.has(cmdName)) {
|
|
1232
1289
|
for (let i = 1; i < args.length - 1; i++) {
|
|
1233
1290
|
const flagParts = args[i].Parts || [];
|
|
1234
|
-
if (flagParts.length !== 1 ||
|
|
1291
|
+
if (flagParts.length !== 1 || flagParts[0].kind !== "Lit" || flagParts[0].Value !== "-c")
|
|
1235
1292
|
continue;
|
|
1236
1293
|
const v = scanArgsForDynamicExec(args, i + 1);
|
|
1237
1294
|
if (v === "block" || v === "review" && result === null) result = v;
|
|
@@ -1255,8 +1312,7 @@ function isDefaultExpansion(part) {
|
|
|
1255
1312
|
}
|
|
1256
1313
|
function lookupSubstitution(part) {
|
|
1257
1314
|
const stmts = part.Stmts ?? [];
|
|
1258
|
-
if (stmts.length !== 1 || !stmts[0].Cmd ||
|
|
1259
|
-
return null;
|
|
1315
|
+
if (stmts.length !== 1 || !stmts[0].Cmd || stmts[0].Cmd.kind !== "CallExpr") return null;
|
|
1260
1316
|
const words = (stmts[0].Cmd.Args ?? []).map((a) => resolveWordLiteral(a));
|
|
1261
1317
|
if (words.some((w) => w === null)) return null;
|
|
1262
1318
|
if (words.length === 2 && words[0] === "which") return words[1];
|
|
@@ -1266,9 +1322,9 @@ function lookupSubstitution(part) {
|
|
|
1266
1322
|
}
|
|
1267
1323
|
function assignedNames(f) {
|
|
1268
1324
|
const names = /* @__PURE__ */ new Set();
|
|
1269
|
-
|
|
1325
|
+
walk(f, (node) => {
|
|
1270
1326
|
if (!node) return false;
|
|
1271
|
-
const kind =
|
|
1327
|
+
const kind = node.kind;
|
|
1272
1328
|
if (kind === "Assign" && node.Name?.Value) names.add(node.Name.Value);
|
|
1273
1329
|
if (kind === "ForClause" && node.Loop?.Name?.Value) names.add(node.Loop.Name.Value);
|
|
1274
1330
|
if (kind === "CallExpr") {
|
|
@@ -1283,7 +1339,7 @@ function assignedNames(f) {
|
|
|
1283
1339
|
}
|
|
1284
1340
|
function executableParts(word, assigned, quoted = false) {
|
|
1285
1341
|
return (word?.Parts ?? []).flatMap((part) => {
|
|
1286
|
-
const kind =
|
|
1342
|
+
const kind = part.kind;
|
|
1287
1343
|
if (kind === "DblQuoted") return executableParts(part, assigned, true);
|
|
1288
1344
|
if (kind === "Lit") return [{ kind: "Lit", value: part.Value ?? "", quoted }];
|
|
1289
1345
|
if (kind === "SglQuoted")
|
|
@@ -1351,10 +1407,10 @@ function chmodHasOpenPermMode(command) {
|
|
|
1351
1407
|
if (f === PARSE_FAIL) return false;
|
|
1352
1408
|
let found = false;
|
|
1353
1409
|
try {
|
|
1354
|
-
|
|
1410
|
+
walk(f, (node) => {
|
|
1355
1411
|
if (!node || found) return false;
|
|
1356
1412
|
const n = node;
|
|
1357
|
-
if (
|
|
1413
|
+
if (n.kind !== "CallExpr") return true;
|
|
1358
1414
|
const words = (n.Args || []).map((a) => resolveWordLiteral(a));
|
|
1359
1415
|
if (words.length === 0) return true;
|
|
1360
1416
|
const name = (words[0] ?? "").toLowerCase();
|
|
@@ -1407,11 +1463,11 @@ function redirStdinOps() {
|
|
|
1407
1463
|
}
|
|
1408
1464
|
function deriveBinaryOp(sample) {
|
|
1409
1465
|
try {
|
|
1410
|
-
const f = sharedParser.Parse(sample, "cmd");
|
|
1466
|
+
const f = toPlainTree(sharedParser.Parse(sample, "cmd"));
|
|
1411
1467
|
let op = -1;
|
|
1412
|
-
|
|
1468
|
+
walk(f, (node) => {
|
|
1413
1469
|
const n = node;
|
|
1414
|
-
if (n &&
|
|
1470
|
+
if (n && n.kind === "BinaryCmd" && op < 0) op = n.Op;
|
|
1415
1471
|
return true;
|
|
1416
1472
|
});
|
|
1417
1473
|
return op;
|
|
@@ -1485,7 +1541,7 @@ function heredocText(word) {
|
|
|
1485
1541
|
const parts = word?.Parts ?? [];
|
|
1486
1542
|
let out = "";
|
|
1487
1543
|
for (const p of parts) {
|
|
1488
|
-
if (
|
|
1544
|
+
if (p.kind !== "Lit") return null;
|
|
1489
1545
|
out += p.Value ?? "";
|
|
1490
1546
|
}
|
|
1491
1547
|
return out;
|
|
@@ -1521,7 +1577,7 @@ function attachedCode(interp, flag) {
|
|
|
1521
1577
|
}
|
|
1522
1578
|
function inlineExecCall(stmt, pipeFed) {
|
|
1523
1579
|
const cmd = stmt?.Cmd;
|
|
1524
|
-
if (!cmd ||
|
|
1580
|
+
if (!cmd || cmd.kind !== "CallExpr") return null;
|
|
1525
1581
|
const words = (cmd.Args || []).map((a) => resolveWordLiteral(a));
|
|
1526
1582
|
if (words.length === 0) return null;
|
|
1527
1583
|
const headIdx = unwrapCommandHead(words);
|
|
@@ -1602,10 +1658,10 @@ function shellPayloadNeedsReview(payload) {
|
|
|
1602
1658
|
let review = false;
|
|
1603
1659
|
payloadNesting++;
|
|
1604
1660
|
try {
|
|
1605
|
-
|
|
1661
|
+
walk(f, (node) => {
|
|
1606
1662
|
if (!node || review) return false;
|
|
1607
1663
|
const n = node;
|
|
1608
|
-
if (
|
|
1664
|
+
if (n.kind !== "CallExpr") return true;
|
|
1609
1665
|
const words = (n.Args || []).map((a) => resolveWordLiteral(a));
|
|
1610
1666
|
if (words.length === 0) return true;
|
|
1611
1667
|
const h = unwrapCommandHead(words);
|
|
@@ -1639,10 +1695,10 @@ function inlineExecCalls(command) {
|
|
|
1639
1695
|
if (f === PARSE_FAIL) return PARSE_FAIL;
|
|
1640
1696
|
const calls = [];
|
|
1641
1697
|
try {
|
|
1642
|
-
|
|
1698
|
+
walk(f, (node) => {
|
|
1643
1699
|
if (!node || calls.length >= MAX_INLINE_CALLS) return false;
|
|
1644
1700
|
const n = node;
|
|
1645
|
-
const t =
|
|
1701
|
+
const t = n.kind;
|
|
1646
1702
|
if (t === "BinaryCmd" && n.Y && !listOps().has(n.Op)) {
|
|
1647
1703
|
const call = inlineExecCall(n.Y, true);
|
|
1648
1704
|
if (call) calls.push(call);
|
|
@@ -1777,7 +1833,7 @@ function shellScriptOperands(command) {
|
|
|
1777
1833
|
walkWithCalls(f, (node) => {
|
|
1778
1834
|
if (!node) return false;
|
|
1779
1835
|
const n = node;
|
|
1780
|
-
if (
|
|
1836
|
+
if (n.kind !== "CallExpr") return true;
|
|
1781
1837
|
const callArgs = n.Args || [];
|
|
1782
1838
|
if (callArgs.length === 0) return true;
|
|
1783
1839
|
const words = callArgs.map((a) => resolveWordLiteral(a));
|
|
@@ -1813,7 +1869,7 @@ function shellScriptOperands(command) {
|
|
|
1813
1869
|
}
|
|
1814
1870
|
function windowsDrivePathSpelling(word) {
|
|
1815
1871
|
const parts = word?.Parts || [];
|
|
1816
|
-
if (parts.length === 0 || !parts.every((p) =>
|
|
1872
|
+
if (parts.length === 0 || !parts.every((p) => p.kind === "Lit")) return null;
|
|
1817
1873
|
const raw = parts.map((p) => p.Value ?? "").join("");
|
|
1818
1874
|
return /^[A-Za-z]:\\[^\s'"$`]*$/.test(raw) ? raw : null;
|
|
1819
1875
|
}
|
|
@@ -1851,15 +1907,15 @@ function recordTopLevelAssignments(f) {
|
|
|
1851
1907
|
}
|
|
1852
1908
|
function probeBinOp(src) {
|
|
1853
1909
|
try {
|
|
1854
|
-
const cmd =
|
|
1855
|
-
if (cmd &&
|
|
1910
|
+
const cmd = toPlainTree(syntax2.NewParser().Parse(src, "probe"))?.Stmts?.[0]?.Cmd;
|
|
1911
|
+
if (cmd && cmd.kind === "BinaryCmd") return cmd.Op;
|
|
1856
1912
|
} catch {
|
|
1857
1913
|
}
|
|
1858
1914
|
return null;
|
|
1859
1915
|
}
|
|
1860
1916
|
function recordTopLevelStmt(stmt) {
|
|
1861
1917
|
if (!stmt || !stmt.Cmd) return false;
|
|
1862
|
-
const t =
|
|
1918
|
+
const t = stmt.Cmd.kind;
|
|
1863
1919
|
if (t === "BinaryCmd") {
|
|
1864
1920
|
if (!AND_OR_OPS.has(stmt.Cmd.Op)) return false;
|
|
1865
1921
|
if (!recordTopLevelStmt(stmt.Cmd.X)) return false;
|
|
@@ -1869,7 +1925,7 @@ function recordTopLevelStmt(stmt) {
|
|
|
1869
1925
|
if (t !== "CallExpr" && t !== "DeclClause") return false;
|
|
1870
1926
|
let at = 0;
|
|
1871
1927
|
try {
|
|
1872
|
-
at = stmt.
|
|
1928
|
+
at = stmt.pos;
|
|
1873
1929
|
} catch {
|
|
1874
1930
|
at = 0;
|
|
1875
1931
|
}
|
|
@@ -1880,14 +1936,14 @@ function recordTopLevelStmt(stmt) {
|
|
|
1880
1936
|
}
|
|
1881
1937
|
function recordAssignments(n, at) {
|
|
1882
1938
|
if (!assignmentTable) return;
|
|
1883
|
-
const t =
|
|
1939
|
+
const t = n.kind;
|
|
1884
1940
|
let assigns = [];
|
|
1885
1941
|
if (t === "CallExpr") {
|
|
1886
1942
|
if ((n.Args || []).length > 0) return;
|
|
1887
1943
|
assigns = n.Assigns || [];
|
|
1888
1944
|
} else if (t === "DeclClause") {
|
|
1889
1945
|
if (!ASSIGNMENT_HEADS.has(n.Variant?.Value ?? "")) return;
|
|
1890
|
-
assigns = (n.Args || []).filter((a) =>
|
|
1946
|
+
assigns = (n.Args || []).filter((a) => a.kind === "Assign");
|
|
1891
1947
|
} else return;
|
|
1892
1948
|
for (const a of assigns) {
|
|
1893
1949
|
const name = a?.Name?.Value;
|
|
@@ -1896,13 +1952,13 @@ function recordAssignments(n, at) {
|
|
|
1896
1952
|
}
|
|
1897
1953
|
}
|
|
1898
1954
|
function resolveTrivialSubst(part) {
|
|
1899
|
-
if (
|
|
1955
|
+
if (part.kind !== "CmdSubst") return void 0;
|
|
1900
1956
|
const stmts = part.Stmts || [];
|
|
1901
1957
|
if (stmts.length !== 1) return void 0;
|
|
1902
1958
|
const st = stmts[0];
|
|
1903
1959
|
if ((st.Redirs || []).length > 0 || st.Negated || st.Background) return void 0;
|
|
1904
1960
|
const cmd = st.Cmd;
|
|
1905
|
-
if (!cmd ||
|
|
1961
|
+
if (!cmd || cmd.kind !== "CallExpr") return void 0;
|
|
1906
1962
|
if ((cmd.Assigns || []).length > 0) return void 0;
|
|
1907
1963
|
const words = (cmd.Args || []).map((a) => resolveWordLiteral(a));
|
|
1908
1964
|
if (words.length === 0 || words.some((w) => w === null)) return void 0;
|
|
@@ -1927,7 +1983,7 @@ function recordedExpansion(name) {
|
|
|
1927
1983
|
return rec.value;
|
|
1928
1984
|
}
|
|
1929
1985
|
function isPlainParam(p) {
|
|
1930
|
-
if (
|
|
1986
|
+
if (p.kind !== "ParamExp") return false;
|
|
1931
1987
|
return !(p.Excl || p.Length || p.Width || p.Index || p.Slice || p.Repl || p.Exp);
|
|
1932
1988
|
}
|
|
1933
1989
|
function expandPlainParam(p) {
|
|
@@ -1939,9 +1995,9 @@ function expandPlainParam(p) {
|
|
|
1939
1995
|
}
|
|
1940
1996
|
function functionBodies(f) {
|
|
1941
1997
|
const out = /* @__PURE__ */ new Map();
|
|
1942
|
-
|
|
1998
|
+
walk(f, (node) => {
|
|
1943
1999
|
const n = node;
|
|
1944
|
-
if (n &&
|
|
2000
|
+
if (n && n.kind === "FuncDecl" && n.Name?.Value && n.Body) {
|
|
1945
2001
|
const list2 = out.get(n.Name.Value) ?? [];
|
|
1946
2002
|
list2.push(n.Body);
|
|
1947
2003
|
out.set(n.Name.Value, list2);
|
|
@@ -1952,10 +2008,10 @@ function functionBodies(f) {
|
|
|
1952
2008
|
}
|
|
1953
2009
|
function rebindsPositionals(body) {
|
|
1954
2010
|
let found = false;
|
|
1955
|
-
|
|
2011
|
+
walk(body, (node) => {
|
|
1956
2012
|
const n = node;
|
|
1957
2013
|
if (found || !n) return false;
|
|
1958
|
-
if (
|
|
2014
|
+
if (n.kind === "CallExpr") {
|
|
1959
2015
|
const words = (n.Args || []).map((a) => resolveWordLiteral(a));
|
|
1960
2016
|
if (words[0] === "shift") found = true;
|
|
1961
2017
|
else if (words[0] === "set" && words.slice(1).some((w) => w === null || w === "--" || !/^[-+]/.test(w)))
|
|
@@ -1975,7 +2031,7 @@ function tableBindings() {
|
|
|
1975
2031
|
function walkWithCalls(f, visit2) {
|
|
1976
2032
|
const bodies = functionBodies(f);
|
|
1977
2033
|
if (bodies.size === 0) {
|
|
1978
|
-
|
|
2034
|
+
walk(f, visit2);
|
|
1979
2035
|
return;
|
|
1980
2036
|
}
|
|
1981
2037
|
const seenCalls = /* @__PURE__ */ new Set();
|
|
@@ -2009,8 +2065,8 @@ function walkWithCalls(f, visit2) {
|
|
|
2009
2065
|
currentStmtOffset = Number.MAX_SAFE_INTEGER;
|
|
2010
2066
|
callDepth++;
|
|
2011
2067
|
try {
|
|
2012
|
-
if (body.Cmd &&
|
|
2013
|
-
|
|
2068
|
+
if (body.Cmd && body.Cmd.kind === "Block") recordTopLevelAssignments(body.Cmd);
|
|
2069
|
+
walk(body, step);
|
|
2014
2070
|
} finally {
|
|
2015
2071
|
callDepth--;
|
|
2016
2072
|
assignmentTable = outerTable;
|
|
@@ -2021,10 +2077,10 @@ function walkWithCalls(f, visit2) {
|
|
|
2021
2077
|
const step = (node) => {
|
|
2022
2078
|
if (!node) return false;
|
|
2023
2079
|
const go = visit2(node);
|
|
2024
|
-
if (go !== false &&
|
|
2080
|
+
if (go !== false && node.kind === "CallExpr") inline(node);
|
|
2025
2081
|
return go;
|
|
2026
2082
|
};
|
|
2027
|
-
|
|
2083
|
+
walk(f, step);
|
|
2028
2084
|
}
|
|
2029
2085
|
function withAssignments(f, body) {
|
|
2030
2086
|
const outerTable = assignmentTable;
|
|
@@ -2049,7 +2105,7 @@ function walkCommandScope(command, visit2) {
|
|
|
2049
2105
|
if (f === PARSE_FAIL) return "parse-fail";
|
|
2050
2106
|
withAssignments(f, () => {
|
|
2051
2107
|
walkWithCalls(f, (node) => {
|
|
2052
|
-
if (node &&
|
|
2108
|
+
if (node && node.kind === "Stmt") trackStatement(node);
|
|
2053
2109
|
return visit2(node);
|
|
2054
2110
|
});
|
|
2055
2111
|
});
|
|
@@ -2057,7 +2113,7 @@ function walkCommandScope(command, visit2) {
|
|
|
2057
2113
|
}
|
|
2058
2114
|
function trackStatement(n) {
|
|
2059
2115
|
try {
|
|
2060
|
-
currentStmtOffset = n.
|
|
2116
|
+
currentStmtOffset = n.pos;
|
|
2061
2117
|
} catch {
|
|
2062
2118
|
currentStmtOffset = Number.MAX_SAFE_INTEGER;
|
|
2063
2119
|
}
|
|
@@ -2073,11 +2129,11 @@ function resolveWordLiteral(w) {
|
|
|
2073
2129
|
return s;
|
|
2074
2130
|
}
|
|
2075
2131
|
function resolveWordPrefix(w) {
|
|
2076
|
-
const
|
|
2132
|
+
const walk3 = (parts) => {
|
|
2077
2133
|
let s = "";
|
|
2078
2134
|
for (const p of parts) {
|
|
2079
2135
|
if (p && typeof p === "object" && Array.isArray(p.Parts) && p.Dollar !== void 0) {
|
|
2080
|
-
const inner =
|
|
2136
|
+
const inner = walk3(p.Parts);
|
|
2081
2137
|
s += inner.prefix;
|
|
2082
2138
|
if (inner.dynamic) return { prefix: s, dynamic: true };
|
|
2083
2139
|
continue;
|
|
@@ -2088,10 +2144,10 @@ function resolveWordPrefix(w) {
|
|
|
2088
2144
|
}
|
|
2089
2145
|
return { prefix: s, dynamic: false };
|
|
2090
2146
|
};
|
|
2091
|
-
return
|
|
2147
|
+
return walk3(w?.Parts || []);
|
|
2092
2148
|
}
|
|
2093
2149
|
function resolvePart(p, inQuotes) {
|
|
2094
|
-
const t =
|
|
2150
|
+
const t = p.kind;
|
|
2095
2151
|
if (t === "Lit") {
|
|
2096
2152
|
const raw = p.Value ?? "";
|
|
2097
2153
|
if (!inQuotes) return raw.replace(/\\(.)/g, "$1");
|
|
@@ -2187,7 +2243,7 @@ function extractShellDestinations(command) {
|
|
|
2187
2243
|
() => walkWithCalls(f, (node) => {
|
|
2188
2244
|
if (!node) return false;
|
|
2189
2245
|
const n = node;
|
|
2190
|
-
if (
|
|
2246
|
+
if (n.kind === "Stmt") {
|
|
2191
2247
|
trackStatement(n);
|
|
2192
2248
|
for (const raw of redirectWords(n)) {
|
|
2193
2249
|
const host2 = devNetRedirectHost(raw);
|
|
@@ -2199,7 +2255,7 @@ function extractShellDestinations(command) {
|
|
|
2199
2255
|
out.push({ host: host2, binary, raw });
|
|
2200
2256
|
}
|
|
2201
2257
|
}
|
|
2202
|
-
if (
|
|
2258
|
+
if (n.kind !== "CallExpr") return true;
|
|
2203
2259
|
const callArgs = n.Args || [];
|
|
2204
2260
|
if (callArgs.length === 0) return true;
|
|
2205
2261
|
const words = callArgs.map((a) => resolveWordLiteral(a));
|
|
@@ -2234,7 +2290,7 @@ function extractShellDestTokens(command) {
|
|
|
2234
2290
|
() => walkWithCalls(f, (node) => {
|
|
2235
2291
|
if (!node) return false;
|
|
2236
2292
|
const n = node;
|
|
2237
|
-
if (
|
|
2293
|
+
if (n.kind === "Stmt") {
|
|
2238
2294
|
trackStatement(n);
|
|
2239
2295
|
for (const raw of redirectWords(n)) {
|
|
2240
2296
|
const host2 = devNetRedirectHost(raw);
|
|
@@ -2246,7 +2302,7 @@ function extractShellDestTokens(command) {
|
|
|
2246
2302
|
out.push({ token: host2, binary });
|
|
2247
2303
|
}
|
|
2248
2304
|
}
|
|
2249
|
-
if (
|
|
2305
|
+
if (n.kind !== "CallExpr") return true;
|
|
2250
2306
|
const callArgs = n.Args || [];
|
|
2251
2307
|
if (callArgs.length === 0) return true;
|
|
2252
2308
|
const words = callArgs.map((a) => resolveWordLiteral(a));
|
|
@@ -2314,11 +2370,11 @@ function isWaivableCleanupTarget(p) {
|
|
|
2314
2370
|
}
|
|
2315
2371
|
function deriveRedirOp(sample) {
|
|
2316
2372
|
try {
|
|
2317
|
-
const f = sharedParser.Parse(sample, "cmd");
|
|
2373
|
+
const f = toPlainTree(sharedParser.Parse(sample, "cmd"));
|
|
2318
2374
|
let op = -1;
|
|
2319
|
-
|
|
2375
|
+
walk(f, (node) => {
|
|
2320
2376
|
const n = node;
|
|
2321
|
-
if (n &&
|
|
2377
|
+
if (n && n.kind === "Redirect" && op < 0) op = n.Op;
|
|
2322
2378
|
return true;
|
|
2323
2379
|
});
|
|
2324
2380
|
return op;
|
|
@@ -2331,7 +2387,7 @@ function collectSameCommandCreations(f) {
|
|
|
2331
2387
|
try {
|
|
2332
2388
|
const stmts = Array.isArray(f?.Stmts) ? f.Stmts : [];
|
|
2333
2389
|
for (const stmt of stmts) {
|
|
2334
|
-
if (!stmt || !stmt.Cmd ||
|
|
2390
|
+
if (!stmt || !stmt.Cmd || stmt.Cmd.kind !== "CallExpr") continue;
|
|
2335
2391
|
const redirs = stmt.Redirs || [];
|
|
2336
2392
|
if (!redirs.some((r) => r && REDIR_HEREDOC_OPS.has(r.Op))) continue;
|
|
2337
2393
|
for (const r of redirs) {
|
|
@@ -2355,10 +2411,10 @@ function isRmCreatedInCommandCleanup(command) {
|
|
|
2355
2411
|
let sawRm = false;
|
|
2356
2412
|
let ok2 = true;
|
|
2357
2413
|
try {
|
|
2358
|
-
|
|
2414
|
+
walk(f, (node) => {
|
|
2359
2415
|
if (!node || !ok2) return false;
|
|
2360
2416
|
const n = node;
|
|
2361
|
-
if (
|
|
2417
|
+
if (n.kind !== "CallExpr") return true;
|
|
2362
2418
|
const args = n.Args || [];
|
|
2363
2419
|
const name = (resolveWordLiteral(args[0]) ?? "").toLowerCase();
|
|
2364
2420
|
if (name !== "rm") return true;
|
|
@@ -2408,10 +2464,10 @@ function analyzeFsOperationImpl(command, depth = 0) {
|
|
|
2408
2464
|
walkWithCalls(f, (node) => {
|
|
2409
2465
|
if (!node || result?.verdict === "block") return false;
|
|
2410
2466
|
const n = node;
|
|
2411
|
-
const nodeType =
|
|
2467
|
+
const nodeType = n.kind;
|
|
2412
2468
|
if (nodeType === "Stmt") {
|
|
2413
2469
|
try {
|
|
2414
|
-
currentStmtOffset = n.
|
|
2470
|
+
currentStmtOffset = n.pos;
|
|
2415
2471
|
} catch {
|
|
2416
2472
|
currentStmtOffset = Number.MAX_SAFE_INTEGER;
|
|
2417
2473
|
}
|
|
@@ -2924,11 +2980,12 @@ function analyzeShellCommand(command) {
|
|
|
2924
2980
|
if (lower2.startsWith("-")) allTokens.push(lower2.replace(/^-+/, ""));
|
|
2925
2981
|
};
|
|
2926
2982
|
try {
|
|
2927
|
-
const f =
|
|
2928
|
-
|
|
2983
|
+
const f = parseShared(command);
|
|
2984
|
+
if (f === PARSE_FAIL) throw new Error("parse failed");
|
|
2985
|
+
walk(f, (node) => {
|
|
2929
2986
|
if (!node) return false;
|
|
2930
2987
|
const n = node;
|
|
2931
|
-
if (
|
|
2988
|
+
if (n.kind !== "CallExpr") return true;
|
|
2932
2989
|
const wordValues2 = (n.Args || []).map((arg) => {
|
|
2933
2990
|
return (arg.Parts || []).map((p) => (p.Value ?? "").replace(/\\(.)/g, "$1")).join("");
|
|
2934
2991
|
}).filter((s) => s.length > 0);
|
|
@@ -3200,7 +3257,7 @@ function runnablePrefix(command) {
|
|
|
3200
3257
|
let text = command;
|
|
3201
3258
|
for (let attempt = 0; attempt < 32; attempt++) {
|
|
3202
3259
|
try {
|
|
3203
|
-
return
|
|
3260
|
+
return toPlainTree(syntax3.NewParser().Parse(text, ""));
|
|
3204
3261
|
} catch (error) {
|
|
3205
3262
|
const message = String(typeof error?.Error === "function" ? error.Error() : error?.message);
|
|
3206
3263
|
const line = Number(/^(\d+):/.exec(message)?.[1]);
|
|
@@ -3268,7 +3325,7 @@ function wordValues(word, state) {
|
|
|
3268
3325
|
out2 = next.slice(0, MAX_ALTERNATIVES);
|
|
3269
3326
|
};
|
|
3270
3327
|
for (const node of nodes) {
|
|
3271
|
-
const kind =
|
|
3328
|
+
const kind = node.kind;
|
|
3272
3329
|
let pieces;
|
|
3273
3330
|
if (kind === "ParamExp") pieces = paramValues(node, quoted);
|
|
3274
3331
|
else if (kind === "DblQuoted") {
|
|
@@ -3336,9 +3393,9 @@ function collectShellFileAccesses(command, context = {}) {
|
|
|
3336
3393
|
}
|
|
3337
3394
|
function substitutions2(word, state, depth) {
|
|
3338
3395
|
if (!word) return;
|
|
3339
|
-
|
|
3396
|
+
walk(word, (node) => {
|
|
3340
3397
|
if (!node) return false;
|
|
3341
|
-
if (["CmdSubst", "ProcSubst"].includes(
|
|
3398
|
+
if (["CmdSubst", "ProcSubst"].includes(node.kind)) {
|
|
3342
3399
|
statements(node.Stmts ?? [], clone(state), depth + 1);
|
|
3343
3400
|
return false;
|
|
3344
3401
|
}
|
|
@@ -3762,7 +3819,7 @@ function collectShellFileAccesses(command, context = {}) {
|
|
|
3762
3819
|
}
|
|
3763
3820
|
function readsScriptFromStdin(stmt) {
|
|
3764
3821
|
const cmd = stmt?.Cmd;
|
|
3765
|
-
if (!cmd ||
|
|
3822
|
+
if (!cmd || cmd.kind !== "CallExpr") return false;
|
|
3766
3823
|
const words = (cmd.Args ?? []).map((a) => resolveWordLiteral(a));
|
|
3767
3824
|
const head = unwrapCommandHead(words);
|
|
3768
3825
|
return SHELL_INTERPRETERS.has(base(words[head])) && shellConsumesPipe(words.slice(head + 1));
|
|
@@ -3776,7 +3833,7 @@ function collectShellFileAccesses(command, context = {}) {
|
|
|
3776
3833
|
}
|
|
3777
3834
|
function producedText(stmt) {
|
|
3778
3835
|
const cmd = stmt?.Cmd;
|
|
3779
|
-
if (!cmd ||
|
|
3836
|
+
if (!cmd || cmd.kind !== "CallExpr") return null;
|
|
3780
3837
|
const words = (cmd.Args ?? []).map((a) => resolveWordLiteral(a));
|
|
3781
3838
|
if (words.some((w) => w === null)) return null;
|
|
3782
3839
|
const head = unwrapCommandHead(words);
|
|
@@ -3795,7 +3852,7 @@ function collectShellFileAccesses(command, context = {}) {
|
|
|
3795
3852
|
if (!stmt || depth > MAX_DEPTH) return;
|
|
3796
3853
|
state.onSuccess = void 0;
|
|
3797
3854
|
if (stmt.Background) state = clone(state);
|
|
3798
|
-
const headWord = stmt.Cmd &&
|
|
3855
|
+
const headWord = stmt.Cmd && stmt.Cmd.kind === "CallExpr" ? base(resolveWordLiteral(stmt.Cmd.Args?.[0])) : "";
|
|
3799
3856
|
const sends = SENDING_STDIN.has(headWord);
|
|
3800
3857
|
for (const redir of stmt.Redirs ?? []) {
|
|
3801
3858
|
substitutions2(redir.Word, state, depth);
|
|
@@ -3811,7 +3868,7 @@ function collectShellFileAccesses(command, context = {}) {
|
|
|
3811
3868
|
const cmd = stmt.Cmd;
|
|
3812
3869
|
if (!cmd) return;
|
|
3813
3870
|
stdinScript(stmt, state, depth);
|
|
3814
|
-
switch (
|
|
3871
|
+
switch (cmd.kind) {
|
|
3815
3872
|
case "CallExpr":
|
|
3816
3873
|
call(cmd, state, depth);
|
|
3817
3874
|
break;
|
|
@@ -3825,7 +3882,7 @@ function collectShellFileAccesses(command, context = {}) {
|
|
|
3825
3882
|
for (; ; ) {
|
|
3826
3883
|
segments2.unshift({ op: node.Op, part: node.Y });
|
|
3827
3884
|
const left = node.X;
|
|
3828
|
-
if (left?.Cmd &&
|
|
3885
|
+
if (left?.Cmd && left.Cmd.kind === "BinaryCmd" && !left.Redirs?.length && !left.Negated && !left.Background)
|
|
3829
3886
|
node = left.Cmd;
|
|
3830
3887
|
else {
|
|
3831
3888
|
segments2.unshift({ op: node.Op, part: left });
|
|
@@ -4121,13 +4178,13 @@ function pipelineTexts(command) {
|
|
|
4121
4178
|
const visit2 = (stmt, depth) => {
|
|
4122
4179
|
if (!stmt || depth > 64) return;
|
|
4123
4180
|
const cmd = stmt.Cmd;
|
|
4124
|
-
if (cmd &&
|
|
4181
|
+
if (cmd && cmd.kind === "BinaryCmd" && SEQUENCE_OPS.has(cmd.Op)) {
|
|
4125
4182
|
visit2(cmd.X, depth + 1);
|
|
4126
4183
|
visit2(cmd.Y, depth + 1);
|
|
4127
4184
|
return;
|
|
4128
4185
|
}
|
|
4129
|
-
const s = at(stmt.
|
|
4130
|
-
const e = at(stmt.
|
|
4186
|
+
const s = at(stmt.pos);
|
|
4187
|
+
const e = at(stmt.end);
|
|
4131
4188
|
const text = s >= 0 && e > s ? command.slice(s, e).replace(/[\s;&]+$/, "") : "";
|
|
4132
4189
|
if (text) out.push(text);
|
|
4133
4190
|
};
|
|
@@ -5289,11 +5346,10 @@ function shellWriteUnseen(command, target) {
|
|
|
5289
5346
|
let unseen = false;
|
|
5290
5347
|
let seen = false;
|
|
5291
5348
|
try {
|
|
5292
|
-
|
|
5349
|
+
walk(f, (node) => {
|
|
5293
5350
|
if (!node || unseen) return false;
|
|
5294
5351
|
const n = node;
|
|
5295
|
-
if (
|
|
5296
|
-
return true;
|
|
5352
|
+
if (n.kind !== "Stmt" || !n.Cmd || n.Cmd.kind !== "CallExpr") return true;
|
|
5297
5353
|
const words = (n.Cmd.Args || []).map(
|
|
5298
5354
|
(a) => resolveWordLiteral(a)
|
|
5299
5355
|
);
|
|
@@ -5348,14 +5404,14 @@ function analyzeGitImpl(command) {
|
|
|
5348
5404
|
seen.add(key);
|
|
5349
5405
|
findings.push(f);
|
|
5350
5406
|
};
|
|
5351
|
-
const
|
|
5352
|
-
const parsed =
|
|
5407
|
+
const walk3 = new GitWalk((inv) => classify(inv).forEach(found), found);
|
|
5408
|
+
const parsed = walk3.run(command, 0);
|
|
5353
5409
|
return { parsed, findings };
|
|
5354
5410
|
}
|
|
5355
5411
|
function isPositionalParam(word) {
|
|
5356
5412
|
let parts = word?.Parts || [];
|
|
5357
|
-
if (parts.length === 1 &&
|
|
5358
|
-
if (parts.length !== 1 ||
|
|
5413
|
+
if (parts.length === 1 && parts[0].kind === "DblQuoted") parts = parts[0].Parts || [];
|
|
5414
|
+
if (parts.length !== 1 || parts[0].kind !== "ParamExp") return false;
|
|
5359
5415
|
return /^(\d+|@|\*)$/.test(parts[0].Param?.Value ?? "");
|
|
5360
5416
|
}
|
|
5361
5417
|
function boundPositionals() {
|
|
@@ -5369,11 +5425,11 @@ function boundPositionals() {
|
|
|
5369
5425
|
function plainParamName(word) {
|
|
5370
5426
|
let parts = word?.Parts || [];
|
|
5371
5427
|
let quoted = false;
|
|
5372
|
-
if (parts.length === 1 &&
|
|
5428
|
+
if (parts.length === 1 && parts[0].kind === "DblQuoted") {
|
|
5373
5429
|
parts = parts[0].Parts || [];
|
|
5374
5430
|
quoted = true;
|
|
5375
5431
|
}
|
|
5376
|
-
if (parts.length !== 1 ||
|
|
5432
|
+
if (parts.length !== 1 || parts[0].kind !== "ParamExp") return null;
|
|
5377
5433
|
const p = parts[0];
|
|
5378
5434
|
if (p.Excl || p.Length || p.Width || p.Index || p.Slice || p.Repl || p.Exp) return null;
|
|
5379
5435
|
const name = p.Param?.Value;
|
|
@@ -5398,7 +5454,7 @@ function resolveGitWords(word) {
|
|
|
5398
5454
|
}
|
|
5399
5455
|
const parts = word?.Parts || [];
|
|
5400
5456
|
const expansion = parts.some((p) => {
|
|
5401
|
-
const t =
|
|
5457
|
+
const t = p.kind;
|
|
5402
5458
|
return t === "ParamExp" || t === "CmdSubst";
|
|
5403
5459
|
});
|
|
5404
5460
|
return expansion ? splitFields(value) : [value];
|
|
@@ -5799,14 +5855,14 @@ function hookCommands(text) {
|
|
|
5799
5855
|
const root = parsed?.hooks;
|
|
5800
5856
|
if (!root || typeof root !== "object") return null;
|
|
5801
5857
|
const out = [];
|
|
5802
|
-
const
|
|
5858
|
+
const walk3 = (v, depth) => {
|
|
5803
5859
|
if (depth > 8 || !v || typeof v !== "object") return;
|
|
5804
5860
|
for (const [k, x] of Object.entries(v)) {
|
|
5805
5861
|
if (k === "command" && typeof x === "string") out.push(x);
|
|
5806
|
-
else
|
|
5862
|
+
else walk3(x, depth + 1);
|
|
5807
5863
|
}
|
|
5808
5864
|
};
|
|
5809
|
-
|
|
5865
|
+
walk3(root, 0);
|
|
5810
5866
|
return out;
|
|
5811
5867
|
}
|
|
5812
5868
|
function hooksDisarmed(text) {
|
|
@@ -5973,9 +6029,9 @@ function isDupInOp(op) {
|
|
|
5973
6029
|
if (dupInOp === null) {
|
|
5974
6030
|
let found = -1;
|
|
5975
6031
|
try {
|
|
5976
|
-
|
|
6032
|
+
walk(parseShared("cat <&3"), (node) => {
|
|
5977
6033
|
const n = node;
|
|
5978
|
-
if (found === -1 && n &&
|
|
6034
|
+
if (found === -1 && n && n.kind === "Stmt" && n.Redirs?.[0]) {
|
|
5979
6035
|
found = n.Redirs[0].Op;
|
|
5980
6036
|
}
|
|
5981
6037
|
return true;
|
|
@@ -5996,10 +6052,10 @@ function analyzeOutbound(command) {
|
|
|
5996
6052
|
return hit;
|
|
5997
6053
|
}
|
|
5998
6054
|
const findings = [];
|
|
5999
|
-
const
|
|
6000
|
-
const parsed =
|
|
6055
|
+
const walk3 = new OutboundWalk(findings);
|
|
6056
|
+
const parsed = walk3.run(command, 0);
|
|
6001
6057
|
const envOut = findings.find((f) => f.checkId === "data.env-exfil");
|
|
6002
|
-
if (envOut) envOut.destinations =
|
|
6058
|
+
if (envOut) envOut.destinations = walk3.envDestinations();
|
|
6003
6059
|
const computed = { parsed, findings };
|
|
6004
6060
|
if (cache2.size >= CACHE_MAX2) {
|
|
6005
6061
|
const oldest = cache2.keys().next().value;
|
|
@@ -6015,7 +6071,7 @@ function wordShape(word) {
|
|
|
6015
6071
|
const parts = word?.Parts || [];
|
|
6016
6072
|
let out = "";
|
|
6017
6073
|
for (const p of parts) {
|
|
6018
|
-
const t =
|
|
6074
|
+
const t = p.kind;
|
|
6019
6075
|
if (t === "Lit" || t === "SglQuoted") out += p.Value ?? "";
|
|
6020
6076
|
else if (t === "DblQuoted") out += wordShape(p);
|
|
6021
6077
|
else out += "X";
|
|
@@ -6150,16 +6206,16 @@ function declDumpsEnv(decl) {
|
|
|
6150
6206
|
}
|
|
6151
6207
|
function paramName(word) {
|
|
6152
6208
|
let parts = word?.Parts || [];
|
|
6153
|
-
if (parts.length === 1 &&
|
|
6154
|
-
if (parts.length !== 1 ||
|
|
6209
|
+
if (parts.length === 1 && parts[0].kind === "DblQuoted") parts = parts[0].Parts || [];
|
|
6210
|
+
if (parts.length !== 1 || parts[0].kind !== "ParamExp") return null;
|
|
6155
6211
|
return parts[0].Param?.Value ?? null;
|
|
6156
6212
|
}
|
|
6157
6213
|
function refersTo(word, names) {
|
|
6158
6214
|
let hit = false;
|
|
6159
|
-
|
|
6215
|
+
walk(word, (node) => {
|
|
6160
6216
|
if (hit || !node) return false;
|
|
6161
6217
|
const n = node;
|
|
6162
|
-
if (
|
|
6218
|
+
if (n.kind === "ParamExp" && names.has(n.Param?.Value)) hit = true;
|
|
6163
6219
|
return !hit;
|
|
6164
6220
|
});
|
|
6165
6221
|
return hit;
|
|
@@ -6167,10 +6223,10 @@ function refersTo(word, names) {
|
|
|
6167
6223
|
function runsCommandsIn(stmt) {
|
|
6168
6224
|
const readVars = /* @__PURE__ */ new Set();
|
|
6169
6225
|
let hit = false;
|
|
6170
|
-
|
|
6226
|
+
walk(stmt, (node) => {
|
|
6171
6227
|
if (hit || !node) return false;
|
|
6172
6228
|
const n = node;
|
|
6173
|
-
if (
|
|
6229
|
+
if (n.kind !== "CallExpr") return true;
|
|
6174
6230
|
const words = wordsOf(n);
|
|
6175
6231
|
if (words.length === 0) return true;
|
|
6176
6232
|
const { head, at } = headOf(words);
|
|
@@ -6747,7 +6803,7 @@ function localContexts(file2) {
|
|
|
6747
6803
|
if (depth > 64) return null;
|
|
6748
6804
|
if (!stmt || stmt.Background || stmt.Negated || stmt.Redirs?.length) return null;
|
|
6749
6805
|
const cmd = stmt.Cmd;
|
|
6750
|
-
const kind =
|
|
6806
|
+
const kind = cmd.kind;
|
|
6751
6807
|
if (kind === "BinaryCmd" && cmd.Op === andOp) {
|
|
6752
6808
|
const next = visit2(cmd.X, steps, depth + 1);
|
|
6753
6809
|
return next === null ? null : visit2(cmd.Y, next, depth + 1);
|
|
@@ -6763,7 +6819,7 @@ function localContexts(file2) {
|
|
|
6763
6819
|
)
|
|
6764
6820
|
))
|
|
6765
6821
|
return null;
|
|
6766
|
-
contexts.set(cmd.
|
|
6822
|
+
contexts.set(cmd.pos, steps);
|
|
6767
6823
|
return null;
|
|
6768
6824
|
}
|
|
6769
6825
|
if (file2.Stmts?.length === 1) visit2(file2.Stmts[0], []);
|
|
@@ -6774,12 +6830,12 @@ function collect(command, out, depth) {
|
|
|
6774
6830
|
const f = parseShared(command);
|
|
6775
6831
|
if (typeof f === "symbol") return;
|
|
6776
6832
|
const contexts = depth === 0 ? localContexts(f) : /* @__PURE__ */ new Map();
|
|
6777
|
-
|
|
6778
|
-
if (node &&
|
|
6833
|
+
walk(f, (node) => {
|
|
6834
|
+
if (node && node.kind === "CallExpr") {
|
|
6779
6835
|
const words = (node.Args || []).map((a) => resolveWordLiteral(a));
|
|
6780
6836
|
const start = out.length;
|
|
6781
6837
|
if (words.length > 0) fromCall(words, out, depth);
|
|
6782
|
-
const steps = contexts.get(node.
|
|
6838
|
+
const steps = contexts.get(node.pos);
|
|
6783
6839
|
for (let i = start; i < out.length; i++) {
|
|
6784
6840
|
if (["npx", "bunx", "npm exec"].includes(out[i].manager)) {
|
|
6785
6841
|
if (!steps) out[i].localCwd = null;
|
|
@@ -6826,7 +6882,7 @@ function collect2(command, out, depth) {
|
|
|
6826
6882
|
try {
|
|
6827
6883
|
walkCommandScope(command, (node) => {
|
|
6828
6884
|
const n = node;
|
|
6829
|
-
if (!n ||
|
|
6885
|
+
if (!n || n.kind !== "CallExpr") return true;
|
|
6830
6886
|
const words = (n.Args || []).map((a) => resolveWordLiteral(a));
|
|
6831
6887
|
if (words.length === 0) return true;
|
|
6832
6888
|
const prefix = (n.Assigns || []).filter((a) => a?.Name?.Value).map(
|
|
@@ -8256,12 +8312,12 @@ function extractContentDestinations(text) {
|
|
|
8256
8312
|
function extractArgDestinations(args) {
|
|
8257
8313
|
const out = /* @__PURE__ */ new Set();
|
|
8258
8314
|
try {
|
|
8259
|
-
|
|
8315
|
+
walk2(args, void 0, out, 0);
|
|
8260
8316
|
} catch {
|
|
8261
8317
|
}
|
|
8262
8318
|
return [...out];
|
|
8263
8319
|
}
|
|
8264
|
-
function
|
|
8320
|
+
function walk2(value, key, out, depth) {
|
|
8265
8321
|
if (out.size >= MAX_DESTINATIONS || depth > 12) return;
|
|
8266
8322
|
if (typeof value === "string") {
|
|
8267
8323
|
const s = value.length > MAX_SCAN_CHARS ? value.slice(0, MAX_SCAN_CHARS) : value;
|
|
@@ -8280,12 +8336,12 @@ function walk(value, key, out, depth) {
|
|
|
8280
8336
|
return;
|
|
8281
8337
|
}
|
|
8282
8338
|
if (Array.isArray(value)) {
|
|
8283
|
-
for (const v of value)
|
|
8339
|
+
for (const v of value) walk2(v, key, out, depth + 1);
|
|
8284
8340
|
return;
|
|
8285
8341
|
}
|
|
8286
8342
|
if (value && typeof value === "object") {
|
|
8287
8343
|
for (const [k, v] of Object.entries(value))
|
|
8288
|
-
|
|
8344
|
+
walk2(v, k, out, depth + 1);
|
|
8289
8345
|
}
|
|
8290
8346
|
}
|
|
8291
8347
|
function destinationDomain(dest) {
|
|
@@ -8937,9 +8993,9 @@ function classifyCall(head, args, prefixes2, env, state, out) {
|
|
|
8937
8993
|
function walkCalls(command, out, depth, state) {
|
|
8938
8994
|
const f = parseShared(command);
|
|
8939
8995
|
if (typeof f === "symbol" || !f) return;
|
|
8940
|
-
|
|
8996
|
+
walk(f, (node) => {
|
|
8941
8997
|
if (!node) return false;
|
|
8942
|
-
const type =
|
|
8998
|
+
const type = node.kind;
|
|
8943
8999
|
if (type === "DeclClause") {
|
|
8944
9000
|
for (const a of node.Args || []) if (a?.Name?.Value) state.env.add(a.Name.Value);
|
|
8945
9001
|
return true;
|
|
@@ -9343,7 +9399,7 @@ function matchCanaryArgs(args, values) {
|
|
|
9343
9399
|
if (values.length === 0) return null;
|
|
9344
9400
|
const needles = prepare(values);
|
|
9345
9401
|
if (needles.length === 0) return null;
|
|
9346
|
-
const
|
|
9402
|
+
const walk3 = (v, depth, fieldPath) => {
|
|
9347
9403
|
if (depth > MAX_DEPTH4) return null;
|
|
9348
9404
|
if (typeof v === "string") {
|
|
9349
9405
|
const hit = matchPrepared(v, needles);
|
|
@@ -9352,31 +9408,31 @@ function matchCanaryArgs(args, values) {
|
|
|
9352
9408
|
const trimmed = v.trim();
|
|
9353
9409
|
if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
|
9354
9410
|
try {
|
|
9355
|
-
return
|
|
9411
|
+
return walk3(JSON.parse(v), depth + 1, fieldPath);
|
|
9356
9412
|
} catch {
|
|
9357
9413
|
}
|
|
9358
9414
|
}
|
|
9359
9415
|
}
|
|
9360
9416
|
return null;
|
|
9361
9417
|
}
|
|
9362
|
-
if (typeof v === "number" && Number.isFinite(v)) return
|
|
9418
|
+
if (typeof v === "number" && Number.isFinite(v)) return walk3(String(v), depth, fieldPath);
|
|
9363
9419
|
if (Array.isArray(v)) {
|
|
9364
9420
|
for (let i = 0; i < v.length; i++) {
|
|
9365
|
-
const h =
|
|
9421
|
+
const h = walk3(v[i], depth + 1, `${fieldPath}[${i}]`);
|
|
9366
9422
|
if (h) return h;
|
|
9367
9423
|
}
|
|
9368
9424
|
return null;
|
|
9369
9425
|
}
|
|
9370
9426
|
if (v && typeof v === "object") {
|
|
9371
9427
|
for (const [k, child] of Object.entries(v)) {
|
|
9372
|
-
const h =
|
|
9428
|
+
const h = walk3(child, depth + 1, fieldPath ? `${fieldPath}.${k}` : k);
|
|
9373
9429
|
if (h) return h;
|
|
9374
9430
|
}
|
|
9375
9431
|
}
|
|
9376
9432
|
return null;
|
|
9377
9433
|
};
|
|
9378
9434
|
try {
|
|
9379
|
-
return
|
|
9435
|
+
return walk3(args, 0, "");
|
|
9380
9436
|
} catch (e) {
|
|
9381
9437
|
console.error(
|
|
9382
9438
|
"[node9 engine] canary args walk failed:",
|
|
@@ -9809,26 +9865,20 @@ function* stringLeavesWithKeys(obj, depth = 0, key) {
|
|
|
9809
9865
|
}
|
|
9810
9866
|
for (const [k, v] of Object.entries(obj)) yield* stringLeavesWithKeys(v, depth + 1, k);
|
|
9811
9867
|
}
|
|
9812
|
-
var
|
|
9868
|
+
var import_picomatch, import_safe_regex2, import_picomatch2, import_mvdan_sh, import_mvdan_sh2, import_mvdan_sh3, import_safe_regex22, import_crypto2, import_crypto3, import_crypto4, import_crypto5, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, finance_default, identity_default, publishing_default, settings_default, branches_default, ACTION_TEMPLATES, ACTION_CHECKS, GAP, prog, httpTo, httpWrite, HTTP_WRITE, HTTP_WRITE_ANY, NOT_STRIPE_TEST, postTo, STRIPE_OBJECTS, APP_SHIELD_SPECS, APP_SHIELDS, APP_SHIELD_NAMES, APP_SERVER_GLOB, GENERAL_ACTION_RULES, REPLACED_SHIELDS, BUILTIN_SHIELDS, SSRF_MAX_HOST, METADATA_ADDRESSES, STRICT_TIERS, METADATA_HOSTNAMES, v4Octets, TIER_REASON, CHECK_TEXT, LEVELS, MODE_VALUES, SOFT, STRICT, BLOCK_ALL, LEVEL_CHECKS, VERDICTS, CHECK_VERDICT_RANK, CHECK_GROUPS, ALL, BUILTIN_DANGEROUS_WORDS, PRODUCT_CHECKS, SHIELD_RULE_NAME, SHIELD_RULES_FOLDED_INTO_PRODUCT, CHECKS, CHECK_BY_ID, LEGACY_TO_ID, PROJECT_JAIL_BLOCK_READ, OVERRIDE_PREFIX, LABEL_WRAPPERS, LEGACY_READERS, syntax, KINDS, STRUCTS, unknownKinds, INVISIBLE_RE, INVISIBLE_ONE, HOMOGLYPHS, HOMOGLYPH_RE, MAX_BLOBS, MIN_DECODED, BASE64_RUN_RE, HEX_RUN_RE, HEX_ESCAPE_RUN_RE, SYSTEM_AUTH_FILES, SYSTEM_AUTH_DIRS, PASSWORD_HASH_FILES, STARTUP_NAMES, SYSTEM_DIRS, SYSTEM_DIRS_EXEMPT, UPLOAD_SYSTEM_DIRS, AGENT_INSTRUCTION_FILES, AGENT_INSTRUCTION_DIRS, LEGACY_SYSTEM_PATH_RE, MAX_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, DLP_SCAN_LIMITS, BATCH_SEPARATOR, ENCODED_RUN_RE, IMPERATIVE, SECRET_TARGET, GUARD, SIGNALS, DESTINATION_RE, REUSED_SIGNALS, NEVER_NEGATED, NEGATION_BEFORE, RDR_IN, COPY_VERBS2, LITERAL_VERBS, GIT_PRESCREEN_RE, MAX_PAYLOAD_DEPTH2, MAX_ALIAS_DEPTH, CACHE_MAX, cache, PAYLOAD_SHELLS, CONFIG_SCOPE_FLAGS, CONFIG_VALUE_FLAGS, GLOBAL_VALUE_OPTIONS, GLOBAL_FLAG_OPTIONS, MAX_LOOP_ITEMS, GitWalk, POSITIONAL2, FORCE_PUSH, DESTRUCTIVE, UNREADABLE, PUSH_LONG_VALUE, BRANCH_LONG_VALUE, TAG_LONG_VALUE, CLEAN_LONG_VALUE, HOOKS_BYPASS, NO_VERIFY_SUBS, HOOK_KILL_SWITCHES, COMMIT_LONG_VALUE, CHMOD_OPTION_RE, STAGE_ALL, ADD_LONG_VALUE, CHECK_ID, RULE_NAME, INERT_LINE_RE, CHECK_ID2, INSTRUCTION_NAMES, SETTINGS_RE, HOOK_WORDS_RE, SHELLS, NETCATS, NET_CLIENTS, SENDERS, NETCAT_EXEC_LONG, DECL_DUMPS, PROC_ENVIRON_RE, DEV_NET_RE, SOCAT_PROGRAM_RE, SOCAT_NET_RE, PRESCREEN_RE, MAX_PAYLOAD_DEPTH3, CACHE_MAX2, cache2, dupInOp, REVERSE_SHELL_DESCRIPTION, ENV_EXFIL_DESCRIPTION, DYNAMIC_WORD, MULTICALL, SHELL_VALUE_OPTIONS, INTERPRETERS, OutboundWalk, NPM_INSTALL_VERBS, NPM_EXEC_VERBS, PNPM_INSTALL_VERBS, YARN_INSTALL_VERBS, BUN_INSTALL_VERBS, DLX_VERBS, NON_REGISTRY_RE, NPM_NAME_RE, EXACT_SEMVER_RE, NPM_VALUE_FLAGS, PNPM_VALUE_FLAGS, YARN_VALUE_FLAGS, BUN_VALUE_FLAGS, PIP_VALUE_FLAGS, UV_VALUE_FLAGS, POETRY_VALUE_FLAGS, PIPENV_VALUE_FLAGS, PIPX_VALUE_FLAGS, PY_SPEC_RE, PY_EXACT_RE, EXACT_VERSION_RE, GEM_NAME_RE, GEM_VALUE_FLAGS, BUNDLE_VALUE_FLAGS, CRATE_NAME_RE, CARGO_ADD_VALUE_FLAGS, CARGO_INSTALL_VALUE_FLAGS, GO_VALUE_FLAGS, GO_PATH_RE, NUGET_NAME_RE, DOTNET_ADD_VALUE_FLAGS, DOTNET_TOOL_VALUE_FLAGS, NUGET_VALUE_FLAGS, MAVEN_COORD_RE, COMPOSER_NAME_RE, COMPOSER_VALUE_FLAGS, EDITOR_HEADS, EXTENSION_ID_RE, MANAGER_HEADS, SHELLS2, MAX_NESTING, SHELL_VALUE_FLAGS, NPM_FAMILY, PIP_FAMILY, NPM_FETCH_VERBS, PIP_FETCH_VERBS, NPM_FLAGS, PIP_FLAGS, NPM_VARS, PIP_VARS, PRESCREEN_RE2, PIP_ATTACHED_RE, MAX_PAYLOAD_DEPTH4, PUBLIC_REGISTRY_HOSTS, TUNING_FIELDS, MAP_GOVERNED, KNOB_GOVERNED, CONFIGURABLE_CHECK_IDS, DEFAULT_EGRESS_ALLOWLIST, PRIVATE_HOST_SUFFIXES, SOURCE_COMMANDS, SINK_COMMANDS, OBFUSCATORS, SENSITIVE_PATTERNS, FLAGS_WITH_VALUES, DESTINATION_ARGS, MCP_DESTINATION_KEYS, ARGUMENT_SCAN_LIMITS, RUN_CHAR, runChar, VERDICT_RANK, SQL_DML_KEYWORDS, MAX_DESTINATIONS, MAX_SCAN_CHARS, EMAIL_RE, URL_RE, IBAN_RE, RECIPIENT_KEYS, DESTRUCTIVE_VERB_RE, PII_EMAIL_RE, PII_SSN_RE, PII_PHONE_RE, PII_CC16_RE, PII_CC15_RE, PII_CC14_RE, PII_CC13_RE, PII_CC19_RE, CARD_PATTERNS, PII_ROUTING_RE, ROUTING_LABEL_RE, ROUTING_LABEL_WINDOW, NATIONAL_ID_SHAPES, NATIONAL_ID_LABEL_RE, NATIONAL_ID_LABEL_CS_RE, idLabelIn, NATIONAL_ID_LABEL_WINDOW, NATIONAL_ID_GUARD_RE, PII_IBAN_RE, REALTIME_PII_PATTERNS, MAX_PII_SCAN_BYTES, IBAN_DESTINATION_KEYS, WHOLE_IBAN_RE, JSON_ESCAPE_RE, JSON_ESCAPES, MASKED_KINDS, ENTRY_RE, IBAN_TEXT_RE, EMAIL_TEXT_RE, ACCOUNT_KEY_RE, ACCOUNT_VALUE_RE, MAX_MASK_DEPTH, EMPTY, CURL_SEND_FLAGS, WGET_SEND_FLAGS, IWR_SEND_FLAGS, WRITE_METHODS, GH_READ, GH_WRITE, GH_HOSTNAME_FLAGS, GH_REPO_VALUE_FLAGS, GH_API_VALUE_FLAGS, GIT_FETCH, PUBLISH, SHELLS3, OWN_HEADS, MAX_DEPTH3, CWD_CHANGERS, TERMINAL_ESCAPE_RE, CONTROL_CHAR_RE, SHIELD_RULE, ACTION_RULE, LOOP_MAX_RECORDS, FINDING_TO_SIGNAL, SCAN_SIGNAL_WEIGHTS, LOOP_THRESHOLD_FOR_WASTE, MAX_BLAST_PATH, DESTRUCTIVE_OP_RE, SENSITIVE_PATH_RE, FILE_TOOLS, CANARY_MIN_LENGTH, MAX_TEXT, MAX_DEPTH4, MAX_JSON_PARSE, URL_DEPTH, B64_DEPTH, MIN_SEGMENT, SEPARATORS, stripSeparators, looksText, CANARY_DECODERS, VIEWS, LONG_OUTPUT_THRESHOLD_BYTES, CANONICAL_EXTRACTOR_VERSION, DEDUPE_PREVIEW_LEN, ENGINE_VERSION;
|
|
9813
9869
|
var init_dist = __esm({
|
|
9814
9870
|
"packages/policy-engine/dist/index.mjs"() {
|
|
9815
9871
|
"use strict";
|
|
9816
|
-
import_mvdan_sh = __toESM(require("mvdan-sh"), 1);
|
|
9817
9872
|
import_picomatch = __toESM(require("picomatch"), 1);
|
|
9818
9873
|
import_safe_regex2 = __toESM(require("safe-regex2"), 1);
|
|
9819
9874
|
import_picomatch2 = __toESM(require("picomatch"), 1);
|
|
9875
|
+
import_mvdan_sh = __toESM(require("mvdan-sh"), 1);
|
|
9820
9876
|
import_mvdan_sh2 = __toESM(require("mvdan-sh"), 1);
|
|
9821
9877
|
import_mvdan_sh3 = __toESM(require("mvdan-sh"), 1);
|
|
9822
9878
|
import_safe_regex22 = __toESM(require("safe-regex2"), 1);
|
|
9823
9879
|
import_crypto2 = require("crypto");
|
|
9824
9880
|
import_crypto3 = require("crypto");
|
|
9825
|
-
import_mvdan_sh4 = __toESM(require("mvdan-sh"), 1);
|
|
9826
|
-
import_mvdan_sh5 = __toESM(require("mvdan-sh"), 1);
|
|
9827
|
-
import_mvdan_sh6 = __toESM(require("mvdan-sh"), 1);
|
|
9828
|
-
import_mvdan_sh7 = __toESM(require("mvdan-sh"), 1);
|
|
9829
|
-
import_mvdan_sh8 = __toESM(require("mvdan-sh"), 1);
|
|
9830
9881
|
import_crypto4 = require("crypto");
|
|
9831
|
-
import_mvdan_sh9 = __toESM(require("mvdan-sh"), 1);
|
|
9832
9882
|
import_crypto5 = __toESM(require("crypto"), 1);
|
|
9833
9883
|
MAX_REGEX_LENGTH = 256;
|
|
9834
9884
|
REGEX_CACHE_MAX = 500;
|
|
@@ -12490,6 +12540,61 @@ var init_dist = __esm({
|
|
|
12490
12540
|
return s.untrustedContent.afterExposure === "log-only" ? "log" : "review";
|
|
12491
12541
|
}
|
|
12492
12542
|
};
|
|
12543
|
+
({ syntax } = import_mvdan_sh2.default);
|
|
12544
|
+
KINDS = {
|
|
12545
|
+
File: { nodes: ["Stmts"] },
|
|
12546
|
+
Stmt: { nodes: ["Cmd", "Redirs"], scalars: ["Negated", "Background", "Coprocess"] },
|
|
12547
|
+
CallExpr: { nodes: ["Assigns", "Args"] },
|
|
12548
|
+
Assign: { nodes: ["Name", "Value", "Index", "Array"], scalars: ["Append", "Naked"] },
|
|
12549
|
+
Word: { nodes: ["Parts"] },
|
|
12550
|
+
Lit: { scalars: ["Value"] },
|
|
12551
|
+
SglQuoted: { scalars: ["Value", "Dollar"] },
|
|
12552
|
+
DblQuoted: { nodes: ["Parts"], scalars: ["Dollar"] },
|
|
12553
|
+
ParamExp: {
|
|
12554
|
+
nodes: ["Param", "Index", "Slice", "Repl", "Exp"],
|
|
12555
|
+
scalars: ["Short", "Excl", "Length", "Width"]
|
|
12556
|
+
},
|
|
12557
|
+
CmdSubst: { nodes: ["Stmts"], scalars: ["Backquotes", "TempFile", "ReplyVar"] },
|
|
12558
|
+
ArithmExp: { nodes: ["X"], scalars: ["Bracket", "Unsigned"] },
|
|
12559
|
+
ArithmCmd: { nodes: ["X"], scalars: ["Unsigned"] },
|
|
12560
|
+
ProcSubst: { nodes: ["Stmts"], scalars: ["Op"] },
|
|
12561
|
+
ExtGlob: { nodes: ["Pattern"], scalars: ["Op"] },
|
|
12562
|
+
BraceExp: { nodes: ["Elems"], scalars: ["Sequence"] },
|
|
12563
|
+
// only with SplitBraces, unused here
|
|
12564
|
+
BinaryCmd: { nodes: ["X", "Y"], scalars: ["Op"] },
|
|
12565
|
+
BinaryArithm: { nodes: ["X", "Y"], scalars: ["Op"] },
|
|
12566
|
+
UnaryArithm: { nodes: ["X"], scalars: ["Op", "Post"] },
|
|
12567
|
+
ParenArithm: { nodes: ["X"] },
|
|
12568
|
+
BinaryTest: { nodes: ["X", "Y"], scalars: ["Op"] },
|
|
12569
|
+
UnaryTest: { nodes: ["X"], scalars: ["Op"] },
|
|
12570
|
+
ParenTest: { nodes: ["X"] },
|
|
12571
|
+
Redirect: { nodes: ["N", "Word", "Hdoc"], scalars: ["Op"] },
|
|
12572
|
+
Block: { nodes: ["Stmts"] },
|
|
12573
|
+
Subshell: { nodes: ["Stmts"] },
|
|
12574
|
+
IfClause: { nodes: ["Cond", "Then", "Else"] },
|
|
12575
|
+
WhileClause: { nodes: ["Cond", "Do"], scalars: ["Until"] },
|
|
12576
|
+
ForClause: { nodes: ["Loop", "Do"], scalars: ["Select"] },
|
|
12577
|
+
WordIter: { nodes: ["Name", "Items"] },
|
|
12578
|
+
CStyleLoop: { nodes: ["Init", "Cond", "Post"] },
|
|
12579
|
+
CaseClause: { nodes: ["Word", "Items"] },
|
|
12580
|
+
CaseItem: { nodes: ["Patterns", "Stmts"], scalars: ["Op"] },
|
|
12581
|
+
FuncDecl: { nodes: ["Name", "Body"], scalars: ["RsrvWord"] },
|
|
12582
|
+
DeclClause: { nodes: ["Variant", "Args"], notWalked: ["Variant"] },
|
|
12583
|
+
LetClause: { nodes: ["Exprs"] },
|
|
12584
|
+
TimeClause: { nodes: ["Stmt"], scalars: ["PosixFormat"] },
|
|
12585
|
+
CoprocClause: { nodes: ["Name", "Stmt"] },
|
|
12586
|
+
TestClause: { nodes: ["X"] },
|
|
12587
|
+
TestDecl: { nodes: ["Description", "Body"] },
|
|
12588
|
+
ArrayExpr: { nodes: ["Elems"] },
|
|
12589
|
+
ArrayElem: { nodes: ["Index", "Value"] },
|
|
12590
|
+
Comment: { scalars: ["Text"] }
|
|
12591
|
+
};
|
|
12592
|
+
STRUCTS = {
|
|
12593
|
+
"ParamExp.Slice": { nodes: ["Offset", "Length"], notWalked: ["Offset", "Length"] },
|
|
12594
|
+
"ParamExp.Repl": { nodes: ["Orig", "With"], scalars: ["All"] },
|
|
12595
|
+
"ParamExp.Exp": { nodes: ["Word"], scalars: ["Op"] }
|
|
12596
|
+
};
|
|
12597
|
+
unknownKinds = /* @__PURE__ */ new Set();
|
|
12493
12598
|
INVISIBLE_RE = /[͏----︀-️]|[\u{E0000}-\u{E007F}]/gu;
|
|
12494
12599
|
INVISIBLE_ONE = new RegExp(`^(?:${INVISIBLE_RE.source.split("|")[0]})$`, "u");
|
|
12495
12600
|
HOMOGLYPHS = {
|
|
@@ -12775,8 +12880,8 @@ var init_dist = __esm({
|
|
|
12775
12880
|
php: PHP
|
|
12776
12881
|
};
|
|
12777
12882
|
STRING_LITERAL = /(['"`])((?:\\.|(?!\1)[^\\\n]){1,4096})\1/g;
|
|
12778
|
-
({ syntax } =
|
|
12779
|
-
sharedParser =
|
|
12883
|
+
({ syntax: syntax2 } = import_mvdan_sh.default);
|
|
12884
|
+
sharedParser = syntax2.NewParser();
|
|
12780
12885
|
MESSAGE_FLAGS = /* @__PURE__ */ new Set([
|
|
12781
12886
|
"-m",
|
|
12782
12887
|
"--message",
|
|
@@ -12810,7 +12915,7 @@ var init_dist = __esm({
|
|
|
12810
12915
|
NORMALIZE_CACHE_MAX = 5e3;
|
|
12811
12916
|
normalizeCache = /* @__PURE__ */ new Map();
|
|
12812
12917
|
codeBodiesCache = /* @__PURE__ */ new Map();
|
|
12813
|
-
AST_CACHE_MAX =
|
|
12918
|
+
AST_CACHE_MAX = 256;
|
|
12814
12919
|
astCache = /* @__PURE__ */ new Map();
|
|
12815
12920
|
PARSE_FAIL = /* @__PURE__ */ Symbol("parse-fail");
|
|
12816
12921
|
INTERPRETER_LINE = /^#![ \t]*[\w./+-]+(?:[ \t]+[\w./+=:-]+)*[ \t]*\r?(?=\n|$)/;
|
|
@@ -13795,7 +13900,7 @@ var init_dist = __esm({
|
|
|
13795
13900
|
ARGV_FIELDS = ["args", "argv"];
|
|
13796
13901
|
BARE_WORD = /^[A-Za-z0-9_./+:@%,-]+$/;
|
|
13797
13902
|
MAX_ARGV_CHARACTERS = 262144;
|
|
13798
|
-
({ syntax:
|
|
13903
|
+
({ syntax: syntax3 } = import_mvdan_sh3.default);
|
|
13799
13904
|
MAX_ALTERNATIVES = 64;
|
|
13800
13905
|
MAX_CALL_DEPTH2 = 4;
|
|
13801
13906
|
MAX_CALL_WALKS2 = 256;
|
|
@@ -13887,7 +13992,6 @@ var init_dist = __esm({
|
|
|
13887
13992
|
);
|
|
13888
13993
|
exempt = (p) => SYSTEM_DIRS_EXEMPT.some((d) => within(p, d));
|
|
13889
13994
|
RANK = { allow: 0, review: 1, block: 2 };
|
|
13890
|
-
({ syntax: syntax3 } = import_mvdan_sh.default);
|
|
13891
13995
|
SEQUENCE_OPS = (() => {
|
|
13892
13996
|
const ops = /* @__PURE__ */ new Set();
|
|
13893
13997
|
for (const text of ["x && y", "x || y"]) {
|
|
@@ -17056,7 +17160,6 @@ zoo`.split("\n");
|
|
|
17056
17160
|
);
|
|
17057
17161
|
NEVER_NEGATED = /* @__PURE__ */ new Set([SIGNALS[1].any[0]]);
|
|
17058
17162
|
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;
|
|
17059
|
-
({ syntax: syntax4 } = import_mvdan_sh4.default);
|
|
17060
17163
|
RDR_IN = (() => {
|
|
17061
17164
|
try {
|
|
17062
17165
|
return parseShared("x <y").Stmts[0].Redirs[0].Op;
|
|
@@ -17085,7 +17188,6 @@ zoo`.split("\n");
|
|
|
17085
17188
|
"tar"
|
|
17086
17189
|
]);
|
|
17087
17190
|
LITERAL_VERBS = /* @__PURE__ */ new Set(["echo", "printf", ":", "true", "touch", "sed"]);
|
|
17088
|
-
({ syntax: syntax5 } = import_mvdan_sh5.default);
|
|
17089
17191
|
GIT_PRESCREEN_RE = /git|husky/i;
|
|
17090
17192
|
MAX_PAYLOAD_DEPTH2 = 8;
|
|
17091
17193
|
MAX_ALIAS_DEPTH = 5;
|
|
@@ -17154,7 +17256,7 @@ zoo`.split("\n");
|
|
|
17154
17256
|
visit(node, depth) {
|
|
17155
17257
|
if (!node) return false;
|
|
17156
17258
|
const n = node;
|
|
17157
|
-
const type =
|
|
17259
|
+
const type = n.kind;
|
|
17158
17260
|
if (type === "Stmt") {
|
|
17159
17261
|
this.visitHeredocShell(n, depth);
|
|
17160
17262
|
return true;
|
|
@@ -17162,7 +17264,7 @@ zoo`.split("\n");
|
|
|
17162
17264
|
if (type === "ForClause") {
|
|
17163
17265
|
const loop = n.Loop;
|
|
17164
17266
|
const name = loop?.Name?.Value;
|
|
17165
|
-
if (name &&
|
|
17267
|
+
if (name && loop.kind === "WordIter") {
|
|
17166
17268
|
const items = (loop.Items || []).map((w) => resolveWordLiteral(w));
|
|
17167
17269
|
const literal = items.length > 0 && items.every((v) => v !== null);
|
|
17168
17270
|
this.loopVars.set(name, literal ? items.slice(0, MAX_LOOP_ITEMS) : null);
|
|
@@ -17229,7 +17331,7 @@ zoo`.split("\n");
|
|
|
17229
17331
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
17230
17332
|
visitHeredocShell(stmt, depth) {
|
|
17231
17333
|
const cmd = stmt.Cmd;
|
|
17232
|
-
if (!cmd ||
|
|
17334
|
+
if (!cmd || cmd.kind !== "CallExpr" || depth >= MAX_PAYLOAD_DEPTH2) return;
|
|
17233
17335
|
const redirs = stmt.Redirs || [];
|
|
17234
17336
|
const hdoc = redirs.find((r) => r?.Hdoc && (r.N == null || r.N.Value === "0"));
|
|
17235
17337
|
if (!hdoc) return;
|
|
@@ -17483,7 +17585,6 @@ zoo`.split("\n");
|
|
|
17483
17585
|
]);
|
|
17484
17586
|
SETTINGS_RE = /\/(?:\.claude\/settings(?:\.local)?\.json|\.gemini\/settings\.json|\.codex\/hooks\.json|\.cursor\/hooks\.json)$/i;
|
|
17485
17587
|
HOOK_WORDS_RE = /"hooks"|\b(PreToolUse|PostToolUse|UserPromptSubmit|Notification|Stop|SubagentStop|SessionStart|SessionEnd|PreCompact|BeforeTool|AfterTool)\b|"command"\s*:/;
|
|
17486
|
-
({ syntax: syntax6 } = import_mvdan_sh6.default);
|
|
17487
17588
|
SHELLS = /* @__PURE__ */ new Set([...SHELL_INTERPRETERS, "mksh", "ash", "csh", "tcsh"]);
|
|
17488
17589
|
NETCATS = /* @__PURE__ */ new Set(["nc", "ncat", "netcat", "nc.traditional", "nc.openbsd"]);
|
|
17489
17590
|
NET_CLIENTS = /* @__PURE__ */ new Set([...NETCATS, "telnet", "socat"]);
|
|
@@ -17541,7 +17642,7 @@ zoo`.split("\n");
|
|
|
17541
17642
|
const f = parseShared(command);
|
|
17542
17643
|
if (!f || typeof f !== "object" || !f.Stmts) return false;
|
|
17543
17644
|
try {
|
|
17544
|
-
|
|
17645
|
+
walk(f, (node) => this.visit(node, depth));
|
|
17545
17646
|
} catch {
|
|
17546
17647
|
return false;
|
|
17547
17648
|
}
|
|
@@ -17580,7 +17681,7 @@ zoo`.split("\n");
|
|
|
17580
17681
|
}
|
|
17581
17682
|
visit(n, depth) {
|
|
17582
17683
|
if (!n) return false;
|
|
17583
|
-
const type =
|
|
17684
|
+
const type = n.kind;
|
|
17584
17685
|
if (type === "Stmt") {
|
|
17585
17686
|
this.visitStmt(n, depth);
|
|
17586
17687
|
return true;
|
|
@@ -17600,7 +17701,7 @@ zoo`.split("\n");
|
|
|
17600
17701
|
const redirs = redirectWords(stmt);
|
|
17601
17702
|
const toSocket = redirs.some((w) => w !== null && DEV_NET_RE.test(w));
|
|
17602
17703
|
if (!cmd) return;
|
|
17603
|
-
const type =
|
|
17704
|
+
const type = cmd.kind;
|
|
17604
17705
|
if (type !== "CallExpr" || wordsOf(cmd).length > 0) {
|
|
17605
17706
|
if (this.readsNetFd(stmt) && runsCommandsIn(stmt)) {
|
|
17606
17707
|
this.reverseShell("commands are read from a network socket opened with exec");
|
|
@@ -17686,7 +17787,7 @@ zoo`.split("\n");
|
|
|
17686
17787
|
const stages = [];
|
|
17687
17788
|
const flatten = (s) => {
|
|
17688
17789
|
const c = s?.Cmd;
|
|
17689
|
-
if (c &&
|
|
17790
|
+
if (c && c.kind === "BinaryCmd" && isPipeOp(c.Op)) {
|
|
17690
17791
|
flatten(c.X);
|
|
17691
17792
|
flatten(c.Y);
|
|
17692
17793
|
} else stages.push(s);
|
|
@@ -17708,8 +17809,8 @@ zoo`.split("\n");
|
|
|
17708
17809
|
sinks.set(i, socketHosts(st));
|
|
17709
17810
|
}
|
|
17710
17811
|
if (!c) return;
|
|
17711
|
-
if (
|
|
17712
|
-
if (
|
|
17812
|
+
if (c.kind === "DeclClause" && declDumpsEnv(c) && dump < 0) dump = i;
|
|
17813
|
+
if (c.kind !== "CallExpr") {
|
|
17713
17814
|
if (runsCommandsIn(st)) shell = i;
|
|
17714
17815
|
return;
|
|
17715
17816
|
}
|
|
@@ -17750,10 +17851,10 @@ zoo`.split("\n");
|
|
|
17750
17851
|
/** A word (or assignment value) whose text includes an env dump's output. */
|
|
17751
17852
|
carriesEnv(word) {
|
|
17752
17853
|
let hit = false;
|
|
17753
|
-
|
|
17854
|
+
walk(word, (node) => {
|
|
17754
17855
|
if (hit || !node) return false;
|
|
17755
17856
|
const n = node;
|
|
17756
|
-
const t =
|
|
17857
|
+
const t = n.kind;
|
|
17757
17858
|
if (t === "ParamExp" && n.Param?.Value && this.envVars.has(n.Param.Value)) {
|
|
17758
17859
|
hit = true;
|
|
17759
17860
|
return false;
|
|
@@ -17770,11 +17871,11 @@ zoo`.split("\n");
|
|
|
17770
17871
|
stmtsDumpEnv(stmts) {
|
|
17771
17872
|
let hit = false;
|
|
17772
17873
|
for (const s of stmts) {
|
|
17773
|
-
|
|
17874
|
+
walk(s, (node) => {
|
|
17774
17875
|
if (hit || !node) return false;
|
|
17775
17876
|
const n = node;
|
|
17776
|
-
const t =
|
|
17777
|
-
if (t === "Stmt" && n.Cmd &&
|
|
17877
|
+
const t = n.kind;
|
|
17878
|
+
if (t === "Stmt" && n.Cmd && n.Cmd.kind === "CallExpr") {
|
|
17778
17879
|
const words = wordsOf(n.Cmd);
|
|
17779
17880
|
if (words.length > 0 && dumpsEnv(words, n)) hit = true;
|
|
17780
17881
|
}
|
|
@@ -17791,7 +17892,6 @@ zoo`.split("\n");
|
|
|
17791
17892
|
this.run(payload, depth + 1);
|
|
17792
17893
|
}
|
|
17793
17894
|
};
|
|
17794
|
-
({ syntax: syntax7 } = import_mvdan_sh8.default);
|
|
17795
17895
|
NPM_INSTALL_VERBS = /* @__PURE__ */ new Set([
|
|
17796
17896
|
"install",
|
|
17797
17897
|
"i",
|
|
@@ -18173,7 +18273,6 @@ zoo`.split("\n");
|
|
|
18173
18273
|
SHELLS2 = /* @__PURE__ */ new Set(["sh", "bash", "zsh", "dash", "ksh", "ash"]);
|
|
18174
18274
|
MAX_NESTING = 3;
|
|
18175
18275
|
SHELL_VALUE_FLAGS = /* @__PURE__ */ new Set(["--rcfile", "--init-file", "-O", "+O", "-o", "+o"]);
|
|
18176
|
-
({ syntax: syntax8 } = import_mvdan_sh7.default);
|
|
18177
18276
|
NPM_FAMILY = /* @__PURE__ */ new Set(["npm", "pnpm", "yarn", "bun", "npx", "pnpx", "bunx"]);
|
|
18178
18277
|
PIP_FAMILY = /* @__PURE__ */ new Set(["pip", "pip3", "uv", "uvx", "pipx", "pipenv"]);
|
|
18179
18278
|
NPM_FETCH_VERBS = /* @__PURE__ */ new Set([
|
|
@@ -18705,7 +18804,6 @@ zoo`.split("\n");
|
|
|
18705
18804
|
ACCOUNT_KEY_RE = /("(?:iban|account|account_number|accountNumber)"\s*:\s*")([A-Za-z0-9 .-]{6,40})(")/g;
|
|
18706
18805
|
ACCOUNT_VALUE_RE = /"([A-Z]{2}\d{2}[A-Z0-9]{11,30})"/g;
|
|
18707
18806
|
MAX_MASK_DEPTH = 64;
|
|
18708
|
-
({ syntax: syntax9 } = import_mvdan_sh9.default);
|
|
18709
18807
|
EMPTY = {
|
|
18710
18808
|
fetches: false,
|
|
18711
18809
|
sends: false,
|
|
@@ -33775,18 +33873,18 @@ function statAndFirstLine(file2) {
|
|
|
33775
33873
|
}
|
|
33776
33874
|
function listCodexSessionFiles(base2 = codexSessionsDir()) {
|
|
33777
33875
|
const files = [];
|
|
33778
|
-
const
|
|
33876
|
+
const walk3 = (dir2) => {
|
|
33779
33877
|
try {
|
|
33780
33878
|
for (const entry of import_fs45.default.readdirSync(dir2, { withFileTypes: true })) {
|
|
33781
33879
|
const file2 = import_path46.default.join(dir2, entry.name);
|
|
33782
|
-
if (entry.isDirectory())
|
|
33880
|
+
if (entry.isDirectory()) walk3(file2);
|
|
33783
33881
|
else if (entry.isFile() && entry.name.endsWith(".jsonl")) files.push(file2);
|
|
33784
33882
|
}
|
|
33785
33883
|
} catch {
|
|
33786
33884
|
}
|
|
33787
33885
|
};
|
|
33788
|
-
|
|
33789
|
-
if (import_path46.default.basename(base2) === "sessions")
|
|
33886
|
+
walk3(base2);
|
|
33887
|
+
if (import_path46.default.basename(base2) === "sessions") walk3(import_path46.default.join(import_path46.default.dirname(base2), "archived_sessions"));
|
|
33790
33888
|
const sessions = /* @__PURE__ */ new Map();
|
|
33791
33889
|
for (const file2 of files.sort()) {
|
|
33792
33890
|
try {
|
|
@@ -34277,7 +34375,7 @@ var init_cost_copilot = __esm({
|
|
|
34277
34375
|
// src/session-files.ts
|
|
34278
34376
|
function listSessionFiles(dir2, maxDepth = 6) {
|
|
34279
34377
|
const out = [];
|
|
34280
|
-
const
|
|
34378
|
+
const walk3 = (d, rel, depth) => {
|
|
34281
34379
|
if (depth > maxDepth) return;
|
|
34282
34380
|
let entries;
|
|
34283
34381
|
try {
|
|
@@ -34287,11 +34385,11 @@ function listSessionFiles(dir2, maxDepth = 6) {
|
|
|
34287
34385
|
}
|
|
34288
34386
|
for (const e of entries) {
|
|
34289
34387
|
const childRel = rel ? path50.join(rel, e.name) : e.name;
|
|
34290
|
-
if (e.isDirectory())
|
|
34388
|
+
if (e.isDirectory()) walk3(path50.join(d, e.name), childRel, depth + 1);
|
|
34291
34389
|
else if (e.name.endsWith(".jsonl")) out.push(childRel);
|
|
34292
34390
|
}
|
|
34293
34391
|
};
|
|
34294
|
-
|
|
34392
|
+
walk3(dir2, "", 0);
|
|
34295
34393
|
return out;
|
|
34296
34394
|
}
|
|
34297
34395
|
function sessionIdOf(relPath) {
|
|
@@ -37594,19 +37692,19 @@ function scrubDecoys(subject, values) {
|
|
|
37594
37692
|
for (const v of values) if (v) out = out.split(v).join("[node9-decoy]");
|
|
37595
37693
|
return out;
|
|
37596
37694
|
};
|
|
37597
|
-
const
|
|
37695
|
+
const walk3 = (v, depth) => {
|
|
37598
37696
|
if (depth > 6) return v;
|
|
37599
37697
|
if (typeof v === "string") return scrubText(v);
|
|
37600
|
-
if (Array.isArray(v)) return v.map((x) =>
|
|
37698
|
+
if (Array.isArray(v)) return v.map((x) => walk3(x, depth + 1));
|
|
37601
37699
|
if (v && typeof v === "object") {
|
|
37602
37700
|
const out = {};
|
|
37603
37701
|
for (const [k, child] of Object.entries(v))
|
|
37604
|
-
out[k] =
|
|
37702
|
+
out[k] = walk3(child, depth + 1);
|
|
37605
37703
|
return out;
|
|
37606
37704
|
}
|
|
37607
37705
|
return v;
|
|
37608
37706
|
};
|
|
37609
|
-
return
|
|
37707
|
+
return walk3(subject, 0);
|
|
37610
37708
|
}
|
|
37611
37709
|
function isStaleFinding(timestamp2, now = Date.now()) {
|
|
37612
37710
|
if (!timestamp2) return false;
|
|
@@ -75070,14 +75168,14 @@ function collectToolOutputText(toolResponse, opts = {}) {
|
|
|
75070
75168
|
size += s.length + 1;
|
|
75071
75169
|
return true;
|
|
75072
75170
|
};
|
|
75073
|
-
const
|
|
75171
|
+
const walk3 = (v, depth) => {
|
|
75074
75172
|
if (typeof v === "string") return push3(v);
|
|
75075
75173
|
if (depth >= MAX_DEPTH5 || v === null || typeof v !== "object") return true;
|
|
75076
75174
|
const children = Array.isArray(v) ? v : Object.values(v);
|
|
75077
|
-
for (const child of children) if (!
|
|
75175
|
+
for (const child of children) if (!walk3(child, depth + 1)) return false;
|
|
75078
75176
|
return true;
|
|
75079
75177
|
};
|
|
75080
|
-
|
|
75178
|
+
walk3(toolResponse, 0);
|
|
75081
75179
|
return { text: parts.join("\n"), truncated, fields };
|
|
75082
75180
|
}
|
|
75083
75181
|
function shellOutputText(toolResponse) {
|
|
@@ -75090,15 +75188,15 @@ function shellOutputText(toolResponse) {
|
|
|
75090
75188
|
return collectToolOutputText(toolResponse).text;
|
|
75091
75189
|
}
|
|
75092
75190
|
function mapToolOutputStrings(toolResponse, fn) {
|
|
75093
|
-
const
|
|
75191
|
+
const walk3 = (v, depth) => {
|
|
75094
75192
|
if (typeof v === "string") return fn(v);
|
|
75095
75193
|
if (depth >= MAX_DEPTH5 || v === null || typeof v !== "object") return v;
|
|
75096
|
-
if (Array.isArray(v)) return v.map((x) =>
|
|
75194
|
+
if (Array.isArray(v)) return v.map((x) => walk3(x, depth + 1));
|
|
75097
75195
|
const out = {};
|
|
75098
|
-
for (const [k, x] of Object.entries(v)) out[k] =
|
|
75196
|
+
for (const [k, x] of Object.entries(v)) out[k] = walk3(x, depth + 1);
|
|
75099
75197
|
return out;
|
|
75100
75198
|
};
|
|
75101
|
-
return
|
|
75199
|
+
return walk3(toolResponse, 0);
|
|
75102
75200
|
}
|
|
75103
75201
|
function frameToolOutputLeaves(toolResponse, isSuspect, frame, neutralize) {
|
|
75104
75202
|
const suspect = [];
|