@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/index.js
CHANGED
|
@@ -667,6 +667,62 @@ function resolveCheckMap(settings) {
|
|
|
667
667
|
for (const c of CHECKS) out[c.id] = checkValue(settings, c.id);
|
|
668
668
|
return out;
|
|
669
669
|
}
|
|
670
|
+
function isGoNode(v) {
|
|
671
|
+
return v !== null && typeof v === "object" && typeof v.Pos === "function";
|
|
672
|
+
}
|
|
673
|
+
function toPlainTree(goNode) {
|
|
674
|
+
const kind = syntax.NodeType(goNode);
|
|
675
|
+
const out = { kind, pos: goNode.Pos().Offset(), end: goNode.End().Offset() };
|
|
676
|
+
const spec = KINDS[kind];
|
|
677
|
+
if (!spec) {
|
|
678
|
+
unknownKinds.add(kind);
|
|
679
|
+
return out;
|
|
680
|
+
}
|
|
681
|
+
convertFields(goNode, spec, out, kind);
|
|
682
|
+
return out;
|
|
683
|
+
}
|
|
684
|
+
function convertFields(goNode, spec, out, kind) {
|
|
685
|
+
for (const f of spec.scalars ?? []) out[f] = goNode[f];
|
|
686
|
+
for (const f of spec.nodes ?? []) {
|
|
687
|
+
const v = goNode[f];
|
|
688
|
+
if (v === void 0 || v === null) {
|
|
689
|
+
out[f] = null;
|
|
690
|
+
} else if (Array.isArray(v)) {
|
|
691
|
+
out[f] = v.map((c) => isGoNode(c) ? toPlainTree(c) : c);
|
|
692
|
+
} else if (isGoNode(v)) {
|
|
693
|
+
out[f] = toPlainTree(v);
|
|
694
|
+
} else if (typeof v === "object" && STRUCTS[`${kind}.${f}`]) {
|
|
695
|
+
const sub = { struct: `${kind}.${f}` };
|
|
696
|
+
convertFields(v, STRUCTS[`${kind}.${f}`], sub, `${kind}.${f}`);
|
|
697
|
+
out[f] = sub;
|
|
698
|
+
} else {
|
|
699
|
+
out[f] = v;
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
function childrenOf(n) {
|
|
704
|
+
const spec = n.kind ? KINDS[n.kind] : STRUCTS[n.struct];
|
|
705
|
+
if (!spec?.nodes) return [];
|
|
706
|
+
const out = [];
|
|
707
|
+
for (const f of spec.nodes) {
|
|
708
|
+
if (spec.notWalked?.includes(f)) continue;
|
|
709
|
+
const v = n[f];
|
|
710
|
+
if (!v) continue;
|
|
711
|
+
if (Array.isArray(v)) {
|
|
712
|
+
for (const c of v) if (c) out.push(c);
|
|
713
|
+
} else if (v.kind) {
|
|
714
|
+
out.push(v);
|
|
715
|
+
} else if (v.struct) {
|
|
716
|
+
out.push(...childrenOf(v));
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
return out;
|
|
720
|
+
}
|
|
721
|
+
function walk(root, fn) {
|
|
722
|
+
if (!root) return;
|
|
723
|
+
if (!fn(root)) return;
|
|
724
|
+
for (const c of childrenOf(root)) walk(c, fn);
|
|
725
|
+
}
|
|
670
726
|
function isInvisibleChar(char) {
|
|
671
727
|
return INVISIBLE_ONE.test(char);
|
|
672
728
|
}
|
|
@@ -822,7 +878,7 @@ function judgeInlineCode(language, code, isProtected) {
|
|
|
822
878
|
}
|
|
823
879
|
function isCatHeredocOrLit(part) {
|
|
824
880
|
if (!part) return false;
|
|
825
|
-
const t =
|
|
881
|
+
const t = part.kind;
|
|
826
882
|
if (t === "Lit") return true;
|
|
827
883
|
if (t !== "CmdSubst") return false;
|
|
828
884
|
const stmts = part.Stmts || [];
|
|
@@ -832,9 +888,9 @@ function isCatHeredocOrLit(part) {
|
|
|
832
888
|
const hasHeredoc = redirs.some((r) => r && r.Hdoc);
|
|
833
889
|
if (!hasHeredoc) return false;
|
|
834
890
|
const cmd = stmt.Cmd;
|
|
835
|
-
if (!cmd ||
|
|
891
|
+
if (!cmd || cmd.kind !== "CallExpr") return false;
|
|
836
892
|
const firstArg = cmd.Args?.[0]?.Parts || [];
|
|
837
|
-
if (firstArg.length !== 1 ||
|
|
893
|
+
if (firstArg.length !== 1 || firstArg[0].kind !== "Lit") return false;
|
|
838
894
|
return (firstArg[0].Value || "").toLowerCase() === "cat";
|
|
839
895
|
}
|
|
840
896
|
function parseShared(command) {
|
|
@@ -846,7 +902,7 @@ function parseShared(command) {
|
|
|
846
902
|
}
|
|
847
903
|
let parsed;
|
|
848
904
|
try {
|
|
849
|
-
parsed = sharedParser.Parse(command, "cmd");
|
|
905
|
+
parsed = toPlainTree(sharedParser.Parse(command, "cmd"));
|
|
850
906
|
} catch {
|
|
851
907
|
parsed = PARSE_FAIL;
|
|
852
908
|
}
|
|
@@ -907,10 +963,10 @@ function fileAccessViewImpl(command) {
|
|
|
907
963
|
const at = (byteOffset) => toCharIndex === null ? byteOffset : toCharIndex(byteOffset);
|
|
908
964
|
const edits = [];
|
|
909
965
|
try {
|
|
910
|
-
|
|
966
|
+
walk(f, (node) => {
|
|
911
967
|
if (!node) return false;
|
|
912
968
|
const n = node;
|
|
913
|
-
if (
|
|
969
|
+
if (n.kind !== "CallExpr") return true;
|
|
914
970
|
const args = n.Args || [];
|
|
915
971
|
const words = args.map((a) => resolveWordLiteral(a));
|
|
916
972
|
const head = unwrapCommandHead(words);
|
|
@@ -922,8 +978,8 @@ function fileAccessViewImpl(command) {
|
|
|
922
978
|
if (!PROGRAM_DIR.test(word)) continue;
|
|
923
979
|
const name = word.split(/[\\/]/).pop() ?? "";
|
|
924
980
|
if (!name) continue;
|
|
925
|
-
const s = at(args[i].
|
|
926
|
-
const e = at(args[i].
|
|
981
|
+
const s = at(args[i].pos);
|
|
982
|
+
const e = at(args[i].end);
|
|
927
983
|
if (s < 0 || e < 0) continue;
|
|
928
984
|
edits.push([s, e, name]);
|
|
929
985
|
}
|
|
@@ -951,10 +1007,10 @@ function literalPayloadsOf(command, depth) {
|
|
|
951
1007
|
if (f === PARSE_FAIL) return [];
|
|
952
1008
|
const out = [];
|
|
953
1009
|
try {
|
|
954
|
-
|
|
1010
|
+
walk(f, (node) => {
|
|
955
1011
|
if (!node || out.length >= MAX_READING_PAYLOADS) return false;
|
|
956
1012
|
const n = node;
|
|
957
|
-
if (
|
|
1013
|
+
if (n.kind !== "CallExpr") return true;
|
|
958
1014
|
const words = (n.Args || []).map((a) => resolveWordLiteral(a));
|
|
959
1015
|
if (words.length === 0) return true;
|
|
960
1016
|
const payload = literalShellPayload(words, baseWord(words[0]));
|
|
@@ -974,23 +1030,23 @@ function foldCommandWords(reading) {
|
|
|
974
1030
|
const at = (byteOffset) => toCharIndex === null ? byteOffset : toCharIndex(byteOffset);
|
|
975
1031
|
const edits = [];
|
|
976
1032
|
try {
|
|
977
|
-
|
|
1033
|
+
walk(f, (node) => {
|
|
978
1034
|
if (!node) return false;
|
|
979
1035
|
const n = node;
|
|
980
|
-
if (
|
|
1036
|
+
if (n.kind !== "CallExpr") return true;
|
|
981
1037
|
const args = n.Args || [];
|
|
982
1038
|
const words = args.map((a) => resolveWordLiteral(a));
|
|
983
1039
|
const folded = words.map((w) => w === null ? null : foldHomoglyphs(w));
|
|
984
1040
|
const head = unwrapCommandHead(folded);
|
|
985
1041
|
for (let i = 0; i <= head && i < args.length; i++) {
|
|
986
1042
|
const parts = args[i].Parts || [];
|
|
987
|
-
if (parts.length !== 1 ||
|
|
1043
|
+
if (parts.length !== 1 || parts[0].kind !== "Lit") continue;
|
|
988
1044
|
const value = parts[0].Value ?? "";
|
|
989
1045
|
const word = folded[i];
|
|
990
1046
|
if (word === null || word === value || !/[^\x00-\x7f]/.test(value)) continue;
|
|
991
1047
|
if (!/^[A-Za-z0-9._/-]+$/.test(word)) continue;
|
|
992
|
-
const s = at(args[i].
|
|
993
|
-
const e = at(args[i].
|
|
1048
|
+
const s = at(args[i].pos);
|
|
1049
|
+
const e = at(args[i].end);
|
|
994
1050
|
if (s < 0 || e < 0) continue;
|
|
995
1051
|
edits.push([s, e, word]);
|
|
996
1052
|
}
|
|
@@ -1027,10 +1083,10 @@ function interpreterHeredocSpans(f, at) {
|
|
|
1027
1083
|
const stmts = f?.Stmts || [];
|
|
1028
1084
|
const candidates = [];
|
|
1029
1085
|
let unsafe = false;
|
|
1030
|
-
|
|
1086
|
+
walk(f, (node) => {
|
|
1031
1087
|
if (!node || unsafe) return false;
|
|
1032
1088
|
const n = node;
|
|
1033
|
-
const t =
|
|
1089
|
+
const t = n.kind;
|
|
1034
1090
|
if (t === "FuncDecl") {
|
|
1035
1091
|
unsafe = true;
|
|
1036
1092
|
return false;
|
|
@@ -1050,7 +1106,7 @@ function interpreterHeredocSpans(f, at) {
|
|
|
1050
1106
|
});
|
|
1051
1107
|
if (unsafe) return [];
|
|
1052
1108
|
for (const st of stmts) {
|
|
1053
|
-
if (!st?.Cmd ||
|
|
1109
|
+
if (!st?.Cmd || st.Cmd.kind !== "CallExpr") continue;
|
|
1054
1110
|
const words = (st.Cmd.Args || []).map((a) => resolveWordLiteral(a));
|
|
1055
1111
|
const name = (words[0] ?? "").toLowerCase();
|
|
1056
1112
|
if (!STDIN_CODE_INTERPRETERS.test(name)) continue;
|
|
@@ -1065,8 +1121,8 @@ function interpreterHeredocSpans(f, at) {
|
|
|
1065
1121
|
if (stdin.length !== 1 || others > 0) continue;
|
|
1066
1122
|
const r = stdin[0];
|
|
1067
1123
|
if (heredocText(r.Hdoc) === null) continue;
|
|
1068
|
-
const hs = at(r.Hdoc.
|
|
1069
|
-
const he = at(r.Hdoc.
|
|
1124
|
+
const hs = at(r.Hdoc.pos);
|
|
1125
|
+
const he = at(r.Hdoc.end);
|
|
1070
1126
|
if (hs < 0 || he < 0 || he <= hs) continue;
|
|
1071
1127
|
spans.push([hs, he]);
|
|
1072
1128
|
}
|
|
@@ -1086,24 +1142,24 @@ function normalizeCommandForPolicyImpl(command, codeBodies) {
|
|
|
1086
1142
|
strips.push([hs, he]);
|
|
1087
1143
|
msgSpans.add(`${hs}:${he}`);
|
|
1088
1144
|
}
|
|
1089
|
-
|
|
1145
|
+
walk(f, (node) => {
|
|
1090
1146
|
if (!node) return false;
|
|
1091
1147
|
const n = node;
|
|
1092
|
-
if (
|
|
1148
|
+
if (n.kind !== "CallExpr") return true;
|
|
1093
1149
|
const args = n.Args || [];
|
|
1094
1150
|
for (let i = 0; i < args.length - 1; i++) {
|
|
1095
1151
|
const argParts = args[i].Parts || [];
|
|
1096
|
-
if (argParts.length !== 1 ||
|
|
1152
|
+
if (argParts.length !== 1 || argParts[0].kind !== "Lit") continue;
|
|
1097
1153
|
const flagVal = argParts[0].Value || "";
|
|
1098
1154
|
if (!MESSAGE_FLAGS.has(flagVal.toLowerCase())) continue;
|
|
1099
1155
|
const next = args[i + 1];
|
|
1100
1156
|
const nextParts = next.Parts || [];
|
|
1101
1157
|
if (nextParts.length !== 1) continue;
|
|
1102
1158
|
const quotedNode = nextParts[0];
|
|
1103
|
-
const nt =
|
|
1159
|
+
const nt = quotedNode.kind;
|
|
1104
1160
|
const markStrip = () => {
|
|
1105
|
-
const s = at(next.
|
|
1106
|
-
const e = at(next.
|
|
1161
|
+
const s = at(next.pos);
|
|
1162
|
+
const e = at(next.end);
|
|
1107
1163
|
if (s < 0 || e < 0) return;
|
|
1108
1164
|
strips.push([s, e]);
|
|
1109
1165
|
msgSpans.add(`${s}:${e}`);
|
|
@@ -1112,7 +1168,7 @@ function normalizeCommandForPolicyImpl(command, codeBodies) {
|
|
|
1112
1168
|
markStrip();
|
|
1113
1169
|
} else if (nt === "DblQuoted") {
|
|
1114
1170
|
const innerParts = quotedNode.Parts || [];
|
|
1115
|
-
const allLit = innerParts.length === 0 || innerParts.every((p) =>
|
|
1171
|
+
const allLit = innerParts.length === 0 || innerParts.every((p) => p.kind === "Lit");
|
|
1116
1172
|
if (allLit) {
|
|
1117
1173
|
markStrip();
|
|
1118
1174
|
} else if (innerParts.every((p) => isCatHeredocOrLit(p))) {
|
|
@@ -1121,8 +1177,8 @@ function normalizeCommandForPolicyImpl(command, codeBodies) {
|
|
|
1121
1177
|
}
|
|
1122
1178
|
}
|
|
1123
1179
|
for (const arg of args) {
|
|
1124
|
-
const s = at(arg.
|
|
1125
|
-
const e = at(arg.
|
|
1180
|
+
const s = at(arg.pos);
|
|
1181
|
+
const e = at(arg.end);
|
|
1126
1182
|
if (s < 0 || e < 0) continue;
|
|
1127
1183
|
if (msgSpans.has(`${s}:${e}`)) continue;
|
|
1128
1184
|
const resolved = resolveWordLiteral(arg);
|
|
@@ -1162,10 +1218,10 @@ function scanArgsForDynamicExec(args, startIdx) {
|
|
|
1162
1218
|
let hasParamExp = false;
|
|
1163
1219
|
let hasCurl = false;
|
|
1164
1220
|
for (let i = startIdx; i < args.length; i++) {
|
|
1165
|
-
|
|
1221
|
+
walk(args[i], (inner) => {
|
|
1166
1222
|
if (!inner) return false;
|
|
1167
1223
|
const inn = inner;
|
|
1168
|
-
const it =
|
|
1224
|
+
const it = inn.kind;
|
|
1169
1225
|
if (it === "CmdSubst") hasCmdSubst = true;
|
|
1170
1226
|
if (it === "ParamExp") hasParamExp = true;
|
|
1171
1227
|
if (it === "Lit" && DOWNLOAD_CMDS.has(inn.Value?.toLowerCase())) hasCurl = true;
|
|
@@ -1178,15 +1234,16 @@ function scanArgsForDynamicExec(args, startIdx) {
|
|
|
1178
1234
|
}
|
|
1179
1235
|
function detectDangerousShellExec(command) {
|
|
1180
1236
|
try {
|
|
1181
|
-
const f =
|
|
1237
|
+
const f = parseShared(command);
|
|
1238
|
+
if (f === PARSE_FAIL) return null;
|
|
1182
1239
|
let result = null;
|
|
1183
1240
|
const assigned = assignedNames(f);
|
|
1184
|
-
|
|
1241
|
+
walk(f, (node) => {
|
|
1185
1242
|
if (!node || result === "block") return false;
|
|
1186
1243
|
const n = node;
|
|
1187
|
-
if (
|
|
1244
|
+
if (n.kind === "BinaryCmd" && n.Y && !listOps().has(n.Op)) {
|
|
1188
1245
|
const call = n.Y.Cmd;
|
|
1189
|
-
if (call &&
|
|
1246
|
+
if (call && call.kind === "CallExpr") {
|
|
1190
1247
|
const words = (call.Args || []).map((a) => resolveWordLiteral(a));
|
|
1191
1248
|
const head2 = unwrapCommandHead(words);
|
|
1192
1249
|
const name = (words[head2] ?? "").split("/").pop() ?? "";
|
|
@@ -1199,7 +1256,7 @@ function detectDangerousShellExec(command) {
|
|
|
1199
1256
|
result = "review";
|
|
1200
1257
|
}
|
|
1201
1258
|
}
|
|
1202
|
-
if (
|
|
1259
|
+
if (n.kind !== "CallExpr") return true;
|
|
1203
1260
|
const args = n.Args || [];
|
|
1204
1261
|
if (args.length === 0) return true;
|
|
1205
1262
|
const headWords = args.map(
|
|
@@ -1210,7 +1267,7 @@ function detectDangerousShellExec(command) {
|
|
|
1210
1267
|
if (!lookup && (opaqueExecutable(args[0], assigned) || opaqueExecutable(args[head], assigned)))
|
|
1211
1268
|
result = "review";
|
|
1212
1269
|
const firstParts = args[0].Parts || [];
|
|
1213
|
-
if (firstParts.length !== 1 ||
|
|
1270
|
+
if (firstParts.length !== 1 || firstParts[0].kind !== "Lit") return true;
|
|
1214
1271
|
const cmdName = firstParts[0].Value?.toLowerCase() ?? "";
|
|
1215
1272
|
if (cmdName === "eval") {
|
|
1216
1273
|
const v = scanArgsForDynamicExec(args, 1);
|
|
@@ -1218,7 +1275,7 @@ function detectDangerousShellExec(command) {
|
|
|
1218
1275
|
} else if (SHELL_INTERPRETERS.has(cmdName)) {
|
|
1219
1276
|
for (let i = 1; i < args.length - 1; i++) {
|
|
1220
1277
|
const flagParts = args[i].Parts || [];
|
|
1221
|
-
if (flagParts.length !== 1 ||
|
|
1278
|
+
if (flagParts.length !== 1 || flagParts[0].kind !== "Lit" || flagParts[0].Value !== "-c")
|
|
1222
1279
|
continue;
|
|
1223
1280
|
const v = scanArgsForDynamicExec(args, i + 1);
|
|
1224
1281
|
if (v === "block" || v === "review" && result === null) result = v;
|
|
@@ -1242,8 +1299,7 @@ function isDefaultExpansion(part) {
|
|
|
1242
1299
|
}
|
|
1243
1300
|
function lookupSubstitution(part) {
|
|
1244
1301
|
const stmts = part.Stmts ?? [];
|
|
1245
|
-
if (stmts.length !== 1 || !stmts[0].Cmd ||
|
|
1246
|
-
return null;
|
|
1302
|
+
if (stmts.length !== 1 || !stmts[0].Cmd || stmts[0].Cmd.kind !== "CallExpr") return null;
|
|
1247
1303
|
const words = (stmts[0].Cmd.Args ?? []).map((a) => resolveWordLiteral(a));
|
|
1248
1304
|
if (words.some((w) => w === null)) return null;
|
|
1249
1305
|
if (words.length === 2 && words[0] === "which") return words[1];
|
|
@@ -1253,9 +1309,9 @@ function lookupSubstitution(part) {
|
|
|
1253
1309
|
}
|
|
1254
1310
|
function assignedNames(f) {
|
|
1255
1311
|
const names = /* @__PURE__ */ new Set();
|
|
1256
|
-
|
|
1312
|
+
walk(f, (node) => {
|
|
1257
1313
|
if (!node) return false;
|
|
1258
|
-
const kind =
|
|
1314
|
+
const kind = node.kind;
|
|
1259
1315
|
if (kind === "Assign" && node.Name?.Value) names.add(node.Name.Value);
|
|
1260
1316
|
if (kind === "ForClause" && node.Loop?.Name?.Value) names.add(node.Loop.Name.Value);
|
|
1261
1317
|
if (kind === "CallExpr") {
|
|
@@ -1270,7 +1326,7 @@ function assignedNames(f) {
|
|
|
1270
1326
|
}
|
|
1271
1327
|
function executableParts(word, assigned, quoted = false) {
|
|
1272
1328
|
return (word?.Parts ?? []).flatMap((part) => {
|
|
1273
|
-
const kind =
|
|
1329
|
+
const kind = part.kind;
|
|
1274
1330
|
if (kind === "DblQuoted") return executableParts(part, assigned, true);
|
|
1275
1331
|
if (kind === "Lit") return [{ kind: "Lit", value: part.Value ?? "", quoted }];
|
|
1276
1332
|
if (kind === "SglQuoted")
|
|
@@ -1338,10 +1394,10 @@ function chmodHasOpenPermMode(command) {
|
|
|
1338
1394
|
if (f === PARSE_FAIL) return false;
|
|
1339
1395
|
let found = false;
|
|
1340
1396
|
try {
|
|
1341
|
-
|
|
1397
|
+
walk(f, (node) => {
|
|
1342
1398
|
if (!node || found) return false;
|
|
1343
1399
|
const n = node;
|
|
1344
|
-
if (
|
|
1400
|
+
if (n.kind !== "CallExpr") return true;
|
|
1345
1401
|
const words = (n.Args || []).map((a) => resolveWordLiteral(a));
|
|
1346
1402
|
if (words.length === 0) return true;
|
|
1347
1403
|
const name = (words[0] ?? "").toLowerCase();
|
|
@@ -1394,11 +1450,11 @@ function redirStdinOps() {
|
|
|
1394
1450
|
}
|
|
1395
1451
|
function deriveBinaryOp(sample) {
|
|
1396
1452
|
try {
|
|
1397
|
-
const f = sharedParser.Parse(sample, "cmd");
|
|
1453
|
+
const f = toPlainTree(sharedParser.Parse(sample, "cmd"));
|
|
1398
1454
|
let op = -1;
|
|
1399
|
-
|
|
1455
|
+
walk(f, (node) => {
|
|
1400
1456
|
const n = node;
|
|
1401
|
-
if (n &&
|
|
1457
|
+
if (n && n.kind === "BinaryCmd" && op < 0) op = n.Op;
|
|
1402
1458
|
return true;
|
|
1403
1459
|
});
|
|
1404
1460
|
return op;
|
|
@@ -1472,7 +1528,7 @@ function heredocText(word) {
|
|
|
1472
1528
|
const parts = word?.Parts ?? [];
|
|
1473
1529
|
let out = "";
|
|
1474
1530
|
for (const p of parts) {
|
|
1475
|
-
if (
|
|
1531
|
+
if (p.kind !== "Lit") return null;
|
|
1476
1532
|
out += p.Value ?? "";
|
|
1477
1533
|
}
|
|
1478
1534
|
return out;
|
|
@@ -1508,7 +1564,7 @@ function attachedCode(interp, flag) {
|
|
|
1508
1564
|
}
|
|
1509
1565
|
function inlineExecCall(stmt, pipeFed) {
|
|
1510
1566
|
const cmd = stmt?.Cmd;
|
|
1511
|
-
if (!cmd ||
|
|
1567
|
+
if (!cmd || cmd.kind !== "CallExpr") return null;
|
|
1512
1568
|
const words = (cmd.Args || []).map((a) => resolveWordLiteral(a));
|
|
1513
1569
|
if (words.length === 0) return null;
|
|
1514
1570
|
const headIdx = unwrapCommandHead(words);
|
|
@@ -1589,10 +1645,10 @@ function shellPayloadNeedsReview(payload) {
|
|
|
1589
1645
|
let review = false;
|
|
1590
1646
|
payloadNesting++;
|
|
1591
1647
|
try {
|
|
1592
|
-
|
|
1648
|
+
walk(f, (node) => {
|
|
1593
1649
|
if (!node || review) return false;
|
|
1594
1650
|
const n = node;
|
|
1595
|
-
if (
|
|
1651
|
+
if (n.kind !== "CallExpr") return true;
|
|
1596
1652
|
const words = (n.Args || []).map((a) => resolveWordLiteral(a));
|
|
1597
1653
|
if (words.length === 0) return true;
|
|
1598
1654
|
const h = unwrapCommandHead(words);
|
|
@@ -1626,10 +1682,10 @@ function inlineExecCalls(command) {
|
|
|
1626
1682
|
if (f === PARSE_FAIL) return PARSE_FAIL;
|
|
1627
1683
|
const calls = [];
|
|
1628
1684
|
try {
|
|
1629
|
-
|
|
1685
|
+
walk(f, (node) => {
|
|
1630
1686
|
if (!node || calls.length >= MAX_INLINE_CALLS) return false;
|
|
1631
1687
|
const n = node;
|
|
1632
|
-
const t =
|
|
1688
|
+
const t = n.kind;
|
|
1633
1689
|
if (t === "BinaryCmd" && n.Y && !listOps().has(n.Op)) {
|
|
1634
1690
|
const call = inlineExecCall(n.Y, true);
|
|
1635
1691
|
if (call) calls.push(call);
|
|
@@ -1764,7 +1820,7 @@ function shellScriptOperands(command) {
|
|
|
1764
1820
|
walkWithCalls(f, (node) => {
|
|
1765
1821
|
if (!node) return false;
|
|
1766
1822
|
const n = node;
|
|
1767
|
-
if (
|
|
1823
|
+
if (n.kind !== "CallExpr") return true;
|
|
1768
1824
|
const callArgs = n.Args || [];
|
|
1769
1825
|
if (callArgs.length === 0) return true;
|
|
1770
1826
|
const words = callArgs.map((a) => resolveWordLiteral(a));
|
|
@@ -1800,7 +1856,7 @@ function shellScriptOperands(command) {
|
|
|
1800
1856
|
}
|
|
1801
1857
|
function windowsDrivePathSpelling(word) {
|
|
1802
1858
|
const parts = word?.Parts || [];
|
|
1803
|
-
if (parts.length === 0 || !parts.every((p) =>
|
|
1859
|
+
if (parts.length === 0 || !parts.every((p) => p.kind === "Lit")) return null;
|
|
1804
1860
|
const raw = parts.map((p) => p.Value ?? "").join("");
|
|
1805
1861
|
return /^[A-Za-z]:\\[^\s'"$`]*$/.test(raw) ? raw : null;
|
|
1806
1862
|
}
|
|
@@ -1838,15 +1894,15 @@ function recordTopLevelAssignments(f) {
|
|
|
1838
1894
|
}
|
|
1839
1895
|
function probeBinOp(src) {
|
|
1840
1896
|
try {
|
|
1841
|
-
const cmd =
|
|
1842
|
-
if (cmd &&
|
|
1897
|
+
const cmd = toPlainTree(syntax2.NewParser().Parse(src, "probe"))?.Stmts?.[0]?.Cmd;
|
|
1898
|
+
if (cmd && cmd.kind === "BinaryCmd") return cmd.Op;
|
|
1843
1899
|
} catch {
|
|
1844
1900
|
}
|
|
1845
1901
|
return null;
|
|
1846
1902
|
}
|
|
1847
1903
|
function recordTopLevelStmt(stmt) {
|
|
1848
1904
|
if (!stmt || !stmt.Cmd) return false;
|
|
1849
|
-
const t =
|
|
1905
|
+
const t = stmt.Cmd.kind;
|
|
1850
1906
|
if (t === "BinaryCmd") {
|
|
1851
1907
|
if (!AND_OR_OPS.has(stmt.Cmd.Op)) return false;
|
|
1852
1908
|
if (!recordTopLevelStmt(stmt.Cmd.X)) return false;
|
|
@@ -1856,7 +1912,7 @@ function recordTopLevelStmt(stmt) {
|
|
|
1856
1912
|
if (t !== "CallExpr" && t !== "DeclClause") return false;
|
|
1857
1913
|
let at = 0;
|
|
1858
1914
|
try {
|
|
1859
|
-
at = stmt.
|
|
1915
|
+
at = stmt.pos;
|
|
1860
1916
|
} catch {
|
|
1861
1917
|
at = 0;
|
|
1862
1918
|
}
|
|
@@ -1867,14 +1923,14 @@ function recordTopLevelStmt(stmt) {
|
|
|
1867
1923
|
}
|
|
1868
1924
|
function recordAssignments(n, at) {
|
|
1869
1925
|
if (!assignmentTable) return;
|
|
1870
|
-
const t =
|
|
1926
|
+
const t = n.kind;
|
|
1871
1927
|
let assigns = [];
|
|
1872
1928
|
if (t === "CallExpr") {
|
|
1873
1929
|
if ((n.Args || []).length > 0) return;
|
|
1874
1930
|
assigns = n.Assigns || [];
|
|
1875
1931
|
} else if (t === "DeclClause") {
|
|
1876
1932
|
if (!ASSIGNMENT_HEADS.has(n.Variant?.Value ?? "")) return;
|
|
1877
|
-
assigns = (n.Args || []).filter((a) =>
|
|
1933
|
+
assigns = (n.Args || []).filter((a) => a.kind === "Assign");
|
|
1878
1934
|
} else return;
|
|
1879
1935
|
for (const a of assigns) {
|
|
1880
1936
|
const name = a?.Name?.Value;
|
|
@@ -1883,13 +1939,13 @@ function recordAssignments(n, at) {
|
|
|
1883
1939
|
}
|
|
1884
1940
|
}
|
|
1885
1941
|
function resolveTrivialSubst(part) {
|
|
1886
|
-
if (
|
|
1942
|
+
if (part.kind !== "CmdSubst") return void 0;
|
|
1887
1943
|
const stmts = part.Stmts || [];
|
|
1888
1944
|
if (stmts.length !== 1) return void 0;
|
|
1889
1945
|
const st = stmts[0];
|
|
1890
1946
|
if ((st.Redirs || []).length > 0 || st.Negated || st.Background) return void 0;
|
|
1891
1947
|
const cmd = st.Cmd;
|
|
1892
|
-
if (!cmd ||
|
|
1948
|
+
if (!cmd || cmd.kind !== "CallExpr") return void 0;
|
|
1893
1949
|
if ((cmd.Assigns || []).length > 0) return void 0;
|
|
1894
1950
|
const words = (cmd.Args || []).map((a) => resolveWordLiteral(a));
|
|
1895
1951
|
if (words.length === 0 || words.some((w) => w === null)) return void 0;
|
|
@@ -1914,7 +1970,7 @@ function recordedExpansion(name) {
|
|
|
1914
1970
|
return rec.value;
|
|
1915
1971
|
}
|
|
1916
1972
|
function isPlainParam(p) {
|
|
1917
|
-
if (
|
|
1973
|
+
if (p.kind !== "ParamExp") return false;
|
|
1918
1974
|
return !(p.Excl || p.Length || p.Width || p.Index || p.Slice || p.Repl || p.Exp);
|
|
1919
1975
|
}
|
|
1920
1976
|
function expandPlainParam(p) {
|
|
@@ -1926,9 +1982,9 @@ function expandPlainParam(p) {
|
|
|
1926
1982
|
}
|
|
1927
1983
|
function functionBodies(f) {
|
|
1928
1984
|
const out = /* @__PURE__ */ new Map();
|
|
1929
|
-
|
|
1985
|
+
walk(f, (node) => {
|
|
1930
1986
|
const n = node;
|
|
1931
|
-
if (n &&
|
|
1987
|
+
if (n && n.kind === "FuncDecl" && n.Name?.Value && n.Body) {
|
|
1932
1988
|
const list = out.get(n.Name.Value) ?? [];
|
|
1933
1989
|
list.push(n.Body);
|
|
1934
1990
|
out.set(n.Name.Value, list);
|
|
@@ -1939,10 +1995,10 @@ function functionBodies(f) {
|
|
|
1939
1995
|
}
|
|
1940
1996
|
function rebindsPositionals(body) {
|
|
1941
1997
|
let found = false;
|
|
1942
|
-
|
|
1998
|
+
walk(body, (node) => {
|
|
1943
1999
|
const n = node;
|
|
1944
2000
|
if (found || !n) return false;
|
|
1945
|
-
if (
|
|
2001
|
+
if (n.kind === "CallExpr") {
|
|
1946
2002
|
const words = (n.Args || []).map((a) => resolveWordLiteral(a));
|
|
1947
2003
|
if (words[0] === "shift") found = true;
|
|
1948
2004
|
else if (words[0] === "set" && words.slice(1).some((w) => w === null || w === "--" || !/^[-+]/.test(w)))
|
|
@@ -1962,7 +2018,7 @@ function tableBindings() {
|
|
|
1962
2018
|
function walkWithCalls(f, visit) {
|
|
1963
2019
|
const bodies = functionBodies(f);
|
|
1964
2020
|
if (bodies.size === 0) {
|
|
1965
|
-
|
|
2021
|
+
walk(f, visit);
|
|
1966
2022
|
return;
|
|
1967
2023
|
}
|
|
1968
2024
|
const seenCalls = /* @__PURE__ */ new Set();
|
|
@@ -1996,8 +2052,8 @@ function walkWithCalls(f, visit) {
|
|
|
1996
2052
|
currentStmtOffset = Number.MAX_SAFE_INTEGER;
|
|
1997
2053
|
callDepth++;
|
|
1998
2054
|
try {
|
|
1999
|
-
if (body.Cmd &&
|
|
2000
|
-
|
|
2055
|
+
if (body.Cmd && body.Cmd.kind === "Block") recordTopLevelAssignments(body.Cmd);
|
|
2056
|
+
walk(body, step);
|
|
2001
2057
|
} finally {
|
|
2002
2058
|
callDepth--;
|
|
2003
2059
|
assignmentTable = outerTable;
|
|
@@ -2008,10 +2064,10 @@ function walkWithCalls(f, visit) {
|
|
|
2008
2064
|
const step = (node) => {
|
|
2009
2065
|
if (!node) return false;
|
|
2010
2066
|
const go = visit(node);
|
|
2011
|
-
if (go !== false &&
|
|
2067
|
+
if (go !== false && node.kind === "CallExpr") inline(node);
|
|
2012
2068
|
return go;
|
|
2013
2069
|
};
|
|
2014
|
-
|
|
2070
|
+
walk(f, step);
|
|
2015
2071
|
}
|
|
2016
2072
|
function withAssignments(f, body) {
|
|
2017
2073
|
const outerTable = assignmentTable;
|
|
@@ -2036,7 +2092,7 @@ function walkCommandScope(command, visit) {
|
|
|
2036
2092
|
if (f === PARSE_FAIL) return "parse-fail";
|
|
2037
2093
|
withAssignments(f, () => {
|
|
2038
2094
|
walkWithCalls(f, (node) => {
|
|
2039
|
-
if (node &&
|
|
2095
|
+
if (node && node.kind === "Stmt") trackStatement(node);
|
|
2040
2096
|
return visit(node);
|
|
2041
2097
|
});
|
|
2042
2098
|
});
|
|
@@ -2044,7 +2100,7 @@ function walkCommandScope(command, visit) {
|
|
|
2044
2100
|
}
|
|
2045
2101
|
function trackStatement(n) {
|
|
2046
2102
|
try {
|
|
2047
|
-
currentStmtOffset = n.
|
|
2103
|
+
currentStmtOffset = n.pos;
|
|
2048
2104
|
} catch {
|
|
2049
2105
|
currentStmtOffset = Number.MAX_SAFE_INTEGER;
|
|
2050
2106
|
}
|
|
@@ -2060,11 +2116,11 @@ function resolveWordLiteral(w) {
|
|
|
2060
2116
|
return s;
|
|
2061
2117
|
}
|
|
2062
2118
|
function resolveWordPrefix(w) {
|
|
2063
|
-
const
|
|
2119
|
+
const walk3 = (parts) => {
|
|
2064
2120
|
let s = "";
|
|
2065
2121
|
for (const p of parts) {
|
|
2066
2122
|
if (p && typeof p === "object" && Array.isArray(p.Parts) && p.Dollar !== void 0) {
|
|
2067
|
-
const inner =
|
|
2123
|
+
const inner = walk3(p.Parts);
|
|
2068
2124
|
s += inner.prefix;
|
|
2069
2125
|
if (inner.dynamic) return { prefix: s, dynamic: true };
|
|
2070
2126
|
continue;
|
|
@@ -2075,10 +2131,10 @@ function resolveWordPrefix(w) {
|
|
|
2075
2131
|
}
|
|
2076
2132
|
return { prefix: s, dynamic: false };
|
|
2077
2133
|
};
|
|
2078
|
-
return
|
|
2134
|
+
return walk3(w?.Parts || []);
|
|
2079
2135
|
}
|
|
2080
2136
|
function resolvePart(p, inQuotes) {
|
|
2081
|
-
const t =
|
|
2137
|
+
const t = p.kind;
|
|
2082
2138
|
if (t === "Lit") {
|
|
2083
2139
|
const raw = p.Value ?? "";
|
|
2084
2140
|
if (!inQuotes) return raw.replace(/\\(.)/g, "$1");
|
|
@@ -2174,7 +2230,7 @@ function extractShellDestinations(command) {
|
|
|
2174
2230
|
() => walkWithCalls(f, (node) => {
|
|
2175
2231
|
if (!node) return false;
|
|
2176
2232
|
const n = node;
|
|
2177
|
-
if (
|
|
2233
|
+
if (n.kind === "Stmt") {
|
|
2178
2234
|
trackStatement(n);
|
|
2179
2235
|
for (const raw of redirectWords(n)) {
|
|
2180
2236
|
const host2 = devNetRedirectHost(raw);
|
|
@@ -2186,7 +2242,7 @@ function extractShellDestinations(command) {
|
|
|
2186
2242
|
out.push({ host: host2, binary, raw });
|
|
2187
2243
|
}
|
|
2188
2244
|
}
|
|
2189
|
-
if (
|
|
2245
|
+
if (n.kind !== "CallExpr") return true;
|
|
2190
2246
|
const callArgs = n.Args || [];
|
|
2191
2247
|
if (callArgs.length === 0) return true;
|
|
2192
2248
|
const words = callArgs.map((a) => resolveWordLiteral(a));
|
|
@@ -2221,7 +2277,7 @@ function extractShellDestTokens(command) {
|
|
|
2221
2277
|
() => walkWithCalls(f, (node) => {
|
|
2222
2278
|
if (!node) return false;
|
|
2223
2279
|
const n = node;
|
|
2224
|
-
if (
|
|
2280
|
+
if (n.kind === "Stmt") {
|
|
2225
2281
|
trackStatement(n);
|
|
2226
2282
|
for (const raw of redirectWords(n)) {
|
|
2227
2283
|
const host2 = devNetRedirectHost(raw);
|
|
@@ -2233,7 +2289,7 @@ function extractShellDestTokens(command) {
|
|
|
2233
2289
|
out.push({ token: host2, binary });
|
|
2234
2290
|
}
|
|
2235
2291
|
}
|
|
2236
|
-
if (
|
|
2292
|
+
if (n.kind !== "CallExpr") return true;
|
|
2237
2293
|
const callArgs = n.Args || [];
|
|
2238
2294
|
if (callArgs.length === 0) return true;
|
|
2239
2295
|
const words = callArgs.map((a) => resolveWordLiteral(a));
|
|
@@ -2301,11 +2357,11 @@ function isWaivableCleanupTarget(p) {
|
|
|
2301
2357
|
}
|
|
2302
2358
|
function deriveRedirOp(sample) {
|
|
2303
2359
|
try {
|
|
2304
|
-
const f = sharedParser.Parse(sample, "cmd");
|
|
2360
|
+
const f = toPlainTree(sharedParser.Parse(sample, "cmd"));
|
|
2305
2361
|
let op = -1;
|
|
2306
|
-
|
|
2362
|
+
walk(f, (node) => {
|
|
2307
2363
|
const n = node;
|
|
2308
|
-
if (n &&
|
|
2364
|
+
if (n && n.kind === "Redirect" && op < 0) op = n.Op;
|
|
2309
2365
|
return true;
|
|
2310
2366
|
});
|
|
2311
2367
|
return op;
|
|
@@ -2318,7 +2374,7 @@ function collectSameCommandCreations(f) {
|
|
|
2318
2374
|
try {
|
|
2319
2375
|
const stmts = Array.isArray(f?.Stmts) ? f.Stmts : [];
|
|
2320
2376
|
for (const stmt of stmts) {
|
|
2321
|
-
if (!stmt || !stmt.Cmd ||
|
|
2377
|
+
if (!stmt || !stmt.Cmd || stmt.Cmd.kind !== "CallExpr") continue;
|
|
2322
2378
|
const redirs = stmt.Redirs || [];
|
|
2323
2379
|
if (!redirs.some((r) => r && REDIR_HEREDOC_OPS.has(r.Op))) continue;
|
|
2324
2380
|
for (const r of redirs) {
|
|
@@ -2342,10 +2398,10 @@ function isRmCreatedInCommandCleanup(command) {
|
|
|
2342
2398
|
let sawRm = false;
|
|
2343
2399
|
let ok = true;
|
|
2344
2400
|
try {
|
|
2345
|
-
|
|
2401
|
+
walk(f, (node) => {
|
|
2346
2402
|
if (!node || !ok) return false;
|
|
2347
2403
|
const n = node;
|
|
2348
|
-
if (
|
|
2404
|
+
if (n.kind !== "CallExpr") return true;
|
|
2349
2405
|
const args = n.Args || [];
|
|
2350
2406
|
const name = (resolveWordLiteral(args[0]) ?? "").toLowerCase();
|
|
2351
2407
|
if (name !== "rm") return true;
|
|
@@ -2395,10 +2451,10 @@ function analyzeFsOperationImpl(command, depth = 0) {
|
|
|
2395
2451
|
walkWithCalls(f, (node) => {
|
|
2396
2452
|
if (!node || result?.verdict === "block") return false;
|
|
2397
2453
|
const n = node;
|
|
2398
|
-
const nodeType =
|
|
2454
|
+
const nodeType = n.kind;
|
|
2399
2455
|
if (nodeType === "Stmt") {
|
|
2400
2456
|
try {
|
|
2401
|
-
currentStmtOffset = n.
|
|
2457
|
+
currentStmtOffset = n.pos;
|
|
2402
2458
|
} catch {
|
|
2403
2459
|
currentStmtOffset = Number.MAX_SAFE_INTEGER;
|
|
2404
2460
|
}
|
|
@@ -2911,11 +2967,12 @@ function analyzeShellCommand(command) {
|
|
|
2911
2967
|
if (lower2.startsWith("-")) allTokens.push(lower2.replace(/^-+/, ""));
|
|
2912
2968
|
};
|
|
2913
2969
|
try {
|
|
2914
|
-
const f =
|
|
2915
|
-
|
|
2970
|
+
const f = parseShared(command);
|
|
2971
|
+
if (f === PARSE_FAIL) throw new Error("parse failed");
|
|
2972
|
+
walk(f, (node) => {
|
|
2916
2973
|
if (!node) return false;
|
|
2917
2974
|
const n = node;
|
|
2918
|
-
if (
|
|
2975
|
+
if (n.kind !== "CallExpr") return true;
|
|
2919
2976
|
const wordValues2 = (n.Args || []).map((arg) => {
|
|
2920
2977
|
return (arg.Parts || []).map((p) => (p.Value ?? "").replace(/\\(.)/g, "$1")).join("");
|
|
2921
2978
|
}).filter((s) => s.length > 0);
|
|
@@ -3187,7 +3244,7 @@ function runnablePrefix(command) {
|
|
|
3187
3244
|
let text = command;
|
|
3188
3245
|
for (let attempt = 0; attempt < 32; attempt++) {
|
|
3189
3246
|
try {
|
|
3190
|
-
return
|
|
3247
|
+
return toPlainTree(syntax3.NewParser().Parse(text, ""));
|
|
3191
3248
|
} catch (error) {
|
|
3192
3249
|
const message = String(typeof error?.Error === "function" ? error.Error() : error?.message);
|
|
3193
3250
|
const line = Number(/^(\d+):/.exec(message)?.[1]);
|
|
@@ -3255,7 +3312,7 @@ function wordValues(word, state) {
|
|
|
3255
3312
|
out2 = next.slice(0, MAX_ALTERNATIVES);
|
|
3256
3313
|
};
|
|
3257
3314
|
for (const node of nodes) {
|
|
3258
|
-
const kind =
|
|
3315
|
+
const kind = node.kind;
|
|
3259
3316
|
let pieces;
|
|
3260
3317
|
if (kind === "ParamExp") pieces = paramValues(node, quoted);
|
|
3261
3318
|
else if (kind === "DblQuoted") {
|
|
@@ -3323,9 +3380,9 @@ function collectShellFileAccesses(command, context = {}) {
|
|
|
3323
3380
|
}
|
|
3324
3381
|
function substitutions(word, state, depth) {
|
|
3325
3382
|
if (!word) return;
|
|
3326
|
-
|
|
3383
|
+
walk(word, (node) => {
|
|
3327
3384
|
if (!node) return false;
|
|
3328
|
-
if (["CmdSubst", "ProcSubst"].includes(
|
|
3385
|
+
if (["CmdSubst", "ProcSubst"].includes(node.kind)) {
|
|
3329
3386
|
statements(node.Stmts ?? [], clone(state), depth + 1);
|
|
3330
3387
|
return false;
|
|
3331
3388
|
}
|
|
@@ -3749,7 +3806,7 @@ function collectShellFileAccesses(command, context = {}) {
|
|
|
3749
3806
|
}
|
|
3750
3807
|
function readsScriptFromStdin(stmt) {
|
|
3751
3808
|
const cmd = stmt?.Cmd;
|
|
3752
|
-
if (!cmd ||
|
|
3809
|
+
if (!cmd || cmd.kind !== "CallExpr") return false;
|
|
3753
3810
|
const words = (cmd.Args ?? []).map((a) => resolveWordLiteral(a));
|
|
3754
3811
|
const head = unwrapCommandHead(words);
|
|
3755
3812
|
return SHELL_INTERPRETERS.has(base(words[head])) && shellConsumesPipe(words.slice(head + 1));
|
|
@@ -3763,7 +3820,7 @@ function collectShellFileAccesses(command, context = {}) {
|
|
|
3763
3820
|
}
|
|
3764
3821
|
function producedText(stmt) {
|
|
3765
3822
|
const cmd = stmt?.Cmd;
|
|
3766
|
-
if (!cmd ||
|
|
3823
|
+
if (!cmd || cmd.kind !== "CallExpr") return null;
|
|
3767
3824
|
const words = (cmd.Args ?? []).map((a) => resolveWordLiteral(a));
|
|
3768
3825
|
if (words.some((w) => w === null)) return null;
|
|
3769
3826
|
const head = unwrapCommandHead(words);
|
|
@@ -3782,7 +3839,7 @@ function collectShellFileAccesses(command, context = {}) {
|
|
|
3782
3839
|
if (!stmt || depth > MAX_DEPTH) return;
|
|
3783
3840
|
state.onSuccess = void 0;
|
|
3784
3841
|
if (stmt.Background) state = clone(state);
|
|
3785
|
-
const headWord = stmt.Cmd &&
|
|
3842
|
+
const headWord = stmt.Cmd && stmt.Cmd.kind === "CallExpr" ? base(resolveWordLiteral(stmt.Cmd.Args?.[0])) : "";
|
|
3786
3843
|
const sends = SENDING_STDIN.has(headWord);
|
|
3787
3844
|
for (const redir of stmt.Redirs ?? []) {
|
|
3788
3845
|
substitutions(redir.Word, state, depth);
|
|
@@ -3798,7 +3855,7 @@ function collectShellFileAccesses(command, context = {}) {
|
|
|
3798
3855
|
const cmd = stmt.Cmd;
|
|
3799
3856
|
if (!cmd) return;
|
|
3800
3857
|
stdinScript(stmt, state, depth);
|
|
3801
|
-
switch (
|
|
3858
|
+
switch (cmd.kind) {
|
|
3802
3859
|
case "CallExpr":
|
|
3803
3860
|
call(cmd, state, depth);
|
|
3804
3861
|
break;
|
|
@@ -3812,7 +3869,7 @@ function collectShellFileAccesses(command, context = {}) {
|
|
|
3812
3869
|
for (; ; ) {
|
|
3813
3870
|
segments2.unshift({ op: node.Op, part: node.Y });
|
|
3814
3871
|
const left = node.X;
|
|
3815
|
-
if (left?.Cmd &&
|
|
3872
|
+
if (left?.Cmd && left.Cmd.kind === "BinaryCmd" && !left.Redirs?.length && !left.Negated && !left.Background)
|
|
3816
3873
|
node = left.Cmd;
|
|
3817
3874
|
else {
|
|
3818
3875
|
segments2.unshift({ op: node.Op, part: left });
|
|
@@ -5150,11 +5207,10 @@ function shellWriteUnseen(command, target) {
|
|
|
5150
5207
|
let unseen = false;
|
|
5151
5208
|
let seen = false;
|
|
5152
5209
|
try {
|
|
5153
|
-
|
|
5210
|
+
walk(f, (node) => {
|
|
5154
5211
|
if (!node || unseen) return false;
|
|
5155
5212
|
const n = node;
|
|
5156
|
-
if (
|
|
5157
|
-
return true;
|
|
5213
|
+
if (n.kind !== "Stmt" || !n.Cmd || n.Cmd.kind !== "CallExpr") return true;
|
|
5158
5214
|
const words = (n.Cmd.Args || []).map(
|
|
5159
5215
|
(a) => resolveWordLiteral(a)
|
|
5160
5216
|
);
|
|
@@ -5209,14 +5265,14 @@ function analyzeGitImpl(command) {
|
|
|
5209
5265
|
seen.add(key);
|
|
5210
5266
|
findings.push(f);
|
|
5211
5267
|
};
|
|
5212
|
-
const
|
|
5213
|
-
const parsed =
|
|
5268
|
+
const walk3 = new GitWalk((inv) => classify(inv).forEach(found), found);
|
|
5269
|
+
const parsed = walk3.run(command, 0);
|
|
5214
5270
|
return { parsed, findings };
|
|
5215
5271
|
}
|
|
5216
5272
|
function isPositionalParam(word) {
|
|
5217
5273
|
let parts = word?.Parts || [];
|
|
5218
|
-
if (parts.length === 1 &&
|
|
5219
|
-
if (parts.length !== 1 ||
|
|
5274
|
+
if (parts.length === 1 && parts[0].kind === "DblQuoted") parts = parts[0].Parts || [];
|
|
5275
|
+
if (parts.length !== 1 || parts[0].kind !== "ParamExp") return false;
|
|
5220
5276
|
return /^(\d+|@|\*)$/.test(parts[0].Param?.Value ?? "");
|
|
5221
5277
|
}
|
|
5222
5278
|
function boundPositionals() {
|
|
@@ -5230,11 +5286,11 @@ function boundPositionals() {
|
|
|
5230
5286
|
function plainParamName(word) {
|
|
5231
5287
|
let parts = word?.Parts || [];
|
|
5232
5288
|
let quoted = false;
|
|
5233
|
-
if (parts.length === 1 &&
|
|
5289
|
+
if (parts.length === 1 && parts[0].kind === "DblQuoted") {
|
|
5234
5290
|
parts = parts[0].Parts || [];
|
|
5235
5291
|
quoted = true;
|
|
5236
5292
|
}
|
|
5237
|
-
if (parts.length !== 1 ||
|
|
5293
|
+
if (parts.length !== 1 || parts[0].kind !== "ParamExp") return null;
|
|
5238
5294
|
const p = parts[0];
|
|
5239
5295
|
if (p.Excl || p.Length || p.Width || p.Index || p.Slice || p.Repl || p.Exp) return null;
|
|
5240
5296
|
const name = p.Param?.Value;
|
|
@@ -5259,7 +5315,7 @@ function resolveGitWords(word) {
|
|
|
5259
5315
|
}
|
|
5260
5316
|
const parts = word?.Parts || [];
|
|
5261
5317
|
const expansion = parts.some((p) => {
|
|
5262
|
-
const t =
|
|
5318
|
+
const t = p.kind;
|
|
5263
5319
|
return t === "ParamExp" || t === "CmdSubst";
|
|
5264
5320
|
});
|
|
5265
5321
|
return expansion ? splitFields(value) : [value];
|
|
@@ -5660,14 +5716,14 @@ function hookCommands(text) {
|
|
|
5660
5716
|
const root = parsed?.hooks;
|
|
5661
5717
|
if (!root || typeof root !== "object") return null;
|
|
5662
5718
|
const out = [];
|
|
5663
|
-
const
|
|
5719
|
+
const walk3 = (v, depth) => {
|
|
5664
5720
|
if (depth > 8 || !v || typeof v !== "object") return;
|
|
5665
5721
|
for (const [k, x] of Object.entries(v)) {
|
|
5666
5722
|
if (k === "command" && typeof x === "string") out.push(x);
|
|
5667
|
-
else
|
|
5723
|
+
else walk3(x, depth + 1);
|
|
5668
5724
|
}
|
|
5669
5725
|
};
|
|
5670
|
-
|
|
5726
|
+
walk3(root, 0);
|
|
5671
5727
|
return out;
|
|
5672
5728
|
}
|
|
5673
5729
|
function hooksDisarmed(text) {
|
|
@@ -5834,9 +5890,9 @@ function isDupInOp(op) {
|
|
|
5834
5890
|
if (dupInOp === null) {
|
|
5835
5891
|
let found = -1;
|
|
5836
5892
|
try {
|
|
5837
|
-
|
|
5893
|
+
walk(parseShared("cat <&3"), (node) => {
|
|
5838
5894
|
const n = node;
|
|
5839
|
-
if (found === -1 && n &&
|
|
5895
|
+
if (found === -1 && n && n.kind === "Stmt" && n.Redirs?.[0]) {
|
|
5840
5896
|
found = n.Redirs[0].Op;
|
|
5841
5897
|
}
|
|
5842
5898
|
return true;
|
|
@@ -5857,10 +5913,10 @@ function analyzeOutbound(command) {
|
|
|
5857
5913
|
return hit;
|
|
5858
5914
|
}
|
|
5859
5915
|
const findings = [];
|
|
5860
|
-
const
|
|
5861
|
-
const parsed =
|
|
5916
|
+
const walk3 = new OutboundWalk(findings);
|
|
5917
|
+
const parsed = walk3.run(command, 0);
|
|
5862
5918
|
const envOut = findings.find((f) => f.checkId === "data.env-exfil");
|
|
5863
|
-
if (envOut) envOut.destinations =
|
|
5919
|
+
if (envOut) envOut.destinations = walk3.envDestinations();
|
|
5864
5920
|
const computed = { parsed, findings };
|
|
5865
5921
|
if (cache2.size >= CACHE_MAX2) {
|
|
5866
5922
|
const oldest = cache2.keys().next().value;
|
|
@@ -5876,7 +5932,7 @@ function wordShape(word) {
|
|
|
5876
5932
|
const parts = word?.Parts || [];
|
|
5877
5933
|
let out = "";
|
|
5878
5934
|
for (const p of parts) {
|
|
5879
|
-
const t =
|
|
5935
|
+
const t = p.kind;
|
|
5880
5936
|
if (t === "Lit" || t === "SglQuoted") out += p.Value ?? "";
|
|
5881
5937
|
else if (t === "DblQuoted") out += wordShape(p);
|
|
5882
5938
|
else out += "X";
|
|
@@ -6011,16 +6067,16 @@ function declDumpsEnv(decl) {
|
|
|
6011
6067
|
}
|
|
6012
6068
|
function paramName(word) {
|
|
6013
6069
|
let parts = word?.Parts || [];
|
|
6014
|
-
if (parts.length === 1 &&
|
|
6015
|
-
if (parts.length !== 1 ||
|
|
6070
|
+
if (parts.length === 1 && parts[0].kind === "DblQuoted") parts = parts[0].Parts || [];
|
|
6071
|
+
if (parts.length !== 1 || parts[0].kind !== "ParamExp") return null;
|
|
6016
6072
|
return parts[0].Param?.Value ?? null;
|
|
6017
6073
|
}
|
|
6018
6074
|
function refersTo(word, names) {
|
|
6019
6075
|
let hit = false;
|
|
6020
|
-
|
|
6076
|
+
walk(word, (node) => {
|
|
6021
6077
|
if (hit || !node) return false;
|
|
6022
6078
|
const n = node;
|
|
6023
|
-
if (
|
|
6079
|
+
if (n.kind === "ParamExp" && names.has(n.Param?.Value)) hit = true;
|
|
6024
6080
|
return !hit;
|
|
6025
6081
|
});
|
|
6026
6082
|
return hit;
|
|
@@ -6028,10 +6084,10 @@ function refersTo(word, names) {
|
|
|
6028
6084
|
function runsCommandsIn(stmt) {
|
|
6029
6085
|
const readVars = /* @__PURE__ */ new Set();
|
|
6030
6086
|
let hit = false;
|
|
6031
|
-
|
|
6087
|
+
walk(stmt, (node) => {
|
|
6032
6088
|
if (hit || !node) return false;
|
|
6033
6089
|
const n = node;
|
|
6034
|
-
if (
|
|
6090
|
+
if (n.kind !== "CallExpr") return true;
|
|
6035
6091
|
const words = wordsOf(n);
|
|
6036
6092
|
if (words.length === 0) return true;
|
|
6037
6093
|
const { head, at } = headOf(words);
|
|
@@ -6608,7 +6664,7 @@ function localContexts(file2) {
|
|
|
6608
6664
|
if (depth > 64) return null;
|
|
6609
6665
|
if (!stmt || stmt.Background || stmt.Negated || stmt.Redirs?.length) return null;
|
|
6610
6666
|
const cmd = stmt.Cmd;
|
|
6611
|
-
const kind =
|
|
6667
|
+
const kind = cmd.kind;
|
|
6612
6668
|
if (kind === "BinaryCmd" && cmd.Op === andOp) {
|
|
6613
6669
|
const next = visit(cmd.X, steps, depth + 1);
|
|
6614
6670
|
return next === null ? null : visit(cmd.Y, next, depth + 1);
|
|
@@ -6624,7 +6680,7 @@ function localContexts(file2) {
|
|
|
6624
6680
|
)
|
|
6625
6681
|
))
|
|
6626
6682
|
return null;
|
|
6627
|
-
contexts.set(cmd.
|
|
6683
|
+
contexts.set(cmd.pos, steps);
|
|
6628
6684
|
return null;
|
|
6629
6685
|
}
|
|
6630
6686
|
if (file2.Stmts?.length === 1) visit(file2.Stmts[0], []);
|
|
@@ -6635,12 +6691,12 @@ function collect(command, out, depth) {
|
|
|
6635
6691
|
const f = parseShared(command);
|
|
6636
6692
|
if (typeof f === "symbol") return;
|
|
6637
6693
|
const contexts = depth === 0 ? localContexts(f) : /* @__PURE__ */ new Map();
|
|
6638
|
-
|
|
6639
|
-
if (node &&
|
|
6694
|
+
walk(f, (node) => {
|
|
6695
|
+
if (node && node.kind === "CallExpr") {
|
|
6640
6696
|
const words = (node.Args || []).map((a) => resolveWordLiteral(a));
|
|
6641
6697
|
const start = out.length;
|
|
6642
6698
|
if (words.length > 0) fromCall(words, out, depth);
|
|
6643
|
-
const steps = contexts.get(node.
|
|
6699
|
+
const steps = contexts.get(node.pos);
|
|
6644
6700
|
for (let i = start; i < out.length; i++) {
|
|
6645
6701
|
if (["npx", "bunx", "npm exec"].includes(out[i].manager)) {
|
|
6646
6702
|
if (!steps) out[i].localCwd = null;
|
|
@@ -6687,7 +6743,7 @@ function collect2(command, out, depth) {
|
|
|
6687
6743
|
try {
|
|
6688
6744
|
walkCommandScope(command, (node) => {
|
|
6689
6745
|
const n = node;
|
|
6690
|
-
if (!n ||
|
|
6746
|
+
if (!n || n.kind !== "CallExpr") return true;
|
|
6691
6747
|
const words = (n.Args || []).map((a) => resolveWordLiteral(a));
|
|
6692
6748
|
if (words.length === 0) return true;
|
|
6693
6749
|
const prefix = (n.Assigns || []).filter((a) => a?.Name?.Value).map(
|
|
@@ -8072,12 +8128,12 @@ function extractContentDestinations(text) {
|
|
|
8072
8128
|
function extractArgDestinations(args) {
|
|
8073
8129
|
const out = /* @__PURE__ */ new Set();
|
|
8074
8130
|
try {
|
|
8075
|
-
|
|
8131
|
+
walk2(args, void 0, out, 0);
|
|
8076
8132
|
} catch {
|
|
8077
8133
|
}
|
|
8078
8134
|
return [...out];
|
|
8079
8135
|
}
|
|
8080
|
-
function
|
|
8136
|
+
function walk2(value, key, out, depth) {
|
|
8081
8137
|
if (out.size >= MAX_DESTINATIONS || depth > 12) return;
|
|
8082
8138
|
if (typeof value === "string") {
|
|
8083
8139
|
const s = value.length > MAX_SCAN_CHARS ? value.slice(0, MAX_SCAN_CHARS) : value;
|
|
@@ -8096,12 +8152,12 @@ function walk(value, key, out, depth) {
|
|
|
8096
8152
|
return;
|
|
8097
8153
|
}
|
|
8098
8154
|
if (Array.isArray(value)) {
|
|
8099
|
-
for (const v of value)
|
|
8155
|
+
for (const v of value) walk2(v, key, out, depth + 1);
|
|
8100
8156
|
return;
|
|
8101
8157
|
}
|
|
8102
8158
|
if (value && typeof value === "object") {
|
|
8103
8159
|
for (const [k, v] of Object.entries(value))
|
|
8104
|
-
|
|
8160
|
+
walk2(v, k, out, depth + 1);
|
|
8105
8161
|
}
|
|
8106
8162
|
}
|
|
8107
8163
|
function destinationDomain(dest) {
|
|
@@ -8753,9 +8809,9 @@ function classifyCall(head, args, prefixes, env, state, out) {
|
|
|
8753
8809
|
function walkCalls(command, out, depth, state) {
|
|
8754
8810
|
const f = parseShared(command);
|
|
8755
8811
|
if (typeof f === "symbol" || !f) return;
|
|
8756
|
-
|
|
8812
|
+
walk(f, (node) => {
|
|
8757
8813
|
if (!node) return false;
|
|
8758
|
-
const type =
|
|
8814
|
+
const type = node.kind;
|
|
8759
8815
|
if (type === "DeclClause") {
|
|
8760
8816
|
for (const a of node.Args || []) if (a?.Name?.Value) state.env.add(a.Name.Value);
|
|
8761
8817
|
return true;
|
|
@@ -8896,7 +8952,7 @@ function matchCanaryArgs(args, values) {
|
|
|
8896
8952
|
if (values.length === 0) return null;
|
|
8897
8953
|
const needles = prepare(values);
|
|
8898
8954
|
if (needles.length === 0) return null;
|
|
8899
|
-
const
|
|
8955
|
+
const walk3 = (v, depth, fieldPath) => {
|
|
8900
8956
|
if (depth > MAX_DEPTH4) return null;
|
|
8901
8957
|
if (typeof v === "string") {
|
|
8902
8958
|
const hit = matchPrepared(v, needles);
|
|
@@ -8905,31 +8961,31 @@ function matchCanaryArgs(args, values) {
|
|
|
8905
8961
|
const trimmed = v.trim();
|
|
8906
8962
|
if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
|
8907
8963
|
try {
|
|
8908
|
-
return
|
|
8964
|
+
return walk3(JSON.parse(v), depth + 1, fieldPath);
|
|
8909
8965
|
} catch {
|
|
8910
8966
|
}
|
|
8911
8967
|
}
|
|
8912
8968
|
}
|
|
8913
8969
|
return null;
|
|
8914
8970
|
}
|
|
8915
|
-
if (typeof v === "number" && Number.isFinite(v)) return
|
|
8971
|
+
if (typeof v === "number" && Number.isFinite(v)) return walk3(String(v), depth, fieldPath);
|
|
8916
8972
|
if (Array.isArray(v)) {
|
|
8917
8973
|
for (let i = 0; i < v.length; i++) {
|
|
8918
|
-
const h =
|
|
8974
|
+
const h = walk3(v[i], depth + 1, `${fieldPath}[${i}]`);
|
|
8919
8975
|
if (h) return h;
|
|
8920
8976
|
}
|
|
8921
8977
|
return null;
|
|
8922
8978
|
}
|
|
8923
8979
|
if (v && typeof v === "object") {
|
|
8924
8980
|
for (const [k, child] of Object.entries(v)) {
|
|
8925
|
-
const h =
|
|
8981
|
+
const h = walk3(child, depth + 1, fieldPath ? `${fieldPath}.${k}` : k);
|
|
8926
8982
|
if (h) return h;
|
|
8927
8983
|
}
|
|
8928
8984
|
}
|
|
8929
8985
|
return null;
|
|
8930
8986
|
};
|
|
8931
8987
|
try {
|
|
8932
|
-
return
|
|
8988
|
+
return walk3(args, 0, "");
|
|
8933
8989
|
} catch (e) {
|
|
8934
8990
|
console.error(
|
|
8935
8991
|
"[node9 engine] canary args walk failed:",
|
|
@@ -8938,26 +8994,20 @@ function matchCanaryArgs(args, values) {
|
|
|
8938
8994
|
return null;
|
|
8939
8995
|
}
|
|
8940
8996
|
}
|
|
8941
|
-
var
|
|
8997
|
+
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, 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, 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;
|
|
8942
8998
|
var init_dist = __esm({
|
|
8943
8999
|
"packages/policy-engine/dist/index.mjs"() {
|
|
8944
9000
|
"use strict";
|
|
8945
|
-
import_mvdan_sh = __toESM(require("mvdan-sh"), 1);
|
|
8946
9001
|
import_picomatch = __toESM(require("picomatch"), 1);
|
|
8947
9002
|
import_safe_regex2 = __toESM(require("safe-regex2"), 1);
|
|
8948
9003
|
import_picomatch2 = __toESM(require("picomatch"), 1);
|
|
9004
|
+
import_mvdan_sh = __toESM(require("mvdan-sh"), 1);
|
|
8949
9005
|
import_mvdan_sh2 = __toESM(require("mvdan-sh"), 1);
|
|
8950
9006
|
import_mvdan_sh3 = __toESM(require("mvdan-sh"), 1);
|
|
8951
9007
|
import_safe_regex22 = __toESM(require("safe-regex2"), 1);
|
|
8952
9008
|
import_crypto2 = require("crypto");
|
|
8953
9009
|
import_crypto3 = require("crypto");
|
|
8954
|
-
import_mvdan_sh4 = __toESM(require("mvdan-sh"), 1);
|
|
8955
|
-
import_mvdan_sh5 = __toESM(require("mvdan-sh"), 1);
|
|
8956
|
-
import_mvdan_sh6 = __toESM(require("mvdan-sh"), 1);
|
|
8957
|
-
import_mvdan_sh7 = __toESM(require("mvdan-sh"), 1);
|
|
8958
|
-
import_mvdan_sh8 = __toESM(require("mvdan-sh"), 1);
|
|
8959
9010
|
import_crypto4 = require("crypto");
|
|
8960
|
-
import_mvdan_sh9 = __toESM(require("mvdan-sh"), 1);
|
|
8961
9011
|
import_crypto5 = __toESM(require("crypto"), 1);
|
|
8962
9012
|
MAX_REGEX_LENGTH = 256;
|
|
8963
9013
|
REGEX_CACHE_MAX = 500;
|
|
@@ -11603,6 +11653,61 @@ var init_dist = __esm({
|
|
|
11603
11653
|
return s.untrustedContent.afterExposure === "log-only" ? "log" : "review";
|
|
11604
11654
|
}
|
|
11605
11655
|
};
|
|
11656
|
+
({ syntax } = import_mvdan_sh2.default);
|
|
11657
|
+
KINDS = {
|
|
11658
|
+
File: { nodes: ["Stmts"] },
|
|
11659
|
+
Stmt: { nodes: ["Cmd", "Redirs"], scalars: ["Negated", "Background", "Coprocess"] },
|
|
11660
|
+
CallExpr: { nodes: ["Assigns", "Args"] },
|
|
11661
|
+
Assign: { nodes: ["Name", "Value", "Index", "Array"], scalars: ["Append", "Naked"] },
|
|
11662
|
+
Word: { nodes: ["Parts"] },
|
|
11663
|
+
Lit: { scalars: ["Value"] },
|
|
11664
|
+
SglQuoted: { scalars: ["Value", "Dollar"] },
|
|
11665
|
+
DblQuoted: { nodes: ["Parts"], scalars: ["Dollar"] },
|
|
11666
|
+
ParamExp: {
|
|
11667
|
+
nodes: ["Param", "Index", "Slice", "Repl", "Exp"],
|
|
11668
|
+
scalars: ["Short", "Excl", "Length", "Width"]
|
|
11669
|
+
},
|
|
11670
|
+
CmdSubst: { nodes: ["Stmts"], scalars: ["Backquotes", "TempFile", "ReplyVar"] },
|
|
11671
|
+
ArithmExp: { nodes: ["X"], scalars: ["Bracket", "Unsigned"] },
|
|
11672
|
+
ArithmCmd: { nodes: ["X"], scalars: ["Unsigned"] },
|
|
11673
|
+
ProcSubst: { nodes: ["Stmts"], scalars: ["Op"] },
|
|
11674
|
+
ExtGlob: { nodes: ["Pattern"], scalars: ["Op"] },
|
|
11675
|
+
BraceExp: { nodes: ["Elems"], scalars: ["Sequence"] },
|
|
11676
|
+
// only with SplitBraces, unused here
|
|
11677
|
+
BinaryCmd: { nodes: ["X", "Y"], scalars: ["Op"] },
|
|
11678
|
+
BinaryArithm: { nodes: ["X", "Y"], scalars: ["Op"] },
|
|
11679
|
+
UnaryArithm: { nodes: ["X"], scalars: ["Op", "Post"] },
|
|
11680
|
+
ParenArithm: { nodes: ["X"] },
|
|
11681
|
+
BinaryTest: { nodes: ["X", "Y"], scalars: ["Op"] },
|
|
11682
|
+
UnaryTest: { nodes: ["X"], scalars: ["Op"] },
|
|
11683
|
+
ParenTest: { nodes: ["X"] },
|
|
11684
|
+
Redirect: { nodes: ["N", "Word", "Hdoc"], scalars: ["Op"] },
|
|
11685
|
+
Block: { nodes: ["Stmts"] },
|
|
11686
|
+
Subshell: { nodes: ["Stmts"] },
|
|
11687
|
+
IfClause: { nodes: ["Cond", "Then", "Else"] },
|
|
11688
|
+
WhileClause: { nodes: ["Cond", "Do"], scalars: ["Until"] },
|
|
11689
|
+
ForClause: { nodes: ["Loop", "Do"], scalars: ["Select"] },
|
|
11690
|
+
WordIter: { nodes: ["Name", "Items"] },
|
|
11691
|
+
CStyleLoop: { nodes: ["Init", "Cond", "Post"] },
|
|
11692
|
+
CaseClause: { nodes: ["Word", "Items"] },
|
|
11693
|
+
CaseItem: { nodes: ["Patterns", "Stmts"], scalars: ["Op"] },
|
|
11694
|
+
FuncDecl: { nodes: ["Name", "Body"], scalars: ["RsrvWord"] },
|
|
11695
|
+
DeclClause: { nodes: ["Variant", "Args"], notWalked: ["Variant"] },
|
|
11696
|
+
LetClause: { nodes: ["Exprs"] },
|
|
11697
|
+
TimeClause: { nodes: ["Stmt"], scalars: ["PosixFormat"] },
|
|
11698
|
+
CoprocClause: { nodes: ["Name", "Stmt"] },
|
|
11699
|
+
TestClause: { nodes: ["X"] },
|
|
11700
|
+
TestDecl: { nodes: ["Description", "Body"] },
|
|
11701
|
+
ArrayExpr: { nodes: ["Elems"] },
|
|
11702
|
+
ArrayElem: { nodes: ["Index", "Value"] },
|
|
11703
|
+
Comment: { scalars: ["Text"] }
|
|
11704
|
+
};
|
|
11705
|
+
STRUCTS = {
|
|
11706
|
+
"ParamExp.Slice": { nodes: ["Offset", "Length"], notWalked: ["Offset", "Length"] },
|
|
11707
|
+
"ParamExp.Repl": { nodes: ["Orig", "With"], scalars: ["All"] },
|
|
11708
|
+
"ParamExp.Exp": { nodes: ["Word"], scalars: ["Op"] }
|
|
11709
|
+
};
|
|
11710
|
+
unknownKinds = /* @__PURE__ */ new Set();
|
|
11606
11711
|
INVISIBLE_RE = /[͏----︀-️]|[\u{E0000}-\u{E007F}]/gu;
|
|
11607
11712
|
INVISIBLE_ONE = new RegExp(`^(?:${INVISIBLE_RE.source.split("|")[0]})$`, "u");
|
|
11608
11713
|
HOMOGLYPHS = {
|
|
@@ -11888,8 +11993,8 @@ var init_dist = __esm({
|
|
|
11888
11993
|
php: PHP
|
|
11889
11994
|
};
|
|
11890
11995
|
STRING_LITERAL = /(['"`])((?:\\.|(?!\1)[^\\\n]){1,4096})\1/g;
|
|
11891
|
-
({ syntax } =
|
|
11892
|
-
sharedParser =
|
|
11996
|
+
({ syntax: syntax2 } = import_mvdan_sh.default);
|
|
11997
|
+
sharedParser = syntax2.NewParser();
|
|
11893
11998
|
MESSAGE_FLAGS = /* @__PURE__ */ new Set([
|
|
11894
11999
|
"-m",
|
|
11895
12000
|
"--message",
|
|
@@ -11923,7 +12028,7 @@ var init_dist = __esm({
|
|
|
11923
12028
|
NORMALIZE_CACHE_MAX = 5e3;
|
|
11924
12029
|
normalizeCache = /* @__PURE__ */ new Map();
|
|
11925
12030
|
codeBodiesCache = /* @__PURE__ */ new Map();
|
|
11926
|
-
AST_CACHE_MAX =
|
|
12031
|
+
AST_CACHE_MAX = 256;
|
|
11927
12032
|
astCache = /* @__PURE__ */ new Map();
|
|
11928
12033
|
PARSE_FAIL = /* @__PURE__ */ Symbol("parse-fail");
|
|
11929
12034
|
INTERPRETER_LINE = /^#![ \t]*[\w./+-]+(?:[ \t]+[\w./+=:-]+)*[ \t]*\r?(?=\n|$)/;
|
|
@@ -12908,7 +13013,7 @@ var init_dist = __esm({
|
|
|
12908
13013
|
ARGV_FIELDS = ["args", "argv"];
|
|
12909
13014
|
BARE_WORD = /^[A-Za-z0-9_./+:@%,-]+$/;
|
|
12910
13015
|
MAX_ARGV_CHARACTERS = 262144;
|
|
12911
|
-
({ syntax:
|
|
13016
|
+
({ syntax: syntax3 } = import_mvdan_sh3.default);
|
|
12912
13017
|
MAX_ALTERNATIVES = 64;
|
|
12913
13018
|
MAX_CALL_DEPTH2 = 4;
|
|
12914
13019
|
MAX_CALL_WALKS2 = 256;
|
|
@@ -13000,7 +13105,6 @@ var init_dist = __esm({
|
|
|
13000
13105
|
);
|
|
13001
13106
|
exempt = (p) => SYSTEM_DIRS_EXEMPT.some((d) => within(p, d));
|
|
13002
13107
|
RANK = { allow: 0, review: 1, block: 2 };
|
|
13003
|
-
({ syntax: syntax3 } = import_mvdan_sh.default);
|
|
13004
13108
|
SEQUENCE_OPS = (() => {
|
|
13005
13109
|
const ops = /* @__PURE__ */ new Set();
|
|
13006
13110
|
for (const text of ["x && y", "x || y"]) {
|
|
@@ -16161,7 +16265,6 @@ zoo`.split("\n");
|
|
|
16161
16265
|
);
|
|
16162
16266
|
NEVER_NEGATED = /* @__PURE__ */ new Set([SIGNALS[1].any[0]]);
|
|
16163
16267
|
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;
|
|
16164
|
-
({ syntax: syntax4 } = import_mvdan_sh4.default);
|
|
16165
16268
|
RDR_IN = (() => {
|
|
16166
16269
|
try {
|
|
16167
16270
|
return parseShared("x <y").Stmts[0].Redirs[0].Op;
|
|
@@ -16190,7 +16293,6 @@ zoo`.split("\n");
|
|
|
16190
16293
|
"tar"
|
|
16191
16294
|
]);
|
|
16192
16295
|
LITERAL_VERBS = /* @__PURE__ */ new Set(["echo", "printf", ":", "true", "touch", "sed"]);
|
|
16193
|
-
({ syntax: syntax5 } = import_mvdan_sh5.default);
|
|
16194
16296
|
GIT_PRESCREEN_RE = /git|husky/i;
|
|
16195
16297
|
MAX_PAYLOAD_DEPTH2 = 8;
|
|
16196
16298
|
MAX_ALIAS_DEPTH = 5;
|
|
@@ -16259,7 +16361,7 @@ zoo`.split("\n");
|
|
|
16259
16361
|
visit(node, depth) {
|
|
16260
16362
|
if (!node) return false;
|
|
16261
16363
|
const n = node;
|
|
16262
|
-
const type =
|
|
16364
|
+
const type = n.kind;
|
|
16263
16365
|
if (type === "Stmt") {
|
|
16264
16366
|
this.visitHeredocShell(n, depth);
|
|
16265
16367
|
return true;
|
|
@@ -16267,7 +16369,7 @@ zoo`.split("\n");
|
|
|
16267
16369
|
if (type === "ForClause") {
|
|
16268
16370
|
const loop = n.Loop;
|
|
16269
16371
|
const name = loop?.Name?.Value;
|
|
16270
|
-
if (name &&
|
|
16372
|
+
if (name && loop.kind === "WordIter") {
|
|
16271
16373
|
const items = (loop.Items || []).map((w) => resolveWordLiteral(w));
|
|
16272
16374
|
const literal = items.length > 0 && items.every((v) => v !== null);
|
|
16273
16375
|
this.loopVars.set(name, literal ? items.slice(0, MAX_LOOP_ITEMS) : null);
|
|
@@ -16334,7 +16436,7 @@ zoo`.split("\n");
|
|
|
16334
16436
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
16335
16437
|
visitHeredocShell(stmt, depth) {
|
|
16336
16438
|
const cmd = stmt.Cmd;
|
|
16337
|
-
if (!cmd ||
|
|
16439
|
+
if (!cmd || cmd.kind !== "CallExpr" || depth >= MAX_PAYLOAD_DEPTH2) return;
|
|
16338
16440
|
const redirs = stmt.Redirs || [];
|
|
16339
16441
|
const hdoc = redirs.find((r) => r?.Hdoc && (r.N == null || r.N.Value === "0"));
|
|
16340
16442
|
if (!hdoc) return;
|
|
@@ -16588,7 +16690,6 @@ zoo`.split("\n");
|
|
|
16588
16690
|
]);
|
|
16589
16691
|
SETTINGS_RE = /\/(?:\.claude\/settings(?:\.local)?\.json|\.gemini\/settings\.json|\.codex\/hooks\.json|\.cursor\/hooks\.json)$/i;
|
|
16590
16692
|
HOOK_WORDS_RE = /"hooks"|\b(PreToolUse|PostToolUse|UserPromptSubmit|Notification|Stop|SubagentStop|SessionStart|SessionEnd|PreCompact|BeforeTool|AfterTool)\b|"command"\s*:/;
|
|
16591
|
-
({ syntax: syntax6 } = import_mvdan_sh6.default);
|
|
16592
16693
|
SHELLS = /* @__PURE__ */ new Set([...SHELL_INTERPRETERS, "mksh", "ash", "csh", "tcsh"]);
|
|
16593
16694
|
NETCATS = /* @__PURE__ */ new Set(["nc", "ncat", "netcat", "nc.traditional", "nc.openbsd"]);
|
|
16594
16695
|
NET_CLIENTS = /* @__PURE__ */ new Set([...NETCATS, "telnet", "socat"]);
|
|
@@ -16646,7 +16747,7 @@ zoo`.split("\n");
|
|
|
16646
16747
|
const f = parseShared(command);
|
|
16647
16748
|
if (!f || typeof f !== "object" || !f.Stmts) return false;
|
|
16648
16749
|
try {
|
|
16649
|
-
|
|
16750
|
+
walk(f, (node) => this.visit(node, depth));
|
|
16650
16751
|
} catch {
|
|
16651
16752
|
return false;
|
|
16652
16753
|
}
|
|
@@ -16685,7 +16786,7 @@ zoo`.split("\n");
|
|
|
16685
16786
|
}
|
|
16686
16787
|
visit(n, depth) {
|
|
16687
16788
|
if (!n) return false;
|
|
16688
|
-
const type =
|
|
16789
|
+
const type = n.kind;
|
|
16689
16790
|
if (type === "Stmt") {
|
|
16690
16791
|
this.visitStmt(n, depth);
|
|
16691
16792
|
return true;
|
|
@@ -16705,7 +16806,7 @@ zoo`.split("\n");
|
|
|
16705
16806
|
const redirs = redirectWords(stmt);
|
|
16706
16807
|
const toSocket = redirs.some((w) => w !== null && DEV_NET_RE.test(w));
|
|
16707
16808
|
if (!cmd) return;
|
|
16708
|
-
const type =
|
|
16809
|
+
const type = cmd.kind;
|
|
16709
16810
|
if (type !== "CallExpr" || wordsOf(cmd).length > 0) {
|
|
16710
16811
|
if (this.readsNetFd(stmt) && runsCommandsIn(stmt)) {
|
|
16711
16812
|
this.reverseShell("commands are read from a network socket opened with exec");
|
|
@@ -16791,7 +16892,7 @@ zoo`.split("\n");
|
|
|
16791
16892
|
const stages = [];
|
|
16792
16893
|
const flatten = (s) => {
|
|
16793
16894
|
const c = s?.Cmd;
|
|
16794
|
-
if (c &&
|
|
16895
|
+
if (c && c.kind === "BinaryCmd" && isPipeOp(c.Op)) {
|
|
16795
16896
|
flatten(c.X);
|
|
16796
16897
|
flatten(c.Y);
|
|
16797
16898
|
} else stages.push(s);
|
|
@@ -16813,8 +16914,8 @@ zoo`.split("\n");
|
|
|
16813
16914
|
sinks.set(i, socketHosts(st));
|
|
16814
16915
|
}
|
|
16815
16916
|
if (!c) return;
|
|
16816
|
-
if (
|
|
16817
|
-
if (
|
|
16917
|
+
if (c.kind === "DeclClause" && declDumpsEnv(c) && dump < 0) dump = i;
|
|
16918
|
+
if (c.kind !== "CallExpr") {
|
|
16818
16919
|
if (runsCommandsIn(st)) shell = i;
|
|
16819
16920
|
return;
|
|
16820
16921
|
}
|
|
@@ -16855,10 +16956,10 @@ zoo`.split("\n");
|
|
|
16855
16956
|
/** A word (or assignment value) whose text includes an env dump's output. */
|
|
16856
16957
|
carriesEnv(word) {
|
|
16857
16958
|
let hit = false;
|
|
16858
|
-
|
|
16959
|
+
walk(word, (node) => {
|
|
16859
16960
|
if (hit || !node) return false;
|
|
16860
16961
|
const n = node;
|
|
16861
|
-
const t =
|
|
16962
|
+
const t = n.kind;
|
|
16862
16963
|
if (t === "ParamExp" && n.Param?.Value && this.envVars.has(n.Param.Value)) {
|
|
16863
16964
|
hit = true;
|
|
16864
16965
|
return false;
|
|
@@ -16875,11 +16976,11 @@ zoo`.split("\n");
|
|
|
16875
16976
|
stmtsDumpEnv(stmts) {
|
|
16876
16977
|
let hit = false;
|
|
16877
16978
|
for (const s of stmts) {
|
|
16878
|
-
|
|
16979
|
+
walk(s, (node) => {
|
|
16879
16980
|
if (hit || !node) return false;
|
|
16880
16981
|
const n = node;
|
|
16881
|
-
const t =
|
|
16882
|
-
if (t === "Stmt" && n.Cmd &&
|
|
16982
|
+
const t = n.kind;
|
|
16983
|
+
if (t === "Stmt" && n.Cmd && n.Cmd.kind === "CallExpr") {
|
|
16883
16984
|
const words = wordsOf(n.Cmd);
|
|
16884
16985
|
if (words.length > 0 && dumpsEnv(words, n)) hit = true;
|
|
16885
16986
|
}
|
|
@@ -16896,7 +16997,6 @@ zoo`.split("\n");
|
|
|
16896
16997
|
this.run(payload, depth + 1);
|
|
16897
16998
|
}
|
|
16898
16999
|
};
|
|
16899
|
-
({ syntax: syntax7 } = import_mvdan_sh8.default);
|
|
16900
17000
|
NPM_INSTALL_VERBS = /* @__PURE__ */ new Set([
|
|
16901
17001
|
"install",
|
|
16902
17002
|
"i",
|
|
@@ -17278,7 +17378,6 @@ zoo`.split("\n");
|
|
|
17278
17378
|
SHELLS2 = /* @__PURE__ */ new Set(["sh", "bash", "zsh", "dash", "ksh", "ash"]);
|
|
17279
17379
|
MAX_NESTING = 3;
|
|
17280
17380
|
SHELL_VALUE_FLAGS = /* @__PURE__ */ new Set(["--rcfile", "--init-file", "-O", "+O", "-o", "+o"]);
|
|
17281
|
-
({ syntax: syntax8 } = import_mvdan_sh7.default);
|
|
17282
17381
|
NPM_FAMILY = /* @__PURE__ */ new Set(["npm", "pnpm", "yarn", "bun", "npx", "pnpx", "bunx"]);
|
|
17283
17382
|
PIP_FAMILY = /* @__PURE__ */ new Set(["pip", "pip3", "uv", "uvx", "pipx", "pipenv"]);
|
|
17284
17383
|
NPM_FETCH_VERBS = /* @__PURE__ */ new Set([
|
|
@@ -17810,7 +17909,6 @@ zoo`.split("\n");
|
|
|
17810
17909
|
ACCOUNT_KEY_RE = /("(?:iban|account|account_number|accountNumber)"\s*:\s*")([A-Za-z0-9 .-]{6,40})(")/g;
|
|
17811
17910
|
ACCOUNT_VALUE_RE = /"([A-Z]{2}\d{2}[A-Z0-9]{11,30})"/g;
|
|
17812
17911
|
MAX_MASK_DEPTH = 64;
|
|
17813
|
-
({ syntax: syntax9 } = import_mvdan_sh9.default);
|
|
17814
17912
|
EMPTY = {
|
|
17815
17913
|
fetches: false,
|
|
17816
17914
|
sends: false,
|