@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.mjs
CHANGED
|
@@ -101,22 +101,16 @@ var init_forecast_context = __esm({
|
|
|
101
101
|
});
|
|
102
102
|
|
|
103
103
|
// packages/policy-engine/dist/index.mjs
|
|
104
|
-
import mvdanSh3 from "mvdan-sh";
|
|
105
104
|
import picomatch from "picomatch";
|
|
106
105
|
import safeRegex from "safe-regex2";
|
|
107
106
|
import pm from "picomatch";
|
|
108
|
-
import mvdanSh from "mvdan-sh";
|
|
109
107
|
import mvdanSh2 from "mvdan-sh";
|
|
108
|
+
import mvdanSh from "mvdan-sh";
|
|
109
|
+
import mvdanSh3 from "mvdan-sh";
|
|
110
110
|
import safeRegex2 from "safe-regex2";
|
|
111
111
|
import { createHash as createHash2 } from "crypto";
|
|
112
112
|
import { createHash as createHash22 } from "crypto";
|
|
113
|
-
import mvdanSh4 from "mvdan-sh";
|
|
114
|
-
import mvdanSh5 from "mvdan-sh";
|
|
115
|
-
import mvdanSh6 from "mvdan-sh";
|
|
116
|
-
import mvdanSh8 from "mvdan-sh";
|
|
117
|
-
import mvdanSh7 from "mvdan-sh";
|
|
118
113
|
import { createHash as createHash3 } from "crypto";
|
|
119
|
-
import mvdanSh9 from "mvdan-sh";
|
|
120
114
|
import crypto from "crypto";
|
|
121
115
|
function validateRegex(pattern) {
|
|
122
116
|
if (!pattern) return "Pattern is required";
|
|
@@ -701,6 +695,62 @@ function resolveCheckMap(settings) {
|
|
|
701
695
|
for (const c of CHECKS) out[c.id] = checkValue(settings, c.id);
|
|
702
696
|
return out;
|
|
703
697
|
}
|
|
698
|
+
function isGoNode(v) {
|
|
699
|
+
return v !== null && typeof v === "object" && typeof v.Pos === "function";
|
|
700
|
+
}
|
|
701
|
+
function toPlainTree(goNode) {
|
|
702
|
+
const kind = syntax.NodeType(goNode);
|
|
703
|
+
const out = { kind, pos: goNode.Pos().Offset(), end: goNode.End().Offset() };
|
|
704
|
+
const spec = KINDS[kind];
|
|
705
|
+
if (!spec) {
|
|
706
|
+
unknownKinds.add(kind);
|
|
707
|
+
return out;
|
|
708
|
+
}
|
|
709
|
+
convertFields(goNode, spec, out, kind);
|
|
710
|
+
return out;
|
|
711
|
+
}
|
|
712
|
+
function convertFields(goNode, spec, out, kind) {
|
|
713
|
+
for (const f of spec.scalars ?? []) out[f] = goNode[f];
|
|
714
|
+
for (const f of spec.nodes ?? []) {
|
|
715
|
+
const v = goNode[f];
|
|
716
|
+
if (v === void 0 || v === null) {
|
|
717
|
+
out[f] = null;
|
|
718
|
+
} else if (Array.isArray(v)) {
|
|
719
|
+
out[f] = v.map((c) => isGoNode(c) ? toPlainTree(c) : c);
|
|
720
|
+
} else if (isGoNode(v)) {
|
|
721
|
+
out[f] = toPlainTree(v);
|
|
722
|
+
} else if (typeof v === "object" && STRUCTS[`${kind}.${f}`]) {
|
|
723
|
+
const sub = { struct: `${kind}.${f}` };
|
|
724
|
+
convertFields(v, STRUCTS[`${kind}.${f}`], sub, `${kind}.${f}`);
|
|
725
|
+
out[f] = sub;
|
|
726
|
+
} else {
|
|
727
|
+
out[f] = v;
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
function childrenOf(n) {
|
|
732
|
+
const spec = n.kind ? KINDS[n.kind] : STRUCTS[n.struct];
|
|
733
|
+
if (!spec?.nodes) return [];
|
|
734
|
+
const out = [];
|
|
735
|
+
for (const f of spec.nodes) {
|
|
736
|
+
if (spec.notWalked?.includes(f)) continue;
|
|
737
|
+
const v = n[f];
|
|
738
|
+
if (!v) continue;
|
|
739
|
+
if (Array.isArray(v)) {
|
|
740
|
+
for (const c of v) if (c) out.push(c);
|
|
741
|
+
} else if (v.kind) {
|
|
742
|
+
out.push(v);
|
|
743
|
+
} else if (v.struct) {
|
|
744
|
+
out.push(...childrenOf(v));
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
return out;
|
|
748
|
+
}
|
|
749
|
+
function walk(root, fn) {
|
|
750
|
+
if (!root) return;
|
|
751
|
+
if (!fn(root)) return;
|
|
752
|
+
for (const c of childrenOf(root)) walk(c, fn);
|
|
753
|
+
}
|
|
704
754
|
function isInvisibleChar(char) {
|
|
705
755
|
return INVISIBLE_ONE.test(char);
|
|
706
756
|
}
|
|
@@ -856,7 +906,7 @@ function judgeInlineCode(language, code2, isProtected) {
|
|
|
856
906
|
}
|
|
857
907
|
function isCatHeredocOrLit(part) {
|
|
858
908
|
if (!part) return false;
|
|
859
|
-
const t =
|
|
909
|
+
const t = part.kind;
|
|
860
910
|
if (t === "Lit") return true;
|
|
861
911
|
if (t !== "CmdSubst") return false;
|
|
862
912
|
const stmts = part.Stmts || [];
|
|
@@ -866,9 +916,9 @@ function isCatHeredocOrLit(part) {
|
|
|
866
916
|
const hasHeredoc = redirs.some((r) => r && r.Hdoc);
|
|
867
917
|
if (!hasHeredoc) return false;
|
|
868
918
|
const cmd = stmt.Cmd;
|
|
869
|
-
if (!cmd ||
|
|
919
|
+
if (!cmd || cmd.kind !== "CallExpr") return false;
|
|
870
920
|
const firstArg2 = cmd.Args?.[0]?.Parts || [];
|
|
871
|
-
if (firstArg2.length !== 1 ||
|
|
921
|
+
if (firstArg2.length !== 1 || firstArg2[0].kind !== "Lit") return false;
|
|
872
922
|
return (firstArg2[0].Value || "").toLowerCase() === "cat";
|
|
873
923
|
}
|
|
874
924
|
function parseShared(command) {
|
|
@@ -880,7 +930,7 @@ function parseShared(command) {
|
|
|
880
930
|
}
|
|
881
931
|
let parsed;
|
|
882
932
|
try {
|
|
883
|
-
parsed = sharedParser.Parse(command, "cmd");
|
|
933
|
+
parsed = toPlainTree(sharedParser.Parse(command, "cmd"));
|
|
884
934
|
} catch {
|
|
885
935
|
parsed = PARSE_FAIL;
|
|
886
936
|
}
|
|
@@ -941,10 +991,10 @@ function fileAccessViewImpl(command) {
|
|
|
941
991
|
const at = (byteOffset) => toCharIndex === null ? byteOffset : toCharIndex(byteOffset);
|
|
942
992
|
const edits = [];
|
|
943
993
|
try {
|
|
944
|
-
|
|
994
|
+
walk(f, (node) => {
|
|
945
995
|
if (!node) return false;
|
|
946
996
|
const n = node;
|
|
947
|
-
if (
|
|
997
|
+
if (n.kind !== "CallExpr") return true;
|
|
948
998
|
const args = n.Args || [];
|
|
949
999
|
const words = args.map((a) => resolveWordLiteral(a));
|
|
950
1000
|
const head = unwrapCommandHead(words);
|
|
@@ -956,8 +1006,8 @@ function fileAccessViewImpl(command) {
|
|
|
956
1006
|
if (!PROGRAM_DIR.test(word)) continue;
|
|
957
1007
|
const name = word.split(/[\\/]/).pop() ?? "";
|
|
958
1008
|
if (!name) continue;
|
|
959
|
-
const s = at(args[i].
|
|
960
|
-
const e = at(args[i].
|
|
1009
|
+
const s = at(args[i].pos);
|
|
1010
|
+
const e = at(args[i].end);
|
|
961
1011
|
if (s < 0 || e < 0) continue;
|
|
962
1012
|
edits.push([s, e, name]);
|
|
963
1013
|
}
|
|
@@ -985,10 +1035,10 @@ function literalPayloadsOf(command, depth) {
|
|
|
985
1035
|
if (f === PARSE_FAIL) return [];
|
|
986
1036
|
const out = [];
|
|
987
1037
|
try {
|
|
988
|
-
|
|
1038
|
+
walk(f, (node) => {
|
|
989
1039
|
if (!node || out.length >= MAX_READING_PAYLOADS) return false;
|
|
990
1040
|
const n = node;
|
|
991
|
-
if (
|
|
1041
|
+
if (n.kind !== "CallExpr") return true;
|
|
992
1042
|
const words = (n.Args || []).map((a) => resolveWordLiteral(a));
|
|
993
1043
|
if (words.length === 0) return true;
|
|
994
1044
|
const payload = literalShellPayload(words, baseWord(words[0]));
|
|
@@ -1008,23 +1058,23 @@ function foldCommandWords(reading) {
|
|
|
1008
1058
|
const at = (byteOffset) => toCharIndex === null ? byteOffset : toCharIndex(byteOffset);
|
|
1009
1059
|
const edits = [];
|
|
1010
1060
|
try {
|
|
1011
|
-
|
|
1061
|
+
walk(f, (node) => {
|
|
1012
1062
|
if (!node) return false;
|
|
1013
1063
|
const n = node;
|
|
1014
|
-
if (
|
|
1064
|
+
if (n.kind !== "CallExpr") return true;
|
|
1015
1065
|
const args = n.Args || [];
|
|
1016
1066
|
const words = args.map((a) => resolveWordLiteral(a));
|
|
1017
1067
|
const folded = words.map((w) => w === null ? null : foldHomoglyphs(w));
|
|
1018
1068
|
const head = unwrapCommandHead(folded);
|
|
1019
1069
|
for (let i = 0; i <= head && i < args.length; i++) {
|
|
1020
1070
|
const parts = args[i].Parts || [];
|
|
1021
|
-
if (parts.length !== 1 ||
|
|
1071
|
+
if (parts.length !== 1 || parts[0].kind !== "Lit") continue;
|
|
1022
1072
|
const value = parts[0].Value ?? "";
|
|
1023
1073
|
const word = folded[i];
|
|
1024
1074
|
if (word === null || word === value || !/[^\x00-\x7f]/.test(value)) continue;
|
|
1025
1075
|
if (!/^[A-Za-z0-9._/-]+$/.test(word)) continue;
|
|
1026
|
-
const s = at(args[i].
|
|
1027
|
-
const e = at(args[i].
|
|
1076
|
+
const s = at(args[i].pos);
|
|
1077
|
+
const e = at(args[i].end);
|
|
1028
1078
|
if (s < 0 || e < 0) continue;
|
|
1029
1079
|
edits.push([s, e, word]);
|
|
1030
1080
|
}
|
|
@@ -1061,10 +1111,10 @@ function interpreterHeredocSpans(f, at) {
|
|
|
1061
1111
|
const stmts = f?.Stmts || [];
|
|
1062
1112
|
const candidates2 = [];
|
|
1063
1113
|
let unsafe = false;
|
|
1064
|
-
|
|
1114
|
+
walk(f, (node) => {
|
|
1065
1115
|
if (!node || unsafe) return false;
|
|
1066
1116
|
const n = node;
|
|
1067
|
-
const t =
|
|
1117
|
+
const t = n.kind;
|
|
1068
1118
|
if (t === "FuncDecl") {
|
|
1069
1119
|
unsafe = true;
|
|
1070
1120
|
return false;
|
|
@@ -1084,7 +1134,7 @@ function interpreterHeredocSpans(f, at) {
|
|
|
1084
1134
|
});
|
|
1085
1135
|
if (unsafe) return [];
|
|
1086
1136
|
for (const st of stmts) {
|
|
1087
|
-
if (!st?.Cmd ||
|
|
1137
|
+
if (!st?.Cmd || st.Cmd.kind !== "CallExpr") continue;
|
|
1088
1138
|
const words = (st.Cmd.Args || []).map((a) => resolveWordLiteral(a));
|
|
1089
1139
|
const name = (words[0] ?? "").toLowerCase();
|
|
1090
1140
|
if (!STDIN_CODE_INTERPRETERS.test(name)) continue;
|
|
@@ -1099,8 +1149,8 @@ function interpreterHeredocSpans(f, at) {
|
|
|
1099
1149
|
if (stdin.length !== 1 || others > 0) continue;
|
|
1100
1150
|
const r = stdin[0];
|
|
1101
1151
|
if (heredocText(r.Hdoc) === null) continue;
|
|
1102
|
-
const hs = at(r.Hdoc.
|
|
1103
|
-
const he = at(r.Hdoc.
|
|
1152
|
+
const hs = at(r.Hdoc.pos);
|
|
1153
|
+
const he = at(r.Hdoc.end);
|
|
1104
1154
|
if (hs < 0 || he < 0 || he <= hs) continue;
|
|
1105
1155
|
spans.push([hs, he]);
|
|
1106
1156
|
}
|
|
@@ -1120,24 +1170,24 @@ function normalizeCommandForPolicyImpl(command, codeBodies) {
|
|
|
1120
1170
|
strips.push([hs, he]);
|
|
1121
1171
|
msgSpans.add(`${hs}:${he}`);
|
|
1122
1172
|
}
|
|
1123
|
-
|
|
1173
|
+
walk(f, (node) => {
|
|
1124
1174
|
if (!node) return false;
|
|
1125
1175
|
const n = node;
|
|
1126
|
-
if (
|
|
1176
|
+
if (n.kind !== "CallExpr") return true;
|
|
1127
1177
|
const args = n.Args || [];
|
|
1128
1178
|
for (let i = 0; i < args.length - 1; i++) {
|
|
1129
1179
|
const argParts = args[i].Parts || [];
|
|
1130
|
-
if (argParts.length !== 1 ||
|
|
1180
|
+
if (argParts.length !== 1 || argParts[0].kind !== "Lit") continue;
|
|
1131
1181
|
const flagVal = argParts[0].Value || "";
|
|
1132
1182
|
if (!MESSAGE_FLAGS.has(flagVal.toLowerCase())) continue;
|
|
1133
1183
|
const next = args[i + 1];
|
|
1134
1184
|
const nextParts = next.Parts || [];
|
|
1135
1185
|
if (nextParts.length !== 1) continue;
|
|
1136
1186
|
const quotedNode = nextParts[0];
|
|
1137
|
-
const nt =
|
|
1187
|
+
const nt = quotedNode.kind;
|
|
1138
1188
|
const markStrip = () => {
|
|
1139
|
-
const s = at(next.
|
|
1140
|
-
const e = at(next.
|
|
1189
|
+
const s = at(next.pos);
|
|
1190
|
+
const e = at(next.end);
|
|
1141
1191
|
if (s < 0 || e < 0) return;
|
|
1142
1192
|
strips.push([s, e]);
|
|
1143
1193
|
msgSpans.add(`${s}:${e}`);
|
|
@@ -1146,7 +1196,7 @@ function normalizeCommandForPolicyImpl(command, codeBodies) {
|
|
|
1146
1196
|
markStrip();
|
|
1147
1197
|
} else if (nt === "DblQuoted") {
|
|
1148
1198
|
const innerParts = quotedNode.Parts || [];
|
|
1149
|
-
const allLit = innerParts.length === 0 || innerParts.every((p) =>
|
|
1199
|
+
const allLit = innerParts.length === 0 || innerParts.every((p) => p.kind === "Lit");
|
|
1150
1200
|
if (allLit) {
|
|
1151
1201
|
markStrip();
|
|
1152
1202
|
} else if (innerParts.every((p) => isCatHeredocOrLit(p))) {
|
|
@@ -1155,8 +1205,8 @@ function normalizeCommandForPolicyImpl(command, codeBodies) {
|
|
|
1155
1205
|
}
|
|
1156
1206
|
}
|
|
1157
1207
|
for (const arg of args) {
|
|
1158
|
-
const s = at(arg.
|
|
1159
|
-
const e = at(arg.
|
|
1208
|
+
const s = at(arg.pos);
|
|
1209
|
+
const e = at(arg.end);
|
|
1160
1210
|
if (s < 0 || e < 0) continue;
|
|
1161
1211
|
if (msgSpans.has(`${s}:${e}`)) continue;
|
|
1162
1212
|
const resolved = resolveWordLiteral(arg);
|
|
@@ -1196,10 +1246,10 @@ function scanArgsForDynamicExec(args, startIdx) {
|
|
|
1196
1246
|
let hasParamExp = false;
|
|
1197
1247
|
let hasCurl = false;
|
|
1198
1248
|
for (let i = startIdx; i < args.length; i++) {
|
|
1199
|
-
|
|
1249
|
+
walk(args[i], (inner) => {
|
|
1200
1250
|
if (!inner) return false;
|
|
1201
1251
|
const inn = inner;
|
|
1202
|
-
const it =
|
|
1252
|
+
const it = inn.kind;
|
|
1203
1253
|
if (it === "CmdSubst") hasCmdSubst = true;
|
|
1204
1254
|
if (it === "ParamExp") hasParamExp = true;
|
|
1205
1255
|
if (it === "Lit" && DOWNLOAD_CMDS.has(inn.Value?.toLowerCase())) hasCurl = true;
|
|
@@ -1212,15 +1262,16 @@ function scanArgsForDynamicExec(args, startIdx) {
|
|
|
1212
1262
|
}
|
|
1213
1263
|
function detectDangerousShellExec(command) {
|
|
1214
1264
|
try {
|
|
1215
|
-
const f =
|
|
1265
|
+
const f = parseShared(command);
|
|
1266
|
+
if (f === PARSE_FAIL) return null;
|
|
1216
1267
|
let result = null;
|
|
1217
1268
|
const assigned = assignedNames(f);
|
|
1218
|
-
|
|
1269
|
+
walk(f, (node) => {
|
|
1219
1270
|
if (!node || result === "block") return false;
|
|
1220
1271
|
const n = node;
|
|
1221
|
-
if (
|
|
1272
|
+
if (n.kind === "BinaryCmd" && n.Y && !listOps().has(n.Op)) {
|
|
1222
1273
|
const call = n.Y.Cmd;
|
|
1223
|
-
if (call &&
|
|
1274
|
+
if (call && call.kind === "CallExpr") {
|
|
1224
1275
|
const words = (call.Args || []).map((a) => resolveWordLiteral(a));
|
|
1225
1276
|
const head2 = unwrapCommandHead(words);
|
|
1226
1277
|
const name = (words[head2] ?? "").split("/").pop() ?? "";
|
|
@@ -1233,7 +1284,7 @@ function detectDangerousShellExec(command) {
|
|
|
1233
1284
|
result = "review";
|
|
1234
1285
|
}
|
|
1235
1286
|
}
|
|
1236
|
-
if (
|
|
1287
|
+
if (n.kind !== "CallExpr") return true;
|
|
1237
1288
|
const args = n.Args || [];
|
|
1238
1289
|
if (args.length === 0) return true;
|
|
1239
1290
|
const headWords = args.map(
|
|
@@ -1244,7 +1295,7 @@ function detectDangerousShellExec(command) {
|
|
|
1244
1295
|
if (!lookup && (opaqueExecutable(args[0], assigned) || opaqueExecutable(args[head], assigned)))
|
|
1245
1296
|
result = "review";
|
|
1246
1297
|
const firstParts = args[0].Parts || [];
|
|
1247
|
-
if (firstParts.length !== 1 ||
|
|
1298
|
+
if (firstParts.length !== 1 || firstParts[0].kind !== "Lit") return true;
|
|
1248
1299
|
const cmdName = firstParts[0].Value?.toLowerCase() ?? "";
|
|
1249
1300
|
if (cmdName === "eval") {
|
|
1250
1301
|
const v = scanArgsForDynamicExec(args, 1);
|
|
@@ -1252,7 +1303,7 @@ function detectDangerousShellExec(command) {
|
|
|
1252
1303
|
} else if (SHELL_INTERPRETERS.has(cmdName)) {
|
|
1253
1304
|
for (let i = 1; i < args.length - 1; i++) {
|
|
1254
1305
|
const flagParts = args[i].Parts || [];
|
|
1255
|
-
if (flagParts.length !== 1 ||
|
|
1306
|
+
if (flagParts.length !== 1 || flagParts[0].kind !== "Lit" || flagParts[0].Value !== "-c")
|
|
1256
1307
|
continue;
|
|
1257
1308
|
const v = scanArgsForDynamicExec(args, i + 1);
|
|
1258
1309
|
if (v === "block" || v === "review" && result === null) result = v;
|
|
@@ -1276,8 +1327,7 @@ function isDefaultExpansion(part) {
|
|
|
1276
1327
|
}
|
|
1277
1328
|
function lookupSubstitution(part) {
|
|
1278
1329
|
const stmts = part.Stmts ?? [];
|
|
1279
|
-
if (stmts.length !== 1 || !stmts[0].Cmd ||
|
|
1280
|
-
return null;
|
|
1330
|
+
if (stmts.length !== 1 || !stmts[0].Cmd || stmts[0].Cmd.kind !== "CallExpr") return null;
|
|
1281
1331
|
const words = (stmts[0].Cmd.Args ?? []).map((a) => resolveWordLiteral(a));
|
|
1282
1332
|
if (words.some((w) => w === null)) return null;
|
|
1283
1333
|
if (words.length === 2 && words[0] === "which") return words[1];
|
|
@@ -1287,9 +1337,9 @@ function lookupSubstitution(part) {
|
|
|
1287
1337
|
}
|
|
1288
1338
|
function assignedNames(f) {
|
|
1289
1339
|
const names = /* @__PURE__ */ new Set();
|
|
1290
|
-
|
|
1340
|
+
walk(f, (node) => {
|
|
1291
1341
|
if (!node) return false;
|
|
1292
|
-
const kind =
|
|
1342
|
+
const kind = node.kind;
|
|
1293
1343
|
if (kind === "Assign" && node.Name?.Value) names.add(node.Name.Value);
|
|
1294
1344
|
if (kind === "ForClause" && node.Loop?.Name?.Value) names.add(node.Loop.Name.Value);
|
|
1295
1345
|
if (kind === "CallExpr") {
|
|
@@ -1304,7 +1354,7 @@ function assignedNames(f) {
|
|
|
1304
1354
|
}
|
|
1305
1355
|
function executableParts(word, assigned, quoted = false) {
|
|
1306
1356
|
return (word?.Parts ?? []).flatMap((part) => {
|
|
1307
|
-
const kind =
|
|
1357
|
+
const kind = part.kind;
|
|
1308
1358
|
if (kind === "DblQuoted") return executableParts(part, assigned, true);
|
|
1309
1359
|
if (kind === "Lit") return [{ kind: "Lit", value: part.Value ?? "", quoted }];
|
|
1310
1360
|
if (kind === "SglQuoted")
|
|
@@ -1372,10 +1422,10 @@ function chmodHasOpenPermMode(command) {
|
|
|
1372
1422
|
if (f === PARSE_FAIL) return false;
|
|
1373
1423
|
let found = false;
|
|
1374
1424
|
try {
|
|
1375
|
-
|
|
1425
|
+
walk(f, (node) => {
|
|
1376
1426
|
if (!node || found) return false;
|
|
1377
1427
|
const n = node;
|
|
1378
|
-
if (
|
|
1428
|
+
if (n.kind !== "CallExpr") return true;
|
|
1379
1429
|
const words = (n.Args || []).map((a) => resolveWordLiteral(a));
|
|
1380
1430
|
if (words.length === 0) return true;
|
|
1381
1431
|
const name = (words[0] ?? "").toLowerCase();
|
|
@@ -1428,11 +1478,11 @@ function redirStdinOps() {
|
|
|
1428
1478
|
}
|
|
1429
1479
|
function deriveBinaryOp(sample) {
|
|
1430
1480
|
try {
|
|
1431
|
-
const f = sharedParser.Parse(sample, "cmd");
|
|
1481
|
+
const f = toPlainTree(sharedParser.Parse(sample, "cmd"));
|
|
1432
1482
|
let op = -1;
|
|
1433
|
-
|
|
1483
|
+
walk(f, (node) => {
|
|
1434
1484
|
const n = node;
|
|
1435
|
-
if (n &&
|
|
1485
|
+
if (n && n.kind === "BinaryCmd" && op < 0) op = n.Op;
|
|
1436
1486
|
return true;
|
|
1437
1487
|
});
|
|
1438
1488
|
return op;
|
|
@@ -1506,7 +1556,7 @@ function heredocText(word) {
|
|
|
1506
1556
|
const parts = word?.Parts ?? [];
|
|
1507
1557
|
let out = "";
|
|
1508
1558
|
for (const p of parts) {
|
|
1509
|
-
if (
|
|
1559
|
+
if (p.kind !== "Lit") return null;
|
|
1510
1560
|
out += p.Value ?? "";
|
|
1511
1561
|
}
|
|
1512
1562
|
return out;
|
|
@@ -1542,7 +1592,7 @@ function attachedCode(interp, flag) {
|
|
|
1542
1592
|
}
|
|
1543
1593
|
function inlineExecCall(stmt, pipeFed) {
|
|
1544
1594
|
const cmd = stmt?.Cmd;
|
|
1545
|
-
if (!cmd ||
|
|
1595
|
+
if (!cmd || cmd.kind !== "CallExpr") return null;
|
|
1546
1596
|
const words = (cmd.Args || []).map((a) => resolveWordLiteral(a));
|
|
1547
1597
|
if (words.length === 0) return null;
|
|
1548
1598
|
const headIdx = unwrapCommandHead(words);
|
|
@@ -1623,10 +1673,10 @@ function shellPayloadNeedsReview(payload) {
|
|
|
1623
1673
|
let review = false;
|
|
1624
1674
|
payloadNesting++;
|
|
1625
1675
|
try {
|
|
1626
|
-
|
|
1676
|
+
walk(f, (node) => {
|
|
1627
1677
|
if (!node || review) return false;
|
|
1628
1678
|
const n = node;
|
|
1629
|
-
if (
|
|
1679
|
+
if (n.kind !== "CallExpr") return true;
|
|
1630
1680
|
const words = (n.Args || []).map((a) => resolveWordLiteral(a));
|
|
1631
1681
|
if (words.length === 0) return true;
|
|
1632
1682
|
const h = unwrapCommandHead(words);
|
|
@@ -1660,10 +1710,10 @@ function inlineExecCalls(command) {
|
|
|
1660
1710
|
if (f === PARSE_FAIL) return PARSE_FAIL;
|
|
1661
1711
|
const calls = [];
|
|
1662
1712
|
try {
|
|
1663
|
-
|
|
1713
|
+
walk(f, (node) => {
|
|
1664
1714
|
if (!node || calls.length >= MAX_INLINE_CALLS) return false;
|
|
1665
1715
|
const n = node;
|
|
1666
|
-
const t =
|
|
1716
|
+
const t = n.kind;
|
|
1667
1717
|
if (t === "BinaryCmd" && n.Y && !listOps().has(n.Op)) {
|
|
1668
1718
|
const call = inlineExecCall(n.Y, true);
|
|
1669
1719
|
if (call) calls.push(call);
|
|
@@ -1798,7 +1848,7 @@ function shellScriptOperands(command) {
|
|
|
1798
1848
|
walkWithCalls(f, (node) => {
|
|
1799
1849
|
if (!node) return false;
|
|
1800
1850
|
const n = node;
|
|
1801
|
-
if (
|
|
1851
|
+
if (n.kind !== "CallExpr") return true;
|
|
1802
1852
|
const callArgs = n.Args || [];
|
|
1803
1853
|
if (callArgs.length === 0) return true;
|
|
1804
1854
|
const words = callArgs.map((a) => resolveWordLiteral(a));
|
|
@@ -1834,7 +1884,7 @@ function shellScriptOperands(command) {
|
|
|
1834
1884
|
}
|
|
1835
1885
|
function windowsDrivePathSpelling(word) {
|
|
1836
1886
|
const parts = word?.Parts || [];
|
|
1837
|
-
if (parts.length === 0 || !parts.every((p) =>
|
|
1887
|
+
if (parts.length === 0 || !parts.every((p) => p.kind === "Lit")) return null;
|
|
1838
1888
|
const raw = parts.map((p) => p.Value ?? "").join("");
|
|
1839
1889
|
return /^[A-Za-z]:\\[^\s'"$`]*$/.test(raw) ? raw : null;
|
|
1840
1890
|
}
|
|
@@ -1872,15 +1922,15 @@ function recordTopLevelAssignments(f) {
|
|
|
1872
1922
|
}
|
|
1873
1923
|
function probeBinOp(src) {
|
|
1874
1924
|
try {
|
|
1875
|
-
const cmd =
|
|
1876
|
-
if (cmd &&
|
|
1925
|
+
const cmd = toPlainTree(syntax2.NewParser().Parse(src, "probe"))?.Stmts?.[0]?.Cmd;
|
|
1926
|
+
if (cmd && cmd.kind === "BinaryCmd") return cmd.Op;
|
|
1877
1927
|
} catch {
|
|
1878
1928
|
}
|
|
1879
1929
|
return null;
|
|
1880
1930
|
}
|
|
1881
1931
|
function recordTopLevelStmt(stmt) {
|
|
1882
1932
|
if (!stmt || !stmt.Cmd) return false;
|
|
1883
|
-
const t =
|
|
1933
|
+
const t = stmt.Cmd.kind;
|
|
1884
1934
|
if (t === "BinaryCmd") {
|
|
1885
1935
|
if (!AND_OR_OPS.has(stmt.Cmd.Op)) return false;
|
|
1886
1936
|
if (!recordTopLevelStmt(stmt.Cmd.X)) return false;
|
|
@@ -1890,7 +1940,7 @@ function recordTopLevelStmt(stmt) {
|
|
|
1890
1940
|
if (t !== "CallExpr" && t !== "DeclClause") return false;
|
|
1891
1941
|
let at = 0;
|
|
1892
1942
|
try {
|
|
1893
|
-
at = stmt.
|
|
1943
|
+
at = stmt.pos;
|
|
1894
1944
|
} catch {
|
|
1895
1945
|
at = 0;
|
|
1896
1946
|
}
|
|
@@ -1901,14 +1951,14 @@ function recordTopLevelStmt(stmt) {
|
|
|
1901
1951
|
}
|
|
1902
1952
|
function recordAssignments(n, at) {
|
|
1903
1953
|
if (!assignmentTable) return;
|
|
1904
|
-
const t =
|
|
1954
|
+
const t = n.kind;
|
|
1905
1955
|
let assigns = [];
|
|
1906
1956
|
if (t === "CallExpr") {
|
|
1907
1957
|
if ((n.Args || []).length > 0) return;
|
|
1908
1958
|
assigns = n.Assigns || [];
|
|
1909
1959
|
} else if (t === "DeclClause") {
|
|
1910
1960
|
if (!ASSIGNMENT_HEADS.has(n.Variant?.Value ?? "")) return;
|
|
1911
|
-
assigns = (n.Args || []).filter((a) =>
|
|
1961
|
+
assigns = (n.Args || []).filter((a) => a.kind === "Assign");
|
|
1912
1962
|
} else return;
|
|
1913
1963
|
for (const a of assigns) {
|
|
1914
1964
|
const name = a?.Name?.Value;
|
|
@@ -1917,13 +1967,13 @@ function recordAssignments(n, at) {
|
|
|
1917
1967
|
}
|
|
1918
1968
|
}
|
|
1919
1969
|
function resolveTrivialSubst(part) {
|
|
1920
|
-
if (
|
|
1970
|
+
if (part.kind !== "CmdSubst") return void 0;
|
|
1921
1971
|
const stmts = part.Stmts || [];
|
|
1922
1972
|
if (stmts.length !== 1) return void 0;
|
|
1923
1973
|
const st = stmts[0];
|
|
1924
1974
|
if ((st.Redirs || []).length > 0 || st.Negated || st.Background) return void 0;
|
|
1925
1975
|
const cmd = st.Cmd;
|
|
1926
|
-
if (!cmd ||
|
|
1976
|
+
if (!cmd || cmd.kind !== "CallExpr") return void 0;
|
|
1927
1977
|
if ((cmd.Assigns || []).length > 0) return void 0;
|
|
1928
1978
|
const words = (cmd.Args || []).map((a) => resolveWordLiteral(a));
|
|
1929
1979
|
if (words.length === 0 || words.some((w) => w === null)) return void 0;
|
|
@@ -1948,7 +1998,7 @@ function recordedExpansion(name) {
|
|
|
1948
1998
|
return rec.value;
|
|
1949
1999
|
}
|
|
1950
2000
|
function isPlainParam(p) {
|
|
1951
|
-
if (
|
|
2001
|
+
if (p.kind !== "ParamExp") return false;
|
|
1952
2002
|
return !(p.Excl || p.Length || p.Width || p.Index || p.Slice || p.Repl || p.Exp);
|
|
1953
2003
|
}
|
|
1954
2004
|
function expandPlainParam(p) {
|
|
@@ -1960,9 +2010,9 @@ function expandPlainParam(p) {
|
|
|
1960
2010
|
}
|
|
1961
2011
|
function functionBodies(f) {
|
|
1962
2012
|
const out = /* @__PURE__ */ new Map();
|
|
1963
|
-
|
|
2013
|
+
walk(f, (node) => {
|
|
1964
2014
|
const n = node;
|
|
1965
|
-
if (n &&
|
|
2015
|
+
if (n && n.kind === "FuncDecl" && n.Name?.Value && n.Body) {
|
|
1966
2016
|
const list2 = out.get(n.Name.Value) ?? [];
|
|
1967
2017
|
list2.push(n.Body);
|
|
1968
2018
|
out.set(n.Name.Value, list2);
|
|
@@ -1973,10 +2023,10 @@ function functionBodies(f) {
|
|
|
1973
2023
|
}
|
|
1974
2024
|
function rebindsPositionals(body) {
|
|
1975
2025
|
let found = false;
|
|
1976
|
-
|
|
2026
|
+
walk(body, (node) => {
|
|
1977
2027
|
const n = node;
|
|
1978
2028
|
if (found || !n) return false;
|
|
1979
|
-
if (
|
|
2029
|
+
if (n.kind === "CallExpr") {
|
|
1980
2030
|
const words = (n.Args || []).map((a) => resolveWordLiteral(a));
|
|
1981
2031
|
if (words[0] === "shift") found = true;
|
|
1982
2032
|
else if (words[0] === "set" && words.slice(1).some((w) => w === null || w === "--" || !/^[-+]/.test(w)))
|
|
@@ -1996,7 +2046,7 @@ function tableBindings() {
|
|
|
1996
2046
|
function walkWithCalls(f, visit2) {
|
|
1997
2047
|
const bodies = functionBodies(f);
|
|
1998
2048
|
if (bodies.size === 0) {
|
|
1999
|
-
|
|
2049
|
+
walk(f, visit2);
|
|
2000
2050
|
return;
|
|
2001
2051
|
}
|
|
2002
2052
|
const seenCalls = /* @__PURE__ */ new Set();
|
|
@@ -2030,8 +2080,8 @@ function walkWithCalls(f, visit2) {
|
|
|
2030
2080
|
currentStmtOffset = Number.MAX_SAFE_INTEGER;
|
|
2031
2081
|
callDepth++;
|
|
2032
2082
|
try {
|
|
2033
|
-
if (body.Cmd &&
|
|
2034
|
-
|
|
2083
|
+
if (body.Cmd && body.Cmd.kind === "Block") recordTopLevelAssignments(body.Cmd);
|
|
2084
|
+
walk(body, step);
|
|
2035
2085
|
} finally {
|
|
2036
2086
|
callDepth--;
|
|
2037
2087
|
assignmentTable = outerTable;
|
|
@@ -2042,10 +2092,10 @@ function walkWithCalls(f, visit2) {
|
|
|
2042
2092
|
const step = (node) => {
|
|
2043
2093
|
if (!node) return false;
|
|
2044
2094
|
const go = visit2(node);
|
|
2045
|
-
if (go !== false &&
|
|
2095
|
+
if (go !== false && node.kind === "CallExpr") inline(node);
|
|
2046
2096
|
return go;
|
|
2047
2097
|
};
|
|
2048
|
-
|
|
2098
|
+
walk(f, step);
|
|
2049
2099
|
}
|
|
2050
2100
|
function withAssignments(f, body) {
|
|
2051
2101
|
const outerTable = assignmentTable;
|
|
@@ -2070,7 +2120,7 @@ function walkCommandScope(command, visit2) {
|
|
|
2070
2120
|
if (f === PARSE_FAIL) return "parse-fail";
|
|
2071
2121
|
withAssignments(f, () => {
|
|
2072
2122
|
walkWithCalls(f, (node) => {
|
|
2073
|
-
if (node &&
|
|
2123
|
+
if (node && node.kind === "Stmt") trackStatement(node);
|
|
2074
2124
|
return visit2(node);
|
|
2075
2125
|
});
|
|
2076
2126
|
});
|
|
@@ -2078,7 +2128,7 @@ function walkCommandScope(command, visit2) {
|
|
|
2078
2128
|
}
|
|
2079
2129
|
function trackStatement(n) {
|
|
2080
2130
|
try {
|
|
2081
|
-
currentStmtOffset = n.
|
|
2131
|
+
currentStmtOffset = n.pos;
|
|
2082
2132
|
} catch {
|
|
2083
2133
|
currentStmtOffset = Number.MAX_SAFE_INTEGER;
|
|
2084
2134
|
}
|
|
@@ -2094,11 +2144,11 @@ function resolveWordLiteral(w) {
|
|
|
2094
2144
|
return s;
|
|
2095
2145
|
}
|
|
2096
2146
|
function resolveWordPrefix(w) {
|
|
2097
|
-
const
|
|
2147
|
+
const walk3 = (parts) => {
|
|
2098
2148
|
let s = "";
|
|
2099
2149
|
for (const p of parts) {
|
|
2100
2150
|
if (p && typeof p === "object" && Array.isArray(p.Parts) && p.Dollar !== void 0) {
|
|
2101
|
-
const inner =
|
|
2151
|
+
const inner = walk3(p.Parts);
|
|
2102
2152
|
s += inner.prefix;
|
|
2103
2153
|
if (inner.dynamic) return { prefix: s, dynamic: true };
|
|
2104
2154
|
continue;
|
|
@@ -2109,10 +2159,10 @@ function resolveWordPrefix(w) {
|
|
|
2109
2159
|
}
|
|
2110
2160
|
return { prefix: s, dynamic: false };
|
|
2111
2161
|
};
|
|
2112
|
-
return
|
|
2162
|
+
return walk3(w?.Parts || []);
|
|
2113
2163
|
}
|
|
2114
2164
|
function resolvePart(p, inQuotes) {
|
|
2115
|
-
const t =
|
|
2165
|
+
const t = p.kind;
|
|
2116
2166
|
if (t === "Lit") {
|
|
2117
2167
|
const raw = p.Value ?? "";
|
|
2118
2168
|
if (!inQuotes) return raw.replace(/\\(.)/g, "$1");
|
|
@@ -2208,7 +2258,7 @@ function extractShellDestinations(command) {
|
|
|
2208
2258
|
() => walkWithCalls(f, (node) => {
|
|
2209
2259
|
if (!node) return false;
|
|
2210
2260
|
const n = node;
|
|
2211
|
-
if (
|
|
2261
|
+
if (n.kind === "Stmt") {
|
|
2212
2262
|
trackStatement(n);
|
|
2213
2263
|
for (const raw of redirectWords(n)) {
|
|
2214
2264
|
const host2 = devNetRedirectHost(raw);
|
|
@@ -2220,7 +2270,7 @@ function extractShellDestinations(command) {
|
|
|
2220
2270
|
out.push({ host: host2, binary, raw });
|
|
2221
2271
|
}
|
|
2222
2272
|
}
|
|
2223
|
-
if (
|
|
2273
|
+
if (n.kind !== "CallExpr") return true;
|
|
2224
2274
|
const callArgs = n.Args || [];
|
|
2225
2275
|
if (callArgs.length === 0) return true;
|
|
2226
2276
|
const words = callArgs.map((a) => resolveWordLiteral(a));
|
|
@@ -2255,7 +2305,7 @@ function extractShellDestTokens(command) {
|
|
|
2255
2305
|
() => walkWithCalls(f, (node) => {
|
|
2256
2306
|
if (!node) return false;
|
|
2257
2307
|
const n = node;
|
|
2258
|
-
if (
|
|
2308
|
+
if (n.kind === "Stmt") {
|
|
2259
2309
|
trackStatement(n);
|
|
2260
2310
|
for (const raw of redirectWords(n)) {
|
|
2261
2311
|
const host2 = devNetRedirectHost(raw);
|
|
@@ -2267,7 +2317,7 @@ function extractShellDestTokens(command) {
|
|
|
2267
2317
|
out.push({ token: host2, binary });
|
|
2268
2318
|
}
|
|
2269
2319
|
}
|
|
2270
|
-
if (
|
|
2320
|
+
if (n.kind !== "CallExpr") return true;
|
|
2271
2321
|
const callArgs = n.Args || [];
|
|
2272
2322
|
if (callArgs.length === 0) return true;
|
|
2273
2323
|
const words = callArgs.map((a) => resolveWordLiteral(a));
|
|
@@ -2335,11 +2385,11 @@ function isWaivableCleanupTarget(p) {
|
|
|
2335
2385
|
}
|
|
2336
2386
|
function deriveRedirOp(sample) {
|
|
2337
2387
|
try {
|
|
2338
|
-
const f = sharedParser.Parse(sample, "cmd");
|
|
2388
|
+
const f = toPlainTree(sharedParser.Parse(sample, "cmd"));
|
|
2339
2389
|
let op = -1;
|
|
2340
|
-
|
|
2390
|
+
walk(f, (node) => {
|
|
2341
2391
|
const n = node;
|
|
2342
|
-
if (n &&
|
|
2392
|
+
if (n && n.kind === "Redirect" && op < 0) op = n.Op;
|
|
2343
2393
|
return true;
|
|
2344
2394
|
});
|
|
2345
2395
|
return op;
|
|
@@ -2352,7 +2402,7 @@ function collectSameCommandCreations(f) {
|
|
|
2352
2402
|
try {
|
|
2353
2403
|
const stmts = Array.isArray(f?.Stmts) ? f.Stmts : [];
|
|
2354
2404
|
for (const stmt of stmts) {
|
|
2355
|
-
if (!stmt || !stmt.Cmd ||
|
|
2405
|
+
if (!stmt || !stmt.Cmd || stmt.Cmd.kind !== "CallExpr") continue;
|
|
2356
2406
|
const redirs = stmt.Redirs || [];
|
|
2357
2407
|
if (!redirs.some((r) => r && REDIR_HEREDOC_OPS.has(r.Op))) continue;
|
|
2358
2408
|
for (const r of redirs) {
|
|
@@ -2376,10 +2426,10 @@ function isRmCreatedInCommandCleanup(command) {
|
|
|
2376
2426
|
let sawRm = false;
|
|
2377
2427
|
let ok2 = true;
|
|
2378
2428
|
try {
|
|
2379
|
-
|
|
2429
|
+
walk(f, (node) => {
|
|
2380
2430
|
if (!node || !ok2) return false;
|
|
2381
2431
|
const n = node;
|
|
2382
|
-
if (
|
|
2432
|
+
if (n.kind !== "CallExpr") return true;
|
|
2383
2433
|
const args = n.Args || [];
|
|
2384
2434
|
const name = (resolveWordLiteral(args[0]) ?? "").toLowerCase();
|
|
2385
2435
|
if (name !== "rm") return true;
|
|
@@ -2429,10 +2479,10 @@ function analyzeFsOperationImpl(command, depth = 0) {
|
|
|
2429
2479
|
walkWithCalls(f, (node) => {
|
|
2430
2480
|
if (!node || result?.verdict === "block") return false;
|
|
2431
2481
|
const n = node;
|
|
2432
|
-
const nodeType =
|
|
2482
|
+
const nodeType = n.kind;
|
|
2433
2483
|
if (nodeType === "Stmt") {
|
|
2434
2484
|
try {
|
|
2435
|
-
currentStmtOffset = n.
|
|
2485
|
+
currentStmtOffset = n.pos;
|
|
2436
2486
|
} catch {
|
|
2437
2487
|
currentStmtOffset = Number.MAX_SAFE_INTEGER;
|
|
2438
2488
|
}
|
|
@@ -2945,11 +2995,12 @@ function analyzeShellCommand(command) {
|
|
|
2945
2995
|
if (lower2.startsWith("-")) allTokens.push(lower2.replace(/^-+/, ""));
|
|
2946
2996
|
};
|
|
2947
2997
|
try {
|
|
2948
|
-
const f =
|
|
2949
|
-
|
|
2998
|
+
const f = parseShared(command);
|
|
2999
|
+
if (f === PARSE_FAIL) throw new Error("parse failed");
|
|
3000
|
+
walk(f, (node) => {
|
|
2950
3001
|
if (!node) return false;
|
|
2951
3002
|
const n = node;
|
|
2952
|
-
if (
|
|
3003
|
+
if (n.kind !== "CallExpr") return true;
|
|
2953
3004
|
const wordValues2 = (n.Args || []).map((arg) => {
|
|
2954
3005
|
return (arg.Parts || []).map((p) => (p.Value ?? "").replace(/\\(.)/g, "$1")).join("");
|
|
2955
3006
|
}).filter((s) => s.length > 0);
|
|
@@ -3221,7 +3272,7 @@ function runnablePrefix(command) {
|
|
|
3221
3272
|
let text = command;
|
|
3222
3273
|
for (let attempt = 0; attempt < 32; attempt++) {
|
|
3223
3274
|
try {
|
|
3224
|
-
return
|
|
3275
|
+
return toPlainTree(syntax3.NewParser().Parse(text, ""));
|
|
3225
3276
|
} catch (error) {
|
|
3226
3277
|
const message = String(typeof error?.Error === "function" ? error.Error() : error?.message);
|
|
3227
3278
|
const line = Number(/^(\d+):/.exec(message)?.[1]);
|
|
@@ -3289,7 +3340,7 @@ function wordValues(word, state) {
|
|
|
3289
3340
|
out2 = next.slice(0, MAX_ALTERNATIVES);
|
|
3290
3341
|
};
|
|
3291
3342
|
for (const node of nodes) {
|
|
3292
|
-
const kind =
|
|
3343
|
+
const kind = node.kind;
|
|
3293
3344
|
let pieces;
|
|
3294
3345
|
if (kind === "ParamExp") pieces = paramValues(node, quoted);
|
|
3295
3346
|
else if (kind === "DblQuoted") {
|
|
@@ -3357,9 +3408,9 @@ function collectShellFileAccesses(command, context = {}) {
|
|
|
3357
3408
|
}
|
|
3358
3409
|
function substitutions2(word, state, depth) {
|
|
3359
3410
|
if (!word) return;
|
|
3360
|
-
|
|
3411
|
+
walk(word, (node) => {
|
|
3361
3412
|
if (!node) return false;
|
|
3362
|
-
if (["CmdSubst", "ProcSubst"].includes(
|
|
3413
|
+
if (["CmdSubst", "ProcSubst"].includes(node.kind)) {
|
|
3363
3414
|
statements(node.Stmts ?? [], clone(state), depth + 1);
|
|
3364
3415
|
return false;
|
|
3365
3416
|
}
|
|
@@ -3783,7 +3834,7 @@ function collectShellFileAccesses(command, context = {}) {
|
|
|
3783
3834
|
}
|
|
3784
3835
|
function readsScriptFromStdin(stmt) {
|
|
3785
3836
|
const cmd = stmt?.Cmd;
|
|
3786
|
-
if (!cmd ||
|
|
3837
|
+
if (!cmd || cmd.kind !== "CallExpr") return false;
|
|
3787
3838
|
const words = (cmd.Args ?? []).map((a) => resolveWordLiteral(a));
|
|
3788
3839
|
const head = unwrapCommandHead(words);
|
|
3789
3840
|
return SHELL_INTERPRETERS.has(base(words[head])) && shellConsumesPipe(words.slice(head + 1));
|
|
@@ -3797,7 +3848,7 @@ function collectShellFileAccesses(command, context = {}) {
|
|
|
3797
3848
|
}
|
|
3798
3849
|
function producedText(stmt) {
|
|
3799
3850
|
const cmd = stmt?.Cmd;
|
|
3800
|
-
if (!cmd ||
|
|
3851
|
+
if (!cmd || cmd.kind !== "CallExpr") return null;
|
|
3801
3852
|
const words = (cmd.Args ?? []).map((a) => resolveWordLiteral(a));
|
|
3802
3853
|
if (words.some((w) => w === null)) return null;
|
|
3803
3854
|
const head = unwrapCommandHead(words);
|
|
@@ -3816,7 +3867,7 @@ function collectShellFileAccesses(command, context = {}) {
|
|
|
3816
3867
|
if (!stmt || depth > MAX_DEPTH) return;
|
|
3817
3868
|
state.onSuccess = void 0;
|
|
3818
3869
|
if (stmt.Background) state = clone(state);
|
|
3819
|
-
const headWord = stmt.Cmd &&
|
|
3870
|
+
const headWord = stmt.Cmd && stmt.Cmd.kind === "CallExpr" ? base(resolveWordLiteral(stmt.Cmd.Args?.[0])) : "";
|
|
3820
3871
|
const sends = SENDING_STDIN.has(headWord);
|
|
3821
3872
|
for (const redir of stmt.Redirs ?? []) {
|
|
3822
3873
|
substitutions2(redir.Word, state, depth);
|
|
@@ -3832,7 +3883,7 @@ function collectShellFileAccesses(command, context = {}) {
|
|
|
3832
3883
|
const cmd = stmt.Cmd;
|
|
3833
3884
|
if (!cmd) return;
|
|
3834
3885
|
stdinScript(stmt, state, depth);
|
|
3835
|
-
switch (
|
|
3886
|
+
switch (cmd.kind) {
|
|
3836
3887
|
case "CallExpr":
|
|
3837
3888
|
call(cmd, state, depth);
|
|
3838
3889
|
break;
|
|
@@ -3846,7 +3897,7 @@ function collectShellFileAccesses(command, context = {}) {
|
|
|
3846
3897
|
for (; ; ) {
|
|
3847
3898
|
segments2.unshift({ op: node.Op, part: node.Y });
|
|
3848
3899
|
const left = node.X;
|
|
3849
|
-
if (left?.Cmd &&
|
|
3900
|
+
if (left?.Cmd && left.Cmd.kind === "BinaryCmd" && !left.Redirs?.length && !left.Negated && !left.Background)
|
|
3850
3901
|
node = left.Cmd;
|
|
3851
3902
|
else {
|
|
3852
3903
|
segments2.unshift({ op: node.Op, part: left });
|
|
@@ -4142,13 +4193,13 @@ function pipelineTexts(command) {
|
|
|
4142
4193
|
const visit2 = (stmt, depth) => {
|
|
4143
4194
|
if (!stmt || depth > 64) return;
|
|
4144
4195
|
const cmd = stmt.Cmd;
|
|
4145
|
-
if (cmd &&
|
|
4196
|
+
if (cmd && cmd.kind === "BinaryCmd" && SEQUENCE_OPS.has(cmd.Op)) {
|
|
4146
4197
|
visit2(cmd.X, depth + 1);
|
|
4147
4198
|
visit2(cmd.Y, depth + 1);
|
|
4148
4199
|
return;
|
|
4149
4200
|
}
|
|
4150
|
-
const s = at(stmt.
|
|
4151
|
-
const e = at(stmt.
|
|
4201
|
+
const s = at(stmt.pos);
|
|
4202
|
+
const e = at(stmt.end);
|
|
4152
4203
|
const text = s >= 0 && e > s ? command.slice(s, e).replace(/[\s;&]+$/, "") : "";
|
|
4153
4204
|
if (text) out.push(text);
|
|
4154
4205
|
};
|
|
@@ -5310,11 +5361,10 @@ function shellWriteUnseen(command, target) {
|
|
|
5310
5361
|
let unseen = false;
|
|
5311
5362
|
let seen = false;
|
|
5312
5363
|
try {
|
|
5313
|
-
|
|
5364
|
+
walk(f, (node) => {
|
|
5314
5365
|
if (!node || unseen) return false;
|
|
5315
5366
|
const n = node;
|
|
5316
|
-
if (
|
|
5317
|
-
return true;
|
|
5367
|
+
if (n.kind !== "Stmt" || !n.Cmd || n.Cmd.kind !== "CallExpr") return true;
|
|
5318
5368
|
const words = (n.Cmd.Args || []).map(
|
|
5319
5369
|
(a) => resolveWordLiteral(a)
|
|
5320
5370
|
);
|
|
@@ -5369,14 +5419,14 @@ function analyzeGitImpl(command) {
|
|
|
5369
5419
|
seen.add(key);
|
|
5370
5420
|
findings.push(f);
|
|
5371
5421
|
};
|
|
5372
|
-
const
|
|
5373
|
-
const parsed =
|
|
5422
|
+
const walk3 = new GitWalk((inv) => classify(inv).forEach(found), found);
|
|
5423
|
+
const parsed = walk3.run(command, 0);
|
|
5374
5424
|
return { parsed, findings };
|
|
5375
5425
|
}
|
|
5376
5426
|
function isPositionalParam(word) {
|
|
5377
5427
|
let parts = word?.Parts || [];
|
|
5378
|
-
if (parts.length === 1 &&
|
|
5379
|
-
if (parts.length !== 1 ||
|
|
5428
|
+
if (parts.length === 1 && parts[0].kind === "DblQuoted") parts = parts[0].Parts || [];
|
|
5429
|
+
if (parts.length !== 1 || parts[0].kind !== "ParamExp") return false;
|
|
5380
5430
|
return /^(\d+|@|\*)$/.test(parts[0].Param?.Value ?? "");
|
|
5381
5431
|
}
|
|
5382
5432
|
function boundPositionals() {
|
|
@@ -5390,11 +5440,11 @@ function boundPositionals() {
|
|
|
5390
5440
|
function plainParamName(word) {
|
|
5391
5441
|
let parts = word?.Parts || [];
|
|
5392
5442
|
let quoted = false;
|
|
5393
|
-
if (parts.length === 1 &&
|
|
5443
|
+
if (parts.length === 1 && parts[0].kind === "DblQuoted") {
|
|
5394
5444
|
parts = parts[0].Parts || [];
|
|
5395
5445
|
quoted = true;
|
|
5396
5446
|
}
|
|
5397
|
-
if (parts.length !== 1 ||
|
|
5447
|
+
if (parts.length !== 1 || parts[0].kind !== "ParamExp") return null;
|
|
5398
5448
|
const p = parts[0];
|
|
5399
5449
|
if (p.Excl || p.Length || p.Width || p.Index || p.Slice || p.Repl || p.Exp) return null;
|
|
5400
5450
|
const name = p.Param?.Value;
|
|
@@ -5419,7 +5469,7 @@ function resolveGitWords(word) {
|
|
|
5419
5469
|
}
|
|
5420
5470
|
const parts = word?.Parts || [];
|
|
5421
5471
|
const expansion = parts.some((p) => {
|
|
5422
|
-
const t =
|
|
5472
|
+
const t = p.kind;
|
|
5423
5473
|
return t === "ParamExp" || t === "CmdSubst";
|
|
5424
5474
|
});
|
|
5425
5475
|
return expansion ? splitFields(value) : [value];
|
|
@@ -5820,14 +5870,14 @@ function hookCommands(text) {
|
|
|
5820
5870
|
const root = parsed?.hooks;
|
|
5821
5871
|
if (!root || typeof root !== "object") return null;
|
|
5822
5872
|
const out = [];
|
|
5823
|
-
const
|
|
5873
|
+
const walk3 = (v, depth) => {
|
|
5824
5874
|
if (depth > 8 || !v || typeof v !== "object") return;
|
|
5825
5875
|
for (const [k, x] of Object.entries(v)) {
|
|
5826
5876
|
if (k === "command" && typeof x === "string") out.push(x);
|
|
5827
|
-
else
|
|
5877
|
+
else walk3(x, depth + 1);
|
|
5828
5878
|
}
|
|
5829
5879
|
};
|
|
5830
|
-
|
|
5880
|
+
walk3(root, 0);
|
|
5831
5881
|
return out;
|
|
5832
5882
|
}
|
|
5833
5883
|
function hooksDisarmed(text) {
|
|
@@ -5994,9 +6044,9 @@ function isDupInOp(op) {
|
|
|
5994
6044
|
if (dupInOp === null) {
|
|
5995
6045
|
let found = -1;
|
|
5996
6046
|
try {
|
|
5997
|
-
|
|
6047
|
+
walk(parseShared("cat <&3"), (node) => {
|
|
5998
6048
|
const n = node;
|
|
5999
|
-
if (found === -1 && n &&
|
|
6049
|
+
if (found === -1 && n && n.kind === "Stmt" && n.Redirs?.[0]) {
|
|
6000
6050
|
found = n.Redirs[0].Op;
|
|
6001
6051
|
}
|
|
6002
6052
|
return true;
|
|
@@ -6017,10 +6067,10 @@ function analyzeOutbound(command) {
|
|
|
6017
6067
|
return hit;
|
|
6018
6068
|
}
|
|
6019
6069
|
const findings = [];
|
|
6020
|
-
const
|
|
6021
|
-
const parsed =
|
|
6070
|
+
const walk3 = new OutboundWalk(findings);
|
|
6071
|
+
const parsed = walk3.run(command, 0);
|
|
6022
6072
|
const envOut = findings.find((f) => f.checkId === "data.env-exfil");
|
|
6023
|
-
if (envOut) envOut.destinations =
|
|
6073
|
+
if (envOut) envOut.destinations = walk3.envDestinations();
|
|
6024
6074
|
const computed = { parsed, findings };
|
|
6025
6075
|
if (cache2.size >= CACHE_MAX2) {
|
|
6026
6076
|
const oldest = cache2.keys().next().value;
|
|
@@ -6036,7 +6086,7 @@ function wordShape(word) {
|
|
|
6036
6086
|
const parts = word?.Parts || [];
|
|
6037
6087
|
let out = "";
|
|
6038
6088
|
for (const p of parts) {
|
|
6039
|
-
const t =
|
|
6089
|
+
const t = p.kind;
|
|
6040
6090
|
if (t === "Lit" || t === "SglQuoted") out += p.Value ?? "";
|
|
6041
6091
|
else if (t === "DblQuoted") out += wordShape(p);
|
|
6042
6092
|
else out += "X";
|
|
@@ -6171,16 +6221,16 @@ function declDumpsEnv(decl) {
|
|
|
6171
6221
|
}
|
|
6172
6222
|
function paramName(word) {
|
|
6173
6223
|
let parts = word?.Parts || [];
|
|
6174
|
-
if (parts.length === 1 &&
|
|
6175
|
-
if (parts.length !== 1 ||
|
|
6224
|
+
if (parts.length === 1 && parts[0].kind === "DblQuoted") parts = parts[0].Parts || [];
|
|
6225
|
+
if (parts.length !== 1 || parts[0].kind !== "ParamExp") return null;
|
|
6176
6226
|
return parts[0].Param?.Value ?? null;
|
|
6177
6227
|
}
|
|
6178
6228
|
function refersTo(word, names) {
|
|
6179
6229
|
let hit = false;
|
|
6180
|
-
|
|
6230
|
+
walk(word, (node) => {
|
|
6181
6231
|
if (hit || !node) return false;
|
|
6182
6232
|
const n = node;
|
|
6183
|
-
if (
|
|
6233
|
+
if (n.kind === "ParamExp" && names.has(n.Param?.Value)) hit = true;
|
|
6184
6234
|
return !hit;
|
|
6185
6235
|
});
|
|
6186
6236
|
return hit;
|
|
@@ -6188,10 +6238,10 @@ function refersTo(word, names) {
|
|
|
6188
6238
|
function runsCommandsIn(stmt) {
|
|
6189
6239
|
const readVars = /* @__PURE__ */ new Set();
|
|
6190
6240
|
let hit = false;
|
|
6191
|
-
|
|
6241
|
+
walk(stmt, (node) => {
|
|
6192
6242
|
if (hit || !node) return false;
|
|
6193
6243
|
const n = node;
|
|
6194
|
-
if (
|
|
6244
|
+
if (n.kind !== "CallExpr") return true;
|
|
6195
6245
|
const words = wordsOf(n);
|
|
6196
6246
|
if (words.length === 0) return true;
|
|
6197
6247
|
const { head, at } = headOf(words);
|
|
@@ -6768,7 +6818,7 @@ function localContexts(file2) {
|
|
|
6768
6818
|
if (depth > 64) return null;
|
|
6769
6819
|
if (!stmt || stmt.Background || stmt.Negated || stmt.Redirs?.length) return null;
|
|
6770
6820
|
const cmd = stmt.Cmd;
|
|
6771
|
-
const kind =
|
|
6821
|
+
const kind = cmd.kind;
|
|
6772
6822
|
if (kind === "BinaryCmd" && cmd.Op === andOp) {
|
|
6773
6823
|
const next = visit2(cmd.X, steps, depth + 1);
|
|
6774
6824
|
return next === null ? null : visit2(cmd.Y, next, depth + 1);
|
|
@@ -6784,7 +6834,7 @@ function localContexts(file2) {
|
|
|
6784
6834
|
)
|
|
6785
6835
|
))
|
|
6786
6836
|
return null;
|
|
6787
|
-
contexts.set(cmd.
|
|
6837
|
+
contexts.set(cmd.pos, steps);
|
|
6788
6838
|
return null;
|
|
6789
6839
|
}
|
|
6790
6840
|
if (file2.Stmts?.length === 1) visit2(file2.Stmts[0], []);
|
|
@@ -6795,12 +6845,12 @@ function collect(command, out, depth) {
|
|
|
6795
6845
|
const f = parseShared(command);
|
|
6796
6846
|
if (typeof f === "symbol") return;
|
|
6797
6847
|
const contexts = depth === 0 ? localContexts(f) : /* @__PURE__ */ new Map();
|
|
6798
|
-
|
|
6799
|
-
if (node &&
|
|
6848
|
+
walk(f, (node) => {
|
|
6849
|
+
if (node && node.kind === "CallExpr") {
|
|
6800
6850
|
const words = (node.Args || []).map((a) => resolveWordLiteral(a));
|
|
6801
6851
|
const start = out.length;
|
|
6802
6852
|
if (words.length > 0) fromCall(words, out, depth);
|
|
6803
|
-
const steps = contexts.get(node.
|
|
6853
|
+
const steps = contexts.get(node.pos);
|
|
6804
6854
|
for (let i = start; i < out.length; i++) {
|
|
6805
6855
|
if (["npx", "bunx", "npm exec"].includes(out[i].manager)) {
|
|
6806
6856
|
if (!steps) out[i].localCwd = null;
|
|
@@ -6847,7 +6897,7 @@ function collect2(command, out, depth) {
|
|
|
6847
6897
|
try {
|
|
6848
6898
|
walkCommandScope(command, (node) => {
|
|
6849
6899
|
const n = node;
|
|
6850
|
-
if (!n ||
|
|
6900
|
+
if (!n || n.kind !== "CallExpr") return true;
|
|
6851
6901
|
const words = (n.Args || []).map((a) => resolveWordLiteral(a));
|
|
6852
6902
|
if (words.length === 0) return true;
|
|
6853
6903
|
const prefix = (n.Assigns || []).filter((a) => a?.Name?.Value).map(
|
|
@@ -8277,12 +8327,12 @@ function extractContentDestinations(text) {
|
|
|
8277
8327
|
function extractArgDestinations(args) {
|
|
8278
8328
|
const out = /* @__PURE__ */ new Set();
|
|
8279
8329
|
try {
|
|
8280
|
-
|
|
8330
|
+
walk2(args, void 0, out, 0);
|
|
8281
8331
|
} catch {
|
|
8282
8332
|
}
|
|
8283
8333
|
return [...out];
|
|
8284
8334
|
}
|
|
8285
|
-
function
|
|
8335
|
+
function walk2(value, key, out, depth) {
|
|
8286
8336
|
if (out.size >= MAX_DESTINATIONS || depth > 12) return;
|
|
8287
8337
|
if (typeof value === "string") {
|
|
8288
8338
|
const s = value.length > MAX_SCAN_CHARS ? value.slice(0, MAX_SCAN_CHARS) : value;
|
|
@@ -8301,12 +8351,12 @@ function walk(value, key, out, depth) {
|
|
|
8301
8351
|
return;
|
|
8302
8352
|
}
|
|
8303
8353
|
if (Array.isArray(value)) {
|
|
8304
|
-
for (const v of value)
|
|
8354
|
+
for (const v of value) walk2(v, key, out, depth + 1);
|
|
8305
8355
|
return;
|
|
8306
8356
|
}
|
|
8307
8357
|
if (value && typeof value === "object") {
|
|
8308
8358
|
for (const [k, v] of Object.entries(value))
|
|
8309
|
-
|
|
8359
|
+
walk2(v, k, out, depth + 1);
|
|
8310
8360
|
}
|
|
8311
8361
|
}
|
|
8312
8362
|
function destinationDomain(dest) {
|
|
@@ -8958,9 +9008,9 @@ function classifyCall(head, args, prefixes2, env, state, out) {
|
|
|
8958
9008
|
function walkCalls(command, out, depth, state) {
|
|
8959
9009
|
const f = parseShared(command);
|
|
8960
9010
|
if (typeof f === "symbol" || !f) return;
|
|
8961
|
-
|
|
9011
|
+
walk(f, (node) => {
|
|
8962
9012
|
if (!node) return false;
|
|
8963
|
-
const type =
|
|
9013
|
+
const type = node.kind;
|
|
8964
9014
|
if (type === "DeclClause") {
|
|
8965
9015
|
for (const a of node.Args || []) if (a?.Name?.Value) state.env.add(a.Name.Value);
|
|
8966
9016
|
return true;
|
|
@@ -9364,7 +9414,7 @@ function matchCanaryArgs(args, values) {
|
|
|
9364
9414
|
if (values.length === 0) return null;
|
|
9365
9415
|
const needles = prepare(values);
|
|
9366
9416
|
if (needles.length === 0) return null;
|
|
9367
|
-
const
|
|
9417
|
+
const walk3 = (v, depth, fieldPath) => {
|
|
9368
9418
|
if (depth > MAX_DEPTH4) return null;
|
|
9369
9419
|
if (typeof v === "string") {
|
|
9370
9420
|
const hit = matchPrepared(v, needles);
|
|
@@ -9373,31 +9423,31 @@ function matchCanaryArgs(args, values) {
|
|
|
9373
9423
|
const trimmed = v.trim();
|
|
9374
9424
|
if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
|
9375
9425
|
try {
|
|
9376
|
-
return
|
|
9426
|
+
return walk3(JSON.parse(v), depth + 1, fieldPath);
|
|
9377
9427
|
} catch {
|
|
9378
9428
|
}
|
|
9379
9429
|
}
|
|
9380
9430
|
}
|
|
9381
9431
|
return null;
|
|
9382
9432
|
}
|
|
9383
|
-
if (typeof v === "number" && Number.isFinite(v)) return
|
|
9433
|
+
if (typeof v === "number" && Number.isFinite(v)) return walk3(String(v), depth, fieldPath);
|
|
9384
9434
|
if (Array.isArray(v)) {
|
|
9385
9435
|
for (let i = 0; i < v.length; i++) {
|
|
9386
|
-
const h =
|
|
9436
|
+
const h = walk3(v[i], depth + 1, `${fieldPath}[${i}]`);
|
|
9387
9437
|
if (h) return h;
|
|
9388
9438
|
}
|
|
9389
9439
|
return null;
|
|
9390
9440
|
}
|
|
9391
9441
|
if (v && typeof v === "object") {
|
|
9392
9442
|
for (const [k, child] of Object.entries(v)) {
|
|
9393
|
-
const h =
|
|
9443
|
+
const h = walk3(child, depth + 1, fieldPath ? `${fieldPath}.${k}` : k);
|
|
9394
9444
|
if (h) return h;
|
|
9395
9445
|
}
|
|
9396
9446
|
}
|
|
9397
9447
|
return null;
|
|
9398
9448
|
};
|
|
9399
9449
|
try {
|
|
9400
|
-
return
|
|
9450
|
+
return walk3(args, 0, "");
|
|
9401
9451
|
} catch (e) {
|
|
9402
9452
|
console.error(
|
|
9403
9453
|
"[node9 engine] canary args walk failed:",
|
|
@@ -9830,7 +9880,7 @@ function* stringLeavesWithKeys(obj, depth = 0, key) {
|
|
|
9830
9880
|
}
|
|
9831
9881
|
for (const [k, v] of Object.entries(obj)) yield* stringLeavesWithKeys(v, depth + 1, k);
|
|
9832
9882
|
}
|
|
9833
|
-
var MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, finance_default, identity_default, publishing_default, settings_default, branches_default, ACTION_TEMPLATES, ACTION_CHECKS, GAP, prog, httpTo, httpWrite, HTTP_WRITE, HTTP_WRITE_ANY, NOT_STRIPE_TEST, postTo, STRIPE_OBJECTS, APP_SHIELD_SPECS, APP_SHIELDS, APP_SHIELD_NAMES, APP_SERVER_GLOB, GENERAL_ACTION_RULES, 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, 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,
|
|
9883
|
+
var MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, finance_default, identity_default, publishing_default, settings_default, branches_default, ACTION_TEMPLATES, ACTION_CHECKS, GAP, prog, httpTo, httpWrite, HTTP_WRITE, HTTP_WRITE_ANY, NOT_STRIPE_TEST, postTo, STRIPE_OBJECTS, APP_SHIELD_SPECS, APP_SHIELDS, APP_SHIELD_NAMES, APP_SERVER_GLOB, GENERAL_ACTION_RULES, 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;
|
|
9834
9884
|
var init_dist = __esm({
|
|
9835
9885
|
"packages/policy-engine/dist/index.mjs"() {
|
|
9836
9886
|
"use strict";
|
|
@@ -12494,6 +12544,61 @@ var init_dist = __esm({
|
|
|
12494
12544
|
return s.untrustedContent.afterExposure === "log-only" ? "log" : "review";
|
|
12495
12545
|
}
|
|
12496
12546
|
};
|
|
12547
|
+
({ syntax } = mvdanSh);
|
|
12548
|
+
KINDS = {
|
|
12549
|
+
File: { nodes: ["Stmts"] },
|
|
12550
|
+
Stmt: { nodes: ["Cmd", "Redirs"], scalars: ["Negated", "Background", "Coprocess"] },
|
|
12551
|
+
CallExpr: { nodes: ["Assigns", "Args"] },
|
|
12552
|
+
Assign: { nodes: ["Name", "Value", "Index", "Array"], scalars: ["Append", "Naked"] },
|
|
12553
|
+
Word: { nodes: ["Parts"] },
|
|
12554
|
+
Lit: { scalars: ["Value"] },
|
|
12555
|
+
SglQuoted: { scalars: ["Value", "Dollar"] },
|
|
12556
|
+
DblQuoted: { nodes: ["Parts"], scalars: ["Dollar"] },
|
|
12557
|
+
ParamExp: {
|
|
12558
|
+
nodes: ["Param", "Index", "Slice", "Repl", "Exp"],
|
|
12559
|
+
scalars: ["Short", "Excl", "Length", "Width"]
|
|
12560
|
+
},
|
|
12561
|
+
CmdSubst: { nodes: ["Stmts"], scalars: ["Backquotes", "TempFile", "ReplyVar"] },
|
|
12562
|
+
ArithmExp: { nodes: ["X"], scalars: ["Bracket", "Unsigned"] },
|
|
12563
|
+
ArithmCmd: { nodes: ["X"], scalars: ["Unsigned"] },
|
|
12564
|
+
ProcSubst: { nodes: ["Stmts"], scalars: ["Op"] },
|
|
12565
|
+
ExtGlob: { nodes: ["Pattern"], scalars: ["Op"] },
|
|
12566
|
+
BraceExp: { nodes: ["Elems"], scalars: ["Sequence"] },
|
|
12567
|
+
// only with SplitBraces, unused here
|
|
12568
|
+
BinaryCmd: { nodes: ["X", "Y"], scalars: ["Op"] },
|
|
12569
|
+
BinaryArithm: { nodes: ["X", "Y"], scalars: ["Op"] },
|
|
12570
|
+
UnaryArithm: { nodes: ["X"], scalars: ["Op", "Post"] },
|
|
12571
|
+
ParenArithm: { nodes: ["X"] },
|
|
12572
|
+
BinaryTest: { nodes: ["X", "Y"], scalars: ["Op"] },
|
|
12573
|
+
UnaryTest: { nodes: ["X"], scalars: ["Op"] },
|
|
12574
|
+
ParenTest: { nodes: ["X"] },
|
|
12575
|
+
Redirect: { nodes: ["N", "Word", "Hdoc"], scalars: ["Op"] },
|
|
12576
|
+
Block: { nodes: ["Stmts"] },
|
|
12577
|
+
Subshell: { nodes: ["Stmts"] },
|
|
12578
|
+
IfClause: { nodes: ["Cond", "Then", "Else"] },
|
|
12579
|
+
WhileClause: { nodes: ["Cond", "Do"], scalars: ["Until"] },
|
|
12580
|
+
ForClause: { nodes: ["Loop", "Do"], scalars: ["Select"] },
|
|
12581
|
+
WordIter: { nodes: ["Name", "Items"] },
|
|
12582
|
+
CStyleLoop: { nodes: ["Init", "Cond", "Post"] },
|
|
12583
|
+
CaseClause: { nodes: ["Word", "Items"] },
|
|
12584
|
+
CaseItem: { nodes: ["Patterns", "Stmts"], scalars: ["Op"] },
|
|
12585
|
+
FuncDecl: { nodes: ["Name", "Body"], scalars: ["RsrvWord"] },
|
|
12586
|
+
DeclClause: { nodes: ["Variant", "Args"], notWalked: ["Variant"] },
|
|
12587
|
+
LetClause: { nodes: ["Exprs"] },
|
|
12588
|
+
TimeClause: { nodes: ["Stmt"], scalars: ["PosixFormat"] },
|
|
12589
|
+
CoprocClause: { nodes: ["Name", "Stmt"] },
|
|
12590
|
+
TestClause: { nodes: ["X"] },
|
|
12591
|
+
TestDecl: { nodes: ["Description", "Body"] },
|
|
12592
|
+
ArrayExpr: { nodes: ["Elems"] },
|
|
12593
|
+
ArrayElem: { nodes: ["Index", "Value"] },
|
|
12594
|
+
Comment: { scalars: ["Text"] }
|
|
12595
|
+
};
|
|
12596
|
+
STRUCTS = {
|
|
12597
|
+
"ParamExp.Slice": { nodes: ["Offset", "Length"], notWalked: ["Offset", "Length"] },
|
|
12598
|
+
"ParamExp.Repl": { nodes: ["Orig", "With"], scalars: ["All"] },
|
|
12599
|
+
"ParamExp.Exp": { nodes: ["Word"], scalars: ["Op"] }
|
|
12600
|
+
};
|
|
12601
|
+
unknownKinds = /* @__PURE__ */ new Set();
|
|
12497
12602
|
INVISIBLE_RE = /[͏----︀-️]|[\u{E0000}-\u{E007F}]/gu;
|
|
12498
12603
|
INVISIBLE_ONE = new RegExp(`^(?:${INVISIBLE_RE.source.split("|")[0]})$`, "u");
|
|
12499
12604
|
HOMOGLYPHS = {
|
|
@@ -12779,8 +12884,8 @@ var init_dist = __esm({
|
|
|
12779
12884
|
php: PHP
|
|
12780
12885
|
};
|
|
12781
12886
|
STRING_LITERAL = /(['"`])((?:\\.|(?!\1)[^\\\n]){1,4096})\1/g;
|
|
12782
|
-
({ syntax } =
|
|
12783
|
-
sharedParser =
|
|
12887
|
+
({ syntax: syntax2 } = mvdanSh2);
|
|
12888
|
+
sharedParser = syntax2.NewParser();
|
|
12784
12889
|
MESSAGE_FLAGS = /* @__PURE__ */ new Set([
|
|
12785
12890
|
"-m",
|
|
12786
12891
|
"--message",
|
|
@@ -12814,7 +12919,7 @@ var init_dist = __esm({
|
|
|
12814
12919
|
NORMALIZE_CACHE_MAX = 5e3;
|
|
12815
12920
|
normalizeCache = /* @__PURE__ */ new Map();
|
|
12816
12921
|
codeBodiesCache = /* @__PURE__ */ new Map();
|
|
12817
|
-
AST_CACHE_MAX =
|
|
12922
|
+
AST_CACHE_MAX = 256;
|
|
12818
12923
|
astCache = /* @__PURE__ */ new Map();
|
|
12819
12924
|
PARSE_FAIL = /* @__PURE__ */ Symbol("parse-fail");
|
|
12820
12925
|
INTERPRETER_LINE = /^#![ \t]*[\w./+-]+(?:[ \t]+[\w./+=:-]+)*[ \t]*\r?(?=\n|$)/;
|
|
@@ -13799,7 +13904,7 @@ var init_dist = __esm({
|
|
|
13799
13904
|
ARGV_FIELDS = ["args", "argv"];
|
|
13800
13905
|
BARE_WORD = /^[A-Za-z0-9_./+:@%,-]+$/;
|
|
13801
13906
|
MAX_ARGV_CHARACTERS = 262144;
|
|
13802
|
-
({ syntax:
|
|
13907
|
+
({ syntax: syntax3 } = mvdanSh3);
|
|
13803
13908
|
MAX_ALTERNATIVES = 64;
|
|
13804
13909
|
MAX_CALL_DEPTH2 = 4;
|
|
13805
13910
|
MAX_CALL_WALKS2 = 256;
|
|
@@ -13891,7 +13996,6 @@ var init_dist = __esm({
|
|
|
13891
13996
|
);
|
|
13892
13997
|
exempt = (p) => SYSTEM_DIRS_EXEMPT.some((d) => within(p, d));
|
|
13893
13998
|
RANK = { allow: 0, review: 1, block: 2 };
|
|
13894
|
-
({ syntax: syntax3 } = mvdanSh3);
|
|
13895
13999
|
SEQUENCE_OPS = (() => {
|
|
13896
14000
|
const ops = /* @__PURE__ */ new Set();
|
|
13897
14001
|
for (const text of ["x && y", "x || y"]) {
|
|
@@ -17060,7 +17164,6 @@ zoo`.split("\n");
|
|
|
17060
17164
|
);
|
|
17061
17165
|
NEVER_NEGATED = /* @__PURE__ */ new Set([SIGNALS[1].any[0]]);
|
|
17062
17166
|
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;
|
|
17063
|
-
({ syntax: syntax4 } = mvdanSh4);
|
|
17064
17167
|
RDR_IN = (() => {
|
|
17065
17168
|
try {
|
|
17066
17169
|
return parseShared("x <y").Stmts[0].Redirs[0].Op;
|
|
@@ -17089,7 +17192,6 @@ zoo`.split("\n");
|
|
|
17089
17192
|
"tar"
|
|
17090
17193
|
]);
|
|
17091
17194
|
LITERAL_VERBS = /* @__PURE__ */ new Set(["echo", "printf", ":", "true", "touch", "sed"]);
|
|
17092
|
-
({ syntax: syntax5 } = mvdanSh5);
|
|
17093
17195
|
GIT_PRESCREEN_RE = /git|husky/i;
|
|
17094
17196
|
MAX_PAYLOAD_DEPTH2 = 8;
|
|
17095
17197
|
MAX_ALIAS_DEPTH = 5;
|
|
@@ -17158,7 +17260,7 @@ zoo`.split("\n");
|
|
|
17158
17260
|
visit(node, depth) {
|
|
17159
17261
|
if (!node) return false;
|
|
17160
17262
|
const n = node;
|
|
17161
|
-
const type =
|
|
17263
|
+
const type = n.kind;
|
|
17162
17264
|
if (type === "Stmt") {
|
|
17163
17265
|
this.visitHeredocShell(n, depth);
|
|
17164
17266
|
return true;
|
|
@@ -17166,7 +17268,7 @@ zoo`.split("\n");
|
|
|
17166
17268
|
if (type === "ForClause") {
|
|
17167
17269
|
const loop = n.Loop;
|
|
17168
17270
|
const name = loop?.Name?.Value;
|
|
17169
|
-
if (name &&
|
|
17271
|
+
if (name && loop.kind === "WordIter") {
|
|
17170
17272
|
const items = (loop.Items || []).map((w) => resolveWordLiteral(w));
|
|
17171
17273
|
const literal = items.length > 0 && items.every((v) => v !== null);
|
|
17172
17274
|
this.loopVars.set(name, literal ? items.slice(0, MAX_LOOP_ITEMS) : null);
|
|
@@ -17233,7 +17335,7 @@ zoo`.split("\n");
|
|
|
17233
17335
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
17234
17336
|
visitHeredocShell(stmt, depth) {
|
|
17235
17337
|
const cmd = stmt.Cmd;
|
|
17236
|
-
if (!cmd ||
|
|
17338
|
+
if (!cmd || cmd.kind !== "CallExpr" || depth >= MAX_PAYLOAD_DEPTH2) return;
|
|
17237
17339
|
const redirs = stmt.Redirs || [];
|
|
17238
17340
|
const hdoc = redirs.find((r) => r?.Hdoc && (r.N == null || r.N.Value === "0"));
|
|
17239
17341
|
if (!hdoc) return;
|
|
@@ -17487,7 +17589,6 @@ zoo`.split("\n");
|
|
|
17487
17589
|
]);
|
|
17488
17590
|
SETTINGS_RE = /\/(?:\.claude\/settings(?:\.local)?\.json|\.gemini\/settings\.json|\.codex\/hooks\.json|\.cursor\/hooks\.json)$/i;
|
|
17489
17591
|
HOOK_WORDS_RE = /"hooks"|\b(PreToolUse|PostToolUse|UserPromptSubmit|Notification|Stop|SubagentStop|SessionStart|SessionEnd|PreCompact|BeforeTool|AfterTool)\b|"command"\s*:/;
|
|
17490
|
-
({ syntax: syntax6 } = mvdanSh6);
|
|
17491
17592
|
SHELLS = /* @__PURE__ */ new Set([...SHELL_INTERPRETERS, "mksh", "ash", "csh", "tcsh"]);
|
|
17492
17593
|
NETCATS = /* @__PURE__ */ new Set(["nc", "ncat", "netcat", "nc.traditional", "nc.openbsd"]);
|
|
17493
17594
|
NET_CLIENTS = /* @__PURE__ */ new Set([...NETCATS, "telnet", "socat"]);
|
|
@@ -17545,7 +17646,7 @@ zoo`.split("\n");
|
|
|
17545
17646
|
const f = parseShared(command);
|
|
17546
17647
|
if (!f || typeof f !== "object" || !f.Stmts) return false;
|
|
17547
17648
|
try {
|
|
17548
|
-
|
|
17649
|
+
walk(f, (node) => this.visit(node, depth));
|
|
17549
17650
|
} catch {
|
|
17550
17651
|
return false;
|
|
17551
17652
|
}
|
|
@@ -17584,7 +17685,7 @@ zoo`.split("\n");
|
|
|
17584
17685
|
}
|
|
17585
17686
|
visit(n, depth) {
|
|
17586
17687
|
if (!n) return false;
|
|
17587
|
-
const type =
|
|
17688
|
+
const type = n.kind;
|
|
17588
17689
|
if (type === "Stmt") {
|
|
17589
17690
|
this.visitStmt(n, depth);
|
|
17590
17691
|
return true;
|
|
@@ -17604,7 +17705,7 @@ zoo`.split("\n");
|
|
|
17604
17705
|
const redirs = redirectWords(stmt);
|
|
17605
17706
|
const toSocket = redirs.some((w) => w !== null && DEV_NET_RE.test(w));
|
|
17606
17707
|
if (!cmd) return;
|
|
17607
|
-
const type =
|
|
17708
|
+
const type = cmd.kind;
|
|
17608
17709
|
if (type !== "CallExpr" || wordsOf(cmd).length > 0) {
|
|
17609
17710
|
if (this.readsNetFd(stmt) && runsCommandsIn(stmt)) {
|
|
17610
17711
|
this.reverseShell("commands are read from a network socket opened with exec");
|
|
@@ -17690,7 +17791,7 @@ zoo`.split("\n");
|
|
|
17690
17791
|
const stages = [];
|
|
17691
17792
|
const flatten = (s) => {
|
|
17692
17793
|
const c = s?.Cmd;
|
|
17693
|
-
if (c &&
|
|
17794
|
+
if (c && c.kind === "BinaryCmd" && isPipeOp(c.Op)) {
|
|
17694
17795
|
flatten(c.X);
|
|
17695
17796
|
flatten(c.Y);
|
|
17696
17797
|
} else stages.push(s);
|
|
@@ -17712,8 +17813,8 @@ zoo`.split("\n");
|
|
|
17712
17813
|
sinks.set(i, socketHosts(st));
|
|
17713
17814
|
}
|
|
17714
17815
|
if (!c) return;
|
|
17715
|
-
if (
|
|
17716
|
-
if (
|
|
17816
|
+
if (c.kind === "DeclClause" && declDumpsEnv(c) && dump < 0) dump = i;
|
|
17817
|
+
if (c.kind !== "CallExpr") {
|
|
17717
17818
|
if (runsCommandsIn(st)) shell = i;
|
|
17718
17819
|
return;
|
|
17719
17820
|
}
|
|
@@ -17754,10 +17855,10 @@ zoo`.split("\n");
|
|
|
17754
17855
|
/** A word (or assignment value) whose text includes an env dump's output. */
|
|
17755
17856
|
carriesEnv(word) {
|
|
17756
17857
|
let hit = false;
|
|
17757
|
-
|
|
17858
|
+
walk(word, (node) => {
|
|
17758
17859
|
if (hit || !node) return false;
|
|
17759
17860
|
const n = node;
|
|
17760
|
-
const t =
|
|
17861
|
+
const t = n.kind;
|
|
17761
17862
|
if (t === "ParamExp" && n.Param?.Value && this.envVars.has(n.Param.Value)) {
|
|
17762
17863
|
hit = true;
|
|
17763
17864
|
return false;
|
|
@@ -17774,11 +17875,11 @@ zoo`.split("\n");
|
|
|
17774
17875
|
stmtsDumpEnv(stmts) {
|
|
17775
17876
|
let hit = false;
|
|
17776
17877
|
for (const s of stmts) {
|
|
17777
|
-
|
|
17878
|
+
walk(s, (node) => {
|
|
17778
17879
|
if (hit || !node) return false;
|
|
17779
17880
|
const n = node;
|
|
17780
|
-
const t =
|
|
17781
|
-
if (t === "Stmt" && n.Cmd &&
|
|
17881
|
+
const t = n.kind;
|
|
17882
|
+
if (t === "Stmt" && n.Cmd && n.Cmd.kind === "CallExpr") {
|
|
17782
17883
|
const words = wordsOf(n.Cmd);
|
|
17783
17884
|
if (words.length > 0 && dumpsEnv(words, n)) hit = true;
|
|
17784
17885
|
}
|
|
@@ -17795,7 +17896,6 @@ zoo`.split("\n");
|
|
|
17795
17896
|
this.run(payload, depth + 1);
|
|
17796
17897
|
}
|
|
17797
17898
|
};
|
|
17798
|
-
({ syntax: syntax7 } = mvdanSh7);
|
|
17799
17899
|
NPM_INSTALL_VERBS = /* @__PURE__ */ new Set([
|
|
17800
17900
|
"install",
|
|
17801
17901
|
"i",
|
|
@@ -18177,7 +18277,6 @@ zoo`.split("\n");
|
|
|
18177
18277
|
SHELLS2 = /* @__PURE__ */ new Set(["sh", "bash", "zsh", "dash", "ksh", "ash"]);
|
|
18178
18278
|
MAX_NESTING = 3;
|
|
18179
18279
|
SHELL_VALUE_FLAGS = /* @__PURE__ */ new Set(["--rcfile", "--init-file", "-O", "+O", "-o", "+o"]);
|
|
18180
|
-
({ syntax: syntax8 } = mvdanSh8);
|
|
18181
18280
|
NPM_FAMILY = /* @__PURE__ */ new Set(["npm", "pnpm", "yarn", "bun", "npx", "pnpx", "bunx"]);
|
|
18182
18281
|
PIP_FAMILY = /* @__PURE__ */ new Set(["pip", "pip3", "uv", "uvx", "pipx", "pipenv"]);
|
|
18183
18282
|
NPM_FETCH_VERBS = /* @__PURE__ */ new Set([
|
|
@@ -18709,7 +18808,6 @@ zoo`.split("\n");
|
|
|
18709
18808
|
ACCOUNT_KEY_RE = /("(?:iban|account|account_number|accountNumber)"\s*:\s*")([A-Za-z0-9 .-]{6,40})(")/g;
|
|
18710
18809
|
ACCOUNT_VALUE_RE = /"([A-Z]{2}\d{2}[A-Z0-9]{11,30})"/g;
|
|
18711
18810
|
MAX_MASK_DEPTH = 64;
|
|
18712
|
-
({ syntax: syntax9 } = mvdanSh9);
|
|
18713
18811
|
EMPTY = {
|
|
18714
18812
|
fetches: false,
|
|
18715
18813
|
sends: false,
|
|
@@ -33770,18 +33868,18 @@ function statAndFirstLine(file2) {
|
|
|
33770
33868
|
}
|
|
33771
33869
|
function listCodexSessionFiles(base2 = codexSessionsDir()) {
|
|
33772
33870
|
const files = [];
|
|
33773
|
-
const
|
|
33871
|
+
const walk3 = (dir2) => {
|
|
33774
33872
|
try {
|
|
33775
33873
|
for (const entry of fs46.readdirSync(dir2, { withFileTypes: true })) {
|
|
33776
33874
|
const file2 = path47.join(dir2, entry.name);
|
|
33777
|
-
if (entry.isDirectory())
|
|
33875
|
+
if (entry.isDirectory()) walk3(file2);
|
|
33778
33876
|
else if (entry.isFile() && entry.name.endsWith(".jsonl")) files.push(file2);
|
|
33779
33877
|
}
|
|
33780
33878
|
} catch {
|
|
33781
33879
|
}
|
|
33782
33880
|
};
|
|
33783
|
-
|
|
33784
|
-
if (path47.basename(base2) === "sessions")
|
|
33881
|
+
walk3(base2);
|
|
33882
|
+
if (path47.basename(base2) === "sessions") walk3(path47.join(path47.dirname(base2), "archived_sessions"));
|
|
33785
33883
|
const sessions = /* @__PURE__ */ new Map();
|
|
33786
33884
|
for (const file2 of files.sort()) {
|
|
33787
33885
|
try {
|
|
@@ -34271,7 +34369,7 @@ import * as fs49 from "fs";
|
|
|
34271
34369
|
import * as path50 from "path";
|
|
34272
34370
|
function listSessionFiles(dir2, maxDepth = 6) {
|
|
34273
34371
|
const out = [];
|
|
34274
|
-
const
|
|
34372
|
+
const walk3 = (d, rel, depth) => {
|
|
34275
34373
|
if (depth > maxDepth) return;
|
|
34276
34374
|
let entries;
|
|
34277
34375
|
try {
|
|
@@ -34281,11 +34379,11 @@ function listSessionFiles(dir2, maxDepth = 6) {
|
|
|
34281
34379
|
}
|
|
34282
34380
|
for (const e of entries) {
|
|
34283
34381
|
const childRel = rel ? path50.join(rel, e.name) : e.name;
|
|
34284
|
-
if (e.isDirectory())
|
|
34382
|
+
if (e.isDirectory()) walk3(path50.join(d, e.name), childRel, depth + 1);
|
|
34285
34383
|
else if (e.name.endsWith(".jsonl")) out.push(childRel);
|
|
34286
34384
|
}
|
|
34287
34385
|
};
|
|
34288
|
-
|
|
34386
|
+
walk3(dir2, "", 0);
|
|
34289
34387
|
return out;
|
|
34290
34388
|
}
|
|
34291
34389
|
function sessionIdOf(relPath) {
|
|
@@ -37586,19 +37684,19 @@ function scrubDecoys(subject, values) {
|
|
|
37586
37684
|
for (const v of values) if (v) out = out.split(v).join("[node9-decoy]");
|
|
37587
37685
|
return out;
|
|
37588
37686
|
};
|
|
37589
|
-
const
|
|
37687
|
+
const walk3 = (v, depth) => {
|
|
37590
37688
|
if (depth > 6) return v;
|
|
37591
37689
|
if (typeof v === "string") return scrubText(v);
|
|
37592
|
-
if (Array.isArray(v)) return v.map((x) =>
|
|
37690
|
+
if (Array.isArray(v)) return v.map((x) => walk3(x, depth + 1));
|
|
37593
37691
|
if (v && typeof v === "object") {
|
|
37594
37692
|
const out = {};
|
|
37595
37693
|
for (const [k, child] of Object.entries(v))
|
|
37596
|
-
out[k] =
|
|
37694
|
+
out[k] = walk3(child, depth + 1);
|
|
37597
37695
|
return out;
|
|
37598
37696
|
}
|
|
37599
37697
|
return v;
|
|
37600
37698
|
};
|
|
37601
|
-
return
|
|
37699
|
+
return walk3(subject, 0);
|
|
37602
37700
|
}
|
|
37603
37701
|
function isStaleFinding(timestamp2, now = Date.now()) {
|
|
37604
37702
|
if (!timestamp2) return false;
|
|
@@ -75050,14 +75148,14 @@ function collectToolOutputText(toolResponse, opts = {}) {
|
|
|
75050
75148
|
size += s.length + 1;
|
|
75051
75149
|
return true;
|
|
75052
75150
|
};
|
|
75053
|
-
const
|
|
75151
|
+
const walk3 = (v, depth) => {
|
|
75054
75152
|
if (typeof v === "string") return push3(v);
|
|
75055
75153
|
if (depth >= MAX_DEPTH5 || v === null || typeof v !== "object") return true;
|
|
75056
75154
|
const children = Array.isArray(v) ? v : Object.values(v);
|
|
75057
|
-
for (const child of children) if (!
|
|
75155
|
+
for (const child of children) if (!walk3(child, depth + 1)) return false;
|
|
75058
75156
|
return true;
|
|
75059
75157
|
};
|
|
75060
|
-
|
|
75158
|
+
walk3(toolResponse, 0);
|
|
75061
75159
|
return { text: parts.join("\n"), truncated, fields };
|
|
75062
75160
|
}
|
|
75063
75161
|
function shellOutputText(toolResponse) {
|
|
@@ -75070,15 +75168,15 @@ function shellOutputText(toolResponse) {
|
|
|
75070
75168
|
return collectToolOutputText(toolResponse).text;
|
|
75071
75169
|
}
|
|
75072
75170
|
function mapToolOutputStrings(toolResponse, fn) {
|
|
75073
|
-
const
|
|
75171
|
+
const walk3 = (v, depth) => {
|
|
75074
75172
|
if (typeof v === "string") return fn(v);
|
|
75075
75173
|
if (depth >= MAX_DEPTH5 || v === null || typeof v !== "object") return v;
|
|
75076
|
-
if (Array.isArray(v)) return v.map((x) =>
|
|
75174
|
+
if (Array.isArray(v)) return v.map((x) => walk3(x, depth + 1));
|
|
75077
75175
|
const out = {};
|
|
75078
|
-
for (const [k, x] of Object.entries(v)) out[k] =
|
|
75176
|
+
for (const [k, x] of Object.entries(v)) out[k] = walk3(x, depth + 1);
|
|
75079
75177
|
return out;
|
|
75080
75178
|
};
|
|
75081
|
-
return
|
|
75179
|
+
return walk3(toolResponse, 0);
|
|
75082
75180
|
}
|
|
75083
75181
|
function frameToolOutputLeaves(toolResponse, isSuspect, frame, neutralize) {
|
|
75084
75182
|
const suspect = [];
|