@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.js
CHANGED
|
@@ -1193,6 +1193,120 @@ function expandPlainParam(p) {
|
|
|
1193
1193
|
if (recorded !== void 0) return recorded;
|
|
1194
1194
|
return HOME_VARIABLES.has(p.Param?.Value) ? "~" : void 0;
|
|
1195
1195
|
}
|
|
1196
|
+
function functionBodies(f) {
|
|
1197
|
+
const out = /* @__PURE__ */ new Map();
|
|
1198
|
+
syntax.Walk(f, (node) => {
|
|
1199
|
+
const n = node;
|
|
1200
|
+
if (n && syntax.NodeType(n) === "FuncDecl" && n.Name?.Value && n.Body) {
|
|
1201
|
+
const list = out.get(n.Name.Value) ?? [];
|
|
1202
|
+
list.push(n.Body);
|
|
1203
|
+
out.set(n.Name.Value, list);
|
|
1204
|
+
}
|
|
1205
|
+
return true;
|
|
1206
|
+
});
|
|
1207
|
+
return out;
|
|
1208
|
+
}
|
|
1209
|
+
function rebindsPositionals(body) {
|
|
1210
|
+
let found = false;
|
|
1211
|
+
syntax.Walk(body, (node) => {
|
|
1212
|
+
const n = node;
|
|
1213
|
+
if (found || !n) return false;
|
|
1214
|
+
if (syntax.NodeType(n) === "CallExpr") {
|
|
1215
|
+
const words = (n.Args || []).map((a) => resolveWordLiteral(a));
|
|
1216
|
+
if (words[0] === "shift") found = true;
|
|
1217
|
+
else if (words[0] === "set" && words.slice(1).some((w) => w === null || w === "--" || !/^[-+]/.test(w)))
|
|
1218
|
+
found = true;
|
|
1219
|
+
}
|
|
1220
|
+
return !found;
|
|
1221
|
+
});
|
|
1222
|
+
return found;
|
|
1223
|
+
}
|
|
1224
|
+
function tableBindings() {
|
|
1225
|
+
if (!assignmentTable) return "";
|
|
1226
|
+
const out = [];
|
|
1227
|
+
for (const [name, rec] of assignmentTable)
|
|
1228
|
+
if (rec.at < currentStmtOffset) out.push(`${name}=${rec.value ?? "\0"}`);
|
|
1229
|
+
return out.sort().join("");
|
|
1230
|
+
}
|
|
1231
|
+
function walkWithCalls(f, visit2) {
|
|
1232
|
+
const bodies = functionBodies(f);
|
|
1233
|
+
if (bodies.size === 0) {
|
|
1234
|
+
syntax.Walk(f, visit2);
|
|
1235
|
+
return;
|
|
1236
|
+
}
|
|
1237
|
+
const seenCalls = /* @__PURE__ */ new Set();
|
|
1238
|
+
const inline = (call) => {
|
|
1239
|
+
const words = (call.Args || []).map((a) => resolveWordLiteral(a));
|
|
1240
|
+
const list = words[0] ? bodies.get(words[0]) : void 0;
|
|
1241
|
+
if (!list || callDepth >= MAX_CALL_DEPTH) return;
|
|
1242
|
+
const args = words.slice(1);
|
|
1243
|
+
const key = `${words[0]}\0${JSON.stringify(args)}\0${tableBindings()}`;
|
|
1244
|
+
if (seenCalls.has(key)) return;
|
|
1245
|
+
seenCalls.add(key);
|
|
1246
|
+
const outerTable = assignmentTable;
|
|
1247
|
+
const outerOffset = currentStmtOffset;
|
|
1248
|
+
for (const body of list) {
|
|
1249
|
+
if (callWalks >= MAX_CALL_WALKS) return;
|
|
1250
|
+
callWalks++;
|
|
1251
|
+
const table = /* @__PURE__ */ new Map();
|
|
1252
|
+
for (const [name, rec] of outerTable ?? [])
|
|
1253
|
+
if (rec.at < outerOffset) table.set(name, { value: rec.value, at: -1 });
|
|
1254
|
+
for (const name of ["1", "2", "3", "4", "5", "6", "7", "8", "9", "@", "*", "#"])
|
|
1255
|
+
table.delete(name);
|
|
1256
|
+
if (!rebindsPositionals(body)) {
|
|
1257
|
+
args.slice(0, 9).forEach((value, i) => table.set(String(i + 1), { value, at: -1 }));
|
|
1258
|
+
if (args.length === 1) {
|
|
1259
|
+
table.set("@", { value: args[0], at: -1 });
|
|
1260
|
+
table.set("*", { value: args[0], at: -1 });
|
|
1261
|
+
}
|
|
1262
|
+
table.set("#", { value: String(args.length), at: -1 });
|
|
1263
|
+
}
|
|
1264
|
+
assignmentTable = table;
|
|
1265
|
+
currentStmtOffset = Number.MAX_SAFE_INTEGER;
|
|
1266
|
+
callDepth++;
|
|
1267
|
+
try {
|
|
1268
|
+
if (body.Cmd && syntax.NodeType(body.Cmd) === "Block") recordTopLevelAssignments(body.Cmd);
|
|
1269
|
+
syntax.Walk(body, step);
|
|
1270
|
+
} finally {
|
|
1271
|
+
callDepth--;
|
|
1272
|
+
assignmentTable = outerTable;
|
|
1273
|
+
currentStmtOffset = outerOffset;
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
};
|
|
1277
|
+
const step = (node) => {
|
|
1278
|
+
if (!node) return false;
|
|
1279
|
+
const go = visit2(node);
|
|
1280
|
+
if (go !== false && syntax.NodeType(node) === "CallExpr") inline(node);
|
|
1281
|
+
return go;
|
|
1282
|
+
};
|
|
1283
|
+
syntax.Walk(f, step);
|
|
1284
|
+
}
|
|
1285
|
+
function withAssignments(f, body) {
|
|
1286
|
+
const outerTable = assignmentTable;
|
|
1287
|
+
const outerOffset = currentStmtOffset;
|
|
1288
|
+
const outerWalks = callWalks;
|
|
1289
|
+
callWalks = 0;
|
|
1290
|
+
assignmentTable = new Map(
|
|
1291
|
+
[...outerTable ?? []].map(([k, r]) => [k, { value: r.value, at: -1 }])
|
|
1292
|
+
);
|
|
1293
|
+
currentStmtOffset = Number.MAX_SAFE_INTEGER;
|
|
1294
|
+
try {
|
|
1295
|
+
recordTopLevelAssignments(f);
|
|
1296
|
+
return body();
|
|
1297
|
+
} finally {
|
|
1298
|
+
assignmentTable = outerTable;
|
|
1299
|
+
currentStmtOffset = outerOffset;
|
|
1300
|
+
callWalks = outerWalks;
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
function trackStatement(n) {
|
|
1304
|
+
try {
|
|
1305
|
+
currentStmtOffset = n.Pos().Offset();
|
|
1306
|
+
} catch {
|
|
1307
|
+
currentStmtOffset = Number.MAX_SAFE_INTEGER;
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1196
1310
|
function resolveWordLiteral(w) {
|
|
1197
1311
|
const parts = w?.Parts || [];
|
|
1198
1312
|
let s = "";
|
|
@@ -1313,27 +1427,31 @@ function extractShellDestinations(command) {
|
|
|
1313
1427
|
const out = [];
|
|
1314
1428
|
const seen = /* @__PURE__ */ new Set();
|
|
1315
1429
|
try {
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
const
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1430
|
+
withAssignments(
|
|
1431
|
+
f,
|
|
1432
|
+
() => walkWithCalls(f, (node) => {
|
|
1433
|
+
if (!node) return false;
|
|
1434
|
+
const n = node;
|
|
1435
|
+
if (syntax.NodeType(n) === "Stmt") trackStatement(n);
|
|
1436
|
+
if (syntax.NodeType(n) !== "CallExpr") return true;
|
|
1437
|
+
const callArgs = n.Args || [];
|
|
1438
|
+
if (callArgs.length === 0) return true;
|
|
1439
|
+
const words = callArgs.map((a) => resolveWordLiteral(a));
|
|
1440
|
+
const at = unwrapCommandHead(words);
|
|
1441
|
+
const name = (words[at] || "").toLowerCase().split("/").pop() ?? "";
|
|
1442
|
+
if (!NET_BINARIES.has(name)) return true;
|
|
1443
|
+
const rest = words.slice(at + 1);
|
|
1444
|
+
for (const raw of destTokensForBinary(name, rest)) {
|
|
1445
|
+
const host2 = parseDestHost(raw);
|
|
1446
|
+
if (!host2) continue;
|
|
1447
|
+
const key = `${name}:${host2}`;
|
|
1448
|
+
if (seen.has(key)) continue;
|
|
1449
|
+
seen.add(key);
|
|
1450
|
+
out.push({ host: host2, binary: name, raw });
|
|
1451
|
+
}
|
|
1452
|
+
return true;
|
|
1453
|
+
})
|
|
1454
|
+
);
|
|
1337
1455
|
} catch {
|
|
1338
1456
|
return out;
|
|
1339
1457
|
}
|
|
@@ -1345,39 +1463,45 @@ function extractShellDestTokens(command) {
|
|
|
1345
1463
|
const out = [];
|
|
1346
1464
|
const seen = /* @__PURE__ */ new Set();
|
|
1347
1465
|
try {
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
const
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
tok = tok.
|
|
1466
|
+
withAssignments(
|
|
1467
|
+
f,
|
|
1468
|
+
() => walkWithCalls(f, (node) => {
|
|
1469
|
+
if (!node) return false;
|
|
1470
|
+
const n = node;
|
|
1471
|
+
if (syntax.NodeType(n) === "Stmt") trackStatement(n);
|
|
1472
|
+
if (syntax.NodeType(n) !== "CallExpr") return true;
|
|
1473
|
+
const callArgs = n.Args || [];
|
|
1474
|
+
if (callArgs.length === 0) return true;
|
|
1475
|
+
const words = callArgs.map((a) => resolveWordLiteral(a));
|
|
1476
|
+
const head = unwrapCommandHead(words);
|
|
1477
|
+
const name = (words[head] || "").toLowerCase().split("/").pop() ?? "";
|
|
1478
|
+
if (!NET_BINARIES.has(name)) return true;
|
|
1479
|
+
const rest = words.slice(head + 1);
|
|
1480
|
+
for (const raw of destTokensForBinary(name, rest)) {
|
|
1481
|
+
if (!raw) continue;
|
|
1482
|
+
let tok = raw.trim();
|
|
1483
|
+
const scheme = /^[a-z][a-z0-9+.-]*:\/\//i.exec(tok);
|
|
1484
|
+
const hasScheme = scheme !== null;
|
|
1485
|
+
if (hasScheme) tok = tok.slice(scheme[0].length);
|
|
1486
|
+
tok = tok.split(/[/?#]/)[0];
|
|
1487
|
+
const at = tok.lastIndexOf("@");
|
|
1488
|
+
if (at >= 0) tok = tok.slice(at + 1);
|
|
1489
|
+
if (tok.startsWith("[")) {
|
|
1490
|
+
const close = tok.indexOf("]");
|
|
1491
|
+
if (close > 0) tok = tok.slice(0, close + 1);
|
|
1492
|
+
} else {
|
|
1493
|
+
tok = tok.split(":")[0];
|
|
1494
|
+
}
|
|
1495
|
+
if (!tok) continue;
|
|
1496
|
+
if (!hasScheme && /^\d+$/.test(tok) && Number(tok) < 16777216) continue;
|
|
1497
|
+
const key = `${name}:${tok}`;
|
|
1498
|
+
if (seen.has(key)) continue;
|
|
1499
|
+
seen.add(key);
|
|
1500
|
+
out.push({ token: tok, binary: name });
|
|
1371
1501
|
}
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
if (seen.has(key)) continue;
|
|
1376
|
-
seen.add(key);
|
|
1377
|
-
out.push({ token: tok, binary: name });
|
|
1378
|
-
}
|
|
1379
|
-
return true;
|
|
1380
|
-
});
|
|
1502
|
+
return true;
|
|
1503
|
+
})
|
|
1504
|
+
);
|
|
1381
1505
|
} catch {
|
|
1382
1506
|
return out;
|
|
1383
1507
|
}
|
|
@@ -1496,6 +1620,7 @@ function analyzeFsOperationImpl(command, depth = 0) {
|
|
|
1496
1620
|
if (depth === 0) {
|
|
1497
1621
|
seenPayloads = /* @__PURE__ */ new Set();
|
|
1498
1622
|
payloadBudget = PAYLOAD_BUDGET;
|
|
1623
|
+
if (callDepth === 0) callWalks = 0;
|
|
1499
1624
|
}
|
|
1500
1625
|
assignmentTable = new Map(
|
|
1501
1626
|
[...outerTable ?? []].map(([k, r]) => [k, { value: r.value, at: -1 }])
|
|
@@ -1503,7 +1628,7 @@ function analyzeFsOperationImpl(command, depth = 0) {
|
|
|
1503
1628
|
currentStmtOffset = Number.MAX_SAFE_INTEGER;
|
|
1504
1629
|
try {
|
|
1505
1630
|
recordTopLevelAssignments(f);
|
|
1506
|
-
|
|
1631
|
+
walkWithCalls(f, (node) => {
|
|
1507
1632
|
if (!node || result?.verdict === "block") return false;
|
|
1508
1633
|
const n = node;
|
|
1509
1634
|
const nodeType = syntax.NodeType(n);
|
|
@@ -2292,6 +2417,19 @@ function runnablePrefix(command) {
|
|
|
2292
2417
|
}
|
|
2293
2418
|
return null;
|
|
2294
2419
|
}
|
|
2420
|
+
function uploadedFile(tool, flag, value) {
|
|
2421
|
+
const v = value.value;
|
|
2422
|
+
const file2 = (f) => f && f !== "-" && f !== "." ? { value: f } : null;
|
|
2423
|
+
if (tool === "curl") {
|
|
2424
|
+
if (["-d", "--data", "--data-ascii", "--data-binary", "--json"].includes(flag))
|
|
2425
|
+
return v.startsWith("@") ? file2(v.slice(1)) : null;
|
|
2426
|
+
if (flag === "--data-urlencode") return file2(/^[^=@]*@(.+)$/.exec(v)?.[1]);
|
|
2427
|
+
if (flag === "-F" || flag === "--form") return file2(/^[^=]*=[@<]([^;]+)/.exec(v)?.[1]);
|
|
2428
|
+
if (flag === "-T" || flag === "--upload-file") return file2(v);
|
|
2429
|
+
}
|
|
2430
|
+
if (tool === "wget" && ["--post-file", "--body-file"].includes(flag)) return file2(v);
|
|
2431
|
+
return null;
|
|
2432
|
+
}
|
|
2295
2433
|
function merge(a, b) {
|
|
2296
2434
|
a.cwd = unique([...a.cwd, ...b.cwd]);
|
|
2297
2435
|
for (const key of /* @__PURE__ */ new Set([...a.vars.keys(), ...b.vars.keys()])) {
|
|
@@ -2365,6 +2503,9 @@ function wordValues(word, state) {
|
|
|
2365
2503
|
function collectShellFileAccesses(command, context = {}) {
|
|
2366
2504
|
const out = [];
|
|
2367
2505
|
let payloads = 256;
|
|
2506
|
+
const functions = /* @__PURE__ */ new Map();
|
|
2507
|
+
const seenCalls = /* @__PURE__ */ new Set();
|
|
2508
|
+
let callWalks2 = MAX_CALL_WALKS2;
|
|
2368
2509
|
const initial = {
|
|
2369
2510
|
cwd: [context.cwd ?? ""],
|
|
2370
2511
|
vars: /* @__PURE__ */ new Map([["HOME", [context.homeDirs?.[0] ?? "~"]]])
|
|
@@ -2622,8 +2763,98 @@ function collectShellFileAccesses(command, context = {}) {
|
|
|
2622
2763
|
}
|
|
2623
2764
|
return;
|
|
2624
2765
|
}
|
|
2766
|
+
if (name === "wget" || name === "curl" || name === "aria2c") {
|
|
2767
|
+
downloadOutputs(name, args, state);
|
|
2768
|
+
return;
|
|
2769
|
+
}
|
|
2770
|
+
if (["http", "https", "xh", "xhs"].includes(name)) {
|
|
2771
|
+
for (const token of args) {
|
|
2772
|
+
if (!token || token.dynamic || /^[a-z][a-z0-9+.-]*:\/\//i.test(token.value)) continue;
|
|
2773
|
+
const m = /^(?:@|[^=:@\s]+@|[^=:@\s]*=@)(.+)$/.exec(token.value);
|
|
2774
|
+
if (m && m[1] !== "-") add("read", { value: m[1] }, state, { upload: true });
|
|
2775
|
+
}
|
|
2776
|
+
return;
|
|
2777
|
+
}
|
|
2778
|
+
if (name === "scp" || name === "rsync") {
|
|
2779
|
+
const operands2 = strings.filter((s) => s && !s.startsWith("-"));
|
|
2780
|
+
const remote = operands2.slice(1).some((o) => /^[^/\s]+:/.test(o));
|
|
2781
|
+
for (const p of copySourcePaths(words))
|
|
2782
|
+
add("read", locate(p), state, remote ? { upload: true } : {});
|
|
2783
|
+
return;
|
|
2784
|
+
}
|
|
2625
2785
|
for (const p of copySourcePaths(words)) add("read", locate(p), state);
|
|
2626
2786
|
}
|
|
2787
|
+
function downloadOutputs(name, args, state) {
|
|
2788
|
+
const valueShort = DOWNLOAD_SHORT_VALUE[name];
|
|
2789
|
+
const outputs = [];
|
|
2790
|
+
const urls = [];
|
|
2791
|
+
let dir2 = null;
|
|
2792
|
+
let remoteName = false;
|
|
2793
|
+
let document = false;
|
|
2794
|
+
let serverNamed = false;
|
|
2795
|
+
const take = (flag, value) => {
|
|
2796
|
+
if (!value) return;
|
|
2797
|
+
const sent = uploadedFile(name, flag, value);
|
|
2798
|
+
if (sent) add("read", sent, state, { upload: true });
|
|
2799
|
+
if (DOWNLOAD_OUTPUT_FLAGS[name].includes(flag)) {
|
|
2800
|
+
if (["-O", "--output-document", "--out"].includes(flag) || name === "aria2c" && flag === "-o")
|
|
2801
|
+
document = true;
|
|
2802
|
+
if (value.value !== "-" || value.pattern || value.dynamic) outputs.push(value);
|
|
2803
|
+
} else if (DOWNLOAD_DIR_FLAGS[name].includes(flag)) dir2 = value;
|
|
2804
|
+
else if (name === "wget" && (flag === "-i" || flag === "--input-file")) serverNamed = true;
|
|
2805
|
+
};
|
|
2806
|
+
for (let i = 0; i < args.length; i++) {
|
|
2807
|
+
const token = args[i];
|
|
2808
|
+
if (!token) continue;
|
|
2809
|
+
const v = token.value;
|
|
2810
|
+
if (v.startsWith("--")) {
|
|
2811
|
+
const eq = v.indexOf("=");
|
|
2812
|
+
const flag = eq < 0 ? v : v.slice(0, eq);
|
|
2813
|
+
if (["--remote-name", "--remote-name-all"].includes(flag)) remoteName = true;
|
|
2814
|
+
if (["--remote-header-name", "--recursive", "--mirror", "--page-requisites"].includes(flag))
|
|
2815
|
+
serverNamed = true;
|
|
2816
|
+
if (DOWNLOAD_LONG_VALUE[name].includes(flag))
|
|
2817
|
+
take(flag, eq < 0 ? args[++i] ?? null : { ...token, value: v.slice(eq + 1) });
|
|
2818
|
+
continue;
|
|
2819
|
+
}
|
|
2820
|
+
if (v.startsWith("-") && v.length > 1 && !token.dynamic) {
|
|
2821
|
+
for (let j = 1; j < v.length; j++) {
|
|
2822
|
+
const c = v[j];
|
|
2823
|
+
if (name === "wget" && c === "n") {
|
|
2824
|
+
j++;
|
|
2825
|
+
continue;
|
|
2826
|
+
}
|
|
2827
|
+
if (name === "curl" && c === "O") remoteName = true;
|
|
2828
|
+
if (name === "curl" && c === "J") serverNamed = true;
|
|
2829
|
+
if (name === "wget" && (c === "r" || c === "m" || c === "p")) serverNamed = true;
|
|
2830
|
+
if (valueShort.includes(c)) {
|
|
2831
|
+
const rest = v.slice(j + 1);
|
|
2832
|
+
take(`-${c}`, rest ? { value: rest } : args[++i] ?? null);
|
|
2833
|
+
break;
|
|
2834
|
+
}
|
|
2835
|
+
}
|
|
2836
|
+
continue;
|
|
2837
|
+
}
|
|
2838
|
+
if (/^(?:https?|ftps?):\/\//i.test(v)) urls.push(v);
|
|
2839
|
+
}
|
|
2840
|
+
for (const out2 of outputs) {
|
|
2841
|
+
const spelled = name === "aria2c" && dir2?.value && !out2.value.startsWith("/") ? { ...out2, value: `${dir2.value.replace(/\/+$/, "")}/${out2.value}` } : out2;
|
|
2842
|
+
if (name === "curl" && /#\d/.test(spelled.value))
|
|
2843
|
+
add("write", { value: "", pattern: spelled.value.replace(/#\d+/g, "*"), dot: true }, state);
|
|
2844
|
+
else add("write", spelled, state);
|
|
2845
|
+
}
|
|
2846
|
+
const remote = name === "curl" ? remoteName || serverNamed : !document || serverNamed;
|
|
2847
|
+
if (!remote) return;
|
|
2848
|
+
const into = dir2?.value ? dir2.value.replace(/\/+$/, "") : ".";
|
|
2849
|
+
if (serverNamed || urls.length === 0) {
|
|
2850
|
+
add("write", { value: "", pattern: `${quoteGlob(into)}/*`, dot: true }, state);
|
|
2851
|
+
return;
|
|
2852
|
+
}
|
|
2853
|
+
for (const url of urls) {
|
|
2854
|
+
const file2 = url.replace(/[?#].*$/, "").replace(/^[a-z]+:\/\/[^/]*/i, "").split("/").pop();
|
|
2855
|
+
add("write", { value: `${into}/${file2 || "index.html"}` }, state);
|
|
2856
|
+
}
|
|
2857
|
+
}
|
|
2627
2858
|
function findActions2(tokens, words, head, state, nested, depth) {
|
|
2628
2859
|
const starts = findStartPoints(words, head).starts;
|
|
2629
2860
|
const roots = (starts.length ? starts : ["."]).slice(0, MAX_ALTERNATIVES);
|
|
@@ -2695,7 +2926,35 @@ function collectShellFileAccesses(command, context = {}) {
|
|
|
2695
2926
|
const words = wordValues(node, state);
|
|
2696
2927
|
alternatives = alternatives.flatMap((prefix) => (words.length ? words : [null]).map((w) => [...prefix, w])).slice(0, MAX_ALTERNATIVES);
|
|
2697
2928
|
}
|
|
2698
|
-
for (const tokens of alternatives)
|
|
2929
|
+
for (const tokens of alternatives) {
|
|
2930
|
+
fromWords(tokens, state, nested, depth);
|
|
2931
|
+
inlineCall(tokens, state, depth);
|
|
2932
|
+
}
|
|
2933
|
+
}
|
|
2934
|
+
function inlineCall(tokens, state, depth) {
|
|
2935
|
+
const head = tokens[0];
|
|
2936
|
+
if (!head || head.dynamic || head.pattern) return;
|
|
2937
|
+
const bodies = functions.get(head.value);
|
|
2938
|
+
if (!bodies || (state.callDepth ?? 0) >= MAX_CALL_DEPTH2) return;
|
|
2939
|
+
const args = tokens.slice(1).map((a) => a && !a.dynamic ? a.value : null);
|
|
2940
|
+
const vars = JSON.stringify([...state.vars].sort(([a], [b]) => a < b ? -1 : 1));
|
|
2941
|
+
const key = `${head.value}\0${JSON.stringify(args)}\0${state.cwd.join("")}\0${vars}`;
|
|
2942
|
+
if (seenCalls.has(key)) return;
|
|
2943
|
+
seenCalls.add(key);
|
|
2944
|
+
for (const body of bodies) {
|
|
2945
|
+
if (callWalks2-- <= 0) return;
|
|
2946
|
+
const local = clone(state);
|
|
2947
|
+
local.callDepth = (state.callDepth ?? 0) + 1;
|
|
2948
|
+
for (const name of POSITIONAL) local.vars.delete(name);
|
|
2949
|
+
if (!rebindsPositionals(body)) {
|
|
2950
|
+
args.slice(0, 9).forEach((v, i) => local.vars.set(String(i + 1), v === null ? null : [v]));
|
|
2951
|
+
const all = args.every((v) => v !== null) && args.length ? unique(args) : null;
|
|
2952
|
+
local.vars.set("@", all);
|
|
2953
|
+
local.vars.set("*", all);
|
|
2954
|
+
local.vars.set("#", [String(args.length)]);
|
|
2955
|
+
}
|
|
2956
|
+
statement(body, local, depth + 1);
|
|
2957
|
+
}
|
|
2699
2958
|
}
|
|
2700
2959
|
function readsScriptFromStdin(stmt) {
|
|
2701
2960
|
const cmd = stmt?.Cmd;
|
|
@@ -2732,11 +2991,13 @@ function collectShellFileAccesses(command, context = {}) {
|
|
|
2732
2991
|
if (!stmt || depth > MAX_DEPTH) return;
|
|
2733
2992
|
state.onSuccess = void 0;
|
|
2734
2993
|
if (stmt.Background) state = clone(state);
|
|
2994
|
+
const headWord = stmt.Cmd && syntax2.NodeType(stmt.Cmd) === "CallExpr" ? base(resolveWordLiteral(stmt.Cmd.Args?.[0])) : "";
|
|
2995
|
+
const sends = SENDING_STDIN.has(headWord);
|
|
2735
2996
|
for (const redir of stmt.Redirs ?? []) {
|
|
2736
2997
|
substitutions(redir.Word, state, depth);
|
|
2737
2998
|
for (const word of wordValues(redir.Word, state)) {
|
|
2738
2999
|
if (WRITES.has(redir.Op)) add("write", word, state);
|
|
2739
|
-
if (READS.has(redir.Op)) add("read", word, state);
|
|
3000
|
+
if (READS.has(redir.Op)) add("read", word, state, sends ? { upload: true } : {});
|
|
2740
3001
|
if (redir.Op === DUP_OUT && word.value && !/^\d+$/.test(word.value))
|
|
2741
3002
|
add("write", word, state);
|
|
2742
3003
|
}
|
|
@@ -2845,7 +3106,10 @@ function collectShellFileAccesses(command, context = {}) {
|
|
|
2845
3106
|
}
|
|
2846
3107
|
const parsed = parseShared(command);
|
|
2847
3108
|
const program2 = typeof parsed !== "symbol" ? parsed : runnablePrefix(command);
|
|
2848
|
-
if (program2)
|
|
3109
|
+
if (program2) {
|
|
3110
|
+
for (const [name, bodies] of functionBodies(program2)) functions.set(name, bodies);
|
|
3111
|
+
statements(program2.Stmts ?? [], initial, 0);
|
|
3112
|
+
}
|
|
2849
3113
|
return out;
|
|
2850
3114
|
}
|
|
2851
3115
|
function isAgentInstructionPath(absPath, root) {
|
|
@@ -2961,6 +3225,33 @@ function hits(access, paths, directories, context) {
|
|
|
2961
3225
|
}
|
|
2962
3226
|
return false;
|
|
2963
3227
|
}
|
|
3228
|
+
function hitsSystemDir(access, context, dirs = SYSTEM_DIRS) {
|
|
3229
|
+
const home = context.homeDirs?.[0] ?? "~";
|
|
3230
|
+
const candidates2 = access.candidates ?? [
|
|
3231
|
+
normalizeFilePath(access.path, access.cwd ?? context.cwd, home)
|
|
3232
|
+
];
|
|
3233
|
+
for (const p of candidates2) {
|
|
3234
|
+
if (dirs.some((d) => within(p, d)) && !exempt(p)) return true;
|
|
3235
|
+
if (access.recursive && dirs.some((d) => within(d, p))) return true;
|
|
3236
|
+
}
|
|
3237
|
+
if (!access.glob) return false;
|
|
3238
|
+
const pattern = normalizeFilePath(access.glob, access.cwd ?? context.cwd, home);
|
|
3239
|
+
const magic = pattern.search(/[*?[{]/);
|
|
3240
|
+
const literal = magic < 0 ? pattern : pattern.slice(0, pattern.lastIndexOf("/", magic) + 1);
|
|
3241
|
+
if (literal && exempt(literal.replace(/\/$/, "") || "/")) return false;
|
|
3242
|
+
const options = { dot: access.dotGlob === true, nonegate: true };
|
|
3243
|
+
const components = pattern.split("/");
|
|
3244
|
+
for (const root of dirs) {
|
|
3245
|
+
const count = root.split("/").length;
|
|
3246
|
+
if (components.length > count && (0, import_picomatch.default)(components.slice(0, count).join("/"), options)(root))
|
|
3247
|
+
return true;
|
|
3248
|
+
if (access.recursive && (0, import_picomatch.default)(pattern, options)(root)) return true;
|
|
3249
|
+
}
|
|
3250
|
+
return false;
|
|
3251
|
+
}
|
|
3252
|
+
function uploadDirs(context) {
|
|
3253
|
+
return context.homeDirs?.[0] === "/root" ? UPLOAD_SYSTEM_DIRS.filter((d) => d !== "/root") : UPLOAD_SYSTEM_DIRS;
|
|
3254
|
+
}
|
|
2964
3255
|
function evaluateProtectedFileAccess(accesses, context, settings) {
|
|
2965
3256
|
const homes = [.../* @__PURE__ */ new Set([...context.homeDirs ?? ["~"], "/root"])];
|
|
2966
3257
|
const startup = homes.flatMap(
|
|
@@ -2982,6 +3273,14 @@ function evaluateProtectedFileAccess(accesses, context, settings) {
|
|
|
2982
3273
|
"files.shell-startup",
|
|
2983
3274
|
"review-shell-startup-mutation",
|
|
2984
3275
|
"Changing shell startup files requires approval"
|
|
3276
|
+
] : mutation && hitsSystemDir(access, context) ? [
|
|
3277
|
+
"files.system-dirs",
|
|
3278
|
+
"review-system-dir-mutation",
|
|
3279
|
+
"Changing files in a system directory requires approval"
|
|
3280
|
+
] : !mutation && access.upload && hitsSystemDir(access, context, uploadDirs(context)) ? [
|
|
3281
|
+
"files.system-dirs",
|
|
3282
|
+
"review-system-file-upload",
|
|
3283
|
+
"Uploading a system file requires approval"
|
|
2985
3284
|
] : !mutation && hits(access, PASSWORD_HASH_FILES, [], context) ? [
|
|
2986
3285
|
"files.password-hashes",
|
|
2987
3286
|
"review-password-hash-read",
|
|
@@ -3309,16 +3608,21 @@ function looksGenerated(value, minLength, minEntropy, lettersOnlyAllowed = false
|
|
|
3309
3608
|
if (EMAIL_VALUE_RE.test(value)) return false;
|
|
3310
3609
|
if (looksLikeJoinedWords(value) || hasSequentialRun(value)) return false;
|
|
3311
3610
|
if (PATH_VALUE_RE.test(value) || DOTTED_REFERENCE_RE.test(value)) return false;
|
|
3611
|
+
if (CONCATENATION_RE.test(value)) return false;
|
|
3312
3612
|
if (rejectedByStructuredValidator(value)) return false;
|
|
3313
3613
|
return shannonEntropy(value) >= minEntropy;
|
|
3314
3614
|
}
|
|
3315
3615
|
function nameHoldsCredential(name) {
|
|
3316
3616
|
return !FIXTURE_NAME_RE.test(name) && !DERIVATIVE_NAME_RE.test(name);
|
|
3317
3617
|
}
|
|
3618
|
+
function isOtpDocSecret(value) {
|
|
3619
|
+
return OTP_DOC_SECRETS.has(value.replace(/=+$/, "").toUpperCase());
|
|
3620
|
+
}
|
|
3318
3621
|
function validateQueryCredential(raw) {
|
|
3319
3622
|
const value = capturedValue(raw, QUERY_CREDENTIAL_VALUE_RE);
|
|
3320
3623
|
if (value === null) return false;
|
|
3321
|
-
if (OTP_SECRET_RE.test(value))
|
|
3624
|
+
if (OTP_SECRET_RE.test(value))
|
|
3625
|
+
return !isOtpDocSecret(value) && looksGenerated(value, 16, 3, true);
|
|
3322
3626
|
if (!/[0-9]/.test(value) || !/[A-Za-z]/.test(value)) return false;
|
|
3323
3627
|
return looksGenerated(value, 12, 3);
|
|
3324
3628
|
}
|
|
@@ -3332,13 +3636,40 @@ function keywordEndsAPart(name) {
|
|
|
3332
3636
|
}
|
|
3333
3637
|
function validateAssignedSecret(raw, before = "") {
|
|
3334
3638
|
const value = capturedValue(raw, ASSIGNED_SECRET_VALUE_RE);
|
|
3335
|
-
const name = capturedValue(raw, ASSIGNED_SECRET_NAME_RE);
|
|
3639
|
+
const name = capturedValue(raw, ASSIGNED_SECRET_NAME_RE)?.replace(/^_+/, "") ?? null;
|
|
3336
3640
|
if (value === null || name === null) return false;
|
|
3337
|
-
if (!keywordEndsAPart(name)
|
|
3338
|
-
|
|
3641
|
+
if (!keywordEndsAPart(name)) return false;
|
|
3642
|
+
if (ARN_CONTEXT_RE.test(before)) return false;
|
|
3643
|
+
const prefix = (before.match(/[A-Za-z0-9_]*$/)?.[0] ?? "") + (raw.startsWith("_") ? "_" : "");
|
|
3339
3644
|
if (!nameHoldsCredential(prefix + name)) return false;
|
|
3340
3645
|
return looksGenerated(value, 12, 3.5);
|
|
3341
3646
|
}
|
|
3647
|
+
function validateBasicCredential(raw) {
|
|
3648
|
+
const b64 = capturedValue(raw, BASIC_VALUE_RE);
|
|
3649
|
+
if (b64 === null) return false;
|
|
3650
|
+
const bare = b64.replace(/=+$/, "");
|
|
3651
|
+
if (bare.length % 4 === 1) return false;
|
|
3652
|
+
const bytes = Buffer.from(b64, "base64");
|
|
3653
|
+
if (bytes.toString("base64").replace(/=+$/, "") !== bare) return false;
|
|
3654
|
+
const text = bytes.toString("utf8");
|
|
3655
|
+
if (!Buffer.from(text, "utf8").equals(bytes)) return false;
|
|
3656
|
+
for (let i = 0; i < text.length; i++) {
|
|
3657
|
+
const c = text.charCodeAt(i);
|
|
3658
|
+
if (c < 32 || c === 127) return false;
|
|
3659
|
+
}
|
|
3660
|
+
const pair = BASIC_PAIR_RE.exec(text);
|
|
3661
|
+
if (!pair) return false;
|
|
3662
|
+
const password = pair[2];
|
|
3663
|
+
if (!/[0-9]/.test(password) || !/[A-Za-z]/.test(password)) return false;
|
|
3664
|
+
const lower2 = password.toLowerCase();
|
|
3665
|
+
if (DLP_STOPWORDS.some((sw) => lower2.includes(sw)) || hasSequentialRun(password)) return false;
|
|
3666
|
+
return shannonEntropy(password) >= 3;
|
|
3667
|
+
}
|
|
3668
|
+
function validateOtpSecret(raw) {
|
|
3669
|
+
const secret = capturedValue(raw, OTP_CAPTURE_RE);
|
|
3670
|
+
if (secret === null || isOtpDocSecret(secret)) return false;
|
|
3671
|
+
return looksGenerated(secret.replace(/=+$/, ""), 16, 3, true);
|
|
3672
|
+
}
|
|
3342
3673
|
function matchSensitivePath2(resolvedPath, originalPath, options = {}) {
|
|
3343
3674
|
const normalised = resolvedPath.replace(/\\/g, "/");
|
|
3344
3675
|
if (!options.systemAccessHandled && isLegacySystemPath(normalised))
|
|
@@ -3647,6 +3978,156 @@ function parsePySpec(raw) {
|
|
|
3647
3978
|
const exact = PY_EXACT_RE.exec(m[2].trim());
|
|
3648
3979
|
return { name, version: exact ? exact[1] : void 0 };
|
|
3649
3980
|
}
|
|
3981
|
+
function exactVersion(v) {
|
|
3982
|
+
if (!v) return void 0;
|
|
3983
|
+
const m = EXACT_VERSION_RE.exec(v.trim());
|
|
3984
|
+
return m ? m[1] : void 0;
|
|
3985
|
+
}
|
|
3986
|
+
function push(out, ecosystem, manager, name, version2, raw) {
|
|
3987
|
+
out.push({ ecosystem, manager, name, ...version2 ? { version: version2 } : {}, raw });
|
|
3988
|
+
}
|
|
3989
|
+
function gemInstall(words, from, out) {
|
|
3990
|
+
const dash = words.indexOf("--", from);
|
|
3991
|
+
const own = dash < 0 ? words : words.slice(0, dash);
|
|
3992
|
+
const pinned = exactVersion(flagValues(own, from, ["-v", "--version"])[0]);
|
|
3993
|
+
for (const raw of operands(own, from, GEM_VALUE_FLAGS)) {
|
|
3994
|
+
if (NON_REGISTRY_RE.test(raw) || /\.gem$/i.test(raw)) continue;
|
|
3995
|
+
const colon = raw.indexOf(":");
|
|
3996
|
+
const name = colon < 0 ? raw : raw.slice(0, colon);
|
|
3997
|
+
if (!GEM_NAME_RE.test(name)) continue;
|
|
3998
|
+
push(
|
|
3999
|
+
out,
|
|
4000
|
+
"RubyGems",
|
|
4001
|
+
"gem",
|
|
4002
|
+
name,
|
|
4003
|
+
colon < 0 ? pinned : exactVersion(raw.slice(colon + 1)),
|
|
4004
|
+
raw
|
|
4005
|
+
);
|
|
4006
|
+
}
|
|
4007
|
+
}
|
|
4008
|
+
function cargoCrates(words, from, manager, out) {
|
|
4009
|
+
const flags = manager === "cargo add" ? CARGO_ADD_VALUE_FLAGS : CARGO_INSTALL_VALUE_FLAGS;
|
|
4010
|
+
if (flagValues(words, from, ["--git", "--path", "--registry", "--index"]).length > 0) return;
|
|
4011
|
+
if (words.some((w) => w === "--git" || w === "--path")) return;
|
|
4012
|
+
const flagVersion = manager === "cargo install" ? exactVersion(flagValues(words, from, ["--version", "--vers"])[0]) : void 0;
|
|
4013
|
+
for (const raw of operands(words, from, flags)) {
|
|
4014
|
+
const at = raw.indexOf("@");
|
|
4015
|
+
const name = at < 0 ? raw : raw.slice(0, at);
|
|
4016
|
+
if (!CRATE_NAME_RE.test(name)) continue;
|
|
4017
|
+
push(
|
|
4018
|
+
out,
|
|
4019
|
+
"crates.io",
|
|
4020
|
+
manager,
|
|
4021
|
+
name,
|
|
4022
|
+
at < 0 ? flagVersion : exactVersion(raw.slice(at + 1)),
|
|
4023
|
+
raw
|
|
4024
|
+
);
|
|
4025
|
+
}
|
|
4026
|
+
}
|
|
4027
|
+
function goModules(words, from, manager, out) {
|
|
4028
|
+
let specs = operands(words, from, GO_VALUE_FLAGS);
|
|
4029
|
+
if (manager === "go run") specs = specs.slice(0, 1).filter((s) => s.includes("@"));
|
|
4030
|
+
for (const raw of specs) {
|
|
4031
|
+
const at = raw.indexOf("@");
|
|
4032
|
+
const pathPart = (at < 0 ? raw : raw.slice(0, at)).replace(/\/\.\.\.$/, "");
|
|
4033
|
+
const ver = at < 0 ? void 0 : raw.slice(at + 1);
|
|
4034
|
+
if (ver === "none") continue;
|
|
4035
|
+
if (!GO_PATH_RE.test(pathPart) || /\.go$/i.test(pathPart)) continue;
|
|
4036
|
+
const exact = ver && /^v\d/.test(ver) ? exactVersion(ver) : void 0;
|
|
4037
|
+
push(out, "Go", manager, pathPart, exact, raw);
|
|
4038
|
+
}
|
|
4039
|
+
}
|
|
4040
|
+
function dotnetAdd(words, from, out) {
|
|
4041
|
+
let k = -1;
|
|
4042
|
+
for (let i = from; i < Math.min(words.length, from + 2); i++) {
|
|
4043
|
+
if (lower(words[i]) === "package") {
|
|
4044
|
+
k = i;
|
|
4045
|
+
break;
|
|
4046
|
+
}
|
|
4047
|
+
}
|
|
4048
|
+
if (k < 0) return;
|
|
4049
|
+
const name = operands(words, k + 1, DOTNET_ADD_VALUE_FLAGS)[0];
|
|
4050
|
+
if (!name || !NUGET_NAME_RE.test(name)) return;
|
|
4051
|
+
const version2 = exactVersion(flagValues(words, k + 1, ["-v", "--version"])[0]);
|
|
4052
|
+
push(out, "NuGet", "dotnet add package", name, version2, name);
|
|
4053
|
+
}
|
|
4054
|
+
function dotnetTool(words, from, out) {
|
|
4055
|
+
const name = operands(words, from, DOTNET_TOOL_VALUE_FLAGS)[0];
|
|
4056
|
+
if (!name || !NUGET_NAME_RE.test(name)) return;
|
|
4057
|
+
const version2 = exactVersion(flagValues(words, from, ["--version"])[0]);
|
|
4058
|
+
push(out, "NuGet", "dotnet tool", name, version2, name);
|
|
4059
|
+
}
|
|
4060
|
+
function nugetInstall(words, from, out) {
|
|
4061
|
+
const lowered = words.map((w) => w !== null && w.startsWith("-") ? w.toLowerCase() : w);
|
|
4062
|
+
const name = operands(lowered, from, NUGET_VALUE_FLAGS)[0];
|
|
4063
|
+
if (!name || !NUGET_NAME_RE.test(name) || /\.config$/i.test(name)) return;
|
|
4064
|
+
const version2 = exactVersion(flagValues(lowered, from, ["-version"])[0]);
|
|
4065
|
+
push(out, "NuGet", "nuget install", name, version2, name);
|
|
4066
|
+
}
|
|
4067
|
+
function mavenGet(words, from, out) {
|
|
4068
|
+
const goal = words.slice(from).some(
|
|
4069
|
+
(w) => w !== null && /(?:^|:)dependency:get$|^org\.apache\.maven\.plugins:maven-dependency-plugin:(?:[\w.-]+:)?get$/.test(
|
|
4070
|
+
w
|
|
4071
|
+
)
|
|
4072
|
+
);
|
|
4073
|
+
if (!goal) return;
|
|
4074
|
+
const prop = (key) => {
|
|
4075
|
+
for (let i = from; i < words.length; i++) {
|
|
4076
|
+
const w = words[i];
|
|
4077
|
+
if (w === null) continue;
|
|
4078
|
+
if (w.startsWith(`-D${key}=`)) return w.slice(key.length + 3);
|
|
4079
|
+
if (w === "-D" && words[i + 1]?.startsWith(`${key}=`))
|
|
4080
|
+
return words[i + 1].slice(key.length + 1);
|
|
4081
|
+
}
|
|
4082
|
+
return void 0;
|
|
4083
|
+
};
|
|
4084
|
+
const artifact = prop("artifact");
|
|
4085
|
+
let coord;
|
|
4086
|
+
let ver;
|
|
4087
|
+
if (artifact) {
|
|
4088
|
+
const parts = artifact.split(":");
|
|
4089
|
+
if (parts.length < 2) return;
|
|
4090
|
+
coord = `${parts[0]}:${parts[1]}`;
|
|
4091
|
+
ver = parts[2];
|
|
4092
|
+
} else {
|
|
4093
|
+
const g = prop("groupId");
|
|
4094
|
+
const a = prop("artifactId");
|
|
4095
|
+
if (!g || !a) return;
|
|
4096
|
+
coord = `${g}:${a}`;
|
|
4097
|
+
ver = prop("version");
|
|
4098
|
+
}
|
|
4099
|
+
if (!MAVEN_COORD_RE.test(coord)) return;
|
|
4100
|
+
const exact = ver && !/^(?:LATEST|RELEASE)$/i.test(ver) ? exactVersion(ver) : void 0;
|
|
4101
|
+
push(out, "Maven", "mvn dependency:get", coord, exact, artifact ?? coord);
|
|
4102
|
+
}
|
|
4103
|
+
function composerPackages(words, from, manager, out) {
|
|
4104
|
+
const ops = operands(words, from, COMPOSER_VALUE_FLAGS);
|
|
4105
|
+
const specs = manager === "composer create-project" ? ops.slice(0, 1) : ops;
|
|
4106
|
+
for (const raw of specs) {
|
|
4107
|
+
const sep = raw.search(/[:=]/);
|
|
4108
|
+
const name = sep < 0 ? raw : raw.slice(0, sep);
|
|
4109
|
+
if (!COMPOSER_NAME_RE.test(name)) continue;
|
|
4110
|
+
let ver = sep < 0 ? void 0 : raw.slice(sep + 1);
|
|
4111
|
+
if (manager === "composer create-project" && ver === void 0) ver = ops[2] ?? void 0;
|
|
4112
|
+
push(out, "Packagist", manager, name.toLowerCase(), exactVersion(ver), raw);
|
|
4113
|
+
}
|
|
4114
|
+
}
|
|
4115
|
+
function editorExtensions(words, from, out) {
|
|
4116
|
+
for (const raw of flagValues(words, from, ["--install-extension"])) {
|
|
4117
|
+
if (/\.vsix$/i.test(raw) || NON_REGISTRY_RE.test(raw)) continue;
|
|
4118
|
+
const at = raw.lastIndexOf("@");
|
|
4119
|
+
const name = at <= 0 ? raw : raw.slice(0, at);
|
|
4120
|
+
if (!EXTENSION_ID_RE.test(name)) continue;
|
|
4121
|
+
push(
|
|
4122
|
+
out,
|
|
4123
|
+
"VSCode",
|
|
4124
|
+
"code --install-extension",
|
|
4125
|
+
name,
|
|
4126
|
+
at <= 0 ? void 0 : exactVersion(raw.slice(at + 1)),
|
|
4127
|
+
raw
|
|
4128
|
+
);
|
|
4129
|
+
}
|
|
4130
|
+
}
|
|
3650
4131
|
function basename(w) {
|
|
3651
4132
|
return (w ?? "").toLowerCase().split("/").pop() ?? "";
|
|
3652
4133
|
}
|
|
@@ -3878,7 +4359,63 @@ function fromCall(words, out, depth) {
|
|
|
3878
4359
|
pushPy(out, "pipenv", operands(words, v + 1, PIPENV_VALUE_FLAGS));
|
|
3879
4360
|
return;
|
|
3880
4361
|
}
|
|
4362
|
+
case "gem": {
|
|
4363
|
+
const v = verbIndex(words, start + 1, GEM_VALUE_FLAGS);
|
|
4364
|
+
if (v >= 0 && ["install", "i"].includes(lower(words[v]))) gemInstall(words, v + 1, out);
|
|
4365
|
+
return;
|
|
4366
|
+
}
|
|
4367
|
+
case "bundle": {
|
|
4368
|
+
const v = verbIndex(words, start + 1, BUNDLE_VALUE_FLAGS);
|
|
4369
|
+
if (v < 0 || lower(words[v]) !== "add") return;
|
|
4370
|
+
if (flagValues(words, v + 1, ["--git", "--github", "--path"]).length > 0) return;
|
|
4371
|
+
const version2 = exactVersion(flagValues(words, v + 1, ["-v", "--version"])[0]);
|
|
4372
|
+
for (const raw of operands(words, v + 1, BUNDLE_VALUE_FLAGS))
|
|
4373
|
+
if (GEM_NAME_RE.test(raw)) push(out, "RubyGems", "bundle add", raw, version2, raw);
|
|
4374
|
+
return;
|
|
4375
|
+
}
|
|
4376
|
+
case "cargo": {
|
|
4377
|
+
const from = words[start + 1]?.startsWith("+") ? start + 2 : start + 1;
|
|
4378
|
+
const v = verbIndex(words, from, CARGO_ADD_VALUE_FLAGS);
|
|
4379
|
+
const verb = lower(words[v]);
|
|
4380
|
+
if (verb === "add") cargoCrates(words, v + 1, "cargo add", out);
|
|
4381
|
+
else if (verb === "install") cargoCrates(words, v + 1, "cargo install", out);
|
|
4382
|
+
return;
|
|
4383
|
+
}
|
|
4384
|
+
case "go": {
|
|
4385
|
+
const v = verbIndex(words, start + 1, GO_VALUE_FLAGS);
|
|
4386
|
+
const verb = lower(words[v]);
|
|
4387
|
+
if (verb === "get" || verb === "install" || verb === "run")
|
|
4388
|
+
goModules(words, v + 1, `go ${verb}`, out);
|
|
4389
|
+
return;
|
|
4390
|
+
}
|
|
4391
|
+
case "dotnet": {
|
|
4392
|
+
const v = verbIndex(words, start + 1, DOTNET_ADD_VALUE_FLAGS);
|
|
4393
|
+
const verb = lower(words[v]);
|
|
4394
|
+
if (verb === "add") dotnetAdd(words, v + 1, out);
|
|
4395
|
+
else if (verb === "tool" && ["install", "update"].includes(lower(words[v + 1])))
|
|
4396
|
+
dotnetTool(words, v + 2, out);
|
|
4397
|
+
return;
|
|
4398
|
+
}
|
|
4399
|
+
case "nuget": {
|
|
4400
|
+
if (lower(words[start + 1]) === "install") nugetInstall(words, start + 2, out);
|
|
4401
|
+
return;
|
|
4402
|
+
}
|
|
4403
|
+
case "mvn":
|
|
4404
|
+
case "mvnw":
|
|
4405
|
+
mavenGet(words, start + 1, out);
|
|
4406
|
+
return;
|
|
4407
|
+
case "composer": {
|
|
4408
|
+
let v = verbIndex(words, start + 1, COMPOSER_VALUE_FLAGS);
|
|
4409
|
+
if (v >= 0 && lower(words[v]) === "global") v = verbIndex(words, v + 1, COMPOSER_VALUE_FLAGS);
|
|
4410
|
+
const verb = lower(words[v]);
|
|
4411
|
+
if (verb === "require" || verb === "req" || verb === "r")
|
|
4412
|
+
composerPackages(words, v + 1, "composer require", out);
|
|
4413
|
+
else if (verb === "create-project")
|
|
4414
|
+
composerPackages(words, v + 1, "composer create-project", out);
|
|
4415
|
+
return;
|
|
4416
|
+
}
|
|
3881
4417
|
default:
|
|
4418
|
+
if (EDITOR_HEADS.includes(family)) editorExtensions(words, start + 1, out);
|
|
3882
4419
|
return;
|
|
3883
4420
|
}
|
|
3884
4421
|
}
|
|
@@ -5442,11 +5979,11 @@ function isDecimalFraction(text, index, unseparated) {
|
|
|
5442
5979
|
const a = text[index - 2];
|
|
5443
5980
|
if (sep === ".") {
|
|
5444
5981
|
if (a !== void 0 && a >= "0" && a <= "9") return true;
|
|
5445
|
-
return a === void 0 ||
|
|
5982
|
+
return a === void 0 || (a === "-" || a === "+") && index === 2;
|
|
5446
5983
|
}
|
|
5447
5984
|
if (sep === "," && a === "0") {
|
|
5448
5985
|
const b = text[index - 3];
|
|
5449
|
-
return b === void 0 ||
|
|
5986
|
+
return b === void 0 || b === ";";
|
|
5450
5987
|
}
|
|
5451
5988
|
return false;
|
|
5452
5989
|
}
|
|
@@ -6646,7 +7183,7 @@ function* stringValues(obj, depth = 0) {
|
|
|
6646
7183
|
}
|
|
6647
7184
|
for (const v of Object.values(obj)) yield* stringValues(v, depth + 1);
|
|
6648
7185
|
}
|
|
6649
|
-
var import_picomatch, import_safe_regex2, import_picomatch2, import_mvdan_sh, import_safe_regex22, import_mvdan_sh2, import_safe_regex23, import_crypto2, import_crypto3, import_mvdan_sh3, import_crypto4, import_mvdan_sh4, import_crypto5, 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,
|
|
7186
|
+
var import_picomatch, import_safe_regex2, import_picomatch2, import_mvdan_sh, import_safe_regex22, import_mvdan_sh2, import_safe_regex23, import_crypto2, import_crypto3, import_mvdan_sh3, import_crypto4, import_mvdan_sh4, import_crypto5, 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;
|
|
6650
7187
|
var init_dist = __esm({
|
|
6651
7188
|
"packages/policy-engine/dist/index.mjs"() {
|
|
6652
7189
|
"use strict";
|
|
@@ -7460,6 +7997,11 @@ var init_dist = __esm({
|
|
|
7460
7997
|
example: "cat /etc/shadow",
|
|
7461
7998
|
advice: "Keep Review, or choose Block when agents must never read system password hashes."
|
|
7462
7999
|
},
|
|
8000
|
+
"files.system-dirs": {
|
|
8001
|
+
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.",
|
|
8002
|
+
example: "wget -O /usr/bin/tool https://example.com/tool",
|
|
8003
|
+
advice: "Keep Review on a workstation. A container image build that installs into /usr can set it to Log."
|
|
8004
|
+
},
|
|
7463
8005
|
// ── Commands ──────────────────────────────────────────────────────────────
|
|
7464
8006
|
"commands.inline-exec": {
|
|
7465
8007
|
plain: "The agent runs a small program written inside the command itself.",
|
|
@@ -7656,7 +8198,7 @@ var init_dist = __esm({
|
|
|
7656
8198
|
advice: "Fixed at Block. A person accepts the change on the machine (node9 mcp pin update)."
|
|
7657
8199
|
},
|
|
7658
8200
|
"loading.malicious-package": {
|
|
7659
|
-
plain: "The agent installs a package that the public malicious-package database (OSV) lists, or one published minutes ago.",
|
|
8201
|
+
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.",
|
|
7660
8202
|
example: "npm install of a package flagged as malware",
|
|
7661
8203
|
advice: "Keep Block for known-malicious packages; brand-new packages already stop for Review."
|
|
7662
8204
|
}
|
|
@@ -7702,6 +8244,15 @@ var init_dist = __esm({
|
|
|
7702
8244
|
defaultValue: "review",
|
|
7703
8245
|
legacy: ["review-password-hash-read"]
|
|
7704
8246
|
},
|
|
8247
|
+
{
|
|
8248
|
+
id: "files.system-dirs",
|
|
8249
|
+
group: "files",
|
|
8250
|
+
title: "System directories",
|
|
8251
|
+
catches: "Writes, new files and removals under /usr, /etc, /boot, /bin, /sbin, /lib and /var/log, and uploads of system files",
|
|
8252
|
+
values: ALL,
|
|
8253
|
+
defaultValue: "review",
|
|
8254
|
+
legacy: ["review-system-dir-mutation", "review-system-file-upload"]
|
|
8255
|
+
},
|
|
7705
8256
|
// ── Commands ────────────────────────────────────────────────────────────
|
|
7706
8257
|
{
|
|
7707
8258
|
id: "commands.inline-exec",
|
|
@@ -8999,6 +9550,10 @@ var init_dist = __esm({
|
|
|
8999
9550
|
AND_OP = probeBinOp("a && b");
|
|
9000
9551
|
OR_OP = probeBinOp("a || b");
|
|
9001
9552
|
AND_OR_OPS = new Set([AND_OP, OR_OP].filter((o) => o !== null));
|
|
9553
|
+
MAX_CALL_DEPTH = 4;
|
|
9554
|
+
MAX_CALL_WALKS = 256;
|
|
9555
|
+
callDepth = 0;
|
|
9556
|
+
callWalks = 0;
|
|
9002
9557
|
FS_OP_CACHE_MAX = 5e3;
|
|
9003
9558
|
fsOpCache = /* @__PURE__ */ new Map();
|
|
9004
9559
|
stripDotSlash = (p) => p.replace(/^\.\//, "");
|
|
@@ -9051,10 +9606,79 @@ var init_dist = __esm({
|
|
|
9051
9606
|
MAX_ARGV_CHARACTERS = 262144;
|
|
9052
9607
|
({ syntax: syntax2 } = import_mvdan_sh2.default);
|
|
9053
9608
|
MAX_ALTERNATIVES = 64;
|
|
9609
|
+
MAX_CALL_DEPTH2 = 4;
|
|
9610
|
+
MAX_CALL_WALKS2 = 256;
|
|
9611
|
+
POSITIONAL = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "@", "*", "#"];
|
|
9054
9612
|
MAX_DEPTH = 64;
|
|
9055
9613
|
MAX_PAYLOAD_DEPTH = 24;
|
|
9056
9614
|
unique = (a) => [...new Set(a)].slice(0, MAX_ALTERNATIVES);
|
|
9057
|
-
clone = (s) => ({
|
|
9615
|
+
clone = (s) => ({
|
|
9616
|
+
cwd: [...s.cwd],
|
|
9617
|
+
vars: new Map(s.vars),
|
|
9618
|
+
...s.callDepth ? { callDepth: s.callDepth } : {}
|
|
9619
|
+
});
|
|
9620
|
+
SENDING_STDIN = /* @__PURE__ */ new Set(["nc", "ncat", "netcat", "socat", "telnet", "curl", "wget"]);
|
|
9621
|
+
DOWNLOAD_SHORT_VALUE = {
|
|
9622
|
+
curl: "odDeEFHKmPrtTuUwxXyYzAbcCQ",
|
|
9623
|
+
wget: "OoaPiBetTwQlARDXIUY",
|
|
9624
|
+
aria2c: "odisx"
|
|
9625
|
+
};
|
|
9626
|
+
DOWNLOAD_LONG_VALUE = {
|
|
9627
|
+
curl: [
|
|
9628
|
+
"--output",
|
|
9629
|
+
"--output-dir",
|
|
9630
|
+
"--dump-header",
|
|
9631
|
+
"--cookie-jar",
|
|
9632
|
+
"--data",
|
|
9633
|
+
"--data-binary",
|
|
9634
|
+
"--data-raw",
|
|
9635
|
+
"--data-ascii",
|
|
9636
|
+
"--data-urlencode",
|
|
9637
|
+
"--form",
|
|
9638
|
+
"--header",
|
|
9639
|
+
"--request",
|
|
9640
|
+
"--user",
|
|
9641
|
+
"--user-agent",
|
|
9642
|
+
"--referer",
|
|
9643
|
+
"--cookie",
|
|
9644
|
+
"--config",
|
|
9645
|
+
"--json",
|
|
9646
|
+
"--upload-file",
|
|
9647
|
+
"--max-time",
|
|
9648
|
+
"--proxy",
|
|
9649
|
+
"--write-out",
|
|
9650
|
+
"--url"
|
|
9651
|
+
],
|
|
9652
|
+
wget: [
|
|
9653
|
+
"--output-document",
|
|
9654
|
+
"--output-file",
|
|
9655
|
+
"--append-output",
|
|
9656
|
+
"--directory-prefix",
|
|
9657
|
+
"--input-file",
|
|
9658
|
+
"--post-data",
|
|
9659
|
+
"--post-file",
|
|
9660
|
+
"--body-data",
|
|
9661
|
+
"--body-file",
|
|
9662
|
+
"--method",
|
|
9663
|
+
"--header",
|
|
9664
|
+
"--user-agent",
|
|
9665
|
+
"--user",
|
|
9666
|
+
"--password",
|
|
9667
|
+
"--tries",
|
|
9668
|
+
"--timeout"
|
|
9669
|
+
],
|
|
9670
|
+
aria2c: ["--out", "--dir", "--input-file"]
|
|
9671
|
+
};
|
|
9672
|
+
DOWNLOAD_OUTPUT_FLAGS = {
|
|
9673
|
+
curl: ["-o", "--output", "-D", "--dump-header", "-c", "--cookie-jar"],
|
|
9674
|
+
wget: ["-O", "--output-document", "-o", "--output-file", "-a", "--append-output"],
|
|
9675
|
+
aria2c: ["-o", "--out"]
|
|
9676
|
+
};
|
|
9677
|
+
DOWNLOAD_DIR_FLAGS = {
|
|
9678
|
+
curl: ["--output-dir"],
|
|
9679
|
+
wget: ["-P", "--directory-prefix"],
|
|
9680
|
+
aria2c: ["-d", "--dir"]
|
|
9681
|
+
};
|
|
9058
9682
|
base = (s) => (s ?? "").replace(/\/+$/, "").split("/").pop() ?? "";
|
|
9059
9683
|
quoteGlob = (s) => s.replace(/[\\*?\[\]{}()!+@]/g, "\\$&");
|
|
9060
9684
|
asWord = (p) => ({ value: p.value, ...p.glob ? { pattern: p.pattern } : {} });
|
|
@@ -9079,6 +9703,18 @@ var init_dist = __esm({
|
|
|
9079
9703
|
SYSTEM_AUTH_DIRS = ["/etc/security", "/etc/pam.d", "/etc/sudoers.d"];
|
|
9080
9704
|
PASSWORD_HASH_FILES = ["/etc/shadow", "/etc/gshadow"];
|
|
9081
9705
|
STARTUP_NAMES = [".bashrc", ".zshrc", ".profile", ".bash_profile"];
|
|
9706
|
+
SYSTEM_DIRS = [
|
|
9707
|
+
"/usr",
|
|
9708
|
+
"/etc",
|
|
9709
|
+
"/boot",
|
|
9710
|
+
"/bin",
|
|
9711
|
+
"/sbin",
|
|
9712
|
+
"/lib",
|
|
9713
|
+
"/lib64",
|
|
9714
|
+
"/var/log"
|
|
9715
|
+
];
|
|
9716
|
+
SYSTEM_DIRS_EXEMPT = ["/usr/local", "/usr/src"];
|
|
9717
|
+
UPLOAD_SYSTEM_DIRS = [...SYSTEM_DIRS, "/proc", "/sys", "/root"];
|
|
9082
9718
|
AGENT_INSTRUCTION_FILES = [
|
|
9083
9719
|
".gitlab-ci.yml",
|
|
9084
9720
|
"CLAUDE.md",
|
|
@@ -9096,6 +9732,7 @@ var init_dist = __esm({
|
|
|
9096
9732
|
".husky"
|
|
9097
9733
|
];
|
|
9098
9734
|
LEGACY_SYSTEM_PATH_RE = /^(?:[a-zA-Z]:)?\/etc\/(?:passwd|shadow|sudoers)$/;
|
|
9735
|
+
exempt = (p) => SYSTEM_DIRS_EXEMPT.some((d) => within(p, d));
|
|
9099
9736
|
RANK = { allow: 0, review: 1, block: 2 };
|
|
9100
9737
|
BIP39_ENGLISH = `abandon
|
|
9101
9738
|
ability
|
|
@@ -11422,15 +12059,20 @@ zoo`.split("\n");
|
|
|
11422
12059
|
IDENTIFIER_VALUE_RE = /^[A-Za-z_.-]+$/;
|
|
11423
12060
|
PATH_VALUE_RE = /^(?:\/|\.{1,2}\/|~\/)/;
|
|
11424
12061
|
DOTTED_REFERENCE_RE = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)+$/;
|
|
12062
|
+
CONCATENATION_RE = /^\+[A-Za-z_$][\w$]*\./;
|
|
11425
12063
|
EMAIL_VALUE_RE = /^[^@\s]+@[^@\s]+\.[A-Za-z]{2,}$/;
|
|
11426
12064
|
FIXTURE_NAME_RE = /(?:fake|dummy|example|sample|mock|stub|placeholder|test)/i;
|
|
11427
|
-
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;
|
|
12065
|
+
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;
|
|
11428
12066
|
QUERY_CREDENTIAL_VALUE_RE = /=([A-Za-z0-9%+/_.~-]{8,512}={0,2})$/;
|
|
11429
12067
|
OTP_SECRET_RE = /^[A-Z2-7]{16,}=*$/i;
|
|
11430
|
-
|
|
12068
|
+
OTP_DOC_SECRETS = /* @__PURE__ */ new Set(["JBSWY3DPEHPK3PXP", "GEZDGNBVGY3TQOJQ"]);
|
|
12069
|
+
ASSIGNED_SECRET_VALUE_RE = /(?::=|=>|[:=])[ \t]{0,8}\\?["'`]?([A-Za-z0-9+/_.!@#$%^*~-]{12,512}={0,2})$/;
|
|
11431
12070
|
ASSIGNED_SECRET_NAME_RE = /^([A-Za-z0-9_]+)/;
|
|
11432
|
-
NAME_KEYWORD_RE = /^(?:password|passphrase|passwd|pwd|secret|token|api[_-]?key|auth[_-]?key|private[_-]?key|access[_-]?key|client[_-]?secret)/i;
|
|
12071
|
+
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;
|
|
11433
12072
|
ARN_CONTEXT_RE = /\barn:[A-Za-z0-9-]*:[^\s"'`]*$/i;
|
|
12073
|
+
BASIC_VALUE_RE = /([A-Za-z0-9+/]{8,512}={0,2})$/;
|
|
12074
|
+
BASIC_PAIR_RE = /^([^:\s]{1,128}):(\S{8,256})$/;
|
|
12075
|
+
OTP_CAPTURE_RE = /["']([A-Z2-7]{16,128}=*)["']$/i;
|
|
11434
12076
|
DLP_PATTERNS = [
|
|
11435
12077
|
// ── AWS ───────────────────────────────────────────────────────────────────
|
|
11436
12078
|
{
|
|
@@ -11768,6 +12410,22 @@ zoo`.split("\n");
|
|
|
11768
12410
|
contextBoost: true,
|
|
11769
12411
|
minEntropy: 3
|
|
11770
12412
|
},
|
|
12413
|
+
// ── HTTP Basic credential ─────────────────────────────────────────────────
|
|
12414
|
+
// `Authorization: Basic <base64 of user:password>`, or the bare scheme in a
|
|
12415
|
+
// config list. The validator decodes the value and accepts only a
|
|
12416
|
+
// `user:password` whose password looks generated (letters and digits, 8+
|
|
12417
|
+
// characters, the query-credential rule), so prose ("Basic authentication")
|
|
12418
|
+
// and documentation pairs (`admin:changeme`, `user:pass1234`) stay allowed.
|
|
12419
|
+
// review: the same weight as a bare Bearer token.
|
|
12420
|
+
{
|
|
12421
|
+
name: "HTTP Basic Credential",
|
|
12422
|
+
regex: /\bBasic[ \t]{1,8}([A-Za-z0-9+/]{8,512}={0,2})/i,
|
|
12423
|
+
severity: "review",
|
|
12424
|
+
keywords: ["basic"],
|
|
12425
|
+
validate: validateBasicCredential,
|
|
12426
|
+
redactGroup: 1,
|
|
12427
|
+
argumentsOnly: true
|
|
12428
|
+
},
|
|
11771
12429
|
// ── Resend ────────────────────────────────────────────────────────────────
|
|
11772
12430
|
{
|
|
11773
12431
|
name: "Resend API Key",
|
|
@@ -11909,6 +12567,19 @@ zoo`.split("\n");
|
|
|
11909
12567
|
validate: validateQueryCredential,
|
|
11910
12568
|
argumentsOnly: true
|
|
11911
12569
|
},
|
|
12570
|
+
// ── OTP secret in a constructor ───────────────────────────────────────────
|
|
12571
|
+
// `ROTP::TOTP.new('<base32>')`, `pyotp.TOTP("<base32>")`, `new HOTP('...')`:
|
|
12572
|
+
// the shared secret of a one-time-password generator written as a literal.
|
|
12573
|
+
// The otpauth URI form is `Credential in URL Query`. review.
|
|
12574
|
+
{
|
|
12575
|
+
name: "OTP Secret",
|
|
12576
|
+
regex: /\b(?:TOTP|HOTP|OTP)(?:\.new|\.create)?[ \t]{0,8}\(?[ \t]{0,8}["']([A-Z2-7]{16,128}=*)["']/i,
|
|
12577
|
+
severity: "review",
|
|
12578
|
+
keywords: ["otp"],
|
|
12579
|
+
validate: validateOtpSecret,
|
|
12580
|
+
redactGroup: 1,
|
|
12581
|
+
argumentsOnly: true
|
|
12582
|
+
},
|
|
11912
12583
|
// ── Secret assigned to a telling name ─────────────────────────────────────
|
|
11913
12584
|
// `password: ...`, `JWT_SECRET=...`, `api_key = "..."`. The name alone is
|
|
11914
12585
|
// not a finding: most such lines hold a reference (`process.env.X`), a
|
|
@@ -11916,12 +12587,14 @@ zoo`.split("\n");
|
|
|
11916
12587
|
// generated: 12+ characters, not an identifier, Shannon entropy of 3.5
|
|
11917
12588
|
// bits per character or more. review, never block (node9Firewall#348,
|
|
11918
12589
|
// item 4). Tuned on CredData's public split; its look-alike lines are the
|
|
11919
|
-
// negative control.
|
|
12590
|
+
// negative control. A quote may be escaped (`\"access_token\":\"...\"`):
|
|
12591
|
+
// JSON written inside a string literal. The two optional quotes after the
|
|
12592
|
+
// name are written out, not as a counted group, to keep star height 1.
|
|
11920
12593
|
{
|
|
11921
12594
|
name: "Assigned Secret",
|
|
11922
|
-
regex: /(?:password|passphrase|passwd|pwd|secret|token|api[_-]?key|auth[_-]?key|private[_-]?key|access[_-]?key|client[_-]?secret)[A-Za-z0-9_]{0,40}["'\]]
|
|
12595
|
+
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,
|
|
11923
12596
|
severity: "review",
|
|
11924
|
-
keywords: ["
|
|
12597
|
+
keywords: ["pass", "pwd", "secret", "token", "key", "ticket"],
|
|
11925
12598
|
validate: validateAssignedSecret,
|
|
11926
12599
|
argumentsOnly: true
|
|
11927
12600
|
}
|
|
@@ -12189,7 +12862,169 @@ zoo`.split("\n");
|
|
|
12189
12862
|
]);
|
|
12190
12863
|
PY_SPEC_RE = /^([A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?)(?:\[[^\]]*\])?\s*(.*)$/;
|
|
12191
12864
|
PY_EXACT_RE = /^===?\s*v?(\d+(?:\.\d+)*(?:[a-zA-Z0-9.+!-]*))$/;
|
|
12865
|
+
EXACT_VERSION_RE = /^=?v?(\d+(?:\.\d+)*(?:[-+][0-9A-Za-z][0-9A-Za-z.+-]*)?)$/;
|
|
12866
|
+
GEM_NAME_RE = /^[\p{L}\p{N}][\p{L}\p{N}_.-]*$/u;
|
|
12867
|
+
GEM_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
12868
|
+
"-v",
|
|
12869
|
+
"--version",
|
|
12870
|
+
"-i",
|
|
12871
|
+
"--install-dir",
|
|
12872
|
+
"-n",
|
|
12873
|
+
"--bindir",
|
|
12874
|
+
"--document",
|
|
12875
|
+
"--build-root",
|
|
12876
|
+
"-P",
|
|
12877
|
+
"--trust-policy",
|
|
12878
|
+
"-s",
|
|
12879
|
+
"--source",
|
|
12880
|
+
"-p",
|
|
12881
|
+
"--http-proxy",
|
|
12882
|
+
"--platform",
|
|
12883
|
+
"-g",
|
|
12884
|
+
"--file",
|
|
12885
|
+
"--without"
|
|
12886
|
+
]);
|
|
12887
|
+
BUNDLE_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
12888
|
+
"-v",
|
|
12889
|
+
"--version",
|
|
12890
|
+
"-g",
|
|
12891
|
+
"--group",
|
|
12892
|
+
"-s",
|
|
12893
|
+
"--source",
|
|
12894
|
+
"-r",
|
|
12895
|
+
"--require",
|
|
12896
|
+
"--path",
|
|
12897
|
+
"--git",
|
|
12898
|
+
"--github",
|
|
12899
|
+
"--branch",
|
|
12900
|
+
"--ref",
|
|
12901
|
+
"--glob"
|
|
12902
|
+
]);
|
|
12903
|
+
CRATE_NAME_RE = /^[\p{L}\p{N}][\p{L}\p{N}_-]*$/u;
|
|
12904
|
+
CARGO_ADD_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
12905
|
+
"-F",
|
|
12906
|
+
"--features",
|
|
12907
|
+
"--rename",
|
|
12908
|
+
"--registry",
|
|
12909
|
+
"--manifest-path",
|
|
12910
|
+
"-p",
|
|
12911
|
+
"--package",
|
|
12912
|
+
"--git",
|
|
12913
|
+
"--branch",
|
|
12914
|
+
"--tag",
|
|
12915
|
+
"--rev",
|
|
12916
|
+
"--path",
|
|
12917
|
+
"--base",
|
|
12918
|
+
"--target",
|
|
12919
|
+
"--color",
|
|
12920
|
+
"--config",
|
|
12921
|
+
"-Z",
|
|
12922
|
+
"--lockfile-path"
|
|
12923
|
+
]);
|
|
12924
|
+
CARGO_INSTALL_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
12925
|
+
"--version",
|
|
12926
|
+
"--vers",
|
|
12927
|
+
"--git",
|
|
12928
|
+
"--branch",
|
|
12929
|
+
"--tag",
|
|
12930
|
+
"--rev",
|
|
12931
|
+
"--path",
|
|
12932
|
+
"--root",
|
|
12933
|
+
"--index",
|
|
12934
|
+
"--registry",
|
|
12935
|
+
"-F",
|
|
12936
|
+
"--features",
|
|
12937
|
+
"--profile",
|
|
12938
|
+
"--target",
|
|
12939
|
+
"--target-dir",
|
|
12940
|
+
"-j",
|
|
12941
|
+
"--jobs",
|
|
12942
|
+
"--bin",
|
|
12943
|
+
"--example",
|
|
12944
|
+
"--color",
|
|
12945
|
+
"--config",
|
|
12946
|
+
"-Z"
|
|
12947
|
+
]);
|
|
12948
|
+
GO_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
12949
|
+
"-C",
|
|
12950
|
+
"-tags",
|
|
12951
|
+
"-ldflags",
|
|
12952
|
+
"-gcflags",
|
|
12953
|
+
"-asmflags",
|
|
12954
|
+
"-mod",
|
|
12955
|
+
"-modfile",
|
|
12956
|
+
"-o",
|
|
12957
|
+
"-p",
|
|
12958
|
+
"-pkgdir",
|
|
12959
|
+
"-toolexec",
|
|
12960
|
+
"-exec",
|
|
12961
|
+
"-overlay",
|
|
12962
|
+
"-pgo",
|
|
12963
|
+
"-buildmode",
|
|
12964
|
+
"-compiler",
|
|
12965
|
+
"-gccgoflags",
|
|
12966
|
+
"-installsuffix",
|
|
12967
|
+
"-covermode",
|
|
12968
|
+
"-coverpkg"
|
|
12969
|
+
]);
|
|
12970
|
+
GO_PATH_RE = /^[\p{L}\p{N}-]+(?:\.[\p{L}\p{N}-]+)+(?:\/[\p{L}\p{N}_.~+-]+)*$/u;
|
|
12971
|
+
NUGET_NAME_RE = /^[\p{L}\p{N}_][\p{L}\p{N}_.-]*$/u;
|
|
12972
|
+
DOTNET_ADD_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
12973
|
+
"-v",
|
|
12974
|
+
"--version",
|
|
12975
|
+
"-s",
|
|
12976
|
+
"--source",
|
|
12977
|
+
"-f",
|
|
12978
|
+
"--framework",
|
|
12979
|
+
"--package-directory"
|
|
12980
|
+
]);
|
|
12981
|
+
DOTNET_TOOL_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
12982
|
+
"--version",
|
|
12983
|
+
"--add-source",
|
|
12984
|
+
"--configfile",
|
|
12985
|
+
"--framework",
|
|
12986
|
+
"--tool-path",
|
|
12987
|
+
"-a",
|
|
12988
|
+
"--arch",
|
|
12989
|
+
"--tool-manifest",
|
|
12990
|
+
"-v",
|
|
12991
|
+
"--verbosity"
|
|
12992
|
+
]);
|
|
12993
|
+
NUGET_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
12994
|
+
"-version",
|
|
12995
|
+
"-outputdirectory",
|
|
12996
|
+
"-o",
|
|
12997
|
+
"-source",
|
|
12998
|
+
"-configfile",
|
|
12999
|
+
"-fallbacksource",
|
|
13000
|
+
"-framework",
|
|
13001
|
+
"-packagesavemode",
|
|
13002
|
+
"-dependencyversion",
|
|
13003
|
+
"-solutiondirectory",
|
|
13004
|
+
"-verbosity"
|
|
13005
|
+
]);
|
|
13006
|
+
MAVEN_COORD_RE = /^[\p{L}\p{N}_.-]+:[\p{L}\p{N}_.-]+$/u;
|
|
13007
|
+
COMPOSER_NAME_RE = /^[\p{L}\p{N}][\p{L}\p{N}_.-]*\/[\p{L}\p{N}][\p{L}\p{N}_.-]*$/u;
|
|
13008
|
+
COMPOSER_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
13009
|
+
"-d",
|
|
13010
|
+
"--working-dir",
|
|
13011
|
+
"--stability",
|
|
13012
|
+
"--repository",
|
|
13013
|
+
"--add-repository"
|
|
13014
|
+
]);
|
|
13015
|
+
EDITOR_HEADS = ["code", "code-insiders", "codium", "vscodium", "cursor"];
|
|
13016
|
+
EXTENSION_ID_RE = /^[\p{L}\p{N}][\p{L}\p{N}_-]*\.[\p{L}\p{N}][\p{L}\p{N}_.-]*$/u;
|
|
12192
13017
|
MANAGER_HEADS = /* @__PURE__ */ new Set([
|
|
13018
|
+
"gem",
|
|
13019
|
+
"bundle",
|
|
13020
|
+
"cargo",
|
|
13021
|
+
"go",
|
|
13022
|
+
"dotnet",
|
|
13023
|
+
"nuget",
|
|
13024
|
+
"mvn",
|
|
13025
|
+
"mvnw",
|
|
13026
|
+
"composer",
|
|
13027
|
+
...EDITOR_HEADS,
|
|
12193
13028
|
"npm",
|
|
12194
13029
|
"npx",
|
|
12195
13030
|
"pnpm",
|
|
@@ -12268,6 +13103,7 @@ zoo`.split("\n");
|
|
|
12268
13103
|
MAP_GOVERNED = /* @__PURE__ */ new Set([
|
|
12269
13104
|
"files.shell-startup",
|
|
12270
13105
|
"files.password-hashes",
|
|
13106
|
+
"files.system-dirs",
|
|
12271
13107
|
"commands.inline-exec",
|
|
12272
13108
|
"commands.eval-dynamic",
|
|
12273
13109
|
"commands.curl-pipe-shell",
|
|
@@ -12536,7 +13372,6 @@ zoo`.split("\n");
|
|
|
12536
13372
|
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/;
|
|
12537
13373
|
PII_CC15_RE = /\b3[47]\d{2}[-\s]?\d{6}[-\s]?\d{5}\b/;
|
|
12538
13374
|
PII_IBAN_RE = /\b[A-Z]{2}\d{2}(?:[A-Z0-9]|[ -][A-Z0-9]){11,30}\b/;
|
|
12539
|
-
NUMBER_LEAD_RE = /[\s:=;([{"'+-]/;
|
|
12540
13375
|
REALTIME_PII_PATTERNS = ["SSN", "Credit Card", "IBAN"];
|
|
12541
13376
|
MAX_PII_SCAN_BYTES = 1e5;
|
|
12542
13377
|
MASKED_KINDS = /* @__PURE__ */ new Set(["email", "iban", "account"]);
|
|
@@ -12758,7 +13593,7 @@ zoo`.split("\n");
|
|
|
12758
13593
|
{ view: "separators-stripped", decoder: "separators", stripped: true }
|
|
12759
13594
|
];
|
|
12760
13595
|
LONG_OUTPUT_THRESHOLD_BYTES = 100 * 1024;
|
|
12761
|
-
CANONICAL_EXTRACTOR_VERSION = "canonical-
|
|
13596
|
+
CANONICAL_EXTRACTOR_VERSION = "canonical-v28";
|
|
12762
13597
|
DEDUPE_PREVIEW_LEN = 120;
|
|
12763
13598
|
ENGINE_VERSION = "1.4.0";
|
|
12764
13599
|
}
|
|
@@ -18121,7 +18956,20 @@ function ecoDir(eco) {
|
|
|
18121
18956
|
return import_path24.default.join(osvRoot(), eco);
|
|
18122
18957
|
}
|
|
18123
18958
|
function indexKey(eco, name) {
|
|
18124
|
-
|
|
18959
|
+
switch (eco) {
|
|
18960
|
+
case "PyPI":
|
|
18961
|
+
return normalizePyPiName(name);
|
|
18962
|
+
case "crates.io":
|
|
18963
|
+
return name.toLowerCase().replace(/_/g, "-");
|
|
18964
|
+
case "Go":
|
|
18965
|
+
case "Maven":
|
|
18966
|
+
return name;
|
|
18967
|
+
default:
|
|
18968
|
+
return name.toLowerCase();
|
|
18969
|
+
}
|
|
18970
|
+
}
|
|
18971
|
+
function recordEcosystemMatches(eco, recordEco) {
|
|
18972
|
+
return typeof recordEco === "string" && (recordEco === eco || recordEco.startsWith(`${eco}:`));
|
|
18125
18973
|
}
|
|
18126
18974
|
function shardOf(key) {
|
|
18127
18975
|
return (0, import_crypto11.createHash)("sha1").update(key).digest("hex").slice(0, 2);
|
|
@@ -18167,7 +19015,8 @@ function entriesFromRecord(eco, record2) {
|
|
|
18167
19015
|
const modifiedMs = typeof r.modified === "string" ? Date.parse(r.modified) || 0 : 0;
|
|
18168
19016
|
const rows = /* @__PURE__ */ new Map();
|
|
18169
19017
|
for (const a of r.affected ?? []) {
|
|
18170
|
-
if (a?.package?.ecosystem
|
|
19018
|
+
if (!recordEcosystemMatches(eco, a?.package?.ecosystem) || typeof a?.package?.name !== "string")
|
|
19019
|
+
continue;
|
|
18171
19020
|
const key = indexKey(eco, a.package.name);
|
|
18172
19021
|
const entry = rows.get(key) ?? { id: r.id };
|
|
18173
19022
|
if (Array.isArray(a.versions)) {
|
|
@@ -18410,9 +19259,11 @@ async function pypiInfo(name, version2) {
|
|
|
18410
19259
|
};
|
|
18411
19260
|
}
|
|
18412
19261
|
async function registryInfo(eco, name, version2, freshWindowMs, raw) {
|
|
18413
|
-
|
|
19262
|
+
if (eco === "npm") return npmInfo(name, version2, freshWindowMs, raw);
|
|
19263
|
+
if (eco === "PyPI") return pypiInfo(name, version2);
|
|
19264
|
+
return null;
|
|
18414
19265
|
}
|
|
18415
|
-
var import_semver, TIMEOUT_MS2, MAX_BYTES;
|
|
19266
|
+
var import_semver, TIMEOUT_MS2, MAX_BYTES, HAS_REGISTRY_CLIENT;
|
|
18416
19267
|
var init_registry2 = __esm({
|
|
18417
19268
|
"src/supply-chain/registry.ts"() {
|
|
18418
19269
|
"use strict";
|
|
@@ -18420,6 +19271,7 @@ var init_registry2 = __esm({
|
|
|
18420
19271
|
init_net();
|
|
18421
19272
|
TIMEOUT_MS2 = 1500;
|
|
18422
19273
|
MAX_BYTES = 2 * 1024 * 1024;
|
|
19274
|
+
HAS_REGISTRY_CLIENT = /* @__PURE__ */ new Set(["npm", "PyPI"]);
|
|
18423
19275
|
}
|
|
18424
19276
|
});
|
|
18425
19277
|
|
|
@@ -18464,25 +19316,39 @@ function commandOf(toolName, args) {
|
|
|
18464
19316
|
return typeof a.command === "string" ? a.command : typeof a.cmd === "string" ? a.cmd : null;
|
|
18465
19317
|
}
|
|
18466
19318
|
function label(p, version2) {
|
|
18467
|
-
return `${p.ecosystem
|
|
19319
|
+
return `${KIND[p.ecosystem]} "${p.name}${version2 ? `@${version2}` : ""}"`;
|
|
19320
|
+
}
|
|
19321
|
+
function osvNames(p) {
|
|
19322
|
+
if (p.ecosystem !== "Go") return [p.name];
|
|
19323
|
+
const parts = p.name.split("/");
|
|
19324
|
+
const out = [];
|
|
19325
|
+
for (let n = parts.length; n >= 2 && out.length < 6; n--) out.push(parts.slice(0, n).join("/"));
|
|
19326
|
+
return out;
|
|
18468
19327
|
}
|
|
18469
19328
|
async function maliciousFor(p, version2, cfg, misses, networkCapped) {
|
|
18470
|
-
const
|
|
18471
|
-
const
|
|
18472
|
-
|
|
19329
|
+
const names = osvNames(p);
|
|
19330
|
+
const local = names.map((n) => lookupIndex(p.ecosystem, n));
|
|
19331
|
+
const hits2 = local.flatMap((l) => l.status === "hit" ? l.entries : []);
|
|
19332
|
+
const entries = hits2.length > 0 ? hits2 : null;
|
|
19333
|
+
const freshClean = local.every((l) => l.status === "clean" && l.fresh);
|
|
19334
|
+
const status = local.some((l) => l.status === "unavailable") ? "unavailable" : local[0].status;
|
|
19335
|
+
if (!entries && !freshClean) {
|
|
18473
19336
|
if (!cfg.onlineFallback || networkCapped) {
|
|
18474
19337
|
misses.push(
|
|
18475
|
-
`${label(p, version2)}: local index ${
|
|
19338
|
+
`${label(p, version2)}: local index ${status}, no online lookup (${networkCapped ? "over the per-command network cap" : "online fallback off"})`
|
|
18476
19339
|
);
|
|
18477
19340
|
return null;
|
|
18478
19341
|
}
|
|
18479
|
-
let
|
|
19342
|
+
let perName = null;
|
|
18480
19343
|
try {
|
|
18481
|
-
|
|
19344
|
+
perName = await queryOsvMalicious(
|
|
19345
|
+
names.map((name) => ({ ecosystem: p.ecosystem, name, version: version2 }))
|
|
19346
|
+
);
|
|
18482
19347
|
} catch (err2) {
|
|
18483
19348
|
misses.push(`${label(p, version2)}: OSV online ${err2.message}`);
|
|
18484
19349
|
return null;
|
|
18485
19350
|
}
|
|
19351
|
+
const ids = perName ? [[...new Set(perName.flat())]] : null;
|
|
18486
19352
|
if (!ids) {
|
|
18487
19353
|
misses.push(`${label(p, version2)}: OSV online unavailable`);
|
|
18488
19354
|
return null;
|
|
@@ -18552,7 +19418,7 @@ async function checkOne(p, cfg, misses, networkCapped, cwd) {
|
|
|
18552
19418
|
}
|
|
18553
19419
|
const findings = [];
|
|
18554
19420
|
let info = null;
|
|
18555
|
-
const wantsRegistry = cfg.newPackage !== "off" || cfg.installScript !== "off";
|
|
19421
|
+
const wantsRegistry = (cfg.newPackage !== "off" || cfg.installScript !== "off") && HAS_REGISTRY_CLIENT.has(p.ecosystem);
|
|
18556
19422
|
if (wantsRegistry && !networkCapped) {
|
|
18557
19423
|
try {
|
|
18558
19424
|
const windowMs = cfg.newPackage === "off" ? 0 : cfg.maxAgeHours * 36e5;
|
|
@@ -18632,7 +19498,7 @@ async function runPackageCheck(toolName, args, cfg, cwd) {
|
|
|
18632
19498
|
return { ...ALLOW, misses: [`package check error: ${err2?.message ?? err2}`] };
|
|
18633
19499
|
}
|
|
18634
19500
|
}
|
|
18635
|
-
var import_picomatch3, import_path26, MAX_NETWORK_PACKAGES, ALLOW;
|
|
19501
|
+
var import_picomatch3, import_path26, MAX_NETWORK_PACKAGES, ALLOW, KIND;
|
|
18636
19502
|
var init_check = __esm({
|
|
18637
19503
|
"src/supply-chain/check.ts"() {
|
|
18638
19504
|
"use strict";
|
|
@@ -18645,6 +19511,17 @@ var init_check = __esm({
|
|
|
18645
19511
|
init_local_resolve();
|
|
18646
19512
|
MAX_NETWORK_PACKAGES = 10;
|
|
18647
19513
|
ALLOW = { verdict: "allow", findings: [], misses: [], checked: 0 };
|
|
19514
|
+
KIND = {
|
|
19515
|
+
npm: "npm package",
|
|
19516
|
+
PyPI: "PyPI package",
|
|
19517
|
+
RubyGems: "RubyGems package",
|
|
19518
|
+
"crates.io": "crates.io package",
|
|
19519
|
+
Go: "Go module",
|
|
19520
|
+
NuGet: "NuGet package",
|
|
19521
|
+
Maven: "Maven artifact",
|
|
19522
|
+
Packagist: "Packagist package",
|
|
19523
|
+
VSCode: "VS Code extension"
|
|
19524
|
+
};
|
|
18648
19525
|
}
|
|
18649
19526
|
});
|
|
18650
19527
|
|
|
@@ -33887,7 +34764,17 @@ var init_osv_sync = __esm({
|
|
|
33887
34764
|
init_zip();
|
|
33888
34765
|
init_osv_index();
|
|
33889
34766
|
init_net();
|
|
33890
|
-
ECOSYSTEMS2 = [
|
|
34767
|
+
ECOSYSTEMS2 = [
|
|
34768
|
+
"npm",
|
|
34769
|
+
"PyPI",
|
|
34770
|
+
"RubyGems",
|
|
34771
|
+
"crates.io",
|
|
34772
|
+
"Go",
|
|
34773
|
+
"NuGet",
|
|
34774
|
+
"Maven",
|
|
34775
|
+
"Packagist",
|
|
34776
|
+
"VSCode"
|
|
34777
|
+
];
|
|
33891
34778
|
SYNC_INTERVAL_MS2 = 6 * 60 * 60 * 1e3;
|
|
33892
34779
|
FIRST_SYNC_DELAY_MS = 2 * 60 * 1e3;
|
|
33893
34780
|
FULL_REBUILD_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
@@ -35877,14 +36764,14 @@ async function onboardMachine(apiKey, opts = {}) {
|
|
|
35877
36764
|
detail: `${sync.rules} cloud rule${sync.rules === 1 ? "" : "s"} active`
|
|
35878
36765
|
} : { name: "policy-sync", ok: false, detail: sync.reason }
|
|
35879
36766
|
);
|
|
35880
|
-
const
|
|
36767
|
+
const push2 = await runPolicyPush();
|
|
35881
36768
|
steps.push(
|
|
35882
|
-
|
|
36769
|
+
push2.ok ? { name: "register", ok: true, detail: "machine visible in the dashboard" } : { name: "register", ok: false, detail: push2.reason }
|
|
35883
36770
|
);
|
|
35884
36771
|
const healed = ensureAutostartHealthy(!!getConfig().settings.autoStartDaemon);
|
|
35885
36772
|
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)";
|
|
35886
36773
|
let daemonOk = true;
|
|
35887
|
-
if (sync.ok &&
|
|
36774
|
+
if (sync.ok && push2.ok && isInteractive() && mayChangeService({ stdoutTTY: !!process.stdout.isTTY, ci: isCI() }) && autostartState() === "absent") {
|
|
35888
36775
|
try {
|
|
35889
36776
|
if (await (0, import_prompts2.confirm)({
|
|
35890
36777
|
message: "Run node9 in the background when this computer starts?",
|
|
@@ -65338,7 +66225,7 @@ function collectToolOutputText(toolResponse, opts = {}) {
|
|
|
65338
66225
|
let size = 0;
|
|
65339
66226
|
let fields = 0;
|
|
65340
66227
|
let truncated = false;
|
|
65341
|
-
const
|
|
66228
|
+
const push2 = (s) => {
|
|
65342
66229
|
if (s.length === 0) return true;
|
|
65343
66230
|
const room = maxBytes - size;
|
|
65344
66231
|
if (s.length > room) {
|
|
@@ -65356,7 +66243,7 @@ function collectToolOutputText(toolResponse, opts = {}) {
|
|
|
65356
66243
|
return true;
|
|
65357
66244
|
};
|
|
65358
66245
|
const walk2 = (v, depth) => {
|
|
65359
|
-
if (typeof v === "string") return
|
|
66246
|
+
if (typeof v === "string") return push2(v);
|
|
65360
66247
|
if (depth >= MAX_DEPTH5 || v === null || typeof v !== "object") return true;
|
|
65361
66248
|
const children = Array.isArray(v) ? v : Object.values(v);
|
|
65362
66249
|
for (const child of children) if (!walk2(child, depth + 1)) return false;
|
|
@@ -75983,7 +76870,7 @@ function addHost(list, host2) {
|
|
|
75983
76870
|
if (!cliGuardPolicyWrite(`egress ${list} ${host2}`)) return false;
|
|
75984
76871
|
return guard(() => addEgressHost(list, host2));
|
|
75985
76872
|
}
|
|
75986
|
-
function
|
|
76873
|
+
function exempt2(address) {
|
|
75987
76874
|
if (!cliGuardPolicyWrite(`egress exempt ${address}`)) return false;
|
|
75988
76875
|
return guard(() => addSsrfExemption(address));
|
|
75989
76876
|
}
|
|
@@ -76166,7 +77053,7 @@ function registerEgressCommand(program2) {
|
|
|
76166
77053
|
process.exitCode = 1;
|
|
76167
77054
|
return;
|
|
76168
77055
|
}
|
|
76169
|
-
if (!
|
|
77056
|
+
if (!exempt2(a)) return;
|
|
76170
77057
|
console.log(import_chalk34.default.green(`
|
|
76171
77058
|
\u2713 ${a} is exempt from the floor.`));
|
|
76172
77059
|
if (!m)
|
|
@@ -77955,17 +78842,18 @@ function registerSessionExposureCommand(program2) {
|
|
|
77955
78842
|
var import_chalk42 = __toESM(require("chalk"));
|
|
77956
78843
|
init_osv_index();
|
|
77957
78844
|
init_osv_sync();
|
|
78845
|
+
var ECO_WIDTH = Math.max(...ECOSYSTEMS2.map((e) => e.length));
|
|
77958
78846
|
function registerPackageIndexCommand(program2) {
|
|
77959
78847
|
const cmd = program2.command("package-index").description("Local OSV malicious-package index used to check installs before they run");
|
|
77960
78848
|
cmd.command("status").description("Show when each ecosystem was last synced and how many records it holds").action(() => {
|
|
77961
78849
|
for (const eco of ECOSYSTEMS2) {
|
|
77962
78850
|
const meta = readMeta(eco);
|
|
77963
78851
|
if (!meta) {
|
|
77964
|
-
console.log(`${eco.padEnd(
|
|
78852
|
+
console.log(`${eco.padEnd(ECO_WIDTH)} ${import_chalk42.default.yellow("not synced")}`);
|
|
77965
78853
|
continue;
|
|
77966
78854
|
}
|
|
77967
78855
|
console.log(
|
|
77968
|
-
`${eco.padEnd(
|
|
78856
|
+
`${eco.padEnd(ECO_WIDTH)} ${meta.records} records synced ${meta.syncedAt} (${meta.mode})`
|
|
77969
78857
|
);
|
|
77970
78858
|
}
|
|
77971
78859
|
});
|
|
@@ -77975,9 +78863,9 @@ function registerPackageIndexCommand(program2) {
|
|
|
77975
78863
|
for (const r of results) {
|
|
77976
78864
|
if (r.error) {
|
|
77977
78865
|
failed = true;
|
|
77978
|
-
console.error(`${r.ecosystem.padEnd(
|
|
78866
|
+
console.error(`${r.ecosystem.padEnd(ECO_WIDTH)} ${import_chalk42.default.red("failed")}: ${r.error}`);
|
|
77979
78867
|
} else {
|
|
77980
|
-
console.log(`${r.ecosystem.padEnd(
|
|
78868
|
+
console.log(`${r.ecosystem.padEnd(ECO_WIDTH)} ${r.mode}: ${r.records} records`);
|
|
77981
78869
|
}
|
|
77982
78870
|
}
|
|
77983
78871
|
process.exitCode = failed ? 1 : 0;
|