@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/cli.mjs
CHANGED
|
@@ -1210,6 +1210,120 @@ function expandPlainParam(p) {
|
|
|
1210
1210
|
if (recorded !== void 0) return recorded;
|
|
1211
1211
|
return HOME_VARIABLES.has(p.Param?.Value) ? "~" : void 0;
|
|
1212
1212
|
}
|
|
1213
|
+
function functionBodies(f) {
|
|
1214
|
+
const out = /* @__PURE__ */ new Map();
|
|
1215
|
+
syntax.Walk(f, (node) => {
|
|
1216
|
+
const n = node;
|
|
1217
|
+
if (n && syntax.NodeType(n) === "FuncDecl" && n.Name?.Value && n.Body) {
|
|
1218
|
+
const list = out.get(n.Name.Value) ?? [];
|
|
1219
|
+
list.push(n.Body);
|
|
1220
|
+
out.set(n.Name.Value, list);
|
|
1221
|
+
}
|
|
1222
|
+
return true;
|
|
1223
|
+
});
|
|
1224
|
+
return out;
|
|
1225
|
+
}
|
|
1226
|
+
function rebindsPositionals(body) {
|
|
1227
|
+
let found = false;
|
|
1228
|
+
syntax.Walk(body, (node) => {
|
|
1229
|
+
const n = node;
|
|
1230
|
+
if (found || !n) return false;
|
|
1231
|
+
if (syntax.NodeType(n) === "CallExpr") {
|
|
1232
|
+
const words = (n.Args || []).map((a) => resolveWordLiteral(a));
|
|
1233
|
+
if (words[0] === "shift") found = true;
|
|
1234
|
+
else if (words[0] === "set" && words.slice(1).some((w) => w === null || w === "--" || !/^[-+]/.test(w)))
|
|
1235
|
+
found = true;
|
|
1236
|
+
}
|
|
1237
|
+
return !found;
|
|
1238
|
+
});
|
|
1239
|
+
return found;
|
|
1240
|
+
}
|
|
1241
|
+
function tableBindings() {
|
|
1242
|
+
if (!assignmentTable) return "";
|
|
1243
|
+
const out = [];
|
|
1244
|
+
for (const [name, rec] of assignmentTable)
|
|
1245
|
+
if (rec.at < currentStmtOffset) out.push(`${name}=${rec.value ?? "\0"}`);
|
|
1246
|
+
return out.sort().join("");
|
|
1247
|
+
}
|
|
1248
|
+
function walkWithCalls(f, visit2) {
|
|
1249
|
+
const bodies = functionBodies(f);
|
|
1250
|
+
if (bodies.size === 0) {
|
|
1251
|
+
syntax.Walk(f, visit2);
|
|
1252
|
+
return;
|
|
1253
|
+
}
|
|
1254
|
+
const seenCalls = /* @__PURE__ */ new Set();
|
|
1255
|
+
const inline = (call) => {
|
|
1256
|
+
const words = (call.Args || []).map((a) => resolveWordLiteral(a));
|
|
1257
|
+
const list = words[0] ? bodies.get(words[0]) : void 0;
|
|
1258
|
+
if (!list || callDepth >= MAX_CALL_DEPTH) return;
|
|
1259
|
+
const args = words.slice(1);
|
|
1260
|
+
const key = `${words[0]}\0${JSON.stringify(args)}\0${tableBindings()}`;
|
|
1261
|
+
if (seenCalls.has(key)) return;
|
|
1262
|
+
seenCalls.add(key);
|
|
1263
|
+
const outerTable = assignmentTable;
|
|
1264
|
+
const outerOffset = currentStmtOffset;
|
|
1265
|
+
for (const body of list) {
|
|
1266
|
+
if (callWalks >= MAX_CALL_WALKS) return;
|
|
1267
|
+
callWalks++;
|
|
1268
|
+
const table = /* @__PURE__ */ new Map();
|
|
1269
|
+
for (const [name, rec] of outerTable ?? [])
|
|
1270
|
+
if (rec.at < outerOffset) table.set(name, { value: rec.value, at: -1 });
|
|
1271
|
+
for (const name of ["1", "2", "3", "4", "5", "6", "7", "8", "9", "@", "*", "#"])
|
|
1272
|
+
table.delete(name);
|
|
1273
|
+
if (!rebindsPositionals(body)) {
|
|
1274
|
+
args.slice(0, 9).forEach((value, i) => table.set(String(i + 1), { value, at: -1 }));
|
|
1275
|
+
if (args.length === 1) {
|
|
1276
|
+
table.set("@", { value: args[0], at: -1 });
|
|
1277
|
+
table.set("*", { value: args[0], at: -1 });
|
|
1278
|
+
}
|
|
1279
|
+
table.set("#", { value: String(args.length), at: -1 });
|
|
1280
|
+
}
|
|
1281
|
+
assignmentTable = table;
|
|
1282
|
+
currentStmtOffset = Number.MAX_SAFE_INTEGER;
|
|
1283
|
+
callDepth++;
|
|
1284
|
+
try {
|
|
1285
|
+
if (body.Cmd && syntax.NodeType(body.Cmd) === "Block") recordTopLevelAssignments(body.Cmd);
|
|
1286
|
+
syntax.Walk(body, step);
|
|
1287
|
+
} finally {
|
|
1288
|
+
callDepth--;
|
|
1289
|
+
assignmentTable = outerTable;
|
|
1290
|
+
currentStmtOffset = outerOffset;
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
};
|
|
1294
|
+
const step = (node) => {
|
|
1295
|
+
if (!node) return false;
|
|
1296
|
+
const go = visit2(node);
|
|
1297
|
+
if (go !== false && syntax.NodeType(node) === "CallExpr") inline(node);
|
|
1298
|
+
return go;
|
|
1299
|
+
};
|
|
1300
|
+
syntax.Walk(f, step);
|
|
1301
|
+
}
|
|
1302
|
+
function withAssignments(f, body) {
|
|
1303
|
+
const outerTable = assignmentTable;
|
|
1304
|
+
const outerOffset = currentStmtOffset;
|
|
1305
|
+
const outerWalks = callWalks;
|
|
1306
|
+
callWalks = 0;
|
|
1307
|
+
assignmentTable = new Map(
|
|
1308
|
+
[...outerTable ?? []].map(([k, r]) => [k, { value: r.value, at: -1 }])
|
|
1309
|
+
);
|
|
1310
|
+
currentStmtOffset = Number.MAX_SAFE_INTEGER;
|
|
1311
|
+
try {
|
|
1312
|
+
recordTopLevelAssignments(f);
|
|
1313
|
+
return body();
|
|
1314
|
+
} finally {
|
|
1315
|
+
assignmentTable = outerTable;
|
|
1316
|
+
currentStmtOffset = outerOffset;
|
|
1317
|
+
callWalks = outerWalks;
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
function trackStatement(n) {
|
|
1321
|
+
try {
|
|
1322
|
+
currentStmtOffset = n.Pos().Offset();
|
|
1323
|
+
} catch {
|
|
1324
|
+
currentStmtOffset = Number.MAX_SAFE_INTEGER;
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1213
1327
|
function resolveWordLiteral(w) {
|
|
1214
1328
|
const parts = w?.Parts || [];
|
|
1215
1329
|
let s = "";
|
|
@@ -1330,27 +1444,31 @@ function extractShellDestinations(command) {
|
|
|
1330
1444
|
const out = [];
|
|
1331
1445
|
const seen = /* @__PURE__ */ new Set();
|
|
1332
1446
|
try {
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
const
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1447
|
+
withAssignments(
|
|
1448
|
+
f,
|
|
1449
|
+
() => walkWithCalls(f, (node) => {
|
|
1450
|
+
if (!node) return false;
|
|
1451
|
+
const n = node;
|
|
1452
|
+
if (syntax.NodeType(n) === "Stmt") trackStatement(n);
|
|
1453
|
+
if (syntax.NodeType(n) !== "CallExpr") return true;
|
|
1454
|
+
const callArgs = n.Args || [];
|
|
1455
|
+
if (callArgs.length === 0) return true;
|
|
1456
|
+
const words = callArgs.map((a) => resolveWordLiteral(a));
|
|
1457
|
+
const at = unwrapCommandHead(words);
|
|
1458
|
+
const name = (words[at] || "").toLowerCase().split("/").pop() ?? "";
|
|
1459
|
+
if (!NET_BINARIES.has(name)) return true;
|
|
1460
|
+
const rest = words.slice(at + 1);
|
|
1461
|
+
for (const raw of destTokensForBinary(name, rest)) {
|
|
1462
|
+
const host2 = parseDestHost(raw);
|
|
1463
|
+
if (!host2) continue;
|
|
1464
|
+
const key = `${name}:${host2}`;
|
|
1465
|
+
if (seen.has(key)) continue;
|
|
1466
|
+
seen.add(key);
|
|
1467
|
+
out.push({ host: host2, binary: name, raw });
|
|
1468
|
+
}
|
|
1469
|
+
return true;
|
|
1470
|
+
})
|
|
1471
|
+
);
|
|
1354
1472
|
} catch {
|
|
1355
1473
|
return out;
|
|
1356
1474
|
}
|
|
@@ -1362,39 +1480,45 @@ function extractShellDestTokens(command) {
|
|
|
1362
1480
|
const out = [];
|
|
1363
1481
|
const seen = /* @__PURE__ */ new Set();
|
|
1364
1482
|
try {
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
const
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
tok = tok.
|
|
1483
|
+
withAssignments(
|
|
1484
|
+
f,
|
|
1485
|
+
() => walkWithCalls(f, (node) => {
|
|
1486
|
+
if (!node) return false;
|
|
1487
|
+
const n = node;
|
|
1488
|
+
if (syntax.NodeType(n) === "Stmt") trackStatement(n);
|
|
1489
|
+
if (syntax.NodeType(n) !== "CallExpr") return true;
|
|
1490
|
+
const callArgs = n.Args || [];
|
|
1491
|
+
if (callArgs.length === 0) return true;
|
|
1492
|
+
const words = callArgs.map((a) => resolveWordLiteral(a));
|
|
1493
|
+
const head = unwrapCommandHead(words);
|
|
1494
|
+
const name = (words[head] || "").toLowerCase().split("/").pop() ?? "";
|
|
1495
|
+
if (!NET_BINARIES.has(name)) return true;
|
|
1496
|
+
const rest = words.slice(head + 1);
|
|
1497
|
+
for (const raw of destTokensForBinary(name, rest)) {
|
|
1498
|
+
if (!raw) continue;
|
|
1499
|
+
let tok = raw.trim();
|
|
1500
|
+
const scheme = /^[a-z][a-z0-9+.-]*:\/\//i.exec(tok);
|
|
1501
|
+
const hasScheme = scheme !== null;
|
|
1502
|
+
if (hasScheme) tok = tok.slice(scheme[0].length);
|
|
1503
|
+
tok = tok.split(/[/?#]/)[0];
|
|
1504
|
+
const at = tok.lastIndexOf("@");
|
|
1505
|
+
if (at >= 0) tok = tok.slice(at + 1);
|
|
1506
|
+
if (tok.startsWith("[")) {
|
|
1507
|
+
const close = tok.indexOf("]");
|
|
1508
|
+
if (close > 0) tok = tok.slice(0, close + 1);
|
|
1509
|
+
} else {
|
|
1510
|
+
tok = tok.split(":")[0];
|
|
1511
|
+
}
|
|
1512
|
+
if (!tok) continue;
|
|
1513
|
+
if (!hasScheme && /^\d+$/.test(tok) && Number(tok) < 16777216) continue;
|
|
1514
|
+
const key = `${name}:${tok}`;
|
|
1515
|
+
if (seen.has(key)) continue;
|
|
1516
|
+
seen.add(key);
|
|
1517
|
+
out.push({ token: tok, binary: name });
|
|
1388
1518
|
}
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
if (seen.has(key)) continue;
|
|
1393
|
-
seen.add(key);
|
|
1394
|
-
out.push({ token: tok, binary: name });
|
|
1395
|
-
}
|
|
1396
|
-
return true;
|
|
1397
|
-
});
|
|
1519
|
+
return true;
|
|
1520
|
+
})
|
|
1521
|
+
);
|
|
1398
1522
|
} catch {
|
|
1399
1523
|
return out;
|
|
1400
1524
|
}
|
|
@@ -1513,6 +1637,7 @@ function analyzeFsOperationImpl(command, depth = 0) {
|
|
|
1513
1637
|
if (depth === 0) {
|
|
1514
1638
|
seenPayloads = /* @__PURE__ */ new Set();
|
|
1515
1639
|
payloadBudget = PAYLOAD_BUDGET;
|
|
1640
|
+
if (callDepth === 0) callWalks = 0;
|
|
1516
1641
|
}
|
|
1517
1642
|
assignmentTable = new Map(
|
|
1518
1643
|
[...outerTable ?? []].map(([k, r]) => [k, { value: r.value, at: -1 }])
|
|
@@ -1520,7 +1645,7 @@ function analyzeFsOperationImpl(command, depth = 0) {
|
|
|
1520
1645
|
currentStmtOffset = Number.MAX_SAFE_INTEGER;
|
|
1521
1646
|
try {
|
|
1522
1647
|
recordTopLevelAssignments(f);
|
|
1523
|
-
|
|
1648
|
+
walkWithCalls(f, (node) => {
|
|
1524
1649
|
if (!node || result?.verdict === "block") return false;
|
|
1525
1650
|
const n = node;
|
|
1526
1651
|
const nodeType = syntax.NodeType(n);
|
|
@@ -2309,6 +2434,19 @@ function runnablePrefix(command) {
|
|
|
2309
2434
|
}
|
|
2310
2435
|
return null;
|
|
2311
2436
|
}
|
|
2437
|
+
function uploadedFile(tool, flag, value) {
|
|
2438
|
+
const v = value.value;
|
|
2439
|
+
const file2 = (f) => f && f !== "-" && f !== "." ? { value: f } : null;
|
|
2440
|
+
if (tool === "curl") {
|
|
2441
|
+
if (["-d", "--data", "--data-ascii", "--data-binary", "--json"].includes(flag))
|
|
2442
|
+
return v.startsWith("@") ? file2(v.slice(1)) : null;
|
|
2443
|
+
if (flag === "--data-urlencode") return file2(/^[^=@]*@(.+)$/.exec(v)?.[1]);
|
|
2444
|
+
if (flag === "-F" || flag === "--form") return file2(/^[^=]*=[@<]([^;]+)/.exec(v)?.[1]);
|
|
2445
|
+
if (flag === "-T" || flag === "--upload-file") return file2(v);
|
|
2446
|
+
}
|
|
2447
|
+
if (tool === "wget" && ["--post-file", "--body-file"].includes(flag)) return file2(v);
|
|
2448
|
+
return null;
|
|
2449
|
+
}
|
|
2312
2450
|
function merge(a, b) {
|
|
2313
2451
|
a.cwd = unique([...a.cwd, ...b.cwd]);
|
|
2314
2452
|
for (const key of /* @__PURE__ */ new Set([...a.vars.keys(), ...b.vars.keys()])) {
|
|
@@ -2382,6 +2520,9 @@ function wordValues(word, state) {
|
|
|
2382
2520
|
function collectShellFileAccesses(command, context = {}) {
|
|
2383
2521
|
const out = [];
|
|
2384
2522
|
let payloads = 256;
|
|
2523
|
+
const functions = /* @__PURE__ */ new Map();
|
|
2524
|
+
const seenCalls = /* @__PURE__ */ new Set();
|
|
2525
|
+
let callWalks2 = MAX_CALL_WALKS2;
|
|
2385
2526
|
const initial = {
|
|
2386
2527
|
cwd: [context.cwd ?? ""],
|
|
2387
2528
|
vars: /* @__PURE__ */ new Map([["HOME", [context.homeDirs?.[0] ?? "~"]]])
|
|
@@ -2639,8 +2780,98 @@ function collectShellFileAccesses(command, context = {}) {
|
|
|
2639
2780
|
}
|
|
2640
2781
|
return;
|
|
2641
2782
|
}
|
|
2783
|
+
if (name === "wget" || name === "curl" || name === "aria2c") {
|
|
2784
|
+
downloadOutputs(name, args, state);
|
|
2785
|
+
return;
|
|
2786
|
+
}
|
|
2787
|
+
if (["http", "https", "xh", "xhs"].includes(name)) {
|
|
2788
|
+
for (const token of args) {
|
|
2789
|
+
if (!token || token.dynamic || /^[a-z][a-z0-9+.-]*:\/\//i.test(token.value)) continue;
|
|
2790
|
+
const m = /^(?:@|[^=:@\s]+@|[^=:@\s]*=@)(.+)$/.exec(token.value);
|
|
2791
|
+
if (m && m[1] !== "-") add("read", { value: m[1] }, state, { upload: true });
|
|
2792
|
+
}
|
|
2793
|
+
return;
|
|
2794
|
+
}
|
|
2795
|
+
if (name === "scp" || name === "rsync") {
|
|
2796
|
+
const operands2 = strings.filter((s) => s && !s.startsWith("-"));
|
|
2797
|
+
const remote = operands2.slice(1).some((o) => /^[^/\s]+:/.test(o));
|
|
2798
|
+
for (const p of copySourcePaths(words))
|
|
2799
|
+
add("read", locate(p), state, remote ? { upload: true } : {});
|
|
2800
|
+
return;
|
|
2801
|
+
}
|
|
2642
2802
|
for (const p of copySourcePaths(words)) add("read", locate(p), state);
|
|
2643
2803
|
}
|
|
2804
|
+
function downloadOutputs(name, args, state) {
|
|
2805
|
+
const valueShort = DOWNLOAD_SHORT_VALUE[name];
|
|
2806
|
+
const outputs = [];
|
|
2807
|
+
const urls = [];
|
|
2808
|
+
let dir2 = null;
|
|
2809
|
+
let remoteName = false;
|
|
2810
|
+
let document = false;
|
|
2811
|
+
let serverNamed = false;
|
|
2812
|
+
const take = (flag, value) => {
|
|
2813
|
+
if (!value) return;
|
|
2814
|
+
const sent = uploadedFile(name, flag, value);
|
|
2815
|
+
if (sent) add("read", sent, state, { upload: true });
|
|
2816
|
+
if (DOWNLOAD_OUTPUT_FLAGS[name].includes(flag)) {
|
|
2817
|
+
if (["-O", "--output-document", "--out"].includes(flag) || name === "aria2c" && flag === "-o")
|
|
2818
|
+
document = true;
|
|
2819
|
+
if (value.value !== "-" || value.pattern || value.dynamic) outputs.push(value);
|
|
2820
|
+
} else if (DOWNLOAD_DIR_FLAGS[name].includes(flag)) dir2 = value;
|
|
2821
|
+
else if (name === "wget" && (flag === "-i" || flag === "--input-file")) serverNamed = true;
|
|
2822
|
+
};
|
|
2823
|
+
for (let i = 0; i < args.length; i++) {
|
|
2824
|
+
const token = args[i];
|
|
2825
|
+
if (!token) continue;
|
|
2826
|
+
const v = token.value;
|
|
2827
|
+
if (v.startsWith("--")) {
|
|
2828
|
+
const eq = v.indexOf("=");
|
|
2829
|
+
const flag = eq < 0 ? v : v.slice(0, eq);
|
|
2830
|
+
if (["--remote-name", "--remote-name-all"].includes(flag)) remoteName = true;
|
|
2831
|
+
if (["--remote-header-name", "--recursive", "--mirror", "--page-requisites"].includes(flag))
|
|
2832
|
+
serverNamed = true;
|
|
2833
|
+
if (DOWNLOAD_LONG_VALUE[name].includes(flag))
|
|
2834
|
+
take(flag, eq < 0 ? args[++i] ?? null : { ...token, value: v.slice(eq + 1) });
|
|
2835
|
+
continue;
|
|
2836
|
+
}
|
|
2837
|
+
if (v.startsWith("-") && v.length > 1 && !token.dynamic) {
|
|
2838
|
+
for (let j = 1; j < v.length; j++) {
|
|
2839
|
+
const c = v[j];
|
|
2840
|
+
if (name === "wget" && c === "n") {
|
|
2841
|
+
j++;
|
|
2842
|
+
continue;
|
|
2843
|
+
}
|
|
2844
|
+
if (name === "curl" && c === "O") remoteName = true;
|
|
2845
|
+
if (name === "curl" && c === "J") serverNamed = true;
|
|
2846
|
+
if (name === "wget" && (c === "r" || c === "m" || c === "p")) serverNamed = true;
|
|
2847
|
+
if (valueShort.includes(c)) {
|
|
2848
|
+
const rest = v.slice(j + 1);
|
|
2849
|
+
take(`-${c}`, rest ? { value: rest } : args[++i] ?? null);
|
|
2850
|
+
break;
|
|
2851
|
+
}
|
|
2852
|
+
}
|
|
2853
|
+
continue;
|
|
2854
|
+
}
|
|
2855
|
+
if (/^(?:https?|ftps?):\/\//i.test(v)) urls.push(v);
|
|
2856
|
+
}
|
|
2857
|
+
for (const out2 of outputs) {
|
|
2858
|
+
const spelled = name === "aria2c" && dir2?.value && !out2.value.startsWith("/") ? { ...out2, value: `${dir2.value.replace(/\/+$/, "")}/${out2.value}` } : out2;
|
|
2859
|
+
if (name === "curl" && /#\d/.test(spelled.value))
|
|
2860
|
+
add("write", { value: "", pattern: spelled.value.replace(/#\d+/g, "*"), dot: true }, state);
|
|
2861
|
+
else add("write", spelled, state);
|
|
2862
|
+
}
|
|
2863
|
+
const remote = name === "curl" ? remoteName || serverNamed : !document || serverNamed;
|
|
2864
|
+
if (!remote) return;
|
|
2865
|
+
const into = dir2?.value ? dir2.value.replace(/\/+$/, "") : ".";
|
|
2866
|
+
if (serverNamed || urls.length === 0) {
|
|
2867
|
+
add("write", { value: "", pattern: `${quoteGlob(into)}/*`, dot: true }, state);
|
|
2868
|
+
return;
|
|
2869
|
+
}
|
|
2870
|
+
for (const url of urls) {
|
|
2871
|
+
const file2 = url.replace(/[?#].*$/, "").replace(/^[a-z]+:\/\/[^/]*/i, "").split("/").pop();
|
|
2872
|
+
add("write", { value: `${into}/${file2 || "index.html"}` }, state);
|
|
2873
|
+
}
|
|
2874
|
+
}
|
|
2644
2875
|
function findActions2(tokens, words, head, state, nested, depth) {
|
|
2645
2876
|
const starts = findStartPoints(words, head).starts;
|
|
2646
2877
|
const roots = (starts.length ? starts : ["."]).slice(0, MAX_ALTERNATIVES);
|
|
@@ -2712,7 +2943,35 @@ function collectShellFileAccesses(command, context = {}) {
|
|
|
2712
2943
|
const words = wordValues(node, state);
|
|
2713
2944
|
alternatives = alternatives.flatMap((prefix) => (words.length ? words : [null]).map((w) => [...prefix, w])).slice(0, MAX_ALTERNATIVES);
|
|
2714
2945
|
}
|
|
2715
|
-
for (const tokens of alternatives)
|
|
2946
|
+
for (const tokens of alternatives) {
|
|
2947
|
+
fromWords(tokens, state, nested, depth);
|
|
2948
|
+
inlineCall(tokens, state, depth);
|
|
2949
|
+
}
|
|
2950
|
+
}
|
|
2951
|
+
function inlineCall(tokens, state, depth) {
|
|
2952
|
+
const head = tokens[0];
|
|
2953
|
+
if (!head || head.dynamic || head.pattern) return;
|
|
2954
|
+
const bodies = functions.get(head.value);
|
|
2955
|
+
if (!bodies || (state.callDepth ?? 0) >= MAX_CALL_DEPTH2) return;
|
|
2956
|
+
const args = tokens.slice(1).map((a) => a && !a.dynamic ? a.value : null);
|
|
2957
|
+
const vars = JSON.stringify([...state.vars].sort(([a], [b]) => a < b ? -1 : 1));
|
|
2958
|
+
const key = `${head.value}\0${JSON.stringify(args)}\0${state.cwd.join("")}\0${vars}`;
|
|
2959
|
+
if (seenCalls.has(key)) return;
|
|
2960
|
+
seenCalls.add(key);
|
|
2961
|
+
for (const body of bodies) {
|
|
2962
|
+
if (callWalks2-- <= 0) return;
|
|
2963
|
+
const local = clone(state);
|
|
2964
|
+
local.callDepth = (state.callDepth ?? 0) + 1;
|
|
2965
|
+
for (const name of POSITIONAL) local.vars.delete(name);
|
|
2966
|
+
if (!rebindsPositionals(body)) {
|
|
2967
|
+
args.slice(0, 9).forEach((v, i) => local.vars.set(String(i + 1), v === null ? null : [v]));
|
|
2968
|
+
const all = args.every((v) => v !== null) && args.length ? unique(args) : null;
|
|
2969
|
+
local.vars.set("@", all);
|
|
2970
|
+
local.vars.set("*", all);
|
|
2971
|
+
local.vars.set("#", [String(args.length)]);
|
|
2972
|
+
}
|
|
2973
|
+
statement(body, local, depth + 1);
|
|
2974
|
+
}
|
|
2716
2975
|
}
|
|
2717
2976
|
function readsScriptFromStdin(stmt) {
|
|
2718
2977
|
const cmd = stmt?.Cmd;
|
|
@@ -2749,11 +3008,13 @@ function collectShellFileAccesses(command, context = {}) {
|
|
|
2749
3008
|
if (!stmt || depth > MAX_DEPTH) return;
|
|
2750
3009
|
state.onSuccess = void 0;
|
|
2751
3010
|
if (stmt.Background) state = clone(state);
|
|
3011
|
+
const headWord = stmt.Cmd && syntax2.NodeType(stmt.Cmd) === "CallExpr" ? base(resolveWordLiteral(stmt.Cmd.Args?.[0])) : "";
|
|
3012
|
+
const sends = SENDING_STDIN.has(headWord);
|
|
2752
3013
|
for (const redir of stmt.Redirs ?? []) {
|
|
2753
3014
|
substitutions(redir.Word, state, depth);
|
|
2754
3015
|
for (const word of wordValues(redir.Word, state)) {
|
|
2755
3016
|
if (WRITES.has(redir.Op)) add("write", word, state);
|
|
2756
|
-
if (READS.has(redir.Op)) add("read", word, state);
|
|
3017
|
+
if (READS.has(redir.Op)) add("read", word, state, sends ? { upload: true } : {});
|
|
2757
3018
|
if (redir.Op === DUP_OUT && word.value && !/^\d+$/.test(word.value))
|
|
2758
3019
|
add("write", word, state);
|
|
2759
3020
|
}
|
|
@@ -2862,7 +3123,10 @@ function collectShellFileAccesses(command, context = {}) {
|
|
|
2862
3123
|
}
|
|
2863
3124
|
const parsed = parseShared(command);
|
|
2864
3125
|
const program2 = typeof parsed !== "symbol" ? parsed : runnablePrefix(command);
|
|
2865
|
-
if (program2)
|
|
3126
|
+
if (program2) {
|
|
3127
|
+
for (const [name, bodies] of functionBodies(program2)) functions.set(name, bodies);
|
|
3128
|
+
statements(program2.Stmts ?? [], initial, 0);
|
|
3129
|
+
}
|
|
2866
3130
|
return out;
|
|
2867
3131
|
}
|
|
2868
3132
|
function isAgentInstructionPath(absPath, root) {
|
|
@@ -2978,6 +3242,33 @@ function hits(access, paths, directories, context) {
|
|
|
2978
3242
|
}
|
|
2979
3243
|
return false;
|
|
2980
3244
|
}
|
|
3245
|
+
function hitsSystemDir(access, context, dirs = SYSTEM_DIRS) {
|
|
3246
|
+
const home = context.homeDirs?.[0] ?? "~";
|
|
3247
|
+
const candidates2 = access.candidates ?? [
|
|
3248
|
+
normalizeFilePath(access.path, access.cwd ?? context.cwd, home)
|
|
3249
|
+
];
|
|
3250
|
+
for (const p of candidates2) {
|
|
3251
|
+
if (dirs.some((d) => within(p, d)) && !exempt(p)) return true;
|
|
3252
|
+
if (access.recursive && dirs.some((d) => within(d, p))) return true;
|
|
3253
|
+
}
|
|
3254
|
+
if (!access.glob) return false;
|
|
3255
|
+
const pattern = normalizeFilePath(access.glob, access.cwd ?? context.cwd, home);
|
|
3256
|
+
const magic = pattern.search(/[*?[{]/);
|
|
3257
|
+
const literal = magic < 0 ? pattern : pattern.slice(0, pattern.lastIndexOf("/", magic) + 1);
|
|
3258
|
+
if (literal && exempt(literal.replace(/\/$/, "") || "/")) return false;
|
|
3259
|
+
const options = { dot: access.dotGlob === true, nonegate: true };
|
|
3260
|
+
const components = pattern.split("/");
|
|
3261
|
+
for (const root of dirs) {
|
|
3262
|
+
const count = root.split("/").length;
|
|
3263
|
+
if (components.length > count && picomatch(components.slice(0, count).join("/"), options)(root))
|
|
3264
|
+
return true;
|
|
3265
|
+
if (access.recursive && picomatch(pattern, options)(root)) return true;
|
|
3266
|
+
}
|
|
3267
|
+
return false;
|
|
3268
|
+
}
|
|
3269
|
+
function uploadDirs(context) {
|
|
3270
|
+
return context.homeDirs?.[0] === "/root" ? UPLOAD_SYSTEM_DIRS.filter((d) => d !== "/root") : UPLOAD_SYSTEM_DIRS;
|
|
3271
|
+
}
|
|
2981
3272
|
function evaluateProtectedFileAccess(accesses, context, settings) {
|
|
2982
3273
|
const homes = [.../* @__PURE__ */ new Set([...context.homeDirs ?? ["~"], "/root"])];
|
|
2983
3274
|
const startup = homes.flatMap(
|
|
@@ -2999,6 +3290,14 @@ function evaluateProtectedFileAccess(accesses, context, settings) {
|
|
|
2999
3290
|
"files.shell-startup",
|
|
3000
3291
|
"review-shell-startup-mutation",
|
|
3001
3292
|
"Changing shell startup files requires approval"
|
|
3293
|
+
] : mutation && hitsSystemDir(access, context) ? [
|
|
3294
|
+
"files.system-dirs",
|
|
3295
|
+
"review-system-dir-mutation",
|
|
3296
|
+
"Changing files in a system directory requires approval"
|
|
3297
|
+
] : !mutation && access.upload && hitsSystemDir(access, context, uploadDirs(context)) ? [
|
|
3298
|
+
"files.system-dirs",
|
|
3299
|
+
"review-system-file-upload",
|
|
3300
|
+
"Uploading a system file requires approval"
|
|
3002
3301
|
] : !mutation && hits(access, PASSWORD_HASH_FILES, [], context) ? [
|
|
3003
3302
|
"files.password-hashes",
|
|
3004
3303
|
"review-password-hash-read",
|
|
@@ -3326,16 +3625,21 @@ function looksGenerated(value, minLength, minEntropy, lettersOnlyAllowed = false
|
|
|
3326
3625
|
if (EMAIL_VALUE_RE.test(value)) return false;
|
|
3327
3626
|
if (looksLikeJoinedWords(value) || hasSequentialRun(value)) return false;
|
|
3328
3627
|
if (PATH_VALUE_RE.test(value) || DOTTED_REFERENCE_RE.test(value)) return false;
|
|
3628
|
+
if (CONCATENATION_RE.test(value)) return false;
|
|
3329
3629
|
if (rejectedByStructuredValidator(value)) return false;
|
|
3330
3630
|
return shannonEntropy(value) >= minEntropy;
|
|
3331
3631
|
}
|
|
3332
3632
|
function nameHoldsCredential(name) {
|
|
3333
3633
|
return !FIXTURE_NAME_RE.test(name) && !DERIVATIVE_NAME_RE.test(name);
|
|
3334
3634
|
}
|
|
3635
|
+
function isOtpDocSecret(value) {
|
|
3636
|
+
return OTP_DOC_SECRETS.has(value.replace(/=+$/, "").toUpperCase());
|
|
3637
|
+
}
|
|
3335
3638
|
function validateQueryCredential(raw) {
|
|
3336
3639
|
const value = capturedValue(raw, QUERY_CREDENTIAL_VALUE_RE);
|
|
3337
3640
|
if (value === null) return false;
|
|
3338
|
-
if (OTP_SECRET_RE.test(value))
|
|
3641
|
+
if (OTP_SECRET_RE.test(value))
|
|
3642
|
+
return !isOtpDocSecret(value) && looksGenerated(value, 16, 3, true);
|
|
3339
3643
|
if (!/[0-9]/.test(value) || !/[A-Za-z]/.test(value)) return false;
|
|
3340
3644
|
return looksGenerated(value, 12, 3);
|
|
3341
3645
|
}
|
|
@@ -3349,13 +3653,40 @@ function keywordEndsAPart(name) {
|
|
|
3349
3653
|
}
|
|
3350
3654
|
function validateAssignedSecret(raw, before = "") {
|
|
3351
3655
|
const value = capturedValue(raw, ASSIGNED_SECRET_VALUE_RE);
|
|
3352
|
-
const name = capturedValue(raw, ASSIGNED_SECRET_NAME_RE);
|
|
3656
|
+
const name = capturedValue(raw, ASSIGNED_SECRET_NAME_RE)?.replace(/^_+/, "") ?? null;
|
|
3353
3657
|
if (value === null || name === null) return false;
|
|
3354
|
-
if (!keywordEndsAPart(name)
|
|
3355
|
-
|
|
3658
|
+
if (!keywordEndsAPart(name)) return false;
|
|
3659
|
+
if (ARN_CONTEXT_RE.test(before)) return false;
|
|
3660
|
+
const prefix = (before.match(/[A-Za-z0-9_]*$/)?.[0] ?? "") + (raw.startsWith("_") ? "_" : "");
|
|
3356
3661
|
if (!nameHoldsCredential(prefix + name)) return false;
|
|
3357
3662
|
return looksGenerated(value, 12, 3.5);
|
|
3358
3663
|
}
|
|
3664
|
+
function validateBasicCredential(raw) {
|
|
3665
|
+
const b64 = capturedValue(raw, BASIC_VALUE_RE);
|
|
3666
|
+
if (b64 === null) return false;
|
|
3667
|
+
const bare = b64.replace(/=+$/, "");
|
|
3668
|
+
if (bare.length % 4 === 1) return false;
|
|
3669
|
+
const bytes = Buffer.from(b64, "base64");
|
|
3670
|
+
if (bytes.toString("base64").replace(/=+$/, "") !== bare) return false;
|
|
3671
|
+
const text = bytes.toString("utf8");
|
|
3672
|
+
if (!Buffer.from(text, "utf8").equals(bytes)) return false;
|
|
3673
|
+
for (let i = 0; i < text.length; i++) {
|
|
3674
|
+
const c = text.charCodeAt(i);
|
|
3675
|
+
if (c < 32 || c === 127) return false;
|
|
3676
|
+
}
|
|
3677
|
+
const pair = BASIC_PAIR_RE.exec(text);
|
|
3678
|
+
if (!pair) return false;
|
|
3679
|
+
const password = pair[2];
|
|
3680
|
+
if (!/[0-9]/.test(password) || !/[A-Za-z]/.test(password)) return false;
|
|
3681
|
+
const lower2 = password.toLowerCase();
|
|
3682
|
+
if (DLP_STOPWORDS.some((sw) => lower2.includes(sw)) || hasSequentialRun(password)) return false;
|
|
3683
|
+
return shannonEntropy(password) >= 3;
|
|
3684
|
+
}
|
|
3685
|
+
function validateOtpSecret(raw) {
|
|
3686
|
+
const secret = capturedValue(raw, OTP_CAPTURE_RE);
|
|
3687
|
+
if (secret === null || isOtpDocSecret(secret)) return false;
|
|
3688
|
+
return looksGenerated(secret.replace(/=+$/, ""), 16, 3, true);
|
|
3689
|
+
}
|
|
3359
3690
|
function matchSensitivePath2(resolvedPath, originalPath, options = {}) {
|
|
3360
3691
|
const normalised = resolvedPath.replace(/\\/g, "/");
|
|
3361
3692
|
if (!options.systemAccessHandled && isLegacySystemPath(normalised))
|
|
@@ -3664,6 +3995,156 @@ function parsePySpec(raw) {
|
|
|
3664
3995
|
const exact = PY_EXACT_RE.exec(m[2].trim());
|
|
3665
3996
|
return { name, version: exact ? exact[1] : void 0 };
|
|
3666
3997
|
}
|
|
3998
|
+
function exactVersion(v) {
|
|
3999
|
+
if (!v) return void 0;
|
|
4000
|
+
const m = EXACT_VERSION_RE.exec(v.trim());
|
|
4001
|
+
return m ? m[1] : void 0;
|
|
4002
|
+
}
|
|
4003
|
+
function push(out, ecosystem, manager, name, version2, raw) {
|
|
4004
|
+
out.push({ ecosystem, manager, name, ...version2 ? { version: version2 } : {}, raw });
|
|
4005
|
+
}
|
|
4006
|
+
function gemInstall(words, from, out) {
|
|
4007
|
+
const dash = words.indexOf("--", from);
|
|
4008
|
+
const own = dash < 0 ? words : words.slice(0, dash);
|
|
4009
|
+
const pinned = exactVersion(flagValues(own, from, ["-v", "--version"])[0]);
|
|
4010
|
+
for (const raw of operands(own, from, GEM_VALUE_FLAGS)) {
|
|
4011
|
+
if (NON_REGISTRY_RE.test(raw) || /\.gem$/i.test(raw)) continue;
|
|
4012
|
+
const colon = raw.indexOf(":");
|
|
4013
|
+
const name = colon < 0 ? raw : raw.slice(0, colon);
|
|
4014
|
+
if (!GEM_NAME_RE.test(name)) continue;
|
|
4015
|
+
push(
|
|
4016
|
+
out,
|
|
4017
|
+
"RubyGems",
|
|
4018
|
+
"gem",
|
|
4019
|
+
name,
|
|
4020
|
+
colon < 0 ? pinned : exactVersion(raw.slice(colon + 1)),
|
|
4021
|
+
raw
|
|
4022
|
+
);
|
|
4023
|
+
}
|
|
4024
|
+
}
|
|
4025
|
+
function cargoCrates(words, from, manager, out) {
|
|
4026
|
+
const flags = manager === "cargo add" ? CARGO_ADD_VALUE_FLAGS : CARGO_INSTALL_VALUE_FLAGS;
|
|
4027
|
+
if (flagValues(words, from, ["--git", "--path", "--registry", "--index"]).length > 0) return;
|
|
4028
|
+
if (words.some((w) => w === "--git" || w === "--path")) return;
|
|
4029
|
+
const flagVersion = manager === "cargo install" ? exactVersion(flagValues(words, from, ["--version", "--vers"])[0]) : void 0;
|
|
4030
|
+
for (const raw of operands(words, from, flags)) {
|
|
4031
|
+
const at = raw.indexOf("@");
|
|
4032
|
+
const name = at < 0 ? raw : raw.slice(0, at);
|
|
4033
|
+
if (!CRATE_NAME_RE.test(name)) continue;
|
|
4034
|
+
push(
|
|
4035
|
+
out,
|
|
4036
|
+
"crates.io",
|
|
4037
|
+
manager,
|
|
4038
|
+
name,
|
|
4039
|
+
at < 0 ? flagVersion : exactVersion(raw.slice(at + 1)),
|
|
4040
|
+
raw
|
|
4041
|
+
);
|
|
4042
|
+
}
|
|
4043
|
+
}
|
|
4044
|
+
function goModules(words, from, manager, out) {
|
|
4045
|
+
let specs = operands(words, from, GO_VALUE_FLAGS);
|
|
4046
|
+
if (manager === "go run") specs = specs.slice(0, 1).filter((s) => s.includes("@"));
|
|
4047
|
+
for (const raw of specs) {
|
|
4048
|
+
const at = raw.indexOf("@");
|
|
4049
|
+
const pathPart = (at < 0 ? raw : raw.slice(0, at)).replace(/\/\.\.\.$/, "");
|
|
4050
|
+
const ver = at < 0 ? void 0 : raw.slice(at + 1);
|
|
4051
|
+
if (ver === "none") continue;
|
|
4052
|
+
if (!GO_PATH_RE.test(pathPart) || /\.go$/i.test(pathPart)) continue;
|
|
4053
|
+
const exact = ver && /^v\d/.test(ver) ? exactVersion(ver) : void 0;
|
|
4054
|
+
push(out, "Go", manager, pathPart, exact, raw);
|
|
4055
|
+
}
|
|
4056
|
+
}
|
|
4057
|
+
function dotnetAdd(words, from, out) {
|
|
4058
|
+
let k = -1;
|
|
4059
|
+
for (let i = from; i < Math.min(words.length, from + 2); i++) {
|
|
4060
|
+
if (lower(words[i]) === "package") {
|
|
4061
|
+
k = i;
|
|
4062
|
+
break;
|
|
4063
|
+
}
|
|
4064
|
+
}
|
|
4065
|
+
if (k < 0) return;
|
|
4066
|
+
const name = operands(words, k + 1, DOTNET_ADD_VALUE_FLAGS)[0];
|
|
4067
|
+
if (!name || !NUGET_NAME_RE.test(name)) return;
|
|
4068
|
+
const version2 = exactVersion(flagValues(words, k + 1, ["-v", "--version"])[0]);
|
|
4069
|
+
push(out, "NuGet", "dotnet add package", name, version2, name);
|
|
4070
|
+
}
|
|
4071
|
+
function dotnetTool(words, from, out) {
|
|
4072
|
+
const name = operands(words, from, DOTNET_TOOL_VALUE_FLAGS)[0];
|
|
4073
|
+
if (!name || !NUGET_NAME_RE.test(name)) return;
|
|
4074
|
+
const version2 = exactVersion(flagValues(words, from, ["--version"])[0]);
|
|
4075
|
+
push(out, "NuGet", "dotnet tool", name, version2, name);
|
|
4076
|
+
}
|
|
4077
|
+
function nugetInstall(words, from, out) {
|
|
4078
|
+
const lowered = words.map((w) => w !== null && w.startsWith("-") ? w.toLowerCase() : w);
|
|
4079
|
+
const name = operands(lowered, from, NUGET_VALUE_FLAGS)[0];
|
|
4080
|
+
if (!name || !NUGET_NAME_RE.test(name) || /\.config$/i.test(name)) return;
|
|
4081
|
+
const version2 = exactVersion(flagValues(lowered, from, ["-version"])[0]);
|
|
4082
|
+
push(out, "NuGet", "nuget install", name, version2, name);
|
|
4083
|
+
}
|
|
4084
|
+
function mavenGet(words, from, out) {
|
|
4085
|
+
const goal = words.slice(from).some(
|
|
4086
|
+
(w) => w !== null && /(?:^|:)dependency:get$|^org\.apache\.maven\.plugins:maven-dependency-plugin:(?:[\w.-]+:)?get$/.test(
|
|
4087
|
+
w
|
|
4088
|
+
)
|
|
4089
|
+
);
|
|
4090
|
+
if (!goal) return;
|
|
4091
|
+
const prop = (key) => {
|
|
4092
|
+
for (let i = from; i < words.length; i++) {
|
|
4093
|
+
const w = words[i];
|
|
4094
|
+
if (w === null) continue;
|
|
4095
|
+
if (w.startsWith(`-D${key}=`)) return w.slice(key.length + 3);
|
|
4096
|
+
if (w === "-D" && words[i + 1]?.startsWith(`${key}=`))
|
|
4097
|
+
return words[i + 1].slice(key.length + 1);
|
|
4098
|
+
}
|
|
4099
|
+
return void 0;
|
|
4100
|
+
};
|
|
4101
|
+
const artifact = prop("artifact");
|
|
4102
|
+
let coord;
|
|
4103
|
+
let ver;
|
|
4104
|
+
if (artifact) {
|
|
4105
|
+
const parts = artifact.split(":");
|
|
4106
|
+
if (parts.length < 2) return;
|
|
4107
|
+
coord = `${parts[0]}:${parts[1]}`;
|
|
4108
|
+
ver = parts[2];
|
|
4109
|
+
} else {
|
|
4110
|
+
const g = prop("groupId");
|
|
4111
|
+
const a = prop("artifactId");
|
|
4112
|
+
if (!g || !a) return;
|
|
4113
|
+
coord = `${g}:${a}`;
|
|
4114
|
+
ver = prop("version");
|
|
4115
|
+
}
|
|
4116
|
+
if (!MAVEN_COORD_RE.test(coord)) return;
|
|
4117
|
+
const exact = ver && !/^(?:LATEST|RELEASE)$/i.test(ver) ? exactVersion(ver) : void 0;
|
|
4118
|
+
push(out, "Maven", "mvn dependency:get", coord, exact, artifact ?? coord);
|
|
4119
|
+
}
|
|
4120
|
+
function composerPackages(words, from, manager, out) {
|
|
4121
|
+
const ops = operands(words, from, COMPOSER_VALUE_FLAGS);
|
|
4122
|
+
const specs = manager === "composer create-project" ? ops.slice(0, 1) : ops;
|
|
4123
|
+
for (const raw of specs) {
|
|
4124
|
+
const sep = raw.search(/[:=]/);
|
|
4125
|
+
const name = sep < 0 ? raw : raw.slice(0, sep);
|
|
4126
|
+
if (!COMPOSER_NAME_RE.test(name)) continue;
|
|
4127
|
+
let ver = sep < 0 ? void 0 : raw.slice(sep + 1);
|
|
4128
|
+
if (manager === "composer create-project" && ver === void 0) ver = ops[2] ?? void 0;
|
|
4129
|
+
push(out, "Packagist", manager, name.toLowerCase(), exactVersion(ver), raw);
|
|
4130
|
+
}
|
|
4131
|
+
}
|
|
4132
|
+
function editorExtensions(words, from, out) {
|
|
4133
|
+
for (const raw of flagValues(words, from, ["--install-extension"])) {
|
|
4134
|
+
if (/\.vsix$/i.test(raw) || NON_REGISTRY_RE.test(raw)) continue;
|
|
4135
|
+
const at = raw.lastIndexOf("@");
|
|
4136
|
+
const name = at <= 0 ? raw : raw.slice(0, at);
|
|
4137
|
+
if (!EXTENSION_ID_RE.test(name)) continue;
|
|
4138
|
+
push(
|
|
4139
|
+
out,
|
|
4140
|
+
"VSCode",
|
|
4141
|
+
"code --install-extension",
|
|
4142
|
+
name,
|
|
4143
|
+
at <= 0 ? void 0 : exactVersion(raw.slice(at + 1)),
|
|
4144
|
+
raw
|
|
4145
|
+
);
|
|
4146
|
+
}
|
|
4147
|
+
}
|
|
3667
4148
|
function basename(w) {
|
|
3668
4149
|
return (w ?? "").toLowerCase().split("/").pop() ?? "";
|
|
3669
4150
|
}
|
|
@@ -3895,7 +4376,63 @@ function fromCall(words, out, depth) {
|
|
|
3895
4376
|
pushPy(out, "pipenv", operands(words, v + 1, PIPENV_VALUE_FLAGS));
|
|
3896
4377
|
return;
|
|
3897
4378
|
}
|
|
4379
|
+
case "gem": {
|
|
4380
|
+
const v = verbIndex(words, start + 1, GEM_VALUE_FLAGS);
|
|
4381
|
+
if (v >= 0 && ["install", "i"].includes(lower(words[v]))) gemInstall(words, v + 1, out);
|
|
4382
|
+
return;
|
|
4383
|
+
}
|
|
4384
|
+
case "bundle": {
|
|
4385
|
+
const v = verbIndex(words, start + 1, BUNDLE_VALUE_FLAGS);
|
|
4386
|
+
if (v < 0 || lower(words[v]) !== "add") return;
|
|
4387
|
+
if (flagValues(words, v + 1, ["--git", "--github", "--path"]).length > 0) return;
|
|
4388
|
+
const version2 = exactVersion(flagValues(words, v + 1, ["-v", "--version"])[0]);
|
|
4389
|
+
for (const raw of operands(words, v + 1, BUNDLE_VALUE_FLAGS))
|
|
4390
|
+
if (GEM_NAME_RE.test(raw)) push(out, "RubyGems", "bundle add", raw, version2, raw);
|
|
4391
|
+
return;
|
|
4392
|
+
}
|
|
4393
|
+
case "cargo": {
|
|
4394
|
+
const from = words[start + 1]?.startsWith("+") ? start + 2 : start + 1;
|
|
4395
|
+
const v = verbIndex(words, from, CARGO_ADD_VALUE_FLAGS);
|
|
4396
|
+
const verb = lower(words[v]);
|
|
4397
|
+
if (verb === "add") cargoCrates(words, v + 1, "cargo add", out);
|
|
4398
|
+
else if (verb === "install") cargoCrates(words, v + 1, "cargo install", out);
|
|
4399
|
+
return;
|
|
4400
|
+
}
|
|
4401
|
+
case "go": {
|
|
4402
|
+
const v = verbIndex(words, start + 1, GO_VALUE_FLAGS);
|
|
4403
|
+
const verb = lower(words[v]);
|
|
4404
|
+
if (verb === "get" || verb === "install" || verb === "run")
|
|
4405
|
+
goModules(words, v + 1, `go ${verb}`, out);
|
|
4406
|
+
return;
|
|
4407
|
+
}
|
|
4408
|
+
case "dotnet": {
|
|
4409
|
+
const v = verbIndex(words, start + 1, DOTNET_ADD_VALUE_FLAGS);
|
|
4410
|
+
const verb = lower(words[v]);
|
|
4411
|
+
if (verb === "add") dotnetAdd(words, v + 1, out);
|
|
4412
|
+
else if (verb === "tool" && ["install", "update"].includes(lower(words[v + 1])))
|
|
4413
|
+
dotnetTool(words, v + 2, out);
|
|
4414
|
+
return;
|
|
4415
|
+
}
|
|
4416
|
+
case "nuget": {
|
|
4417
|
+
if (lower(words[start + 1]) === "install") nugetInstall(words, start + 2, out);
|
|
4418
|
+
return;
|
|
4419
|
+
}
|
|
4420
|
+
case "mvn":
|
|
4421
|
+
case "mvnw":
|
|
4422
|
+
mavenGet(words, start + 1, out);
|
|
4423
|
+
return;
|
|
4424
|
+
case "composer": {
|
|
4425
|
+
let v = verbIndex(words, start + 1, COMPOSER_VALUE_FLAGS);
|
|
4426
|
+
if (v >= 0 && lower(words[v]) === "global") v = verbIndex(words, v + 1, COMPOSER_VALUE_FLAGS);
|
|
4427
|
+
const verb = lower(words[v]);
|
|
4428
|
+
if (verb === "require" || verb === "req" || verb === "r")
|
|
4429
|
+
composerPackages(words, v + 1, "composer require", out);
|
|
4430
|
+
else if (verb === "create-project")
|
|
4431
|
+
composerPackages(words, v + 1, "composer create-project", out);
|
|
4432
|
+
return;
|
|
4433
|
+
}
|
|
3898
4434
|
default:
|
|
4435
|
+
if (EDITOR_HEADS.includes(family)) editorExtensions(words, start + 1, out);
|
|
3899
4436
|
return;
|
|
3900
4437
|
}
|
|
3901
4438
|
}
|
|
@@ -5459,11 +5996,11 @@ function isDecimalFraction(text, index, unseparated) {
|
|
|
5459
5996
|
const a = text[index - 2];
|
|
5460
5997
|
if (sep === ".") {
|
|
5461
5998
|
if (a !== void 0 && a >= "0" && a <= "9") return true;
|
|
5462
|
-
return a === void 0 ||
|
|
5999
|
+
return a === void 0 || (a === "-" || a === "+") && index === 2;
|
|
5463
6000
|
}
|
|
5464
6001
|
if (sep === "," && a === "0") {
|
|
5465
6002
|
const b = text[index - 3];
|
|
5466
|
-
return b === void 0 ||
|
|
6003
|
+
return b === void 0 || b === ";";
|
|
5467
6004
|
}
|
|
5468
6005
|
return false;
|
|
5469
6006
|
}
|
|
@@ -6663,7 +7200,7 @@ function* stringValues(obj, depth = 0) {
|
|
|
6663
7200
|
}
|
|
6664
7201
|
for (const v of Object.values(obj)) yield* stringValues(v, depth + 1);
|
|
6665
7202
|
}
|
|
6666
|
-
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, 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, 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, MAX_BLOBS, MIN_DECODED, BASE64_RUN_RE, HEX_RUN_RE, HEX_ESCAPE_RUN_RE, MAX, UNTRUSTED_TOOLS, SIGNALS, 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, DLP_SCAN_LIMITS, 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,
|
|
7203
|
+
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, 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, 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, MAX_BLOBS, MIN_DECODED, BASE64_RUN_RE, HEX_RUN_RE, HEX_ESCAPE_RUN_RE, MAX, UNTRUSTED_TOOLS, SIGNALS, 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, DLP_SCAN_LIMITS, 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, CONTROL_CHAR_RE, 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;
|
|
6667
7204
|
var init_dist = __esm({
|
|
6668
7205
|
"packages/policy-engine/dist/index.mjs"() {
|
|
6669
7206
|
"use strict";
|
|
@@ -7464,6 +8001,11 @@ var init_dist = __esm({
|
|
|
7464
8001
|
example: "cat /etc/shadow",
|
|
7465
8002
|
advice: "Keep Review, or choose Block when agents must never read system password hashes."
|
|
7466
8003
|
},
|
|
8004
|
+
"files.system-dirs": {
|
|
8005
|
+
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.",
|
|
8006
|
+
example: "wget -O /usr/bin/tool https://example.com/tool",
|
|
8007
|
+
advice: "Keep Review on a workstation. A container image build that installs into /usr can set it to Log."
|
|
8008
|
+
},
|
|
7467
8009
|
// ── Commands ──────────────────────────────────────────────────────────────
|
|
7468
8010
|
"commands.inline-exec": {
|
|
7469
8011
|
plain: "The agent runs a small program written inside the command itself.",
|
|
@@ -7660,7 +8202,7 @@ var init_dist = __esm({
|
|
|
7660
8202
|
advice: "Fixed at Block. A person accepts the change on the machine (node9 mcp pin update)."
|
|
7661
8203
|
},
|
|
7662
8204
|
"loading.malicious-package": {
|
|
7663
|
-
plain: "The agent installs a package that the public malicious-package database (OSV) lists, or one published minutes ago.",
|
|
8205
|
+
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.",
|
|
7664
8206
|
example: "npm install of a package flagged as malware",
|
|
7665
8207
|
advice: "Keep Block for known-malicious packages; brand-new packages already stop for Review."
|
|
7666
8208
|
}
|
|
@@ -7706,6 +8248,15 @@ var init_dist = __esm({
|
|
|
7706
8248
|
defaultValue: "review",
|
|
7707
8249
|
legacy: ["review-password-hash-read"]
|
|
7708
8250
|
},
|
|
8251
|
+
{
|
|
8252
|
+
id: "files.system-dirs",
|
|
8253
|
+
group: "files",
|
|
8254
|
+
title: "System directories",
|
|
8255
|
+
catches: "Writes, new files and removals under /usr, /etc, /boot, /bin, /sbin, /lib and /var/log, and uploads of system files",
|
|
8256
|
+
values: ALL,
|
|
8257
|
+
defaultValue: "review",
|
|
8258
|
+
legacy: ["review-system-dir-mutation", "review-system-file-upload"]
|
|
8259
|
+
},
|
|
7709
8260
|
// ── Commands ────────────────────────────────────────────────────────────
|
|
7710
8261
|
{
|
|
7711
8262
|
id: "commands.inline-exec",
|
|
@@ -9003,6 +9554,10 @@ var init_dist = __esm({
|
|
|
9003
9554
|
AND_OP = probeBinOp("a && b");
|
|
9004
9555
|
OR_OP = probeBinOp("a || b");
|
|
9005
9556
|
AND_OR_OPS = new Set([AND_OP, OR_OP].filter((o) => o !== null));
|
|
9557
|
+
MAX_CALL_DEPTH = 4;
|
|
9558
|
+
MAX_CALL_WALKS = 256;
|
|
9559
|
+
callDepth = 0;
|
|
9560
|
+
callWalks = 0;
|
|
9006
9561
|
FS_OP_CACHE_MAX = 5e3;
|
|
9007
9562
|
fsOpCache = /* @__PURE__ */ new Map();
|
|
9008
9563
|
stripDotSlash = (p) => p.replace(/^\.\//, "");
|
|
@@ -9055,10 +9610,79 @@ var init_dist = __esm({
|
|
|
9055
9610
|
MAX_ARGV_CHARACTERS = 262144;
|
|
9056
9611
|
({ syntax: syntax2 } = mvdanSh2);
|
|
9057
9612
|
MAX_ALTERNATIVES = 64;
|
|
9613
|
+
MAX_CALL_DEPTH2 = 4;
|
|
9614
|
+
MAX_CALL_WALKS2 = 256;
|
|
9615
|
+
POSITIONAL = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "@", "*", "#"];
|
|
9058
9616
|
MAX_DEPTH = 64;
|
|
9059
9617
|
MAX_PAYLOAD_DEPTH = 24;
|
|
9060
9618
|
unique = (a) => [...new Set(a)].slice(0, MAX_ALTERNATIVES);
|
|
9061
|
-
clone = (s) => ({
|
|
9619
|
+
clone = (s) => ({
|
|
9620
|
+
cwd: [...s.cwd],
|
|
9621
|
+
vars: new Map(s.vars),
|
|
9622
|
+
...s.callDepth ? { callDepth: s.callDepth } : {}
|
|
9623
|
+
});
|
|
9624
|
+
SENDING_STDIN = /* @__PURE__ */ new Set(["nc", "ncat", "netcat", "socat", "telnet", "curl", "wget"]);
|
|
9625
|
+
DOWNLOAD_SHORT_VALUE = {
|
|
9626
|
+
curl: "odDeEFHKmPrtTuUwxXyYzAbcCQ",
|
|
9627
|
+
wget: "OoaPiBetTwQlARDXIUY",
|
|
9628
|
+
aria2c: "odisx"
|
|
9629
|
+
};
|
|
9630
|
+
DOWNLOAD_LONG_VALUE = {
|
|
9631
|
+
curl: [
|
|
9632
|
+
"--output",
|
|
9633
|
+
"--output-dir",
|
|
9634
|
+
"--dump-header",
|
|
9635
|
+
"--cookie-jar",
|
|
9636
|
+
"--data",
|
|
9637
|
+
"--data-binary",
|
|
9638
|
+
"--data-raw",
|
|
9639
|
+
"--data-ascii",
|
|
9640
|
+
"--data-urlencode",
|
|
9641
|
+
"--form",
|
|
9642
|
+
"--header",
|
|
9643
|
+
"--request",
|
|
9644
|
+
"--user",
|
|
9645
|
+
"--user-agent",
|
|
9646
|
+
"--referer",
|
|
9647
|
+
"--cookie",
|
|
9648
|
+
"--config",
|
|
9649
|
+
"--json",
|
|
9650
|
+
"--upload-file",
|
|
9651
|
+
"--max-time",
|
|
9652
|
+
"--proxy",
|
|
9653
|
+
"--write-out",
|
|
9654
|
+
"--url"
|
|
9655
|
+
],
|
|
9656
|
+
wget: [
|
|
9657
|
+
"--output-document",
|
|
9658
|
+
"--output-file",
|
|
9659
|
+
"--append-output",
|
|
9660
|
+
"--directory-prefix",
|
|
9661
|
+
"--input-file",
|
|
9662
|
+
"--post-data",
|
|
9663
|
+
"--post-file",
|
|
9664
|
+
"--body-data",
|
|
9665
|
+
"--body-file",
|
|
9666
|
+
"--method",
|
|
9667
|
+
"--header",
|
|
9668
|
+
"--user-agent",
|
|
9669
|
+
"--user",
|
|
9670
|
+
"--password",
|
|
9671
|
+
"--tries",
|
|
9672
|
+
"--timeout"
|
|
9673
|
+
],
|
|
9674
|
+
aria2c: ["--out", "--dir", "--input-file"]
|
|
9675
|
+
};
|
|
9676
|
+
DOWNLOAD_OUTPUT_FLAGS = {
|
|
9677
|
+
curl: ["-o", "--output", "-D", "--dump-header", "-c", "--cookie-jar"],
|
|
9678
|
+
wget: ["-O", "--output-document", "-o", "--output-file", "-a", "--append-output"],
|
|
9679
|
+
aria2c: ["-o", "--out"]
|
|
9680
|
+
};
|
|
9681
|
+
DOWNLOAD_DIR_FLAGS = {
|
|
9682
|
+
curl: ["--output-dir"],
|
|
9683
|
+
wget: ["-P", "--directory-prefix"],
|
|
9684
|
+
aria2c: ["-d", "--dir"]
|
|
9685
|
+
};
|
|
9062
9686
|
base = (s) => (s ?? "").replace(/\/+$/, "").split("/").pop() ?? "";
|
|
9063
9687
|
quoteGlob = (s) => s.replace(/[\\*?\[\]{}()!+@]/g, "\\$&");
|
|
9064
9688
|
asWord = (p) => ({ value: p.value, ...p.glob ? { pattern: p.pattern } : {} });
|
|
@@ -9083,6 +9707,18 @@ var init_dist = __esm({
|
|
|
9083
9707
|
SYSTEM_AUTH_DIRS = ["/etc/security", "/etc/pam.d", "/etc/sudoers.d"];
|
|
9084
9708
|
PASSWORD_HASH_FILES = ["/etc/shadow", "/etc/gshadow"];
|
|
9085
9709
|
STARTUP_NAMES = [".bashrc", ".zshrc", ".profile", ".bash_profile"];
|
|
9710
|
+
SYSTEM_DIRS = [
|
|
9711
|
+
"/usr",
|
|
9712
|
+
"/etc",
|
|
9713
|
+
"/boot",
|
|
9714
|
+
"/bin",
|
|
9715
|
+
"/sbin",
|
|
9716
|
+
"/lib",
|
|
9717
|
+
"/lib64",
|
|
9718
|
+
"/var/log"
|
|
9719
|
+
];
|
|
9720
|
+
SYSTEM_DIRS_EXEMPT = ["/usr/local", "/usr/src"];
|
|
9721
|
+
UPLOAD_SYSTEM_DIRS = [...SYSTEM_DIRS, "/proc", "/sys", "/root"];
|
|
9086
9722
|
AGENT_INSTRUCTION_FILES = [
|
|
9087
9723
|
".gitlab-ci.yml",
|
|
9088
9724
|
"CLAUDE.md",
|
|
@@ -9100,6 +9736,7 @@ var init_dist = __esm({
|
|
|
9100
9736
|
".husky"
|
|
9101
9737
|
];
|
|
9102
9738
|
LEGACY_SYSTEM_PATH_RE = /^(?:[a-zA-Z]:)?\/etc\/(?:passwd|shadow|sudoers)$/;
|
|
9739
|
+
exempt = (p) => SYSTEM_DIRS_EXEMPT.some((d) => within(p, d));
|
|
9103
9740
|
RANK = { allow: 0, review: 1, block: 2 };
|
|
9104
9741
|
BIP39_ENGLISH = `abandon
|
|
9105
9742
|
ability
|
|
@@ -11426,15 +12063,20 @@ zoo`.split("\n");
|
|
|
11426
12063
|
IDENTIFIER_VALUE_RE = /^[A-Za-z_.-]+$/;
|
|
11427
12064
|
PATH_VALUE_RE = /^(?:\/|\.{1,2}\/|~\/)/;
|
|
11428
12065
|
DOTTED_REFERENCE_RE = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)+$/;
|
|
12066
|
+
CONCATENATION_RE = /^\+[A-Za-z_$][\w$]*\./;
|
|
11429
12067
|
EMAIL_VALUE_RE = /^[^@\s]+@[^@\s]+\.[A-Za-z]{2,}$/;
|
|
11430
12068
|
FIXTURE_NAME_RE = /(?:fake|dummy|example|sample|mock|stub|placeholder|test)/i;
|
|
11431
|
-
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;
|
|
12069
|
+
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;
|
|
11432
12070
|
QUERY_CREDENTIAL_VALUE_RE = /=([A-Za-z0-9%+/_.~-]{8,512}={0,2})$/;
|
|
11433
12071
|
OTP_SECRET_RE = /^[A-Z2-7]{16,}=*$/i;
|
|
11434
|
-
|
|
12072
|
+
OTP_DOC_SECRETS = /* @__PURE__ */ new Set(["JBSWY3DPEHPK3PXP", "GEZDGNBVGY3TQOJQ"]);
|
|
12073
|
+
ASSIGNED_SECRET_VALUE_RE = /(?::=|=>|[:=])[ \t]{0,8}\\?["'`]?([A-Za-z0-9+/_.!@#$%^*~-]{12,512}={0,2})$/;
|
|
11435
12074
|
ASSIGNED_SECRET_NAME_RE = /^([A-Za-z0-9_]+)/;
|
|
11436
|
-
NAME_KEYWORD_RE = /^(?:password|passphrase|passwd|pwd|secret|token|api[_-]?key|auth[_-]?key|private[_-]?key|access[_-]?key|client[_-]?secret)/i;
|
|
12075
|
+
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;
|
|
11437
12076
|
ARN_CONTEXT_RE = /\barn:[A-Za-z0-9-]*:[^\s"'`]*$/i;
|
|
12077
|
+
BASIC_VALUE_RE = /([A-Za-z0-9+/]{8,512}={0,2})$/;
|
|
12078
|
+
BASIC_PAIR_RE = /^([^:\s]{1,128}):(\S{8,256})$/;
|
|
12079
|
+
OTP_CAPTURE_RE = /["']([A-Z2-7]{16,128}=*)["']$/i;
|
|
11438
12080
|
DLP_PATTERNS = [
|
|
11439
12081
|
// ── AWS ───────────────────────────────────────────────────────────────────
|
|
11440
12082
|
{
|
|
@@ -11772,6 +12414,22 @@ zoo`.split("\n");
|
|
|
11772
12414
|
contextBoost: true,
|
|
11773
12415
|
minEntropy: 3
|
|
11774
12416
|
},
|
|
12417
|
+
// ── HTTP Basic credential ─────────────────────────────────────────────────
|
|
12418
|
+
// `Authorization: Basic <base64 of user:password>`, or the bare scheme in a
|
|
12419
|
+
// config list. The validator decodes the value and accepts only a
|
|
12420
|
+
// `user:password` whose password looks generated (letters and digits, 8+
|
|
12421
|
+
// characters, the query-credential rule), so prose ("Basic authentication")
|
|
12422
|
+
// and documentation pairs (`admin:changeme`, `user:pass1234`) stay allowed.
|
|
12423
|
+
// review: the same weight as a bare Bearer token.
|
|
12424
|
+
{
|
|
12425
|
+
name: "HTTP Basic Credential",
|
|
12426
|
+
regex: /\bBasic[ \t]{1,8}([A-Za-z0-9+/]{8,512}={0,2})/i,
|
|
12427
|
+
severity: "review",
|
|
12428
|
+
keywords: ["basic"],
|
|
12429
|
+
validate: validateBasicCredential,
|
|
12430
|
+
redactGroup: 1,
|
|
12431
|
+
argumentsOnly: true
|
|
12432
|
+
},
|
|
11775
12433
|
// ── Resend ────────────────────────────────────────────────────────────────
|
|
11776
12434
|
{
|
|
11777
12435
|
name: "Resend API Key",
|
|
@@ -11913,6 +12571,19 @@ zoo`.split("\n");
|
|
|
11913
12571
|
validate: validateQueryCredential,
|
|
11914
12572
|
argumentsOnly: true
|
|
11915
12573
|
},
|
|
12574
|
+
// ── OTP secret in a constructor ───────────────────────────────────────────
|
|
12575
|
+
// `ROTP::TOTP.new('<base32>')`, `pyotp.TOTP("<base32>")`, `new HOTP('...')`:
|
|
12576
|
+
// the shared secret of a one-time-password generator written as a literal.
|
|
12577
|
+
// The otpauth URI form is `Credential in URL Query`. review.
|
|
12578
|
+
{
|
|
12579
|
+
name: "OTP Secret",
|
|
12580
|
+
regex: /\b(?:TOTP|HOTP|OTP)(?:\.new|\.create)?[ \t]{0,8}\(?[ \t]{0,8}["']([A-Z2-7]{16,128}=*)["']/i,
|
|
12581
|
+
severity: "review",
|
|
12582
|
+
keywords: ["otp"],
|
|
12583
|
+
validate: validateOtpSecret,
|
|
12584
|
+
redactGroup: 1,
|
|
12585
|
+
argumentsOnly: true
|
|
12586
|
+
},
|
|
11916
12587
|
// ── Secret assigned to a telling name ─────────────────────────────────────
|
|
11917
12588
|
// `password: ...`, `JWT_SECRET=...`, `api_key = "..."`. The name alone is
|
|
11918
12589
|
// not a finding: most such lines hold a reference (`process.env.X`), a
|
|
@@ -11920,12 +12591,14 @@ zoo`.split("\n");
|
|
|
11920
12591
|
// generated: 12+ characters, not an identifier, Shannon entropy of 3.5
|
|
11921
12592
|
// bits per character or more. review, never block (node9Firewall#348,
|
|
11922
12593
|
// item 4). Tuned on CredData's public split; its look-alike lines are the
|
|
11923
|
-
// negative control.
|
|
12594
|
+
// negative control. A quote may be escaped (`\"access_token\":\"...\"`):
|
|
12595
|
+
// JSON written inside a string literal. The two optional quotes after the
|
|
12596
|
+
// name are written out, not as a counted group, to keep star height 1.
|
|
11924
12597
|
{
|
|
11925
12598
|
name: "Assigned Secret",
|
|
11926
|
-
regex: /(?:password|passphrase|passwd|pwd|secret|token|api[_-]?key|auth[_-]?key|private[_-]?key|access[_-]?key|client[_-]?secret)[A-Za-z0-9_]{0,40}["'\]]
|
|
12599
|
+
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,
|
|
11927
12600
|
severity: "review",
|
|
11928
|
-
keywords: ["
|
|
12601
|
+
keywords: ["pass", "pwd", "secret", "token", "key", "ticket"],
|
|
11929
12602
|
validate: validateAssignedSecret,
|
|
11930
12603
|
argumentsOnly: true
|
|
11931
12604
|
}
|
|
@@ -12193,7 +12866,169 @@ zoo`.split("\n");
|
|
|
12193
12866
|
]);
|
|
12194
12867
|
PY_SPEC_RE = /^([A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?)(?:\[[^\]]*\])?\s*(.*)$/;
|
|
12195
12868
|
PY_EXACT_RE = /^===?\s*v?(\d+(?:\.\d+)*(?:[a-zA-Z0-9.+!-]*))$/;
|
|
12869
|
+
EXACT_VERSION_RE = /^=?v?(\d+(?:\.\d+)*(?:[-+][0-9A-Za-z][0-9A-Za-z.+-]*)?)$/;
|
|
12870
|
+
GEM_NAME_RE = /^[\p{L}\p{N}][\p{L}\p{N}_.-]*$/u;
|
|
12871
|
+
GEM_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
12872
|
+
"-v",
|
|
12873
|
+
"--version",
|
|
12874
|
+
"-i",
|
|
12875
|
+
"--install-dir",
|
|
12876
|
+
"-n",
|
|
12877
|
+
"--bindir",
|
|
12878
|
+
"--document",
|
|
12879
|
+
"--build-root",
|
|
12880
|
+
"-P",
|
|
12881
|
+
"--trust-policy",
|
|
12882
|
+
"-s",
|
|
12883
|
+
"--source",
|
|
12884
|
+
"-p",
|
|
12885
|
+
"--http-proxy",
|
|
12886
|
+
"--platform",
|
|
12887
|
+
"-g",
|
|
12888
|
+
"--file",
|
|
12889
|
+
"--without"
|
|
12890
|
+
]);
|
|
12891
|
+
BUNDLE_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
12892
|
+
"-v",
|
|
12893
|
+
"--version",
|
|
12894
|
+
"-g",
|
|
12895
|
+
"--group",
|
|
12896
|
+
"-s",
|
|
12897
|
+
"--source",
|
|
12898
|
+
"-r",
|
|
12899
|
+
"--require",
|
|
12900
|
+
"--path",
|
|
12901
|
+
"--git",
|
|
12902
|
+
"--github",
|
|
12903
|
+
"--branch",
|
|
12904
|
+
"--ref",
|
|
12905
|
+
"--glob"
|
|
12906
|
+
]);
|
|
12907
|
+
CRATE_NAME_RE = /^[\p{L}\p{N}][\p{L}\p{N}_-]*$/u;
|
|
12908
|
+
CARGO_ADD_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
12909
|
+
"-F",
|
|
12910
|
+
"--features",
|
|
12911
|
+
"--rename",
|
|
12912
|
+
"--registry",
|
|
12913
|
+
"--manifest-path",
|
|
12914
|
+
"-p",
|
|
12915
|
+
"--package",
|
|
12916
|
+
"--git",
|
|
12917
|
+
"--branch",
|
|
12918
|
+
"--tag",
|
|
12919
|
+
"--rev",
|
|
12920
|
+
"--path",
|
|
12921
|
+
"--base",
|
|
12922
|
+
"--target",
|
|
12923
|
+
"--color",
|
|
12924
|
+
"--config",
|
|
12925
|
+
"-Z",
|
|
12926
|
+
"--lockfile-path"
|
|
12927
|
+
]);
|
|
12928
|
+
CARGO_INSTALL_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
12929
|
+
"--version",
|
|
12930
|
+
"--vers",
|
|
12931
|
+
"--git",
|
|
12932
|
+
"--branch",
|
|
12933
|
+
"--tag",
|
|
12934
|
+
"--rev",
|
|
12935
|
+
"--path",
|
|
12936
|
+
"--root",
|
|
12937
|
+
"--index",
|
|
12938
|
+
"--registry",
|
|
12939
|
+
"-F",
|
|
12940
|
+
"--features",
|
|
12941
|
+
"--profile",
|
|
12942
|
+
"--target",
|
|
12943
|
+
"--target-dir",
|
|
12944
|
+
"-j",
|
|
12945
|
+
"--jobs",
|
|
12946
|
+
"--bin",
|
|
12947
|
+
"--example",
|
|
12948
|
+
"--color",
|
|
12949
|
+
"--config",
|
|
12950
|
+
"-Z"
|
|
12951
|
+
]);
|
|
12952
|
+
GO_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
12953
|
+
"-C",
|
|
12954
|
+
"-tags",
|
|
12955
|
+
"-ldflags",
|
|
12956
|
+
"-gcflags",
|
|
12957
|
+
"-asmflags",
|
|
12958
|
+
"-mod",
|
|
12959
|
+
"-modfile",
|
|
12960
|
+
"-o",
|
|
12961
|
+
"-p",
|
|
12962
|
+
"-pkgdir",
|
|
12963
|
+
"-toolexec",
|
|
12964
|
+
"-exec",
|
|
12965
|
+
"-overlay",
|
|
12966
|
+
"-pgo",
|
|
12967
|
+
"-buildmode",
|
|
12968
|
+
"-compiler",
|
|
12969
|
+
"-gccgoflags",
|
|
12970
|
+
"-installsuffix",
|
|
12971
|
+
"-covermode",
|
|
12972
|
+
"-coverpkg"
|
|
12973
|
+
]);
|
|
12974
|
+
GO_PATH_RE = /^[\p{L}\p{N}-]+(?:\.[\p{L}\p{N}-]+)+(?:\/[\p{L}\p{N}_.~+-]+)*$/u;
|
|
12975
|
+
NUGET_NAME_RE = /^[\p{L}\p{N}_][\p{L}\p{N}_.-]*$/u;
|
|
12976
|
+
DOTNET_ADD_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
12977
|
+
"-v",
|
|
12978
|
+
"--version",
|
|
12979
|
+
"-s",
|
|
12980
|
+
"--source",
|
|
12981
|
+
"-f",
|
|
12982
|
+
"--framework",
|
|
12983
|
+
"--package-directory"
|
|
12984
|
+
]);
|
|
12985
|
+
DOTNET_TOOL_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
12986
|
+
"--version",
|
|
12987
|
+
"--add-source",
|
|
12988
|
+
"--configfile",
|
|
12989
|
+
"--framework",
|
|
12990
|
+
"--tool-path",
|
|
12991
|
+
"-a",
|
|
12992
|
+
"--arch",
|
|
12993
|
+
"--tool-manifest",
|
|
12994
|
+
"-v",
|
|
12995
|
+
"--verbosity"
|
|
12996
|
+
]);
|
|
12997
|
+
NUGET_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
12998
|
+
"-version",
|
|
12999
|
+
"-outputdirectory",
|
|
13000
|
+
"-o",
|
|
13001
|
+
"-source",
|
|
13002
|
+
"-configfile",
|
|
13003
|
+
"-fallbacksource",
|
|
13004
|
+
"-framework",
|
|
13005
|
+
"-packagesavemode",
|
|
13006
|
+
"-dependencyversion",
|
|
13007
|
+
"-solutiondirectory",
|
|
13008
|
+
"-verbosity"
|
|
13009
|
+
]);
|
|
13010
|
+
MAVEN_COORD_RE = /^[\p{L}\p{N}_.-]+:[\p{L}\p{N}_.-]+$/u;
|
|
13011
|
+
COMPOSER_NAME_RE = /^[\p{L}\p{N}][\p{L}\p{N}_.-]*\/[\p{L}\p{N}][\p{L}\p{N}_.-]*$/u;
|
|
13012
|
+
COMPOSER_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
13013
|
+
"-d",
|
|
13014
|
+
"--working-dir",
|
|
13015
|
+
"--stability",
|
|
13016
|
+
"--repository",
|
|
13017
|
+
"--add-repository"
|
|
13018
|
+
]);
|
|
13019
|
+
EDITOR_HEADS = ["code", "code-insiders", "codium", "vscodium", "cursor"];
|
|
13020
|
+
EXTENSION_ID_RE = /^[\p{L}\p{N}][\p{L}\p{N}_-]*\.[\p{L}\p{N}][\p{L}\p{N}_.-]*$/u;
|
|
12196
13021
|
MANAGER_HEADS = /* @__PURE__ */ new Set([
|
|
13022
|
+
"gem",
|
|
13023
|
+
"bundle",
|
|
13024
|
+
"cargo",
|
|
13025
|
+
"go",
|
|
13026
|
+
"dotnet",
|
|
13027
|
+
"nuget",
|
|
13028
|
+
"mvn",
|
|
13029
|
+
"mvnw",
|
|
13030
|
+
"composer",
|
|
13031
|
+
...EDITOR_HEADS,
|
|
12197
13032
|
"npm",
|
|
12198
13033
|
"npx",
|
|
12199
13034
|
"pnpm",
|
|
@@ -12272,6 +13107,7 @@ zoo`.split("\n");
|
|
|
12272
13107
|
MAP_GOVERNED = /* @__PURE__ */ new Set([
|
|
12273
13108
|
"files.shell-startup",
|
|
12274
13109
|
"files.password-hashes",
|
|
13110
|
+
"files.system-dirs",
|
|
12275
13111
|
"commands.inline-exec",
|
|
12276
13112
|
"commands.eval-dynamic",
|
|
12277
13113
|
"commands.curl-pipe-shell",
|
|
@@ -12540,7 +13376,6 @@ zoo`.split("\n");
|
|
|
12540
13376
|
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/;
|
|
12541
13377
|
PII_CC15_RE = /\b3[47]\d{2}[-\s]?\d{6}[-\s]?\d{5}\b/;
|
|
12542
13378
|
PII_IBAN_RE = /\b[A-Z]{2}\d{2}(?:[A-Z0-9]|[ -][A-Z0-9]){11,30}\b/;
|
|
12543
|
-
NUMBER_LEAD_RE = /[\s:=;([{"'+-]/;
|
|
12544
13379
|
REALTIME_PII_PATTERNS = ["SSN", "Credit Card", "IBAN"];
|
|
12545
13380
|
MAX_PII_SCAN_BYTES = 1e5;
|
|
12546
13381
|
MASKED_KINDS = /* @__PURE__ */ new Set(["email", "iban", "account"]);
|
|
@@ -12762,7 +13597,7 @@ zoo`.split("\n");
|
|
|
12762
13597
|
{ view: "separators-stripped", decoder: "separators", stripped: true }
|
|
12763
13598
|
];
|
|
12764
13599
|
LONG_OUTPUT_THRESHOLD_BYTES = 100 * 1024;
|
|
12765
|
-
CANONICAL_EXTRACTOR_VERSION = "canonical-
|
|
13600
|
+
CANONICAL_EXTRACTOR_VERSION = "canonical-v28";
|
|
12766
13601
|
DEDUPE_PREVIEW_LEN = 120;
|
|
12767
13602
|
ENGINE_VERSION = "1.4.0";
|
|
12768
13603
|
}
|
|
@@ -18122,7 +18957,20 @@ function ecoDir(eco) {
|
|
|
18122
18957
|
return path24.join(osvRoot(), eco);
|
|
18123
18958
|
}
|
|
18124
18959
|
function indexKey(eco, name) {
|
|
18125
|
-
|
|
18960
|
+
switch (eco) {
|
|
18961
|
+
case "PyPI":
|
|
18962
|
+
return normalizePyPiName(name);
|
|
18963
|
+
case "crates.io":
|
|
18964
|
+
return name.toLowerCase().replace(/_/g, "-");
|
|
18965
|
+
case "Go":
|
|
18966
|
+
case "Maven":
|
|
18967
|
+
return name;
|
|
18968
|
+
default:
|
|
18969
|
+
return name.toLowerCase();
|
|
18970
|
+
}
|
|
18971
|
+
}
|
|
18972
|
+
function recordEcosystemMatches(eco, recordEco) {
|
|
18973
|
+
return typeof recordEco === "string" && (recordEco === eco || recordEco.startsWith(`${eco}:`));
|
|
18126
18974
|
}
|
|
18127
18975
|
function shardOf(key) {
|
|
18128
18976
|
return createHash5("sha1").update(key).digest("hex").slice(0, 2);
|
|
@@ -18168,7 +19016,8 @@ function entriesFromRecord(eco, record2) {
|
|
|
18168
19016
|
const modifiedMs = typeof r.modified === "string" ? Date.parse(r.modified) || 0 : 0;
|
|
18169
19017
|
const rows = /* @__PURE__ */ new Map();
|
|
18170
19018
|
for (const a of r.affected ?? []) {
|
|
18171
|
-
if (a?.package?.ecosystem
|
|
19019
|
+
if (!recordEcosystemMatches(eco, a?.package?.ecosystem) || typeof a?.package?.name !== "string")
|
|
19020
|
+
continue;
|
|
18172
19021
|
const key = indexKey(eco, a.package.name);
|
|
18173
19022
|
const entry = rows.get(key) ?? { id: r.id };
|
|
18174
19023
|
if (Array.isArray(a.versions)) {
|
|
@@ -18408,15 +19257,18 @@ async function pypiInfo(name, version2) {
|
|
|
18408
19257
|
};
|
|
18409
19258
|
}
|
|
18410
19259
|
async function registryInfo(eco, name, version2, freshWindowMs, raw) {
|
|
18411
|
-
|
|
19260
|
+
if (eco === "npm") return npmInfo(name, version2, freshWindowMs, raw);
|
|
19261
|
+
if (eco === "PyPI") return pypiInfo(name, version2);
|
|
19262
|
+
return null;
|
|
18412
19263
|
}
|
|
18413
|
-
var TIMEOUT_MS2, MAX_BYTES;
|
|
19264
|
+
var TIMEOUT_MS2, MAX_BYTES, HAS_REGISTRY_CLIENT;
|
|
18414
19265
|
var init_registry2 = __esm({
|
|
18415
19266
|
"src/supply-chain/registry.ts"() {
|
|
18416
19267
|
"use strict";
|
|
18417
19268
|
init_net();
|
|
18418
19269
|
TIMEOUT_MS2 = 1500;
|
|
18419
19270
|
MAX_BYTES = 2 * 1024 * 1024;
|
|
19271
|
+
HAS_REGISTRY_CLIENT = /* @__PURE__ */ new Set(["npm", "PyPI"]);
|
|
18420
19272
|
}
|
|
18421
19273
|
});
|
|
18422
19274
|
|
|
@@ -18463,25 +19315,39 @@ function commandOf(toolName, args) {
|
|
|
18463
19315
|
return typeof a.command === "string" ? a.command : typeof a.cmd === "string" ? a.cmd : null;
|
|
18464
19316
|
}
|
|
18465
19317
|
function label(p, version2) {
|
|
18466
|
-
return `${p.ecosystem
|
|
19318
|
+
return `${KIND[p.ecosystem]} "${p.name}${version2 ? `@${version2}` : ""}"`;
|
|
19319
|
+
}
|
|
19320
|
+
function osvNames(p) {
|
|
19321
|
+
if (p.ecosystem !== "Go") return [p.name];
|
|
19322
|
+
const parts = p.name.split("/");
|
|
19323
|
+
const out = [];
|
|
19324
|
+
for (let n = parts.length; n >= 2 && out.length < 6; n--) out.push(parts.slice(0, n).join("/"));
|
|
19325
|
+
return out;
|
|
18467
19326
|
}
|
|
18468
19327
|
async function maliciousFor(p, version2, cfg, misses, networkCapped) {
|
|
18469
|
-
const
|
|
18470
|
-
const
|
|
18471
|
-
|
|
19328
|
+
const names = osvNames(p);
|
|
19329
|
+
const local = names.map((n) => lookupIndex(p.ecosystem, n));
|
|
19330
|
+
const hits2 = local.flatMap((l) => l.status === "hit" ? l.entries : []);
|
|
19331
|
+
const entries = hits2.length > 0 ? hits2 : null;
|
|
19332
|
+
const freshClean = local.every((l) => l.status === "clean" && l.fresh);
|
|
19333
|
+
const status = local.some((l) => l.status === "unavailable") ? "unavailable" : local[0].status;
|
|
19334
|
+
if (!entries && !freshClean) {
|
|
18472
19335
|
if (!cfg.onlineFallback || networkCapped) {
|
|
18473
19336
|
misses.push(
|
|
18474
|
-
`${label(p, version2)}: local index ${
|
|
19337
|
+
`${label(p, version2)}: local index ${status}, no online lookup (${networkCapped ? "over the per-command network cap" : "online fallback off"})`
|
|
18475
19338
|
);
|
|
18476
19339
|
return null;
|
|
18477
19340
|
}
|
|
18478
|
-
let
|
|
19341
|
+
let perName = null;
|
|
18479
19342
|
try {
|
|
18480
|
-
|
|
19343
|
+
perName = await queryOsvMalicious(
|
|
19344
|
+
names.map((name) => ({ ecosystem: p.ecosystem, name, version: version2 }))
|
|
19345
|
+
);
|
|
18481
19346
|
} catch (err2) {
|
|
18482
19347
|
misses.push(`${label(p, version2)}: OSV online ${err2.message}`);
|
|
18483
19348
|
return null;
|
|
18484
19349
|
}
|
|
19350
|
+
const ids = perName ? [[...new Set(perName.flat())]] : null;
|
|
18485
19351
|
if (!ids) {
|
|
18486
19352
|
misses.push(`${label(p, version2)}: OSV online unavailable`);
|
|
18487
19353
|
return null;
|
|
@@ -18551,7 +19417,7 @@ async function checkOne(p, cfg, misses, networkCapped, cwd) {
|
|
|
18551
19417
|
}
|
|
18552
19418
|
const findings = [];
|
|
18553
19419
|
let info = null;
|
|
18554
|
-
const wantsRegistry = cfg.newPackage !== "off" || cfg.installScript !== "off";
|
|
19420
|
+
const wantsRegistry = (cfg.newPackage !== "off" || cfg.installScript !== "off") && HAS_REGISTRY_CLIENT.has(p.ecosystem);
|
|
18555
19421
|
if (wantsRegistry && !networkCapped) {
|
|
18556
19422
|
try {
|
|
18557
19423
|
const windowMs = cfg.newPackage === "off" ? 0 : cfg.maxAgeHours * 36e5;
|
|
@@ -18631,7 +19497,7 @@ async function runPackageCheck(toolName, args, cfg, cwd) {
|
|
|
18631
19497
|
return { ...ALLOW, misses: [`package check error: ${err2?.message ?? err2}`] };
|
|
18632
19498
|
}
|
|
18633
19499
|
}
|
|
18634
|
-
var MAX_NETWORK_PACKAGES, ALLOW;
|
|
19500
|
+
var MAX_NETWORK_PACKAGES, ALLOW, KIND;
|
|
18635
19501
|
var init_check = __esm({
|
|
18636
19502
|
"src/supply-chain/check.ts"() {
|
|
18637
19503
|
"use strict";
|
|
@@ -18642,6 +19508,17 @@ var init_check = __esm({
|
|
|
18642
19508
|
init_local_resolve();
|
|
18643
19509
|
MAX_NETWORK_PACKAGES = 10;
|
|
18644
19510
|
ALLOW = { verdict: "allow", findings: [], misses: [], checked: 0 };
|
|
19511
|
+
KIND = {
|
|
19512
|
+
npm: "npm package",
|
|
19513
|
+
PyPI: "PyPI package",
|
|
19514
|
+
RubyGems: "RubyGems package",
|
|
19515
|
+
"crates.io": "crates.io package",
|
|
19516
|
+
Go: "Go module",
|
|
19517
|
+
NuGet: "NuGet package",
|
|
19518
|
+
Maven: "Maven artifact",
|
|
19519
|
+
Packagist: "Packagist package",
|
|
19520
|
+
VSCode: "VS Code extension"
|
|
19521
|
+
};
|
|
18645
19522
|
}
|
|
18646
19523
|
});
|
|
18647
19524
|
|
|
@@ -33873,7 +34750,17 @@ var init_osv_sync = __esm({
|
|
|
33873
34750
|
init_zip();
|
|
33874
34751
|
init_osv_index();
|
|
33875
34752
|
init_net();
|
|
33876
|
-
ECOSYSTEMS2 = [
|
|
34753
|
+
ECOSYSTEMS2 = [
|
|
34754
|
+
"npm",
|
|
34755
|
+
"PyPI",
|
|
34756
|
+
"RubyGems",
|
|
34757
|
+
"crates.io",
|
|
34758
|
+
"Go",
|
|
34759
|
+
"NuGet",
|
|
34760
|
+
"Maven",
|
|
34761
|
+
"Packagist",
|
|
34762
|
+
"VSCode"
|
|
34763
|
+
];
|
|
33877
34764
|
SYNC_INTERVAL_MS2 = 6 * 60 * 60 * 1e3;
|
|
33878
34765
|
FIRST_SYNC_DELAY_MS = 2 * 60 * 1e3;
|
|
33879
34766
|
FULL_REBUILD_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
@@ -35864,14 +36751,14 @@ async function onboardMachine(apiKey, opts = {}) {
|
|
|
35864
36751
|
detail: `${sync.rules} cloud rule${sync.rules === 1 ? "" : "s"} active`
|
|
35865
36752
|
} : { name: "policy-sync", ok: false, detail: sync.reason }
|
|
35866
36753
|
);
|
|
35867
|
-
const
|
|
36754
|
+
const push2 = await runPolicyPush();
|
|
35868
36755
|
steps.push(
|
|
35869
|
-
|
|
36756
|
+
push2.ok ? { name: "register", ok: true, detail: "machine visible in the dashboard" } : { name: "register", ok: false, detail: push2.reason }
|
|
35870
36757
|
);
|
|
35871
36758
|
const healed = ensureAutostartHealthy(!!getConfig().settings.autoStartDaemon);
|
|
35872
36759
|
let detail = healed === "repaired" ? "autostart re-enabled (survives reboot)" : isDaemonRunning() ? "running" : healed === "unsupported" ? "no background service on this platform: starts on agent activity" : "starts on agent activity (make it survive reboots: node9 daemon install)";
|
|
35873
36760
|
let daemonOk = true;
|
|
35874
|
-
if (sync.ok &&
|
|
36761
|
+
if (sync.ok && push2.ok && isInteractive() && mayChangeService({ stdoutTTY: !!process.stdout.isTTY, ci: isCI() }) && autostartState() === "absent") {
|
|
35875
36762
|
try {
|
|
35876
36763
|
if (await confirm2({
|
|
35877
36764
|
message: "Run node9 in the background when this computer starts?",
|
|
@@ -65318,7 +66205,7 @@ function collectToolOutputText(toolResponse, opts = {}) {
|
|
|
65318
66205
|
let size = 0;
|
|
65319
66206
|
let fields = 0;
|
|
65320
66207
|
let truncated = false;
|
|
65321
|
-
const
|
|
66208
|
+
const push2 = (s) => {
|
|
65322
66209
|
if (s.length === 0) return true;
|
|
65323
66210
|
const room = maxBytes - size;
|
|
65324
66211
|
if (s.length > room) {
|
|
@@ -65336,7 +66223,7 @@ function collectToolOutputText(toolResponse, opts = {}) {
|
|
|
65336
66223
|
return true;
|
|
65337
66224
|
};
|
|
65338
66225
|
const walk2 = (v, depth) => {
|
|
65339
|
-
if (typeof v === "string") return
|
|
66226
|
+
if (typeof v === "string") return push2(v);
|
|
65340
66227
|
if (depth >= MAX_DEPTH5 || v === null || typeof v !== "object") return true;
|
|
65341
66228
|
const children = Array.isArray(v) ? v : Object.values(v);
|
|
65342
66229
|
for (const child of children) if (!walk2(child, depth + 1)) return false;
|
|
@@ -75963,7 +76850,7 @@ function addHost(list, host2) {
|
|
|
75963
76850
|
if (!cliGuardPolicyWrite(`egress ${list} ${host2}`)) return false;
|
|
75964
76851
|
return guard(() => addEgressHost(list, host2));
|
|
75965
76852
|
}
|
|
75966
|
-
function
|
|
76853
|
+
function exempt2(address) {
|
|
75967
76854
|
if (!cliGuardPolicyWrite(`egress exempt ${address}`)) return false;
|
|
75968
76855
|
return guard(() => addSsrfExemption(address));
|
|
75969
76856
|
}
|
|
@@ -76146,7 +77033,7 @@ function registerEgressCommand(program2) {
|
|
|
76146
77033
|
process.exitCode = 1;
|
|
76147
77034
|
return;
|
|
76148
77035
|
}
|
|
76149
|
-
if (!
|
|
77036
|
+
if (!exempt2(a)) return;
|
|
76150
77037
|
console.log(chalk34.green(`
|
|
76151
77038
|
\u2713 ${a} is exempt from the floor.`));
|
|
76152
77039
|
if (!m)
|
|
@@ -77935,17 +78822,18 @@ function registerSessionExposureCommand(program2) {
|
|
|
77935
78822
|
init_osv_index();
|
|
77936
78823
|
init_osv_sync();
|
|
77937
78824
|
import chalk42 from "chalk";
|
|
78825
|
+
var ECO_WIDTH = Math.max(...ECOSYSTEMS2.map((e) => e.length));
|
|
77938
78826
|
function registerPackageIndexCommand(program2) {
|
|
77939
78827
|
const cmd = program2.command("package-index").description("Local OSV malicious-package index used to check installs before they run");
|
|
77940
78828
|
cmd.command("status").description("Show when each ecosystem was last synced and how many records it holds").action(() => {
|
|
77941
78829
|
for (const eco of ECOSYSTEMS2) {
|
|
77942
78830
|
const meta = readMeta(eco);
|
|
77943
78831
|
if (!meta) {
|
|
77944
|
-
console.log(`${eco.padEnd(
|
|
78832
|
+
console.log(`${eco.padEnd(ECO_WIDTH)} ${chalk42.yellow("not synced")}`);
|
|
77945
78833
|
continue;
|
|
77946
78834
|
}
|
|
77947
78835
|
console.log(
|
|
77948
|
-
`${eco.padEnd(
|
|
78836
|
+
`${eco.padEnd(ECO_WIDTH)} ${meta.records} records synced ${meta.syncedAt} (${meta.mode})`
|
|
77949
78837
|
);
|
|
77950
78838
|
}
|
|
77951
78839
|
});
|
|
@@ -77955,9 +78843,9 @@ function registerPackageIndexCommand(program2) {
|
|
|
77955
78843
|
for (const r of results) {
|
|
77956
78844
|
if (r.error) {
|
|
77957
78845
|
failed = true;
|
|
77958
|
-
console.error(`${r.ecosystem.padEnd(
|
|
78846
|
+
console.error(`${r.ecosystem.padEnd(ECO_WIDTH)} ${chalk42.red("failed")}: ${r.error}`);
|
|
77959
78847
|
} else {
|
|
77960
|
-
console.log(`${r.ecosystem.padEnd(
|
|
78848
|
+
console.log(`${r.ecosystem.padEnd(ECO_WIDTH)} ${r.mode}: ${r.records} records`);
|
|
77961
78849
|
}
|
|
77962
78850
|
}
|
|
77963
78851
|
process.exitCode = failed ? 1 : 0;
|