@node9/proxy 2.33.2 → 2.34.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 +987 -99
- package/dist/cli.mjs +987 -99
- package/dist/dashboard.mjs +230 -13
- package/dist/index.js +956 -83
- package/dist/index.mjs +956 -83
- package/dist/scan-ink.mjs +134 -11
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1173,6 +1173,120 @@ function expandPlainParam(p) {
|
|
|
1173
1173
|
if (recorded !== void 0) return recorded;
|
|
1174
1174
|
return HOME_VARIABLES.has(p.Param?.Value) ? "~" : void 0;
|
|
1175
1175
|
}
|
|
1176
|
+
function functionBodies(f) {
|
|
1177
|
+
const out = /* @__PURE__ */ new Map();
|
|
1178
|
+
syntax.Walk(f, (node) => {
|
|
1179
|
+
const n = node;
|
|
1180
|
+
if (n && syntax.NodeType(n) === "FuncDecl" && n.Name?.Value && n.Body) {
|
|
1181
|
+
const list = out.get(n.Name.Value) ?? [];
|
|
1182
|
+
list.push(n.Body);
|
|
1183
|
+
out.set(n.Name.Value, list);
|
|
1184
|
+
}
|
|
1185
|
+
return true;
|
|
1186
|
+
});
|
|
1187
|
+
return out;
|
|
1188
|
+
}
|
|
1189
|
+
function rebindsPositionals(body) {
|
|
1190
|
+
let found = false;
|
|
1191
|
+
syntax.Walk(body, (node) => {
|
|
1192
|
+
const n = node;
|
|
1193
|
+
if (found || !n) return false;
|
|
1194
|
+
if (syntax.NodeType(n) === "CallExpr") {
|
|
1195
|
+
const words = (n.Args || []).map((a) => resolveWordLiteral(a));
|
|
1196
|
+
if (words[0] === "shift") found = true;
|
|
1197
|
+
else if (words[0] === "set" && words.slice(1).some((w) => w === null || w === "--" || !/^[-+]/.test(w)))
|
|
1198
|
+
found = true;
|
|
1199
|
+
}
|
|
1200
|
+
return !found;
|
|
1201
|
+
});
|
|
1202
|
+
return found;
|
|
1203
|
+
}
|
|
1204
|
+
function tableBindings() {
|
|
1205
|
+
if (!assignmentTable) return "";
|
|
1206
|
+
const out = [];
|
|
1207
|
+
for (const [name, rec] of assignmentTable)
|
|
1208
|
+
if (rec.at < currentStmtOffset) out.push(`${name}=${rec.value ?? "\0"}`);
|
|
1209
|
+
return out.sort().join("");
|
|
1210
|
+
}
|
|
1211
|
+
function walkWithCalls(f, visit) {
|
|
1212
|
+
const bodies = functionBodies(f);
|
|
1213
|
+
if (bodies.size === 0) {
|
|
1214
|
+
syntax.Walk(f, visit);
|
|
1215
|
+
return;
|
|
1216
|
+
}
|
|
1217
|
+
const seenCalls = /* @__PURE__ */ new Set();
|
|
1218
|
+
const inline = (call) => {
|
|
1219
|
+
const words = (call.Args || []).map((a) => resolveWordLiteral(a));
|
|
1220
|
+
const list = words[0] ? bodies.get(words[0]) : void 0;
|
|
1221
|
+
if (!list || callDepth >= MAX_CALL_DEPTH) return;
|
|
1222
|
+
const args = words.slice(1);
|
|
1223
|
+
const key = `${words[0]}\0${JSON.stringify(args)}\0${tableBindings()}`;
|
|
1224
|
+
if (seenCalls.has(key)) return;
|
|
1225
|
+
seenCalls.add(key);
|
|
1226
|
+
const outerTable = assignmentTable;
|
|
1227
|
+
const outerOffset = currentStmtOffset;
|
|
1228
|
+
for (const body of list) {
|
|
1229
|
+
if (callWalks >= MAX_CALL_WALKS) return;
|
|
1230
|
+
callWalks++;
|
|
1231
|
+
const table = /* @__PURE__ */ new Map();
|
|
1232
|
+
for (const [name, rec] of outerTable ?? [])
|
|
1233
|
+
if (rec.at < outerOffset) table.set(name, { value: rec.value, at: -1 });
|
|
1234
|
+
for (const name of ["1", "2", "3", "4", "5", "6", "7", "8", "9", "@", "*", "#"])
|
|
1235
|
+
table.delete(name);
|
|
1236
|
+
if (!rebindsPositionals(body)) {
|
|
1237
|
+
args.slice(0, 9).forEach((value, i) => table.set(String(i + 1), { value, at: -1 }));
|
|
1238
|
+
if (args.length === 1) {
|
|
1239
|
+
table.set("@", { value: args[0], at: -1 });
|
|
1240
|
+
table.set("*", { value: args[0], at: -1 });
|
|
1241
|
+
}
|
|
1242
|
+
table.set("#", { value: String(args.length), at: -1 });
|
|
1243
|
+
}
|
|
1244
|
+
assignmentTable = table;
|
|
1245
|
+
currentStmtOffset = Number.MAX_SAFE_INTEGER;
|
|
1246
|
+
callDepth++;
|
|
1247
|
+
try {
|
|
1248
|
+
if (body.Cmd && syntax.NodeType(body.Cmd) === "Block") recordTopLevelAssignments(body.Cmd);
|
|
1249
|
+
syntax.Walk(body, step);
|
|
1250
|
+
} finally {
|
|
1251
|
+
callDepth--;
|
|
1252
|
+
assignmentTable = outerTable;
|
|
1253
|
+
currentStmtOffset = outerOffset;
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
};
|
|
1257
|
+
const step = (node) => {
|
|
1258
|
+
if (!node) return false;
|
|
1259
|
+
const go = visit(node);
|
|
1260
|
+
if (go !== false && syntax.NodeType(node) === "CallExpr") inline(node);
|
|
1261
|
+
return go;
|
|
1262
|
+
};
|
|
1263
|
+
syntax.Walk(f, step);
|
|
1264
|
+
}
|
|
1265
|
+
function withAssignments(f, body) {
|
|
1266
|
+
const outerTable = assignmentTable;
|
|
1267
|
+
const outerOffset = currentStmtOffset;
|
|
1268
|
+
const outerWalks = callWalks;
|
|
1269
|
+
callWalks = 0;
|
|
1270
|
+
assignmentTable = new Map(
|
|
1271
|
+
[...outerTable ?? []].map(([k, r]) => [k, { value: r.value, at: -1 }])
|
|
1272
|
+
);
|
|
1273
|
+
currentStmtOffset = Number.MAX_SAFE_INTEGER;
|
|
1274
|
+
try {
|
|
1275
|
+
recordTopLevelAssignments(f);
|
|
1276
|
+
return body();
|
|
1277
|
+
} finally {
|
|
1278
|
+
assignmentTable = outerTable;
|
|
1279
|
+
currentStmtOffset = outerOffset;
|
|
1280
|
+
callWalks = outerWalks;
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
function trackStatement(n) {
|
|
1284
|
+
try {
|
|
1285
|
+
currentStmtOffset = n.Pos().Offset();
|
|
1286
|
+
} catch {
|
|
1287
|
+
currentStmtOffset = Number.MAX_SAFE_INTEGER;
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1176
1290
|
function resolveWordLiteral(w) {
|
|
1177
1291
|
const parts = w?.Parts || [];
|
|
1178
1292
|
let s = "";
|
|
@@ -1293,27 +1407,31 @@ function extractShellDestinations(command) {
|
|
|
1293
1407
|
const out = [];
|
|
1294
1408
|
const seen = /* @__PURE__ */ new Set();
|
|
1295
1409
|
try {
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
const
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1410
|
+
withAssignments(
|
|
1411
|
+
f,
|
|
1412
|
+
() => walkWithCalls(f, (node) => {
|
|
1413
|
+
if (!node) return false;
|
|
1414
|
+
const n = node;
|
|
1415
|
+
if (syntax.NodeType(n) === "Stmt") trackStatement(n);
|
|
1416
|
+
if (syntax.NodeType(n) !== "CallExpr") return true;
|
|
1417
|
+
const callArgs = n.Args || [];
|
|
1418
|
+
if (callArgs.length === 0) return true;
|
|
1419
|
+
const words = callArgs.map((a) => resolveWordLiteral(a));
|
|
1420
|
+
const at = unwrapCommandHead(words);
|
|
1421
|
+
const name = (words[at] || "").toLowerCase().split("/").pop() ?? "";
|
|
1422
|
+
if (!NET_BINARIES.has(name)) return true;
|
|
1423
|
+
const rest = words.slice(at + 1);
|
|
1424
|
+
for (const raw of destTokensForBinary(name, rest)) {
|
|
1425
|
+
const host2 = parseDestHost(raw);
|
|
1426
|
+
if (!host2) continue;
|
|
1427
|
+
const key = `${name}:${host2}`;
|
|
1428
|
+
if (seen.has(key)) continue;
|
|
1429
|
+
seen.add(key);
|
|
1430
|
+
out.push({ host: host2, binary: name, raw });
|
|
1431
|
+
}
|
|
1432
|
+
return true;
|
|
1433
|
+
})
|
|
1434
|
+
);
|
|
1317
1435
|
} catch {
|
|
1318
1436
|
return out;
|
|
1319
1437
|
}
|
|
@@ -1325,39 +1443,45 @@ function extractShellDestTokens(command) {
|
|
|
1325
1443
|
const out = [];
|
|
1326
1444
|
const seen = /* @__PURE__ */ new Set();
|
|
1327
1445
|
try {
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
const
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
tok = tok.
|
|
1446
|
+
withAssignments(
|
|
1447
|
+
f,
|
|
1448
|
+
() => walkWithCalls(f, (node) => {
|
|
1449
|
+
if (!node) return false;
|
|
1450
|
+
const n = node;
|
|
1451
|
+
if (syntax.NodeType(n) === "Stmt") trackStatement(n);
|
|
1452
|
+
if (syntax.NodeType(n) !== "CallExpr") return true;
|
|
1453
|
+
const callArgs = n.Args || [];
|
|
1454
|
+
if (callArgs.length === 0) return true;
|
|
1455
|
+
const words = callArgs.map((a) => resolveWordLiteral(a));
|
|
1456
|
+
const head = unwrapCommandHead(words);
|
|
1457
|
+
const name = (words[head] || "").toLowerCase().split("/").pop() ?? "";
|
|
1458
|
+
if (!NET_BINARIES.has(name)) return true;
|
|
1459
|
+
const rest = words.slice(head + 1);
|
|
1460
|
+
for (const raw of destTokensForBinary(name, rest)) {
|
|
1461
|
+
if (!raw) continue;
|
|
1462
|
+
let tok = raw.trim();
|
|
1463
|
+
const scheme = /^[a-z][a-z0-9+.-]*:\/\//i.exec(tok);
|
|
1464
|
+
const hasScheme = scheme !== null;
|
|
1465
|
+
if (hasScheme) tok = tok.slice(scheme[0].length);
|
|
1466
|
+
tok = tok.split(/[/?#]/)[0];
|
|
1467
|
+
const at = tok.lastIndexOf("@");
|
|
1468
|
+
if (at >= 0) tok = tok.slice(at + 1);
|
|
1469
|
+
if (tok.startsWith("[")) {
|
|
1470
|
+
const close = tok.indexOf("]");
|
|
1471
|
+
if (close > 0) tok = tok.slice(0, close + 1);
|
|
1472
|
+
} else {
|
|
1473
|
+
tok = tok.split(":")[0];
|
|
1474
|
+
}
|
|
1475
|
+
if (!tok) continue;
|
|
1476
|
+
if (!hasScheme && /^\d+$/.test(tok) && Number(tok) < 16777216) continue;
|
|
1477
|
+
const key = `${name}:${tok}`;
|
|
1478
|
+
if (seen.has(key)) continue;
|
|
1479
|
+
seen.add(key);
|
|
1480
|
+
out.push({ token: tok, binary: name });
|
|
1351
1481
|
}
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
if (seen.has(key)) continue;
|
|
1356
|
-
seen.add(key);
|
|
1357
|
-
out.push({ token: tok, binary: name });
|
|
1358
|
-
}
|
|
1359
|
-
return true;
|
|
1360
|
-
});
|
|
1482
|
+
return true;
|
|
1483
|
+
})
|
|
1484
|
+
);
|
|
1361
1485
|
} catch {
|
|
1362
1486
|
return out;
|
|
1363
1487
|
}
|
|
@@ -1476,6 +1600,7 @@ function analyzeFsOperationImpl(command, depth = 0) {
|
|
|
1476
1600
|
if (depth === 0) {
|
|
1477
1601
|
seenPayloads = /* @__PURE__ */ new Set();
|
|
1478
1602
|
payloadBudget = PAYLOAD_BUDGET;
|
|
1603
|
+
if (callDepth === 0) callWalks = 0;
|
|
1479
1604
|
}
|
|
1480
1605
|
assignmentTable = new Map(
|
|
1481
1606
|
[...outerTable ?? []].map(([k, r]) => [k, { value: r.value, at: -1 }])
|
|
@@ -1483,7 +1608,7 @@ function analyzeFsOperationImpl(command, depth = 0) {
|
|
|
1483
1608
|
currentStmtOffset = Number.MAX_SAFE_INTEGER;
|
|
1484
1609
|
try {
|
|
1485
1610
|
recordTopLevelAssignments(f);
|
|
1486
|
-
|
|
1611
|
+
walkWithCalls(f, (node) => {
|
|
1487
1612
|
if (!node || result?.verdict === "block") return false;
|
|
1488
1613
|
const n = node;
|
|
1489
1614
|
const nodeType = syntax.NodeType(n);
|
|
@@ -2272,6 +2397,19 @@ function runnablePrefix(command) {
|
|
|
2272
2397
|
}
|
|
2273
2398
|
return null;
|
|
2274
2399
|
}
|
|
2400
|
+
function uploadedFile(tool, flag, value) {
|
|
2401
|
+
const v = value.value;
|
|
2402
|
+
const file2 = (f) => f && f !== "-" && f !== "." ? { value: f } : null;
|
|
2403
|
+
if (tool === "curl") {
|
|
2404
|
+
if (["-d", "--data", "--data-ascii", "--data-binary", "--json"].includes(flag))
|
|
2405
|
+
return v.startsWith("@") ? file2(v.slice(1)) : null;
|
|
2406
|
+
if (flag === "--data-urlencode") return file2(/^[^=@]*@(.+)$/.exec(v)?.[1]);
|
|
2407
|
+
if (flag === "-F" || flag === "--form") return file2(/^[^=]*=[@<]([^;]+)/.exec(v)?.[1]);
|
|
2408
|
+
if (flag === "-T" || flag === "--upload-file") return file2(v);
|
|
2409
|
+
}
|
|
2410
|
+
if (tool === "wget" && ["--post-file", "--body-file"].includes(flag)) return file2(v);
|
|
2411
|
+
return null;
|
|
2412
|
+
}
|
|
2275
2413
|
function merge(a, b) {
|
|
2276
2414
|
a.cwd = unique([...a.cwd, ...b.cwd]);
|
|
2277
2415
|
for (const key of /* @__PURE__ */ new Set([...a.vars.keys(), ...b.vars.keys()])) {
|
|
@@ -2345,6 +2483,9 @@ function wordValues(word, state) {
|
|
|
2345
2483
|
function collectShellFileAccesses(command, context = {}) {
|
|
2346
2484
|
const out = [];
|
|
2347
2485
|
let payloads = 256;
|
|
2486
|
+
const functions = /* @__PURE__ */ new Map();
|
|
2487
|
+
const seenCalls = /* @__PURE__ */ new Set();
|
|
2488
|
+
let callWalks2 = MAX_CALL_WALKS2;
|
|
2348
2489
|
const initial = {
|
|
2349
2490
|
cwd: [context.cwd ?? ""],
|
|
2350
2491
|
vars: /* @__PURE__ */ new Map([["HOME", [context.homeDirs?.[0] ?? "~"]]])
|
|
@@ -2602,8 +2743,98 @@ function collectShellFileAccesses(command, context = {}) {
|
|
|
2602
2743
|
}
|
|
2603
2744
|
return;
|
|
2604
2745
|
}
|
|
2746
|
+
if (name === "wget" || name === "curl" || name === "aria2c") {
|
|
2747
|
+
downloadOutputs(name, args, state);
|
|
2748
|
+
return;
|
|
2749
|
+
}
|
|
2750
|
+
if (["http", "https", "xh", "xhs"].includes(name)) {
|
|
2751
|
+
for (const token of args) {
|
|
2752
|
+
if (!token || token.dynamic || /^[a-z][a-z0-9+.-]*:\/\//i.test(token.value)) continue;
|
|
2753
|
+
const m = /^(?:@|[^=:@\s]+@|[^=:@\s]*=@)(.+)$/.exec(token.value);
|
|
2754
|
+
if (m && m[1] !== "-") add("read", { value: m[1] }, state, { upload: true });
|
|
2755
|
+
}
|
|
2756
|
+
return;
|
|
2757
|
+
}
|
|
2758
|
+
if (name === "scp" || name === "rsync") {
|
|
2759
|
+
const operands2 = strings.filter((s) => s && !s.startsWith("-"));
|
|
2760
|
+
const remote = operands2.slice(1).some((o) => /^[^/\s]+:/.test(o));
|
|
2761
|
+
for (const p of copySourcePaths(words))
|
|
2762
|
+
add("read", locate(p), state, remote ? { upload: true } : {});
|
|
2763
|
+
return;
|
|
2764
|
+
}
|
|
2605
2765
|
for (const p of copySourcePaths(words)) add("read", locate(p), state);
|
|
2606
2766
|
}
|
|
2767
|
+
function downloadOutputs(name, args, state) {
|
|
2768
|
+
const valueShort = DOWNLOAD_SHORT_VALUE[name];
|
|
2769
|
+
const outputs = [];
|
|
2770
|
+
const urls = [];
|
|
2771
|
+
let dir2 = null;
|
|
2772
|
+
let remoteName = false;
|
|
2773
|
+
let document = false;
|
|
2774
|
+
let serverNamed = false;
|
|
2775
|
+
const take = (flag, value) => {
|
|
2776
|
+
if (!value) return;
|
|
2777
|
+
const sent = uploadedFile(name, flag, value);
|
|
2778
|
+
if (sent) add("read", sent, state, { upload: true });
|
|
2779
|
+
if (DOWNLOAD_OUTPUT_FLAGS[name].includes(flag)) {
|
|
2780
|
+
if (["-O", "--output-document", "--out"].includes(flag) || name === "aria2c" && flag === "-o")
|
|
2781
|
+
document = true;
|
|
2782
|
+
if (value.value !== "-" || value.pattern || value.dynamic) outputs.push(value);
|
|
2783
|
+
} else if (DOWNLOAD_DIR_FLAGS[name].includes(flag)) dir2 = value;
|
|
2784
|
+
else if (name === "wget" && (flag === "-i" || flag === "--input-file")) serverNamed = true;
|
|
2785
|
+
};
|
|
2786
|
+
for (let i = 0; i < args.length; i++) {
|
|
2787
|
+
const token = args[i];
|
|
2788
|
+
if (!token) continue;
|
|
2789
|
+
const v = token.value;
|
|
2790
|
+
if (v.startsWith("--")) {
|
|
2791
|
+
const eq = v.indexOf("=");
|
|
2792
|
+
const flag = eq < 0 ? v : v.slice(0, eq);
|
|
2793
|
+
if (["--remote-name", "--remote-name-all"].includes(flag)) remoteName = true;
|
|
2794
|
+
if (["--remote-header-name", "--recursive", "--mirror", "--page-requisites"].includes(flag))
|
|
2795
|
+
serverNamed = true;
|
|
2796
|
+
if (DOWNLOAD_LONG_VALUE[name].includes(flag))
|
|
2797
|
+
take(flag, eq < 0 ? args[++i] ?? null : { ...token, value: v.slice(eq + 1) });
|
|
2798
|
+
continue;
|
|
2799
|
+
}
|
|
2800
|
+
if (v.startsWith("-") && v.length > 1 && !token.dynamic) {
|
|
2801
|
+
for (let j = 1; j < v.length; j++) {
|
|
2802
|
+
const c = v[j];
|
|
2803
|
+
if (name === "wget" && c === "n") {
|
|
2804
|
+
j++;
|
|
2805
|
+
continue;
|
|
2806
|
+
}
|
|
2807
|
+
if (name === "curl" && c === "O") remoteName = true;
|
|
2808
|
+
if (name === "curl" && c === "J") serverNamed = true;
|
|
2809
|
+
if (name === "wget" && (c === "r" || c === "m" || c === "p")) serverNamed = true;
|
|
2810
|
+
if (valueShort.includes(c)) {
|
|
2811
|
+
const rest = v.slice(j + 1);
|
|
2812
|
+
take(`-${c}`, rest ? { value: rest } : args[++i] ?? null);
|
|
2813
|
+
break;
|
|
2814
|
+
}
|
|
2815
|
+
}
|
|
2816
|
+
continue;
|
|
2817
|
+
}
|
|
2818
|
+
if (/^(?:https?|ftps?):\/\//i.test(v)) urls.push(v);
|
|
2819
|
+
}
|
|
2820
|
+
for (const out2 of outputs) {
|
|
2821
|
+
const spelled = name === "aria2c" && dir2?.value && !out2.value.startsWith("/") ? { ...out2, value: `${dir2.value.replace(/\/+$/, "")}/${out2.value}` } : out2;
|
|
2822
|
+
if (name === "curl" && /#\d/.test(spelled.value))
|
|
2823
|
+
add("write", { value: "", pattern: spelled.value.replace(/#\d+/g, "*"), dot: true }, state);
|
|
2824
|
+
else add("write", spelled, state);
|
|
2825
|
+
}
|
|
2826
|
+
const remote = name === "curl" ? remoteName || serverNamed : !document || serverNamed;
|
|
2827
|
+
if (!remote) return;
|
|
2828
|
+
const into = dir2?.value ? dir2.value.replace(/\/+$/, "") : ".";
|
|
2829
|
+
if (serverNamed || urls.length === 0) {
|
|
2830
|
+
add("write", { value: "", pattern: `${quoteGlob(into)}/*`, dot: true }, state);
|
|
2831
|
+
return;
|
|
2832
|
+
}
|
|
2833
|
+
for (const url of urls) {
|
|
2834
|
+
const file2 = url.replace(/[?#].*$/, "").replace(/^[a-z]+:\/\/[^/]*/i, "").split("/").pop();
|
|
2835
|
+
add("write", { value: `${into}/${file2 || "index.html"}` }, state);
|
|
2836
|
+
}
|
|
2837
|
+
}
|
|
2607
2838
|
function findActions2(tokens, words, head, state, nested, depth) {
|
|
2608
2839
|
const starts = findStartPoints(words, head).starts;
|
|
2609
2840
|
const roots = (starts.length ? starts : ["."]).slice(0, MAX_ALTERNATIVES);
|
|
@@ -2675,7 +2906,35 @@ function collectShellFileAccesses(command, context = {}) {
|
|
|
2675
2906
|
const words = wordValues(node, state);
|
|
2676
2907
|
alternatives = alternatives.flatMap((prefix) => (words.length ? words : [null]).map((w) => [...prefix, w])).slice(0, MAX_ALTERNATIVES);
|
|
2677
2908
|
}
|
|
2678
|
-
for (const tokens of alternatives)
|
|
2909
|
+
for (const tokens of alternatives) {
|
|
2910
|
+
fromWords(tokens, state, nested, depth);
|
|
2911
|
+
inlineCall(tokens, state, depth);
|
|
2912
|
+
}
|
|
2913
|
+
}
|
|
2914
|
+
function inlineCall(tokens, state, depth) {
|
|
2915
|
+
const head = tokens[0];
|
|
2916
|
+
if (!head || head.dynamic || head.pattern) return;
|
|
2917
|
+
const bodies = functions.get(head.value);
|
|
2918
|
+
if (!bodies || (state.callDepth ?? 0) >= MAX_CALL_DEPTH2) return;
|
|
2919
|
+
const args = tokens.slice(1).map((a) => a && !a.dynamic ? a.value : null);
|
|
2920
|
+
const vars = JSON.stringify([...state.vars].sort(([a], [b]) => a < b ? -1 : 1));
|
|
2921
|
+
const key = `${head.value}\0${JSON.stringify(args)}\0${state.cwd.join("")}\0${vars}`;
|
|
2922
|
+
if (seenCalls.has(key)) return;
|
|
2923
|
+
seenCalls.add(key);
|
|
2924
|
+
for (const body of bodies) {
|
|
2925
|
+
if (callWalks2-- <= 0) return;
|
|
2926
|
+
const local = clone(state);
|
|
2927
|
+
local.callDepth = (state.callDepth ?? 0) + 1;
|
|
2928
|
+
for (const name of POSITIONAL) local.vars.delete(name);
|
|
2929
|
+
if (!rebindsPositionals(body)) {
|
|
2930
|
+
args.slice(0, 9).forEach((v, i) => local.vars.set(String(i + 1), v === null ? null : [v]));
|
|
2931
|
+
const all = args.every((v) => v !== null) && args.length ? unique(args) : null;
|
|
2932
|
+
local.vars.set("@", all);
|
|
2933
|
+
local.vars.set("*", all);
|
|
2934
|
+
local.vars.set("#", [String(args.length)]);
|
|
2935
|
+
}
|
|
2936
|
+
statement(body, local, depth + 1);
|
|
2937
|
+
}
|
|
2679
2938
|
}
|
|
2680
2939
|
function readsScriptFromStdin(stmt) {
|
|
2681
2940
|
const cmd = stmt?.Cmd;
|
|
@@ -2712,11 +2971,13 @@ function collectShellFileAccesses(command, context = {}) {
|
|
|
2712
2971
|
if (!stmt || depth > MAX_DEPTH) return;
|
|
2713
2972
|
state.onSuccess = void 0;
|
|
2714
2973
|
if (stmt.Background) state = clone(state);
|
|
2974
|
+
const headWord = stmt.Cmd && syntax2.NodeType(stmt.Cmd) === "CallExpr" ? base(resolveWordLiteral(stmt.Cmd.Args?.[0])) : "";
|
|
2975
|
+
const sends = SENDING_STDIN.has(headWord);
|
|
2715
2976
|
for (const redir of stmt.Redirs ?? []) {
|
|
2716
2977
|
substitutions(redir.Word, state, depth);
|
|
2717
2978
|
for (const word of wordValues(redir.Word, state)) {
|
|
2718
2979
|
if (WRITES.has(redir.Op)) add("write", word, state);
|
|
2719
|
-
if (READS.has(redir.Op)) add("read", word, state);
|
|
2980
|
+
if (READS.has(redir.Op)) add("read", word, state, sends ? { upload: true } : {});
|
|
2720
2981
|
if (redir.Op === DUP_OUT && word.value && !/^\d+$/.test(word.value))
|
|
2721
2982
|
add("write", word, state);
|
|
2722
2983
|
}
|
|
@@ -2825,7 +3086,10 @@ function collectShellFileAccesses(command, context = {}) {
|
|
|
2825
3086
|
}
|
|
2826
3087
|
const parsed = parseShared(command);
|
|
2827
3088
|
const program = typeof parsed !== "symbol" ? parsed : runnablePrefix(command);
|
|
2828
|
-
if (program)
|
|
3089
|
+
if (program) {
|
|
3090
|
+
for (const [name, bodies] of functionBodies(program)) functions.set(name, bodies);
|
|
3091
|
+
statements(program.Stmts ?? [], initial, 0);
|
|
3092
|
+
}
|
|
2829
3093
|
return out;
|
|
2830
3094
|
}
|
|
2831
3095
|
function isAgentInstructionPath(absPath, root) {
|
|
@@ -2941,6 +3205,33 @@ function hits(access, paths, directories, context) {
|
|
|
2941
3205
|
}
|
|
2942
3206
|
return false;
|
|
2943
3207
|
}
|
|
3208
|
+
function hitsSystemDir(access, context, dirs = SYSTEM_DIRS) {
|
|
3209
|
+
const home = context.homeDirs?.[0] ?? "~";
|
|
3210
|
+
const candidates = access.candidates ?? [
|
|
3211
|
+
normalizeFilePath(access.path, access.cwd ?? context.cwd, home)
|
|
3212
|
+
];
|
|
3213
|
+
for (const p of candidates) {
|
|
3214
|
+
if (dirs.some((d) => within(p, d)) && !exempt(p)) return true;
|
|
3215
|
+
if (access.recursive && dirs.some((d) => within(d, p))) return true;
|
|
3216
|
+
}
|
|
3217
|
+
if (!access.glob) return false;
|
|
3218
|
+
const pattern = normalizeFilePath(access.glob, access.cwd ?? context.cwd, home);
|
|
3219
|
+
const magic = pattern.search(/[*?[{]/);
|
|
3220
|
+
const literal = magic < 0 ? pattern : pattern.slice(0, pattern.lastIndexOf("/", magic) + 1);
|
|
3221
|
+
if (literal && exempt(literal.replace(/\/$/, "") || "/")) return false;
|
|
3222
|
+
const options = { dot: access.dotGlob === true, nonegate: true };
|
|
3223
|
+
const components = pattern.split("/");
|
|
3224
|
+
for (const root of dirs) {
|
|
3225
|
+
const count = root.split("/").length;
|
|
3226
|
+
if (components.length > count && picomatch(components.slice(0, count).join("/"), options)(root))
|
|
3227
|
+
return true;
|
|
3228
|
+
if (access.recursive && picomatch(pattern, options)(root)) return true;
|
|
3229
|
+
}
|
|
3230
|
+
return false;
|
|
3231
|
+
}
|
|
3232
|
+
function uploadDirs(context) {
|
|
3233
|
+
return context.homeDirs?.[0] === "/root" ? UPLOAD_SYSTEM_DIRS.filter((d) => d !== "/root") : UPLOAD_SYSTEM_DIRS;
|
|
3234
|
+
}
|
|
2944
3235
|
function evaluateProtectedFileAccess(accesses, context, settings) {
|
|
2945
3236
|
const homes = [.../* @__PURE__ */ new Set([...context.homeDirs ?? ["~"], "/root"])];
|
|
2946
3237
|
const startup = homes.flatMap(
|
|
@@ -2962,6 +3253,14 @@ function evaluateProtectedFileAccess(accesses, context, settings) {
|
|
|
2962
3253
|
"files.shell-startup",
|
|
2963
3254
|
"review-shell-startup-mutation",
|
|
2964
3255
|
"Changing shell startup files requires approval"
|
|
3256
|
+
] : mutation && hitsSystemDir(access, context) ? [
|
|
3257
|
+
"files.system-dirs",
|
|
3258
|
+
"review-system-dir-mutation",
|
|
3259
|
+
"Changing files in a system directory requires approval"
|
|
3260
|
+
] : !mutation && access.upload && hitsSystemDir(access, context, uploadDirs(context)) ? [
|
|
3261
|
+
"files.system-dirs",
|
|
3262
|
+
"review-system-file-upload",
|
|
3263
|
+
"Uploading a system file requires approval"
|
|
2965
3264
|
] : !mutation && hits(access, PASSWORD_HASH_FILES, [], context) ? [
|
|
2966
3265
|
"files.password-hashes",
|
|
2967
3266
|
"review-password-hash-read",
|
|
@@ -3222,16 +3521,21 @@ function looksGenerated(value, minLength, minEntropy, lettersOnlyAllowed = false
|
|
|
3222
3521
|
if (EMAIL_VALUE_RE.test(value)) return false;
|
|
3223
3522
|
if (looksLikeJoinedWords(value) || hasSequentialRun(value)) return false;
|
|
3224
3523
|
if (PATH_VALUE_RE.test(value) || DOTTED_REFERENCE_RE.test(value)) return false;
|
|
3524
|
+
if (CONCATENATION_RE.test(value)) return false;
|
|
3225
3525
|
if (rejectedByStructuredValidator(value)) return false;
|
|
3226
3526
|
return shannonEntropy(value) >= minEntropy;
|
|
3227
3527
|
}
|
|
3228
3528
|
function nameHoldsCredential(name) {
|
|
3229
3529
|
return !FIXTURE_NAME_RE.test(name) && !DERIVATIVE_NAME_RE.test(name);
|
|
3230
3530
|
}
|
|
3531
|
+
function isOtpDocSecret(value) {
|
|
3532
|
+
return OTP_DOC_SECRETS.has(value.replace(/=+$/, "").toUpperCase());
|
|
3533
|
+
}
|
|
3231
3534
|
function validateQueryCredential(raw) {
|
|
3232
3535
|
const value = capturedValue(raw, QUERY_CREDENTIAL_VALUE_RE);
|
|
3233
3536
|
if (value === null) return false;
|
|
3234
|
-
if (OTP_SECRET_RE.test(value))
|
|
3537
|
+
if (OTP_SECRET_RE.test(value))
|
|
3538
|
+
return !isOtpDocSecret(value) && looksGenerated(value, 16, 3, true);
|
|
3235
3539
|
if (!/[0-9]/.test(value) || !/[A-Za-z]/.test(value)) return false;
|
|
3236
3540
|
return looksGenerated(value, 12, 3);
|
|
3237
3541
|
}
|
|
@@ -3245,13 +3549,40 @@ function keywordEndsAPart(name) {
|
|
|
3245
3549
|
}
|
|
3246
3550
|
function validateAssignedSecret(raw, before = "") {
|
|
3247
3551
|
const value = capturedValue(raw, ASSIGNED_SECRET_VALUE_RE);
|
|
3248
|
-
const name = capturedValue(raw, ASSIGNED_SECRET_NAME_RE);
|
|
3552
|
+
const name = capturedValue(raw, ASSIGNED_SECRET_NAME_RE)?.replace(/^_+/, "") ?? null;
|
|
3249
3553
|
if (value === null || name === null) return false;
|
|
3250
|
-
if (!keywordEndsAPart(name)
|
|
3251
|
-
|
|
3554
|
+
if (!keywordEndsAPart(name)) return false;
|
|
3555
|
+
if (ARN_CONTEXT_RE.test(before)) return false;
|
|
3556
|
+
const prefix = (before.match(/[A-Za-z0-9_]*$/)?.[0] ?? "") + (raw.startsWith("_") ? "_" : "");
|
|
3252
3557
|
if (!nameHoldsCredential(prefix + name)) return false;
|
|
3253
3558
|
return looksGenerated(value, 12, 3.5);
|
|
3254
3559
|
}
|
|
3560
|
+
function validateBasicCredential(raw) {
|
|
3561
|
+
const b64 = capturedValue(raw, BASIC_VALUE_RE);
|
|
3562
|
+
if (b64 === null) return false;
|
|
3563
|
+
const bare = b64.replace(/=+$/, "");
|
|
3564
|
+
if (bare.length % 4 === 1) return false;
|
|
3565
|
+
const bytes = Buffer.from(b64, "base64");
|
|
3566
|
+
if (bytes.toString("base64").replace(/=+$/, "") !== bare) return false;
|
|
3567
|
+
const text = bytes.toString("utf8");
|
|
3568
|
+
if (!Buffer.from(text, "utf8").equals(bytes)) return false;
|
|
3569
|
+
for (let i = 0; i < text.length; i++) {
|
|
3570
|
+
const c = text.charCodeAt(i);
|
|
3571
|
+
if (c < 32 || c === 127) return false;
|
|
3572
|
+
}
|
|
3573
|
+
const pair = BASIC_PAIR_RE.exec(text);
|
|
3574
|
+
if (!pair) return false;
|
|
3575
|
+
const password = pair[2];
|
|
3576
|
+
if (!/[0-9]/.test(password) || !/[A-Za-z]/.test(password)) return false;
|
|
3577
|
+
const lower2 = password.toLowerCase();
|
|
3578
|
+
if (DLP_STOPWORDS.some((sw) => lower2.includes(sw)) || hasSequentialRun(password)) return false;
|
|
3579
|
+
return shannonEntropy(password) >= 3;
|
|
3580
|
+
}
|
|
3581
|
+
function validateOtpSecret(raw) {
|
|
3582
|
+
const secret = capturedValue(raw, OTP_CAPTURE_RE);
|
|
3583
|
+
if (secret === null || isOtpDocSecret(secret)) return false;
|
|
3584
|
+
return looksGenerated(secret.replace(/=+$/, ""), 16, 3, true);
|
|
3585
|
+
}
|
|
3255
3586
|
function matchSensitivePath2(resolvedPath, originalPath, options = {}) {
|
|
3256
3587
|
const normalised = resolvedPath.replace(/\\/g, "/");
|
|
3257
3588
|
if (!options.systemAccessHandled && isLegacySystemPath(normalised))
|
|
@@ -3522,6 +3853,156 @@ function parsePySpec(raw) {
|
|
|
3522
3853
|
const exact = PY_EXACT_RE.exec(m[2].trim());
|
|
3523
3854
|
return { name, version: exact ? exact[1] : void 0 };
|
|
3524
3855
|
}
|
|
3856
|
+
function exactVersion(v) {
|
|
3857
|
+
if (!v) return void 0;
|
|
3858
|
+
const m = EXACT_VERSION_RE.exec(v.trim());
|
|
3859
|
+
return m ? m[1] : void 0;
|
|
3860
|
+
}
|
|
3861
|
+
function push(out, ecosystem, manager, name, version, raw) {
|
|
3862
|
+
out.push({ ecosystem, manager, name, ...version ? { version } : {}, raw });
|
|
3863
|
+
}
|
|
3864
|
+
function gemInstall(words, from, out) {
|
|
3865
|
+
const dash = words.indexOf("--", from);
|
|
3866
|
+
const own = dash < 0 ? words : words.slice(0, dash);
|
|
3867
|
+
const pinned = exactVersion(flagValues(own, from, ["-v", "--version"])[0]);
|
|
3868
|
+
for (const raw of operands(own, from, GEM_VALUE_FLAGS)) {
|
|
3869
|
+
if (NON_REGISTRY_RE.test(raw) || /\.gem$/i.test(raw)) continue;
|
|
3870
|
+
const colon = raw.indexOf(":");
|
|
3871
|
+
const name = colon < 0 ? raw : raw.slice(0, colon);
|
|
3872
|
+
if (!GEM_NAME_RE.test(name)) continue;
|
|
3873
|
+
push(
|
|
3874
|
+
out,
|
|
3875
|
+
"RubyGems",
|
|
3876
|
+
"gem",
|
|
3877
|
+
name,
|
|
3878
|
+
colon < 0 ? pinned : exactVersion(raw.slice(colon + 1)),
|
|
3879
|
+
raw
|
|
3880
|
+
);
|
|
3881
|
+
}
|
|
3882
|
+
}
|
|
3883
|
+
function cargoCrates(words, from, manager, out) {
|
|
3884
|
+
const flags = manager === "cargo add" ? CARGO_ADD_VALUE_FLAGS : CARGO_INSTALL_VALUE_FLAGS;
|
|
3885
|
+
if (flagValues(words, from, ["--git", "--path", "--registry", "--index"]).length > 0) return;
|
|
3886
|
+
if (words.some((w) => w === "--git" || w === "--path")) return;
|
|
3887
|
+
const flagVersion = manager === "cargo install" ? exactVersion(flagValues(words, from, ["--version", "--vers"])[0]) : void 0;
|
|
3888
|
+
for (const raw of operands(words, from, flags)) {
|
|
3889
|
+
const at = raw.indexOf("@");
|
|
3890
|
+
const name = at < 0 ? raw : raw.slice(0, at);
|
|
3891
|
+
if (!CRATE_NAME_RE.test(name)) continue;
|
|
3892
|
+
push(
|
|
3893
|
+
out,
|
|
3894
|
+
"crates.io",
|
|
3895
|
+
manager,
|
|
3896
|
+
name,
|
|
3897
|
+
at < 0 ? flagVersion : exactVersion(raw.slice(at + 1)),
|
|
3898
|
+
raw
|
|
3899
|
+
);
|
|
3900
|
+
}
|
|
3901
|
+
}
|
|
3902
|
+
function goModules(words, from, manager, out) {
|
|
3903
|
+
let specs = operands(words, from, GO_VALUE_FLAGS);
|
|
3904
|
+
if (manager === "go run") specs = specs.slice(0, 1).filter((s) => s.includes("@"));
|
|
3905
|
+
for (const raw of specs) {
|
|
3906
|
+
const at = raw.indexOf("@");
|
|
3907
|
+
const pathPart = (at < 0 ? raw : raw.slice(0, at)).replace(/\/\.\.\.$/, "");
|
|
3908
|
+
const ver = at < 0 ? void 0 : raw.slice(at + 1);
|
|
3909
|
+
if (ver === "none") continue;
|
|
3910
|
+
if (!GO_PATH_RE.test(pathPart) || /\.go$/i.test(pathPart)) continue;
|
|
3911
|
+
const exact = ver && /^v\d/.test(ver) ? exactVersion(ver) : void 0;
|
|
3912
|
+
push(out, "Go", manager, pathPart, exact, raw);
|
|
3913
|
+
}
|
|
3914
|
+
}
|
|
3915
|
+
function dotnetAdd(words, from, out) {
|
|
3916
|
+
let k = -1;
|
|
3917
|
+
for (let i = from; i < Math.min(words.length, from + 2); i++) {
|
|
3918
|
+
if (lower(words[i]) === "package") {
|
|
3919
|
+
k = i;
|
|
3920
|
+
break;
|
|
3921
|
+
}
|
|
3922
|
+
}
|
|
3923
|
+
if (k < 0) return;
|
|
3924
|
+
const name = operands(words, k + 1, DOTNET_ADD_VALUE_FLAGS)[0];
|
|
3925
|
+
if (!name || !NUGET_NAME_RE.test(name)) return;
|
|
3926
|
+
const version = exactVersion(flagValues(words, k + 1, ["-v", "--version"])[0]);
|
|
3927
|
+
push(out, "NuGet", "dotnet add package", name, version, name);
|
|
3928
|
+
}
|
|
3929
|
+
function dotnetTool(words, from, out) {
|
|
3930
|
+
const name = operands(words, from, DOTNET_TOOL_VALUE_FLAGS)[0];
|
|
3931
|
+
if (!name || !NUGET_NAME_RE.test(name)) return;
|
|
3932
|
+
const version = exactVersion(flagValues(words, from, ["--version"])[0]);
|
|
3933
|
+
push(out, "NuGet", "dotnet tool", name, version, name);
|
|
3934
|
+
}
|
|
3935
|
+
function nugetInstall(words, from, out) {
|
|
3936
|
+
const lowered = words.map((w) => w !== null && w.startsWith("-") ? w.toLowerCase() : w);
|
|
3937
|
+
const name = operands(lowered, from, NUGET_VALUE_FLAGS)[0];
|
|
3938
|
+
if (!name || !NUGET_NAME_RE.test(name) || /\.config$/i.test(name)) return;
|
|
3939
|
+
const version = exactVersion(flagValues(lowered, from, ["-version"])[0]);
|
|
3940
|
+
push(out, "NuGet", "nuget install", name, version, name);
|
|
3941
|
+
}
|
|
3942
|
+
function mavenGet(words, from, out) {
|
|
3943
|
+
const goal = words.slice(from).some(
|
|
3944
|
+
(w) => w !== null && /(?:^|:)dependency:get$|^org\.apache\.maven\.plugins:maven-dependency-plugin:(?:[\w.-]+:)?get$/.test(
|
|
3945
|
+
w
|
|
3946
|
+
)
|
|
3947
|
+
);
|
|
3948
|
+
if (!goal) return;
|
|
3949
|
+
const prop = (key) => {
|
|
3950
|
+
for (let i = from; i < words.length; i++) {
|
|
3951
|
+
const w = words[i];
|
|
3952
|
+
if (w === null) continue;
|
|
3953
|
+
if (w.startsWith(`-D${key}=`)) return w.slice(key.length + 3);
|
|
3954
|
+
if (w === "-D" && words[i + 1]?.startsWith(`${key}=`))
|
|
3955
|
+
return words[i + 1].slice(key.length + 1);
|
|
3956
|
+
}
|
|
3957
|
+
return void 0;
|
|
3958
|
+
};
|
|
3959
|
+
const artifact = prop("artifact");
|
|
3960
|
+
let coord;
|
|
3961
|
+
let ver;
|
|
3962
|
+
if (artifact) {
|
|
3963
|
+
const parts = artifact.split(":");
|
|
3964
|
+
if (parts.length < 2) return;
|
|
3965
|
+
coord = `${parts[0]}:${parts[1]}`;
|
|
3966
|
+
ver = parts[2];
|
|
3967
|
+
} else {
|
|
3968
|
+
const g = prop("groupId");
|
|
3969
|
+
const a = prop("artifactId");
|
|
3970
|
+
if (!g || !a) return;
|
|
3971
|
+
coord = `${g}:${a}`;
|
|
3972
|
+
ver = prop("version");
|
|
3973
|
+
}
|
|
3974
|
+
if (!MAVEN_COORD_RE.test(coord)) return;
|
|
3975
|
+
const exact = ver && !/^(?:LATEST|RELEASE)$/i.test(ver) ? exactVersion(ver) : void 0;
|
|
3976
|
+
push(out, "Maven", "mvn dependency:get", coord, exact, artifact ?? coord);
|
|
3977
|
+
}
|
|
3978
|
+
function composerPackages(words, from, manager, out) {
|
|
3979
|
+
const ops = operands(words, from, COMPOSER_VALUE_FLAGS);
|
|
3980
|
+
const specs = manager === "composer create-project" ? ops.slice(0, 1) : ops;
|
|
3981
|
+
for (const raw of specs) {
|
|
3982
|
+
const sep = raw.search(/[:=]/);
|
|
3983
|
+
const name = sep < 0 ? raw : raw.slice(0, sep);
|
|
3984
|
+
if (!COMPOSER_NAME_RE.test(name)) continue;
|
|
3985
|
+
let ver = sep < 0 ? void 0 : raw.slice(sep + 1);
|
|
3986
|
+
if (manager === "composer create-project" && ver === void 0) ver = ops[2] ?? void 0;
|
|
3987
|
+
push(out, "Packagist", manager, name.toLowerCase(), exactVersion(ver), raw);
|
|
3988
|
+
}
|
|
3989
|
+
}
|
|
3990
|
+
function editorExtensions(words, from, out) {
|
|
3991
|
+
for (const raw of flagValues(words, from, ["--install-extension"])) {
|
|
3992
|
+
if (/\.vsix$/i.test(raw) || NON_REGISTRY_RE.test(raw)) continue;
|
|
3993
|
+
const at = raw.lastIndexOf("@");
|
|
3994
|
+
const name = at <= 0 ? raw : raw.slice(0, at);
|
|
3995
|
+
if (!EXTENSION_ID_RE.test(name)) continue;
|
|
3996
|
+
push(
|
|
3997
|
+
out,
|
|
3998
|
+
"VSCode",
|
|
3999
|
+
"code --install-extension",
|
|
4000
|
+
name,
|
|
4001
|
+
at <= 0 ? void 0 : exactVersion(raw.slice(at + 1)),
|
|
4002
|
+
raw
|
|
4003
|
+
);
|
|
4004
|
+
}
|
|
4005
|
+
}
|
|
3525
4006
|
function basename(w) {
|
|
3526
4007
|
return (w ?? "").toLowerCase().split("/").pop() ?? "";
|
|
3527
4008
|
}
|
|
@@ -3753,7 +4234,63 @@ function fromCall(words, out, depth) {
|
|
|
3753
4234
|
pushPy(out, "pipenv", operands(words, v + 1, PIPENV_VALUE_FLAGS));
|
|
3754
4235
|
return;
|
|
3755
4236
|
}
|
|
4237
|
+
case "gem": {
|
|
4238
|
+
const v = verbIndex(words, start + 1, GEM_VALUE_FLAGS);
|
|
4239
|
+
if (v >= 0 && ["install", "i"].includes(lower(words[v]))) gemInstall(words, v + 1, out);
|
|
4240
|
+
return;
|
|
4241
|
+
}
|
|
4242
|
+
case "bundle": {
|
|
4243
|
+
const v = verbIndex(words, start + 1, BUNDLE_VALUE_FLAGS);
|
|
4244
|
+
if (v < 0 || lower(words[v]) !== "add") return;
|
|
4245
|
+
if (flagValues(words, v + 1, ["--git", "--github", "--path"]).length > 0) return;
|
|
4246
|
+
const version = exactVersion(flagValues(words, v + 1, ["-v", "--version"])[0]);
|
|
4247
|
+
for (const raw of operands(words, v + 1, BUNDLE_VALUE_FLAGS))
|
|
4248
|
+
if (GEM_NAME_RE.test(raw)) push(out, "RubyGems", "bundle add", raw, version, raw);
|
|
4249
|
+
return;
|
|
4250
|
+
}
|
|
4251
|
+
case "cargo": {
|
|
4252
|
+
const from = words[start + 1]?.startsWith("+") ? start + 2 : start + 1;
|
|
4253
|
+
const v = verbIndex(words, from, CARGO_ADD_VALUE_FLAGS);
|
|
4254
|
+
const verb = lower(words[v]);
|
|
4255
|
+
if (verb === "add") cargoCrates(words, v + 1, "cargo add", out);
|
|
4256
|
+
else if (verb === "install") cargoCrates(words, v + 1, "cargo install", out);
|
|
4257
|
+
return;
|
|
4258
|
+
}
|
|
4259
|
+
case "go": {
|
|
4260
|
+
const v = verbIndex(words, start + 1, GO_VALUE_FLAGS);
|
|
4261
|
+
const verb = lower(words[v]);
|
|
4262
|
+
if (verb === "get" || verb === "install" || verb === "run")
|
|
4263
|
+
goModules(words, v + 1, `go ${verb}`, out);
|
|
4264
|
+
return;
|
|
4265
|
+
}
|
|
4266
|
+
case "dotnet": {
|
|
4267
|
+
const v = verbIndex(words, start + 1, DOTNET_ADD_VALUE_FLAGS);
|
|
4268
|
+
const verb = lower(words[v]);
|
|
4269
|
+
if (verb === "add") dotnetAdd(words, v + 1, out);
|
|
4270
|
+
else if (verb === "tool" && ["install", "update"].includes(lower(words[v + 1])))
|
|
4271
|
+
dotnetTool(words, v + 2, out);
|
|
4272
|
+
return;
|
|
4273
|
+
}
|
|
4274
|
+
case "nuget": {
|
|
4275
|
+
if (lower(words[start + 1]) === "install") nugetInstall(words, start + 2, out);
|
|
4276
|
+
return;
|
|
4277
|
+
}
|
|
4278
|
+
case "mvn":
|
|
4279
|
+
case "mvnw":
|
|
4280
|
+
mavenGet(words, start + 1, out);
|
|
4281
|
+
return;
|
|
4282
|
+
case "composer": {
|
|
4283
|
+
let v = verbIndex(words, start + 1, COMPOSER_VALUE_FLAGS);
|
|
4284
|
+
if (v >= 0 && lower(words[v]) === "global") v = verbIndex(words, v + 1, COMPOSER_VALUE_FLAGS);
|
|
4285
|
+
const verb = lower(words[v]);
|
|
4286
|
+
if (verb === "require" || verb === "req" || verb === "r")
|
|
4287
|
+
composerPackages(words, v + 1, "composer require", out);
|
|
4288
|
+
else if (verb === "create-project")
|
|
4289
|
+
composerPackages(words, v + 1, "composer create-project", out);
|
|
4290
|
+
return;
|
|
4291
|
+
}
|
|
3756
4292
|
default:
|
|
4293
|
+
if (EDITOR_HEADS.includes(family)) editorExtensions(words, start + 1, out);
|
|
3757
4294
|
return;
|
|
3758
4295
|
}
|
|
3759
4296
|
}
|
|
@@ -5258,11 +5795,11 @@ function isDecimalFraction(text, index, unseparated) {
|
|
|
5258
5795
|
const a = text[index - 2];
|
|
5259
5796
|
if (sep === ".") {
|
|
5260
5797
|
if (a !== void 0 && a >= "0" && a <= "9") return true;
|
|
5261
|
-
return a === void 0 ||
|
|
5798
|
+
return a === void 0 || (a === "-" || a === "+") && index === 2;
|
|
5262
5799
|
}
|
|
5263
5800
|
if (sep === "," && a === "0") {
|
|
5264
5801
|
const b = text[index - 3];
|
|
5265
|
-
return b === void 0 ||
|
|
5802
|
+
return b === void 0 || b === ";";
|
|
5266
5803
|
}
|
|
5267
5804
|
return false;
|
|
5268
5805
|
}
|
|
@@ -5936,7 +6473,7 @@ function matchCanaryArgs(args, values) {
|
|
|
5936
6473
|
return null;
|
|
5937
6474
|
}
|
|
5938
6475
|
}
|
|
5939
|
-
var aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, BUILTIN_SHIELDS, SSRF_MAX_HOST, METADATA_ADDRESSES, STRICT_TIERS, METADATA_HOSTNAMES, v4Octets, TIER_REASON, CHECK_TEXT, 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, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, AST_CACHE_MAX, astCache, PARSE_FAIL, 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, 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, NET_BINARIES, VALUE_FLAGS, HOME_VARIABLES, assignmentTable, currentStmtOffset, PAYLOAD_BUDGET, payloadBudget, seenPayloads, UNANALYSABLE_NESTING, ASSIGNMENT_HEADS, AND_OP, OR_OP, AND_OR_OPS, 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, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, FORBIDDEN_PATH_SEGMENTS, BASH_TOOL_NAMES, MCP_EXECUTORS, BUILTIN_FIELDS, ARGV_FIELDS, BARE_WORD, MAX_ARGV_CHARACTERS, syntax2, MAX_ALTERNATIVES, MAX_DEPTH, MAX_PAYLOAD_DEPTH, unique, clone, base, quoteGlob, asWord, WRITES, READS, DUP_OUT, HERE_STRING, AND, OR, DEFAULT_OPS, SYSTEM_AUTH_FILES, SYSTEM_AUTH_DIRS, PASSWORD_HASH_FILES, STARTUP_NAMES, AGENT_INSTRUCTION_FILES, AGENT_INSTRUCTION_DIRS, LEGACY_SYSTEM_PATH_RE, RANK, BIP39_ENGLISH, BIP39_PUBLIC_VECTORS, indices, lengths, hash, MAX_MNEMONIC_HASHES, IBAN_LENGTH, B58, B58_INDEX, XPRV_VERSIONS, B62, CRC32_TABLE, GITHUB_TOKEN_RE, B64URL, B64URL_INDEX, CASK_TWO_ZERO_SUFFIX, CASK_FOUR_ZERO_SUFFIX, INVISIBLE_RE, INVISIBLE_ONE, HOMOGLYPHS, HOMOGLYPH_RE, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, REFERENCE_VALUE_RE, IDENTIFIER_VALUE_RE, PATH_VALUE_RE, DOTTED_REFERENCE_RE, EMAIL_VALUE_RE, FIXTURE_NAME_RE, DERIVATIVE_NAME_RE, QUERY_CREDENTIAL_VALUE_RE, OTP_SECRET_RE, ASSIGNED_SECRET_VALUE_RE, ASSIGNED_SECRET_NAME_RE, NAME_KEYWORD_RE, ARN_CONTEXT_RE, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, SENSITIVE_PATH_REGEXES, MAX_DEPTH2, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, BATCH_SEPARATOR, syntax3, 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, MANAGER_HEADS, SHELLS, MAX_NESTING, SHELL_VALUE_FLAGS, 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, SECRET_VIEW_LIMITS, 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_IBAN_RE,
|
|
6476
|
+
var aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, BUILTIN_SHIELDS, SSRF_MAX_HOST, METADATA_ADDRESSES, STRICT_TIERS, METADATA_HOSTNAMES, v4Octets, TIER_REASON, CHECK_TEXT, 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, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, AST_CACHE_MAX, astCache, PARSE_FAIL, 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, 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, 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, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, FORBIDDEN_PATH_SEGMENTS, BASH_TOOL_NAMES, MCP_EXECUTORS, BUILTIN_FIELDS, ARGV_FIELDS, BARE_WORD, MAX_ARGV_CHARACTERS, syntax2, 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, READS, DUP_OUT, HERE_STRING, AND, OR, DEFAULT_OPS, 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, exempt, RANK, BIP39_ENGLISH, BIP39_PUBLIC_VECTORS, indices, lengths, hash, MAX_MNEMONIC_HASHES, IBAN_LENGTH, B58, B58_INDEX, XPRV_VERSIONS, B62, CRC32_TABLE, GITHUB_TOKEN_RE, B64URL, B64URL_INDEX, CASK_TWO_ZERO_SUFFIX, CASK_FOUR_ZERO_SUFFIX, INVISIBLE_RE, INVISIBLE_ONE, HOMOGLYPHS, HOMOGLYPH_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, BASIC_VALUE_RE, BASIC_PAIR_RE, OTP_CAPTURE_RE, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, SENSITIVE_PATH_REGEXES, MAX_DEPTH2, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, BATCH_SEPARATOR, syntax3, 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, SHELLS, MAX_NESTING, SHELL_VALUE_FLAGS, 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, SECRET_VIEW_LIMITS, 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_IBAN_RE, REALTIME_PII_PATTERNS, MAX_PII_SCAN_BYTES, MASKED_KINDS, ENTRY_RE, IBAN_TEXT_RE, EMAIL_TEXT_RE, ACCOUNT_KEY_RE, ACCOUNT_VALUE_RE, MAX_MASK_DEPTH, syntax4, 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, SHELLS2, 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;
|
|
5940
6477
|
var init_dist = __esm({
|
|
5941
6478
|
"packages/policy-engine/dist/index.mjs"() {
|
|
5942
6479
|
"use strict";
|
|
@@ -6737,6 +7274,11 @@ var init_dist = __esm({
|
|
|
6737
7274
|
example: "cat /etc/shadow",
|
|
6738
7275
|
advice: "Keep Review, or choose Block when agents must never read system password hashes."
|
|
6739
7276
|
},
|
|
7277
|
+
"files.system-dirs": {
|
|
7278
|
+
plain: "The agent writes, creates or deletes a file in a system directory, or sends a system file in a request. /usr/local and a container working directory under /usr/src are ordinary project space and are not included.",
|
|
7279
|
+
example: "wget -O /usr/bin/tool https://example.com/tool",
|
|
7280
|
+
advice: "Keep Review on a workstation. A container image build that installs into /usr can set it to Log."
|
|
7281
|
+
},
|
|
6740
7282
|
// ── Commands ──────────────────────────────────────────────────────────────
|
|
6741
7283
|
"commands.inline-exec": {
|
|
6742
7284
|
plain: "The agent runs a small program written inside the command itself.",
|
|
@@ -6933,7 +7475,7 @@ var init_dist = __esm({
|
|
|
6933
7475
|
advice: "Fixed at Block. A person accepts the change on the machine (node9 mcp pin update)."
|
|
6934
7476
|
},
|
|
6935
7477
|
"loading.malicious-package": {
|
|
6936
|
-
plain: "The agent installs a package that the public malicious-package database (OSV) lists, or one published minutes ago.",
|
|
7478
|
+
plain: "The agent installs a package that the public malicious-package database (OSV) lists, or one published minutes ago. Covers npm, PyPI, RubyGems, crates.io, Go, NuGet, Maven, Packagist and VS Code extensions; the publish-age and install-script signals are npm and PyPI only.",
|
|
6937
7479
|
example: "npm install of a package flagged as malware",
|
|
6938
7480
|
advice: "Keep Block for known-malicious packages; brand-new packages already stop for Review."
|
|
6939
7481
|
}
|
|
@@ -6971,6 +7513,15 @@ var init_dist = __esm({
|
|
|
6971
7513
|
defaultValue: "review",
|
|
6972
7514
|
legacy: ["review-password-hash-read"]
|
|
6973
7515
|
},
|
|
7516
|
+
{
|
|
7517
|
+
id: "files.system-dirs",
|
|
7518
|
+
group: "files",
|
|
7519
|
+
title: "System directories",
|
|
7520
|
+
catches: "Writes, new files and removals under /usr, /etc, /boot, /bin, /sbin, /lib and /var/log, and uploads of system files",
|
|
7521
|
+
values: ALL,
|
|
7522
|
+
defaultValue: "review",
|
|
7523
|
+
legacy: ["review-system-dir-mutation", "review-system-file-upload"]
|
|
7524
|
+
},
|
|
6974
7525
|
// ── Commands ────────────────────────────────────────────────────────────
|
|
6975
7526
|
{
|
|
6976
7527
|
id: "commands.inline-exec",
|
|
@@ -8268,6 +8819,10 @@ var init_dist = __esm({
|
|
|
8268
8819
|
AND_OP = probeBinOp("a && b");
|
|
8269
8820
|
OR_OP = probeBinOp("a || b");
|
|
8270
8821
|
AND_OR_OPS = new Set([AND_OP, OR_OP].filter((o) => o !== null));
|
|
8822
|
+
MAX_CALL_DEPTH = 4;
|
|
8823
|
+
MAX_CALL_WALKS = 256;
|
|
8824
|
+
callDepth = 0;
|
|
8825
|
+
callWalks = 0;
|
|
8271
8826
|
FS_OP_CACHE_MAX = 5e3;
|
|
8272
8827
|
fsOpCache = /* @__PURE__ */ new Map();
|
|
8273
8828
|
stripDotSlash = (p) => p.replace(/^\.\//, "");
|
|
@@ -8320,10 +8875,79 @@ var init_dist = __esm({
|
|
|
8320
8875
|
MAX_ARGV_CHARACTERS = 262144;
|
|
8321
8876
|
({ syntax: syntax2 } = mvdanSh2);
|
|
8322
8877
|
MAX_ALTERNATIVES = 64;
|
|
8878
|
+
MAX_CALL_DEPTH2 = 4;
|
|
8879
|
+
MAX_CALL_WALKS2 = 256;
|
|
8880
|
+
POSITIONAL = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "@", "*", "#"];
|
|
8323
8881
|
MAX_DEPTH = 64;
|
|
8324
8882
|
MAX_PAYLOAD_DEPTH = 24;
|
|
8325
8883
|
unique = (a) => [...new Set(a)].slice(0, MAX_ALTERNATIVES);
|
|
8326
|
-
clone = (s) => ({
|
|
8884
|
+
clone = (s) => ({
|
|
8885
|
+
cwd: [...s.cwd],
|
|
8886
|
+
vars: new Map(s.vars),
|
|
8887
|
+
...s.callDepth ? { callDepth: s.callDepth } : {}
|
|
8888
|
+
});
|
|
8889
|
+
SENDING_STDIN = /* @__PURE__ */ new Set(["nc", "ncat", "netcat", "socat", "telnet", "curl", "wget"]);
|
|
8890
|
+
DOWNLOAD_SHORT_VALUE = {
|
|
8891
|
+
curl: "odDeEFHKmPrtTuUwxXyYzAbcCQ",
|
|
8892
|
+
wget: "OoaPiBetTwQlARDXIUY",
|
|
8893
|
+
aria2c: "odisx"
|
|
8894
|
+
};
|
|
8895
|
+
DOWNLOAD_LONG_VALUE = {
|
|
8896
|
+
curl: [
|
|
8897
|
+
"--output",
|
|
8898
|
+
"--output-dir",
|
|
8899
|
+
"--dump-header",
|
|
8900
|
+
"--cookie-jar",
|
|
8901
|
+
"--data",
|
|
8902
|
+
"--data-binary",
|
|
8903
|
+
"--data-raw",
|
|
8904
|
+
"--data-ascii",
|
|
8905
|
+
"--data-urlencode",
|
|
8906
|
+
"--form",
|
|
8907
|
+
"--header",
|
|
8908
|
+
"--request",
|
|
8909
|
+
"--user",
|
|
8910
|
+
"--user-agent",
|
|
8911
|
+
"--referer",
|
|
8912
|
+
"--cookie",
|
|
8913
|
+
"--config",
|
|
8914
|
+
"--json",
|
|
8915
|
+
"--upload-file",
|
|
8916
|
+
"--max-time",
|
|
8917
|
+
"--proxy",
|
|
8918
|
+
"--write-out",
|
|
8919
|
+
"--url"
|
|
8920
|
+
],
|
|
8921
|
+
wget: [
|
|
8922
|
+
"--output-document",
|
|
8923
|
+
"--output-file",
|
|
8924
|
+
"--append-output",
|
|
8925
|
+
"--directory-prefix",
|
|
8926
|
+
"--input-file",
|
|
8927
|
+
"--post-data",
|
|
8928
|
+
"--post-file",
|
|
8929
|
+
"--body-data",
|
|
8930
|
+
"--body-file",
|
|
8931
|
+
"--method",
|
|
8932
|
+
"--header",
|
|
8933
|
+
"--user-agent",
|
|
8934
|
+
"--user",
|
|
8935
|
+
"--password",
|
|
8936
|
+
"--tries",
|
|
8937
|
+
"--timeout"
|
|
8938
|
+
],
|
|
8939
|
+
aria2c: ["--out", "--dir", "--input-file"]
|
|
8940
|
+
};
|
|
8941
|
+
DOWNLOAD_OUTPUT_FLAGS = {
|
|
8942
|
+
curl: ["-o", "--output", "-D", "--dump-header", "-c", "--cookie-jar"],
|
|
8943
|
+
wget: ["-O", "--output-document", "-o", "--output-file", "-a", "--append-output"],
|
|
8944
|
+
aria2c: ["-o", "--out"]
|
|
8945
|
+
};
|
|
8946
|
+
DOWNLOAD_DIR_FLAGS = {
|
|
8947
|
+
curl: ["--output-dir"],
|
|
8948
|
+
wget: ["-P", "--directory-prefix"],
|
|
8949
|
+
aria2c: ["-d", "--dir"]
|
|
8950
|
+
};
|
|
8327
8951
|
base = (s) => (s ?? "").replace(/\/+$/, "").split("/").pop() ?? "";
|
|
8328
8952
|
quoteGlob = (s) => s.replace(/[\\*?\[\]{}()!+@]/g, "\\$&");
|
|
8329
8953
|
asWord = (p) => ({ value: p.value, ...p.glob ? { pattern: p.pattern } : {} });
|
|
@@ -8348,6 +8972,18 @@ var init_dist = __esm({
|
|
|
8348
8972
|
SYSTEM_AUTH_DIRS = ["/etc/security", "/etc/pam.d", "/etc/sudoers.d"];
|
|
8349
8973
|
PASSWORD_HASH_FILES = ["/etc/shadow", "/etc/gshadow"];
|
|
8350
8974
|
STARTUP_NAMES = [".bashrc", ".zshrc", ".profile", ".bash_profile"];
|
|
8975
|
+
SYSTEM_DIRS = [
|
|
8976
|
+
"/usr",
|
|
8977
|
+
"/etc",
|
|
8978
|
+
"/boot",
|
|
8979
|
+
"/bin",
|
|
8980
|
+
"/sbin",
|
|
8981
|
+
"/lib",
|
|
8982
|
+
"/lib64",
|
|
8983
|
+
"/var/log"
|
|
8984
|
+
];
|
|
8985
|
+
SYSTEM_DIRS_EXEMPT = ["/usr/local", "/usr/src"];
|
|
8986
|
+
UPLOAD_SYSTEM_DIRS = [...SYSTEM_DIRS, "/proc", "/sys", "/root"];
|
|
8351
8987
|
AGENT_INSTRUCTION_FILES = [
|
|
8352
8988
|
".gitlab-ci.yml",
|
|
8353
8989
|
"CLAUDE.md",
|
|
@@ -8365,6 +9001,7 @@ var init_dist = __esm({
|
|
|
8365
9001
|
".husky"
|
|
8366
9002
|
];
|
|
8367
9003
|
LEGACY_SYSTEM_PATH_RE = /^(?:[a-zA-Z]:)?\/etc\/(?:passwd|shadow|sudoers)$/;
|
|
9004
|
+
exempt = (p) => SYSTEM_DIRS_EXEMPT.some((d) => within(p, d));
|
|
8368
9005
|
RANK = { allow: 0, review: 1, block: 2 };
|
|
8369
9006
|
BIP39_ENGLISH = `abandon
|
|
8370
9007
|
ability
|
|
@@ -10650,15 +11287,20 @@ zoo`.split("\n");
|
|
|
10650
11287
|
IDENTIFIER_VALUE_RE = /^[A-Za-z_.-]+$/;
|
|
10651
11288
|
PATH_VALUE_RE = /^(?:\/|\.{1,2}\/|~\/)/;
|
|
10652
11289
|
DOTTED_REFERENCE_RE = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)+$/;
|
|
11290
|
+
CONCATENATION_RE = /^\+[A-Za-z_$][\w$]*\./;
|
|
10653
11291
|
EMAIL_VALUE_RE = /^[^@\s]+@[^@\s]+\.[A-Za-z]{2,}$/;
|
|
10654
11292
|
FIXTURE_NAME_RE = /(?:fake|dummy|example|sample|mock|stub|placeholder|test)/i;
|
|
10655
|
-
DERIVATIVE_NAME_RE = /(?:hash|digest|id|type|name|url|uri|path|file|len|length|count|size|ttl|expires?|expiry|age|date|time|format|field|header|param)$/i;
|
|
11293
|
+
DERIVATIVE_NAME_RE = /(?:hash|digest|fingerprint|thumbprint|checksum|id|type|name|url|uri|path|file|len|length|count|size|ttl|expires?|expiry|age|date|time|format|field|header|param)$/i;
|
|
10656
11294
|
QUERY_CREDENTIAL_VALUE_RE = /=([A-Za-z0-9%+/_.~-]{8,512}={0,2})$/;
|
|
10657
11295
|
OTP_SECRET_RE = /^[A-Z2-7]{16,}=*$/i;
|
|
10658
|
-
|
|
11296
|
+
OTP_DOC_SECRETS = /* @__PURE__ */ new Set(["JBSWY3DPEHPK3PXP", "GEZDGNBVGY3TQOJQ"]);
|
|
11297
|
+
ASSIGNED_SECRET_VALUE_RE = /(?::=|=>|[:=])[ \t]{0,8}\\?["'`]?([A-Za-z0-9+/_.!@#$%^*~-]{12,512}={0,2})$/;
|
|
10659
11298
|
ASSIGNED_SECRET_NAME_RE = /^([A-Za-z0-9_]+)/;
|
|
10660
|
-
NAME_KEYWORD_RE = /^(?:password|passphrase|passwd|pwd|secret|token|api[_-]?key|auth[_-]?key|private[_-]?key|access[_-]?key|client[_-]?secret)/i;
|
|
11299
|
+
NAME_KEYWORD_RE = /^(?:password|passphrase|passwd|pwd|secret|token|api[_-]?key|auth[_-]?key|private[_-]?key|access[_-]?key|client[_-]?secret|consumer[_-]?key|app[_-]?key|signing[_-]?key|encryption[_-]?key|master[_-]?key|jsapi[_-]?ticket|pass)/i;
|
|
10661
11300
|
ARN_CONTEXT_RE = /\barn:[A-Za-z0-9-]*:[^\s"'`]*$/i;
|
|
11301
|
+
BASIC_VALUE_RE = /([A-Za-z0-9+/]{8,512}={0,2})$/;
|
|
11302
|
+
BASIC_PAIR_RE = /^([^:\s]{1,128}):(\S{8,256})$/;
|
|
11303
|
+
OTP_CAPTURE_RE = /["']([A-Z2-7]{16,128}=*)["']$/i;
|
|
10662
11304
|
DLP_PATTERNS = [
|
|
10663
11305
|
// ── AWS ───────────────────────────────────────────────────────────────────
|
|
10664
11306
|
{
|
|
@@ -10996,6 +11638,22 @@ zoo`.split("\n");
|
|
|
10996
11638
|
contextBoost: true,
|
|
10997
11639
|
minEntropy: 3
|
|
10998
11640
|
},
|
|
11641
|
+
// ── HTTP Basic credential ─────────────────────────────────────────────────
|
|
11642
|
+
// `Authorization: Basic <base64 of user:password>`, or the bare scheme in a
|
|
11643
|
+
// config list. The validator decodes the value and accepts only a
|
|
11644
|
+
// `user:password` whose password looks generated (letters and digits, 8+
|
|
11645
|
+
// characters, the query-credential rule), so prose ("Basic authentication")
|
|
11646
|
+
// and documentation pairs (`admin:changeme`, `user:pass1234`) stay allowed.
|
|
11647
|
+
// review: the same weight as a bare Bearer token.
|
|
11648
|
+
{
|
|
11649
|
+
name: "HTTP Basic Credential",
|
|
11650
|
+
regex: /\bBasic[ \t]{1,8}([A-Za-z0-9+/]{8,512}={0,2})/i,
|
|
11651
|
+
severity: "review",
|
|
11652
|
+
keywords: ["basic"],
|
|
11653
|
+
validate: validateBasicCredential,
|
|
11654
|
+
redactGroup: 1,
|
|
11655
|
+
argumentsOnly: true
|
|
11656
|
+
},
|
|
10999
11657
|
// ── Resend ────────────────────────────────────────────────────────────────
|
|
11000
11658
|
{
|
|
11001
11659
|
name: "Resend API Key",
|
|
@@ -11137,6 +11795,19 @@ zoo`.split("\n");
|
|
|
11137
11795
|
validate: validateQueryCredential,
|
|
11138
11796
|
argumentsOnly: true
|
|
11139
11797
|
},
|
|
11798
|
+
// ── OTP secret in a constructor ───────────────────────────────────────────
|
|
11799
|
+
// `ROTP::TOTP.new('<base32>')`, `pyotp.TOTP("<base32>")`, `new HOTP('...')`:
|
|
11800
|
+
// the shared secret of a one-time-password generator written as a literal.
|
|
11801
|
+
// The otpauth URI form is `Credential in URL Query`. review.
|
|
11802
|
+
{
|
|
11803
|
+
name: "OTP Secret",
|
|
11804
|
+
regex: /\b(?:TOTP|HOTP|OTP)(?:\.new|\.create)?[ \t]{0,8}\(?[ \t]{0,8}["']([A-Z2-7]{16,128}=*)["']/i,
|
|
11805
|
+
severity: "review",
|
|
11806
|
+
keywords: ["otp"],
|
|
11807
|
+
validate: validateOtpSecret,
|
|
11808
|
+
redactGroup: 1,
|
|
11809
|
+
argumentsOnly: true
|
|
11810
|
+
},
|
|
11140
11811
|
// ── Secret assigned to a telling name ─────────────────────────────────────
|
|
11141
11812
|
// `password: ...`, `JWT_SECRET=...`, `api_key = "..."`. The name alone is
|
|
11142
11813
|
// not a finding: most such lines hold a reference (`process.env.X`), a
|
|
@@ -11144,12 +11815,14 @@ zoo`.split("\n");
|
|
|
11144
11815
|
// generated: 12+ characters, not an identifier, Shannon entropy of 3.5
|
|
11145
11816
|
// bits per character or more. review, never block (node9Firewall#348,
|
|
11146
11817
|
// item 4). Tuned on CredData's public split; its look-alike lines are the
|
|
11147
|
-
// negative control.
|
|
11818
|
+
// negative control. A quote may be escaped (`\"access_token\":\"...\"`):
|
|
11819
|
+
// JSON written inside a string literal. The two optional quotes after the
|
|
11820
|
+
// name are written out, not as a counted group, to keep star height 1.
|
|
11148
11821
|
{
|
|
11149
11822
|
name: "Assigned Secret",
|
|
11150
|
-
regex: /(?:password|passphrase|passwd|pwd|secret|token|api[_-]?key|auth[_-]?key|private[_-]?key|access[_-]?key|client[_-]?secret)[A-Za-z0-9_]{0,40}["'\]]
|
|
11823
|
+
regex: /(?:password|passphrase|passwd|pwd|secret|token|api[_-]?key|auth[_-]?key|private[_-]?key|access[_-]?key|client[_-]?secret|consumer[_-]?key|app[_-]?key|signing[_-]?key|encryption[_-]?key|master[_-]?key|jsapi[_-]?ticket|\bpass(?![A-Za-z0-9])|_pass(?![A-Za-z0-9]))[A-Za-z0-9_]{0,40}\\?["'\]]?\\?["'\]]?[ \t]{0,8}(?::=|=>|[:=])[ \t]{0,8}\\?["'`]?([A-Za-z0-9+/_.!@#$%^*~-]{12,512}={0,2})/i,
|
|
11151
11824
|
severity: "review",
|
|
11152
|
-
keywords: ["
|
|
11825
|
+
keywords: ["pass", "pwd", "secret", "token", "key", "ticket"],
|
|
11153
11826
|
validate: validateAssignedSecret,
|
|
11154
11827
|
argumentsOnly: true
|
|
11155
11828
|
}
|
|
@@ -11410,7 +12083,169 @@ zoo`.split("\n");
|
|
|
11410
12083
|
]);
|
|
11411
12084
|
PY_SPEC_RE = /^([A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?)(?:\[[^\]]*\])?\s*(.*)$/;
|
|
11412
12085
|
PY_EXACT_RE = /^===?\s*v?(\d+(?:\.\d+)*(?:[a-zA-Z0-9.+!-]*))$/;
|
|
12086
|
+
EXACT_VERSION_RE = /^=?v?(\d+(?:\.\d+)*(?:[-+][0-9A-Za-z][0-9A-Za-z.+-]*)?)$/;
|
|
12087
|
+
GEM_NAME_RE = /^[\p{L}\p{N}][\p{L}\p{N}_.-]*$/u;
|
|
12088
|
+
GEM_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
12089
|
+
"-v",
|
|
12090
|
+
"--version",
|
|
12091
|
+
"-i",
|
|
12092
|
+
"--install-dir",
|
|
12093
|
+
"-n",
|
|
12094
|
+
"--bindir",
|
|
12095
|
+
"--document",
|
|
12096
|
+
"--build-root",
|
|
12097
|
+
"-P",
|
|
12098
|
+
"--trust-policy",
|
|
12099
|
+
"-s",
|
|
12100
|
+
"--source",
|
|
12101
|
+
"-p",
|
|
12102
|
+
"--http-proxy",
|
|
12103
|
+
"--platform",
|
|
12104
|
+
"-g",
|
|
12105
|
+
"--file",
|
|
12106
|
+
"--without"
|
|
12107
|
+
]);
|
|
12108
|
+
BUNDLE_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
12109
|
+
"-v",
|
|
12110
|
+
"--version",
|
|
12111
|
+
"-g",
|
|
12112
|
+
"--group",
|
|
12113
|
+
"-s",
|
|
12114
|
+
"--source",
|
|
12115
|
+
"-r",
|
|
12116
|
+
"--require",
|
|
12117
|
+
"--path",
|
|
12118
|
+
"--git",
|
|
12119
|
+
"--github",
|
|
12120
|
+
"--branch",
|
|
12121
|
+
"--ref",
|
|
12122
|
+
"--glob"
|
|
12123
|
+
]);
|
|
12124
|
+
CRATE_NAME_RE = /^[\p{L}\p{N}][\p{L}\p{N}_-]*$/u;
|
|
12125
|
+
CARGO_ADD_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
12126
|
+
"-F",
|
|
12127
|
+
"--features",
|
|
12128
|
+
"--rename",
|
|
12129
|
+
"--registry",
|
|
12130
|
+
"--manifest-path",
|
|
12131
|
+
"-p",
|
|
12132
|
+
"--package",
|
|
12133
|
+
"--git",
|
|
12134
|
+
"--branch",
|
|
12135
|
+
"--tag",
|
|
12136
|
+
"--rev",
|
|
12137
|
+
"--path",
|
|
12138
|
+
"--base",
|
|
12139
|
+
"--target",
|
|
12140
|
+
"--color",
|
|
12141
|
+
"--config",
|
|
12142
|
+
"-Z",
|
|
12143
|
+
"--lockfile-path"
|
|
12144
|
+
]);
|
|
12145
|
+
CARGO_INSTALL_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
12146
|
+
"--version",
|
|
12147
|
+
"--vers",
|
|
12148
|
+
"--git",
|
|
12149
|
+
"--branch",
|
|
12150
|
+
"--tag",
|
|
12151
|
+
"--rev",
|
|
12152
|
+
"--path",
|
|
12153
|
+
"--root",
|
|
12154
|
+
"--index",
|
|
12155
|
+
"--registry",
|
|
12156
|
+
"-F",
|
|
12157
|
+
"--features",
|
|
12158
|
+
"--profile",
|
|
12159
|
+
"--target",
|
|
12160
|
+
"--target-dir",
|
|
12161
|
+
"-j",
|
|
12162
|
+
"--jobs",
|
|
12163
|
+
"--bin",
|
|
12164
|
+
"--example",
|
|
12165
|
+
"--color",
|
|
12166
|
+
"--config",
|
|
12167
|
+
"-Z"
|
|
12168
|
+
]);
|
|
12169
|
+
GO_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
12170
|
+
"-C",
|
|
12171
|
+
"-tags",
|
|
12172
|
+
"-ldflags",
|
|
12173
|
+
"-gcflags",
|
|
12174
|
+
"-asmflags",
|
|
12175
|
+
"-mod",
|
|
12176
|
+
"-modfile",
|
|
12177
|
+
"-o",
|
|
12178
|
+
"-p",
|
|
12179
|
+
"-pkgdir",
|
|
12180
|
+
"-toolexec",
|
|
12181
|
+
"-exec",
|
|
12182
|
+
"-overlay",
|
|
12183
|
+
"-pgo",
|
|
12184
|
+
"-buildmode",
|
|
12185
|
+
"-compiler",
|
|
12186
|
+
"-gccgoflags",
|
|
12187
|
+
"-installsuffix",
|
|
12188
|
+
"-covermode",
|
|
12189
|
+
"-coverpkg"
|
|
12190
|
+
]);
|
|
12191
|
+
GO_PATH_RE = /^[\p{L}\p{N}-]+(?:\.[\p{L}\p{N}-]+)+(?:\/[\p{L}\p{N}_.~+-]+)*$/u;
|
|
12192
|
+
NUGET_NAME_RE = /^[\p{L}\p{N}_][\p{L}\p{N}_.-]*$/u;
|
|
12193
|
+
DOTNET_ADD_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
12194
|
+
"-v",
|
|
12195
|
+
"--version",
|
|
12196
|
+
"-s",
|
|
12197
|
+
"--source",
|
|
12198
|
+
"-f",
|
|
12199
|
+
"--framework",
|
|
12200
|
+
"--package-directory"
|
|
12201
|
+
]);
|
|
12202
|
+
DOTNET_TOOL_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
12203
|
+
"--version",
|
|
12204
|
+
"--add-source",
|
|
12205
|
+
"--configfile",
|
|
12206
|
+
"--framework",
|
|
12207
|
+
"--tool-path",
|
|
12208
|
+
"-a",
|
|
12209
|
+
"--arch",
|
|
12210
|
+
"--tool-manifest",
|
|
12211
|
+
"-v",
|
|
12212
|
+
"--verbosity"
|
|
12213
|
+
]);
|
|
12214
|
+
NUGET_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
12215
|
+
"-version",
|
|
12216
|
+
"-outputdirectory",
|
|
12217
|
+
"-o",
|
|
12218
|
+
"-source",
|
|
12219
|
+
"-configfile",
|
|
12220
|
+
"-fallbacksource",
|
|
12221
|
+
"-framework",
|
|
12222
|
+
"-packagesavemode",
|
|
12223
|
+
"-dependencyversion",
|
|
12224
|
+
"-solutiondirectory",
|
|
12225
|
+
"-verbosity"
|
|
12226
|
+
]);
|
|
12227
|
+
MAVEN_COORD_RE = /^[\p{L}\p{N}_.-]+:[\p{L}\p{N}_.-]+$/u;
|
|
12228
|
+
COMPOSER_NAME_RE = /^[\p{L}\p{N}][\p{L}\p{N}_.-]*\/[\p{L}\p{N}][\p{L}\p{N}_.-]*$/u;
|
|
12229
|
+
COMPOSER_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
12230
|
+
"-d",
|
|
12231
|
+
"--working-dir",
|
|
12232
|
+
"--stability",
|
|
12233
|
+
"--repository",
|
|
12234
|
+
"--add-repository"
|
|
12235
|
+
]);
|
|
12236
|
+
EDITOR_HEADS = ["code", "code-insiders", "codium", "vscodium", "cursor"];
|
|
12237
|
+
EXTENSION_ID_RE = /^[\p{L}\p{N}][\p{L}\p{N}_-]*\.[\p{L}\p{N}][\p{L}\p{N}_.-]*$/u;
|
|
11413
12238
|
MANAGER_HEADS = /* @__PURE__ */ new Set([
|
|
12239
|
+
"gem",
|
|
12240
|
+
"bundle",
|
|
12241
|
+
"cargo",
|
|
12242
|
+
"go",
|
|
12243
|
+
"dotnet",
|
|
12244
|
+
"nuget",
|
|
12245
|
+
"mvn",
|
|
12246
|
+
"mvnw",
|
|
12247
|
+
"composer",
|
|
12248
|
+
...EDITOR_HEADS,
|
|
11414
12249
|
"npm",
|
|
11415
12250
|
"npx",
|
|
11416
12251
|
"pnpm",
|
|
@@ -11489,6 +12324,7 @@ zoo`.split("\n");
|
|
|
11489
12324
|
MAP_GOVERNED = /* @__PURE__ */ new Set([
|
|
11490
12325
|
"files.shell-startup",
|
|
11491
12326
|
"files.password-hashes",
|
|
12327
|
+
"files.system-dirs",
|
|
11492
12328
|
"commands.inline-exec",
|
|
11493
12329
|
"commands.eval-dynamic",
|
|
11494
12330
|
"commands.curl-pipe-shell",
|
|
@@ -11757,7 +12593,6 @@ zoo`.split("\n");
|
|
|
11757
12593
|
PII_CC16_RE = /\b(?:4\d{3}|5[1-5]\d{2}|6\d{3})[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}\b/;
|
|
11758
12594
|
PII_CC15_RE = /\b3[47]\d{2}[-\s]?\d{6}[-\s]?\d{5}\b/;
|
|
11759
12595
|
PII_IBAN_RE = /\b[A-Z]{2}\d{2}(?:[A-Z0-9]|[ -][A-Z0-9]){11,30}\b/;
|
|
11760
|
-
NUMBER_LEAD_RE = /[\s:=;([{"'+-]/;
|
|
11761
12596
|
REALTIME_PII_PATTERNS = ["SSN", "Credit Card", "IBAN"];
|
|
11762
12597
|
MAX_PII_SCAN_BYTES = 1e5;
|
|
11763
12598
|
MASKED_KINDS = /* @__PURE__ */ new Set(["email", "iban", "account"]);
|
|
@@ -15878,7 +16713,17 @@ function ecoDir(eco) {
|
|
|
15878
16713
|
return path23.join(osvRoot(), eco);
|
|
15879
16714
|
}
|
|
15880
16715
|
function indexKey(eco, name) {
|
|
15881
|
-
|
|
16716
|
+
switch (eco) {
|
|
16717
|
+
case "PyPI":
|
|
16718
|
+
return normalizePyPiName(name);
|
|
16719
|
+
case "crates.io":
|
|
16720
|
+
return name.toLowerCase().replace(/_/g, "-");
|
|
16721
|
+
case "Go":
|
|
16722
|
+
case "Maven":
|
|
16723
|
+
return name;
|
|
16724
|
+
default:
|
|
16725
|
+
return name.toLowerCase();
|
|
16726
|
+
}
|
|
15882
16727
|
}
|
|
15883
16728
|
function shardOf(key) {
|
|
15884
16729
|
return createHash4("sha1").update(key).digest("hex").slice(0, 2);
|
|
@@ -16089,8 +16934,11 @@ async function pypiInfo(name, version) {
|
|
|
16089
16934
|
publishedAtMs: times.length > 0 ? Math.min(...times) : void 0
|
|
16090
16935
|
};
|
|
16091
16936
|
}
|
|
16937
|
+
var HAS_REGISTRY_CLIENT = /* @__PURE__ */ new Set(["npm", "PyPI"]);
|
|
16092
16938
|
async function registryInfo(eco, name, version, freshWindowMs, raw) {
|
|
16093
|
-
|
|
16939
|
+
if (eco === "npm") return npmInfo(name, version, freshWindowMs, raw);
|
|
16940
|
+
if (eco === "PyPI") return pypiInfo(name, version);
|
|
16941
|
+
return null;
|
|
16094
16942
|
}
|
|
16095
16943
|
|
|
16096
16944
|
// src/supply-chain/local-resolve.ts
|
|
@@ -16129,26 +16977,51 @@ function commandOf(toolName, args) {
|
|
|
16129
16977
|
const a = args;
|
|
16130
16978
|
return typeof a.command === "string" ? a.command : typeof a.cmd === "string" ? a.cmd : null;
|
|
16131
16979
|
}
|
|
16980
|
+
var KIND = {
|
|
16981
|
+
npm: "npm package",
|
|
16982
|
+
PyPI: "PyPI package",
|
|
16983
|
+
RubyGems: "RubyGems package",
|
|
16984
|
+
"crates.io": "crates.io package",
|
|
16985
|
+
Go: "Go module",
|
|
16986
|
+
NuGet: "NuGet package",
|
|
16987
|
+
Maven: "Maven artifact",
|
|
16988
|
+
Packagist: "Packagist package",
|
|
16989
|
+
VSCode: "VS Code extension"
|
|
16990
|
+
};
|
|
16132
16991
|
function label(p, version) {
|
|
16133
|
-
return `${p.ecosystem
|
|
16992
|
+
return `${KIND[p.ecosystem]} "${p.name}${version ? `@${version}` : ""}"`;
|
|
16993
|
+
}
|
|
16994
|
+
function osvNames(p) {
|
|
16995
|
+
if (p.ecosystem !== "Go") return [p.name];
|
|
16996
|
+
const parts = p.name.split("/");
|
|
16997
|
+
const out = [];
|
|
16998
|
+
for (let n = parts.length; n >= 2 && out.length < 6; n--) out.push(parts.slice(0, n).join("/"));
|
|
16999
|
+
return out;
|
|
16134
17000
|
}
|
|
16135
17001
|
async function maliciousFor(p, version, cfg, misses, networkCapped) {
|
|
16136
|
-
const
|
|
16137
|
-
const
|
|
16138
|
-
|
|
17002
|
+
const names = osvNames(p);
|
|
17003
|
+
const local = names.map((n) => lookupIndex(p.ecosystem, n));
|
|
17004
|
+
const hits2 = local.flatMap((l) => l.status === "hit" ? l.entries : []);
|
|
17005
|
+
const entries = hits2.length > 0 ? hits2 : null;
|
|
17006
|
+
const freshClean = local.every((l) => l.status === "clean" && l.fresh);
|
|
17007
|
+
const status = local.some((l) => l.status === "unavailable") ? "unavailable" : local[0].status;
|
|
17008
|
+
if (!entries && !freshClean) {
|
|
16139
17009
|
if (!cfg.onlineFallback || networkCapped) {
|
|
16140
17010
|
misses.push(
|
|
16141
|
-
`${label(p, version)}: local index ${
|
|
17011
|
+
`${label(p, version)}: local index ${status}, no online lookup (${networkCapped ? "over the per-command network cap" : "online fallback off"})`
|
|
16142
17012
|
);
|
|
16143
17013
|
return null;
|
|
16144
17014
|
}
|
|
16145
|
-
let
|
|
17015
|
+
let perName = null;
|
|
16146
17016
|
try {
|
|
16147
|
-
|
|
17017
|
+
perName = await queryOsvMalicious(
|
|
17018
|
+
names.map((name) => ({ ecosystem: p.ecosystem, name, version }))
|
|
17019
|
+
);
|
|
16148
17020
|
} catch (err) {
|
|
16149
17021
|
misses.push(`${label(p, version)}: OSV online ${err.message}`);
|
|
16150
17022
|
return null;
|
|
16151
17023
|
}
|
|
17024
|
+
const ids = perName ? [[...new Set(perName.flat())]] : null;
|
|
16152
17025
|
if (!ids) {
|
|
16153
17026
|
misses.push(`${label(p, version)}: OSV online unavailable`);
|
|
16154
17027
|
return null;
|
|
@@ -16218,7 +17091,7 @@ async function checkOne(p, cfg, misses, networkCapped, cwd) {
|
|
|
16218
17091
|
}
|
|
16219
17092
|
const findings = [];
|
|
16220
17093
|
let info = null;
|
|
16221
|
-
const wantsRegistry = cfg.newPackage !== "off" || cfg.installScript !== "off";
|
|
17094
|
+
const wantsRegistry = (cfg.newPackage !== "off" || cfg.installScript !== "off") && HAS_REGISTRY_CLIENT.has(p.ecosystem);
|
|
16222
17095
|
if (wantsRegistry && !networkCapped) {
|
|
16223
17096
|
try {
|
|
16224
17097
|
const windowMs = cfg.newPackage === "off" ? 0 : cfg.maxAgeHours * 36e5;
|