@node9/proxy 2.33.1 → 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 +1277 -249
- package/dist/cli.mjs +1285 -257
- package/dist/dashboard.mjs +230 -13
- package/dist/index.js +1087 -110
- package/dist/index.mjs +1087 -110
- package/dist/scan-ink.mjs +134 -11
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1183,6 +1183,120 @@ function expandPlainParam(p) {
|
|
|
1183
1183
|
if (recorded !== void 0) return recorded;
|
|
1184
1184
|
return HOME_VARIABLES.has(p.Param?.Value) ? "~" : void 0;
|
|
1185
1185
|
}
|
|
1186
|
+
function functionBodies(f) {
|
|
1187
|
+
const out = /* @__PURE__ */ new Map();
|
|
1188
|
+
syntax.Walk(f, (node) => {
|
|
1189
|
+
const n = node;
|
|
1190
|
+
if (n && syntax.NodeType(n) === "FuncDecl" && n.Name?.Value && n.Body) {
|
|
1191
|
+
const list = out.get(n.Name.Value) ?? [];
|
|
1192
|
+
list.push(n.Body);
|
|
1193
|
+
out.set(n.Name.Value, list);
|
|
1194
|
+
}
|
|
1195
|
+
return true;
|
|
1196
|
+
});
|
|
1197
|
+
return out;
|
|
1198
|
+
}
|
|
1199
|
+
function rebindsPositionals(body) {
|
|
1200
|
+
let found = false;
|
|
1201
|
+
syntax.Walk(body, (node) => {
|
|
1202
|
+
const n = node;
|
|
1203
|
+
if (found || !n) return false;
|
|
1204
|
+
if (syntax.NodeType(n) === "CallExpr") {
|
|
1205
|
+
const words = (n.Args || []).map((a) => resolveWordLiteral(a));
|
|
1206
|
+
if (words[0] === "shift") found = true;
|
|
1207
|
+
else if (words[0] === "set" && words.slice(1).some((w) => w === null || w === "--" || !/^[-+]/.test(w)))
|
|
1208
|
+
found = true;
|
|
1209
|
+
}
|
|
1210
|
+
return !found;
|
|
1211
|
+
});
|
|
1212
|
+
return found;
|
|
1213
|
+
}
|
|
1214
|
+
function tableBindings() {
|
|
1215
|
+
if (!assignmentTable) return "";
|
|
1216
|
+
const out = [];
|
|
1217
|
+
for (const [name, rec] of assignmentTable)
|
|
1218
|
+
if (rec.at < currentStmtOffset) out.push(`${name}=${rec.value ?? "\0"}`);
|
|
1219
|
+
return out.sort().join("");
|
|
1220
|
+
}
|
|
1221
|
+
function walkWithCalls(f, visit) {
|
|
1222
|
+
const bodies = functionBodies(f);
|
|
1223
|
+
if (bodies.size === 0) {
|
|
1224
|
+
syntax.Walk(f, visit);
|
|
1225
|
+
return;
|
|
1226
|
+
}
|
|
1227
|
+
const seenCalls = /* @__PURE__ */ new Set();
|
|
1228
|
+
const inline = (call) => {
|
|
1229
|
+
const words = (call.Args || []).map((a) => resolveWordLiteral(a));
|
|
1230
|
+
const list = words[0] ? bodies.get(words[0]) : void 0;
|
|
1231
|
+
if (!list || callDepth >= MAX_CALL_DEPTH) return;
|
|
1232
|
+
const args = words.slice(1);
|
|
1233
|
+
const key = `${words[0]}\0${JSON.stringify(args)}\0${tableBindings()}`;
|
|
1234
|
+
if (seenCalls.has(key)) return;
|
|
1235
|
+
seenCalls.add(key);
|
|
1236
|
+
const outerTable = assignmentTable;
|
|
1237
|
+
const outerOffset = currentStmtOffset;
|
|
1238
|
+
for (const body of list) {
|
|
1239
|
+
if (callWalks >= MAX_CALL_WALKS) return;
|
|
1240
|
+
callWalks++;
|
|
1241
|
+
const table = /* @__PURE__ */ new Map();
|
|
1242
|
+
for (const [name, rec] of outerTable ?? [])
|
|
1243
|
+
if (rec.at < outerOffset) table.set(name, { value: rec.value, at: -1 });
|
|
1244
|
+
for (const name of ["1", "2", "3", "4", "5", "6", "7", "8", "9", "@", "*", "#"])
|
|
1245
|
+
table.delete(name);
|
|
1246
|
+
if (!rebindsPositionals(body)) {
|
|
1247
|
+
args.slice(0, 9).forEach((value, i) => table.set(String(i + 1), { value, at: -1 }));
|
|
1248
|
+
if (args.length === 1) {
|
|
1249
|
+
table.set("@", { value: args[0], at: -1 });
|
|
1250
|
+
table.set("*", { value: args[0], at: -1 });
|
|
1251
|
+
}
|
|
1252
|
+
table.set("#", { value: String(args.length), at: -1 });
|
|
1253
|
+
}
|
|
1254
|
+
assignmentTable = table;
|
|
1255
|
+
currentStmtOffset = Number.MAX_SAFE_INTEGER;
|
|
1256
|
+
callDepth++;
|
|
1257
|
+
try {
|
|
1258
|
+
if (body.Cmd && syntax.NodeType(body.Cmd) === "Block") recordTopLevelAssignments(body.Cmd);
|
|
1259
|
+
syntax.Walk(body, step);
|
|
1260
|
+
} finally {
|
|
1261
|
+
callDepth--;
|
|
1262
|
+
assignmentTable = outerTable;
|
|
1263
|
+
currentStmtOffset = outerOffset;
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
};
|
|
1267
|
+
const step = (node) => {
|
|
1268
|
+
if (!node) return false;
|
|
1269
|
+
const go = visit(node);
|
|
1270
|
+
if (go !== false && syntax.NodeType(node) === "CallExpr") inline(node);
|
|
1271
|
+
return go;
|
|
1272
|
+
};
|
|
1273
|
+
syntax.Walk(f, step);
|
|
1274
|
+
}
|
|
1275
|
+
function withAssignments(f, body) {
|
|
1276
|
+
const outerTable = assignmentTable;
|
|
1277
|
+
const outerOffset = currentStmtOffset;
|
|
1278
|
+
const outerWalks = callWalks;
|
|
1279
|
+
callWalks = 0;
|
|
1280
|
+
assignmentTable = new Map(
|
|
1281
|
+
[...outerTable ?? []].map(([k, r]) => [k, { value: r.value, at: -1 }])
|
|
1282
|
+
);
|
|
1283
|
+
currentStmtOffset = Number.MAX_SAFE_INTEGER;
|
|
1284
|
+
try {
|
|
1285
|
+
recordTopLevelAssignments(f);
|
|
1286
|
+
return body();
|
|
1287
|
+
} finally {
|
|
1288
|
+
assignmentTable = outerTable;
|
|
1289
|
+
currentStmtOffset = outerOffset;
|
|
1290
|
+
callWalks = outerWalks;
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
function trackStatement(n) {
|
|
1294
|
+
try {
|
|
1295
|
+
currentStmtOffset = n.Pos().Offset();
|
|
1296
|
+
} catch {
|
|
1297
|
+
currentStmtOffset = Number.MAX_SAFE_INTEGER;
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1186
1300
|
function resolveWordLiteral(w) {
|
|
1187
1301
|
const parts = w?.Parts || [];
|
|
1188
1302
|
let s = "";
|
|
@@ -1303,27 +1417,31 @@ function extractShellDestinations(command) {
|
|
|
1303
1417
|
const out = [];
|
|
1304
1418
|
const seen = /* @__PURE__ */ new Set();
|
|
1305
1419
|
try {
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
const
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1420
|
+
withAssignments(
|
|
1421
|
+
f,
|
|
1422
|
+
() => walkWithCalls(f, (node) => {
|
|
1423
|
+
if (!node) return false;
|
|
1424
|
+
const n = node;
|
|
1425
|
+
if (syntax.NodeType(n) === "Stmt") trackStatement(n);
|
|
1426
|
+
if (syntax.NodeType(n) !== "CallExpr") return true;
|
|
1427
|
+
const callArgs = n.Args || [];
|
|
1428
|
+
if (callArgs.length === 0) return true;
|
|
1429
|
+
const words = callArgs.map((a) => resolveWordLiteral(a));
|
|
1430
|
+
const at = unwrapCommandHead(words);
|
|
1431
|
+
const name = (words[at] || "").toLowerCase().split("/").pop() ?? "";
|
|
1432
|
+
if (!NET_BINARIES.has(name)) return true;
|
|
1433
|
+
const rest = words.slice(at + 1);
|
|
1434
|
+
for (const raw of destTokensForBinary(name, rest)) {
|
|
1435
|
+
const host2 = parseDestHost(raw);
|
|
1436
|
+
if (!host2) continue;
|
|
1437
|
+
const key = `${name}:${host2}`;
|
|
1438
|
+
if (seen.has(key)) continue;
|
|
1439
|
+
seen.add(key);
|
|
1440
|
+
out.push({ host: host2, binary: name, raw });
|
|
1441
|
+
}
|
|
1442
|
+
return true;
|
|
1443
|
+
})
|
|
1444
|
+
);
|
|
1327
1445
|
} catch {
|
|
1328
1446
|
return out;
|
|
1329
1447
|
}
|
|
@@ -1335,39 +1453,45 @@ function extractShellDestTokens(command) {
|
|
|
1335
1453
|
const out = [];
|
|
1336
1454
|
const seen = /* @__PURE__ */ new Set();
|
|
1337
1455
|
try {
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
const
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
tok = tok.
|
|
1456
|
+
withAssignments(
|
|
1457
|
+
f,
|
|
1458
|
+
() => walkWithCalls(f, (node) => {
|
|
1459
|
+
if (!node) return false;
|
|
1460
|
+
const n = node;
|
|
1461
|
+
if (syntax.NodeType(n) === "Stmt") trackStatement(n);
|
|
1462
|
+
if (syntax.NodeType(n) !== "CallExpr") return true;
|
|
1463
|
+
const callArgs = n.Args || [];
|
|
1464
|
+
if (callArgs.length === 0) return true;
|
|
1465
|
+
const words = callArgs.map((a) => resolveWordLiteral(a));
|
|
1466
|
+
const head = unwrapCommandHead(words);
|
|
1467
|
+
const name = (words[head] || "").toLowerCase().split("/").pop() ?? "";
|
|
1468
|
+
if (!NET_BINARIES.has(name)) return true;
|
|
1469
|
+
const rest = words.slice(head + 1);
|
|
1470
|
+
for (const raw of destTokensForBinary(name, rest)) {
|
|
1471
|
+
if (!raw) continue;
|
|
1472
|
+
let tok = raw.trim();
|
|
1473
|
+
const scheme = /^[a-z][a-z0-9+.-]*:\/\//i.exec(tok);
|
|
1474
|
+
const hasScheme = scheme !== null;
|
|
1475
|
+
if (hasScheme) tok = tok.slice(scheme[0].length);
|
|
1476
|
+
tok = tok.split(/[/?#]/)[0];
|
|
1477
|
+
const at = tok.lastIndexOf("@");
|
|
1478
|
+
if (at >= 0) tok = tok.slice(at + 1);
|
|
1479
|
+
if (tok.startsWith("[")) {
|
|
1480
|
+
const close = tok.indexOf("]");
|
|
1481
|
+
if (close > 0) tok = tok.slice(0, close + 1);
|
|
1482
|
+
} else {
|
|
1483
|
+
tok = tok.split(":")[0];
|
|
1484
|
+
}
|
|
1485
|
+
if (!tok) continue;
|
|
1486
|
+
if (!hasScheme && /^\d+$/.test(tok) && Number(tok) < 16777216) continue;
|
|
1487
|
+
const key = `${name}:${tok}`;
|
|
1488
|
+
if (seen.has(key)) continue;
|
|
1489
|
+
seen.add(key);
|
|
1490
|
+
out.push({ token: tok, binary: name });
|
|
1361
1491
|
}
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
if (seen.has(key)) continue;
|
|
1366
|
-
seen.add(key);
|
|
1367
|
-
out.push({ token: tok, binary: name });
|
|
1368
|
-
}
|
|
1369
|
-
return true;
|
|
1370
|
-
});
|
|
1492
|
+
return true;
|
|
1493
|
+
})
|
|
1494
|
+
);
|
|
1371
1495
|
} catch {
|
|
1372
1496
|
return out;
|
|
1373
1497
|
}
|
|
@@ -1486,6 +1610,7 @@ function analyzeFsOperationImpl(command, depth = 0) {
|
|
|
1486
1610
|
if (depth === 0) {
|
|
1487
1611
|
seenPayloads = /* @__PURE__ */ new Set();
|
|
1488
1612
|
payloadBudget = PAYLOAD_BUDGET;
|
|
1613
|
+
if (callDepth === 0) callWalks = 0;
|
|
1489
1614
|
}
|
|
1490
1615
|
assignmentTable = new Map(
|
|
1491
1616
|
[...outerTable ?? []].map(([k, r]) => [k, { value: r.value, at: -1 }])
|
|
@@ -1493,7 +1618,7 @@ function analyzeFsOperationImpl(command, depth = 0) {
|
|
|
1493
1618
|
currentStmtOffset = Number.MAX_SAFE_INTEGER;
|
|
1494
1619
|
try {
|
|
1495
1620
|
recordTopLevelAssignments(f);
|
|
1496
|
-
|
|
1621
|
+
walkWithCalls(f, (node) => {
|
|
1497
1622
|
if (!node || result?.verdict === "block") return false;
|
|
1498
1623
|
const n = node;
|
|
1499
1624
|
const nodeType = syntax.NodeType(n);
|
|
@@ -2282,6 +2407,19 @@ function runnablePrefix(command) {
|
|
|
2282
2407
|
}
|
|
2283
2408
|
return null;
|
|
2284
2409
|
}
|
|
2410
|
+
function uploadedFile(tool, flag, value) {
|
|
2411
|
+
const v = value.value;
|
|
2412
|
+
const file2 = (f) => f && f !== "-" && f !== "." ? { value: f } : null;
|
|
2413
|
+
if (tool === "curl") {
|
|
2414
|
+
if (["-d", "--data", "--data-ascii", "--data-binary", "--json"].includes(flag))
|
|
2415
|
+
return v.startsWith("@") ? file2(v.slice(1)) : null;
|
|
2416
|
+
if (flag === "--data-urlencode") return file2(/^[^=@]*@(.+)$/.exec(v)?.[1]);
|
|
2417
|
+
if (flag === "-F" || flag === "--form") return file2(/^[^=]*=[@<]([^;]+)/.exec(v)?.[1]);
|
|
2418
|
+
if (flag === "-T" || flag === "--upload-file") return file2(v);
|
|
2419
|
+
}
|
|
2420
|
+
if (tool === "wget" && ["--post-file", "--body-file"].includes(flag)) return file2(v);
|
|
2421
|
+
return null;
|
|
2422
|
+
}
|
|
2285
2423
|
function merge(a, b) {
|
|
2286
2424
|
a.cwd = unique([...a.cwd, ...b.cwd]);
|
|
2287
2425
|
for (const key of /* @__PURE__ */ new Set([...a.vars.keys(), ...b.vars.keys()])) {
|
|
@@ -2355,6 +2493,9 @@ function wordValues(word, state) {
|
|
|
2355
2493
|
function collectShellFileAccesses(command, context = {}) {
|
|
2356
2494
|
const out = [];
|
|
2357
2495
|
let payloads = 256;
|
|
2496
|
+
const functions = /* @__PURE__ */ new Map();
|
|
2497
|
+
const seenCalls = /* @__PURE__ */ new Set();
|
|
2498
|
+
let callWalks2 = MAX_CALL_WALKS2;
|
|
2358
2499
|
const initial = {
|
|
2359
2500
|
cwd: [context.cwd ?? ""],
|
|
2360
2501
|
vars: /* @__PURE__ */ new Map([["HOME", [context.homeDirs?.[0] ?? "~"]]])
|
|
@@ -2612,8 +2753,98 @@ function collectShellFileAccesses(command, context = {}) {
|
|
|
2612
2753
|
}
|
|
2613
2754
|
return;
|
|
2614
2755
|
}
|
|
2756
|
+
if (name === "wget" || name === "curl" || name === "aria2c") {
|
|
2757
|
+
downloadOutputs(name, args, state);
|
|
2758
|
+
return;
|
|
2759
|
+
}
|
|
2760
|
+
if (["http", "https", "xh", "xhs"].includes(name)) {
|
|
2761
|
+
for (const token of args) {
|
|
2762
|
+
if (!token || token.dynamic || /^[a-z][a-z0-9+.-]*:\/\//i.test(token.value)) continue;
|
|
2763
|
+
const m = /^(?:@|[^=:@\s]+@|[^=:@\s]*=@)(.+)$/.exec(token.value);
|
|
2764
|
+
if (m && m[1] !== "-") add("read", { value: m[1] }, state, { upload: true });
|
|
2765
|
+
}
|
|
2766
|
+
return;
|
|
2767
|
+
}
|
|
2768
|
+
if (name === "scp" || name === "rsync") {
|
|
2769
|
+
const operands2 = strings.filter((s) => s && !s.startsWith("-"));
|
|
2770
|
+
const remote = operands2.slice(1).some((o) => /^[^/\s]+:/.test(o));
|
|
2771
|
+
for (const p of copySourcePaths(words))
|
|
2772
|
+
add("read", locate(p), state, remote ? { upload: true } : {});
|
|
2773
|
+
return;
|
|
2774
|
+
}
|
|
2615
2775
|
for (const p of copySourcePaths(words)) add("read", locate(p), state);
|
|
2616
2776
|
}
|
|
2777
|
+
function downloadOutputs(name, args, state) {
|
|
2778
|
+
const valueShort = DOWNLOAD_SHORT_VALUE[name];
|
|
2779
|
+
const outputs = [];
|
|
2780
|
+
const urls = [];
|
|
2781
|
+
let dir2 = null;
|
|
2782
|
+
let remoteName = false;
|
|
2783
|
+
let document = false;
|
|
2784
|
+
let serverNamed = false;
|
|
2785
|
+
const take = (flag, value) => {
|
|
2786
|
+
if (!value) return;
|
|
2787
|
+
const sent = uploadedFile(name, flag, value);
|
|
2788
|
+
if (sent) add("read", sent, state, { upload: true });
|
|
2789
|
+
if (DOWNLOAD_OUTPUT_FLAGS[name].includes(flag)) {
|
|
2790
|
+
if (["-O", "--output-document", "--out"].includes(flag) || name === "aria2c" && flag === "-o")
|
|
2791
|
+
document = true;
|
|
2792
|
+
if (value.value !== "-" || value.pattern || value.dynamic) outputs.push(value);
|
|
2793
|
+
} else if (DOWNLOAD_DIR_FLAGS[name].includes(flag)) dir2 = value;
|
|
2794
|
+
else if (name === "wget" && (flag === "-i" || flag === "--input-file")) serverNamed = true;
|
|
2795
|
+
};
|
|
2796
|
+
for (let i = 0; i < args.length; i++) {
|
|
2797
|
+
const token = args[i];
|
|
2798
|
+
if (!token) continue;
|
|
2799
|
+
const v = token.value;
|
|
2800
|
+
if (v.startsWith("--")) {
|
|
2801
|
+
const eq = v.indexOf("=");
|
|
2802
|
+
const flag = eq < 0 ? v : v.slice(0, eq);
|
|
2803
|
+
if (["--remote-name", "--remote-name-all"].includes(flag)) remoteName = true;
|
|
2804
|
+
if (["--remote-header-name", "--recursive", "--mirror", "--page-requisites"].includes(flag))
|
|
2805
|
+
serverNamed = true;
|
|
2806
|
+
if (DOWNLOAD_LONG_VALUE[name].includes(flag))
|
|
2807
|
+
take(flag, eq < 0 ? args[++i] ?? null : { ...token, value: v.slice(eq + 1) });
|
|
2808
|
+
continue;
|
|
2809
|
+
}
|
|
2810
|
+
if (v.startsWith("-") && v.length > 1 && !token.dynamic) {
|
|
2811
|
+
for (let j = 1; j < v.length; j++) {
|
|
2812
|
+
const c = v[j];
|
|
2813
|
+
if (name === "wget" && c === "n") {
|
|
2814
|
+
j++;
|
|
2815
|
+
continue;
|
|
2816
|
+
}
|
|
2817
|
+
if (name === "curl" && c === "O") remoteName = true;
|
|
2818
|
+
if (name === "curl" && c === "J") serverNamed = true;
|
|
2819
|
+
if (name === "wget" && (c === "r" || c === "m" || c === "p")) serverNamed = true;
|
|
2820
|
+
if (valueShort.includes(c)) {
|
|
2821
|
+
const rest = v.slice(j + 1);
|
|
2822
|
+
take(`-${c}`, rest ? { value: rest } : args[++i] ?? null);
|
|
2823
|
+
break;
|
|
2824
|
+
}
|
|
2825
|
+
}
|
|
2826
|
+
continue;
|
|
2827
|
+
}
|
|
2828
|
+
if (/^(?:https?|ftps?):\/\//i.test(v)) urls.push(v);
|
|
2829
|
+
}
|
|
2830
|
+
for (const out2 of outputs) {
|
|
2831
|
+
const spelled = name === "aria2c" && dir2?.value && !out2.value.startsWith("/") ? { ...out2, value: `${dir2.value.replace(/\/+$/, "")}/${out2.value}` } : out2;
|
|
2832
|
+
if (name === "curl" && /#\d/.test(spelled.value))
|
|
2833
|
+
add("write", { value: "", pattern: spelled.value.replace(/#\d+/g, "*"), dot: true }, state);
|
|
2834
|
+
else add("write", spelled, state);
|
|
2835
|
+
}
|
|
2836
|
+
const remote = name === "curl" ? remoteName || serverNamed : !document || serverNamed;
|
|
2837
|
+
if (!remote) return;
|
|
2838
|
+
const into = dir2?.value ? dir2.value.replace(/\/+$/, "") : ".";
|
|
2839
|
+
if (serverNamed || urls.length === 0) {
|
|
2840
|
+
add("write", { value: "", pattern: `${quoteGlob(into)}/*`, dot: true }, state);
|
|
2841
|
+
return;
|
|
2842
|
+
}
|
|
2843
|
+
for (const url of urls) {
|
|
2844
|
+
const file2 = url.replace(/[?#].*$/, "").replace(/^[a-z]+:\/\/[^/]*/i, "").split("/").pop();
|
|
2845
|
+
add("write", { value: `${into}/${file2 || "index.html"}` }, state);
|
|
2846
|
+
}
|
|
2847
|
+
}
|
|
2617
2848
|
function findActions2(tokens, words, head, state, nested, depth) {
|
|
2618
2849
|
const starts = findStartPoints(words, head).starts;
|
|
2619
2850
|
const roots = (starts.length ? starts : ["."]).slice(0, MAX_ALTERNATIVES);
|
|
@@ -2685,7 +2916,35 @@ function collectShellFileAccesses(command, context = {}) {
|
|
|
2685
2916
|
const words = wordValues(node, state);
|
|
2686
2917
|
alternatives = alternatives.flatMap((prefix) => (words.length ? words : [null]).map((w) => [...prefix, w])).slice(0, MAX_ALTERNATIVES);
|
|
2687
2918
|
}
|
|
2688
|
-
for (const tokens of alternatives)
|
|
2919
|
+
for (const tokens of alternatives) {
|
|
2920
|
+
fromWords(tokens, state, nested, depth);
|
|
2921
|
+
inlineCall(tokens, state, depth);
|
|
2922
|
+
}
|
|
2923
|
+
}
|
|
2924
|
+
function inlineCall(tokens, state, depth) {
|
|
2925
|
+
const head = tokens[0];
|
|
2926
|
+
if (!head || head.dynamic || head.pattern) return;
|
|
2927
|
+
const bodies = functions.get(head.value);
|
|
2928
|
+
if (!bodies || (state.callDepth ?? 0) >= MAX_CALL_DEPTH2) return;
|
|
2929
|
+
const args = tokens.slice(1).map((a) => a && !a.dynamic ? a.value : null);
|
|
2930
|
+
const vars = JSON.stringify([...state.vars].sort(([a], [b]) => a < b ? -1 : 1));
|
|
2931
|
+
const key = `${head.value}\0${JSON.stringify(args)}\0${state.cwd.join("")}\0${vars}`;
|
|
2932
|
+
if (seenCalls.has(key)) return;
|
|
2933
|
+
seenCalls.add(key);
|
|
2934
|
+
for (const body of bodies) {
|
|
2935
|
+
if (callWalks2-- <= 0) return;
|
|
2936
|
+
const local = clone(state);
|
|
2937
|
+
local.callDepth = (state.callDepth ?? 0) + 1;
|
|
2938
|
+
for (const name of POSITIONAL) local.vars.delete(name);
|
|
2939
|
+
if (!rebindsPositionals(body)) {
|
|
2940
|
+
args.slice(0, 9).forEach((v, i) => local.vars.set(String(i + 1), v === null ? null : [v]));
|
|
2941
|
+
const all = args.every((v) => v !== null) && args.length ? unique(args) : null;
|
|
2942
|
+
local.vars.set("@", all);
|
|
2943
|
+
local.vars.set("*", all);
|
|
2944
|
+
local.vars.set("#", [String(args.length)]);
|
|
2945
|
+
}
|
|
2946
|
+
statement(body, local, depth + 1);
|
|
2947
|
+
}
|
|
2689
2948
|
}
|
|
2690
2949
|
function readsScriptFromStdin(stmt) {
|
|
2691
2950
|
const cmd = stmt?.Cmd;
|
|
@@ -2722,11 +2981,13 @@ function collectShellFileAccesses(command, context = {}) {
|
|
|
2722
2981
|
if (!stmt || depth > MAX_DEPTH) return;
|
|
2723
2982
|
state.onSuccess = void 0;
|
|
2724
2983
|
if (stmt.Background) state = clone(state);
|
|
2984
|
+
const headWord = stmt.Cmd && syntax2.NodeType(stmt.Cmd) === "CallExpr" ? base(resolveWordLiteral(stmt.Cmd.Args?.[0])) : "";
|
|
2985
|
+
const sends = SENDING_STDIN.has(headWord);
|
|
2725
2986
|
for (const redir of stmt.Redirs ?? []) {
|
|
2726
2987
|
substitutions(redir.Word, state, depth);
|
|
2727
2988
|
for (const word of wordValues(redir.Word, state)) {
|
|
2728
2989
|
if (WRITES.has(redir.Op)) add("write", word, state);
|
|
2729
|
-
if (READS.has(redir.Op)) add("read", word, state);
|
|
2990
|
+
if (READS.has(redir.Op)) add("read", word, state, sends ? { upload: true } : {});
|
|
2730
2991
|
if (redir.Op === DUP_OUT && word.value && !/^\d+$/.test(word.value))
|
|
2731
2992
|
add("write", word, state);
|
|
2732
2993
|
}
|
|
@@ -2835,7 +3096,10 @@ function collectShellFileAccesses(command, context = {}) {
|
|
|
2835
3096
|
}
|
|
2836
3097
|
const parsed = parseShared(command);
|
|
2837
3098
|
const program = typeof parsed !== "symbol" ? parsed : runnablePrefix(command);
|
|
2838
|
-
if (program)
|
|
3099
|
+
if (program) {
|
|
3100
|
+
for (const [name, bodies] of functionBodies(program)) functions.set(name, bodies);
|
|
3101
|
+
statements(program.Stmts ?? [], initial, 0);
|
|
3102
|
+
}
|
|
2839
3103
|
return out;
|
|
2840
3104
|
}
|
|
2841
3105
|
function isAgentInstructionPath(absPath, root) {
|
|
@@ -2951,6 +3215,33 @@ function hits(access, paths, directories, context) {
|
|
|
2951
3215
|
}
|
|
2952
3216
|
return false;
|
|
2953
3217
|
}
|
|
3218
|
+
function hitsSystemDir(access, context, dirs = SYSTEM_DIRS) {
|
|
3219
|
+
const home = context.homeDirs?.[0] ?? "~";
|
|
3220
|
+
const candidates = access.candidates ?? [
|
|
3221
|
+
normalizeFilePath(access.path, access.cwd ?? context.cwd, home)
|
|
3222
|
+
];
|
|
3223
|
+
for (const p of candidates) {
|
|
3224
|
+
if (dirs.some((d) => within(p, d)) && !exempt(p)) return true;
|
|
3225
|
+
if (access.recursive && dirs.some((d) => within(d, p))) return true;
|
|
3226
|
+
}
|
|
3227
|
+
if (!access.glob) return false;
|
|
3228
|
+
const pattern = normalizeFilePath(access.glob, access.cwd ?? context.cwd, home);
|
|
3229
|
+
const magic = pattern.search(/[*?[{]/);
|
|
3230
|
+
const literal = magic < 0 ? pattern : pattern.slice(0, pattern.lastIndexOf("/", magic) + 1);
|
|
3231
|
+
if (literal && exempt(literal.replace(/\/$/, "") || "/")) return false;
|
|
3232
|
+
const options = { dot: access.dotGlob === true, nonegate: true };
|
|
3233
|
+
const components = pattern.split("/");
|
|
3234
|
+
for (const root of dirs) {
|
|
3235
|
+
const count = root.split("/").length;
|
|
3236
|
+
if (components.length > count && (0, import_picomatch.default)(components.slice(0, count).join("/"), options)(root))
|
|
3237
|
+
return true;
|
|
3238
|
+
if (access.recursive && (0, import_picomatch.default)(pattern, options)(root)) return true;
|
|
3239
|
+
}
|
|
3240
|
+
return false;
|
|
3241
|
+
}
|
|
3242
|
+
function uploadDirs(context) {
|
|
3243
|
+
return context.homeDirs?.[0] === "/root" ? UPLOAD_SYSTEM_DIRS.filter((d) => d !== "/root") : UPLOAD_SYSTEM_DIRS;
|
|
3244
|
+
}
|
|
2954
3245
|
function evaluateProtectedFileAccess(accesses, context, settings) {
|
|
2955
3246
|
const homes = [.../* @__PURE__ */ new Set([...context.homeDirs ?? ["~"], "/root"])];
|
|
2956
3247
|
const startup = homes.flatMap(
|
|
@@ -2972,6 +3263,14 @@ function evaluateProtectedFileAccess(accesses, context, settings) {
|
|
|
2972
3263
|
"files.shell-startup",
|
|
2973
3264
|
"review-shell-startup-mutation",
|
|
2974
3265
|
"Changing shell startup files requires approval"
|
|
3266
|
+
] : mutation && hitsSystemDir(access, context) ? [
|
|
3267
|
+
"files.system-dirs",
|
|
3268
|
+
"review-system-dir-mutation",
|
|
3269
|
+
"Changing files in a system directory requires approval"
|
|
3270
|
+
] : !mutation && access.upload && hitsSystemDir(access, context, uploadDirs(context)) ? [
|
|
3271
|
+
"files.system-dirs",
|
|
3272
|
+
"review-system-file-upload",
|
|
3273
|
+
"Uploading a system file requires approval"
|
|
2975
3274
|
] : !mutation && hits(access, PASSWORD_HASH_FILES, [], context) ? [
|
|
2976
3275
|
"files.password-hashes",
|
|
2977
3276
|
"review-password-hash-read",
|
|
@@ -3232,16 +3531,21 @@ function looksGenerated(value, minLength, minEntropy, lettersOnlyAllowed = false
|
|
|
3232
3531
|
if (EMAIL_VALUE_RE.test(value)) return false;
|
|
3233
3532
|
if (looksLikeJoinedWords(value) || hasSequentialRun(value)) return false;
|
|
3234
3533
|
if (PATH_VALUE_RE.test(value) || DOTTED_REFERENCE_RE.test(value)) return false;
|
|
3534
|
+
if (CONCATENATION_RE.test(value)) return false;
|
|
3235
3535
|
if (rejectedByStructuredValidator(value)) return false;
|
|
3236
3536
|
return shannonEntropy(value) >= minEntropy;
|
|
3237
3537
|
}
|
|
3238
3538
|
function nameHoldsCredential(name) {
|
|
3239
3539
|
return !FIXTURE_NAME_RE.test(name) && !DERIVATIVE_NAME_RE.test(name);
|
|
3240
3540
|
}
|
|
3541
|
+
function isOtpDocSecret(value) {
|
|
3542
|
+
return OTP_DOC_SECRETS.has(value.replace(/=+$/, "").toUpperCase());
|
|
3543
|
+
}
|
|
3241
3544
|
function validateQueryCredential(raw) {
|
|
3242
3545
|
const value = capturedValue(raw, QUERY_CREDENTIAL_VALUE_RE);
|
|
3243
3546
|
if (value === null) return false;
|
|
3244
|
-
if (OTP_SECRET_RE.test(value))
|
|
3547
|
+
if (OTP_SECRET_RE.test(value))
|
|
3548
|
+
return !isOtpDocSecret(value) && looksGenerated(value, 16, 3, true);
|
|
3245
3549
|
if (!/[0-9]/.test(value) || !/[A-Za-z]/.test(value)) return false;
|
|
3246
3550
|
return looksGenerated(value, 12, 3);
|
|
3247
3551
|
}
|
|
@@ -3255,13 +3559,40 @@ function keywordEndsAPart(name) {
|
|
|
3255
3559
|
}
|
|
3256
3560
|
function validateAssignedSecret(raw, before = "") {
|
|
3257
3561
|
const value = capturedValue(raw, ASSIGNED_SECRET_VALUE_RE);
|
|
3258
|
-
const name = capturedValue(raw, ASSIGNED_SECRET_NAME_RE);
|
|
3562
|
+
const name = capturedValue(raw, ASSIGNED_SECRET_NAME_RE)?.replace(/^_+/, "") ?? null;
|
|
3259
3563
|
if (value === null || name === null) return false;
|
|
3260
|
-
if (!keywordEndsAPart(name)
|
|
3261
|
-
|
|
3564
|
+
if (!keywordEndsAPart(name)) return false;
|
|
3565
|
+
if (ARN_CONTEXT_RE.test(before)) return false;
|
|
3566
|
+
const prefix = (before.match(/[A-Za-z0-9_]*$/)?.[0] ?? "") + (raw.startsWith("_") ? "_" : "");
|
|
3262
3567
|
if (!nameHoldsCredential(prefix + name)) return false;
|
|
3263
3568
|
return looksGenerated(value, 12, 3.5);
|
|
3264
3569
|
}
|
|
3570
|
+
function validateBasicCredential(raw) {
|
|
3571
|
+
const b64 = capturedValue(raw, BASIC_VALUE_RE);
|
|
3572
|
+
if (b64 === null) return false;
|
|
3573
|
+
const bare = b64.replace(/=+$/, "");
|
|
3574
|
+
if (bare.length % 4 === 1) return false;
|
|
3575
|
+
const bytes = Buffer.from(b64, "base64");
|
|
3576
|
+
if (bytes.toString("base64").replace(/=+$/, "") !== bare) return false;
|
|
3577
|
+
const text = bytes.toString("utf8");
|
|
3578
|
+
if (!Buffer.from(text, "utf8").equals(bytes)) return false;
|
|
3579
|
+
for (let i = 0; i < text.length; i++) {
|
|
3580
|
+
const c = text.charCodeAt(i);
|
|
3581
|
+
if (c < 32 || c === 127) return false;
|
|
3582
|
+
}
|
|
3583
|
+
const pair = BASIC_PAIR_RE.exec(text);
|
|
3584
|
+
if (!pair) return false;
|
|
3585
|
+
const password = pair[2];
|
|
3586
|
+
if (!/[0-9]/.test(password) || !/[A-Za-z]/.test(password)) return false;
|
|
3587
|
+
const lower2 = password.toLowerCase();
|
|
3588
|
+
if (DLP_STOPWORDS.some((sw) => lower2.includes(sw)) || hasSequentialRun(password)) return false;
|
|
3589
|
+
return shannonEntropy(password) >= 3;
|
|
3590
|
+
}
|
|
3591
|
+
function validateOtpSecret(raw) {
|
|
3592
|
+
const secret = capturedValue(raw, OTP_CAPTURE_RE);
|
|
3593
|
+
if (secret === null || isOtpDocSecret(secret)) return false;
|
|
3594
|
+
return looksGenerated(secret.replace(/=+$/, ""), 16, 3, true);
|
|
3595
|
+
}
|
|
3265
3596
|
function matchSensitivePath2(resolvedPath, originalPath, options = {}) {
|
|
3266
3597
|
const normalised = resolvedPath.replace(/\\/g, "/");
|
|
3267
3598
|
if (!options.systemAccessHandled && isLegacySystemPath(normalised))
|
|
@@ -3532,6 +3863,156 @@ function parsePySpec(raw) {
|
|
|
3532
3863
|
const exact = PY_EXACT_RE.exec(m[2].trim());
|
|
3533
3864
|
return { name, version: exact ? exact[1] : void 0 };
|
|
3534
3865
|
}
|
|
3866
|
+
function exactVersion(v) {
|
|
3867
|
+
if (!v) return void 0;
|
|
3868
|
+
const m = EXACT_VERSION_RE.exec(v.trim());
|
|
3869
|
+
return m ? m[1] : void 0;
|
|
3870
|
+
}
|
|
3871
|
+
function push(out, ecosystem, manager, name, version, raw) {
|
|
3872
|
+
out.push({ ecosystem, manager, name, ...version ? { version } : {}, raw });
|
|
3873
|
+
}
|
|
3874
|
+
function gemInstall(words, from, out) {
|
|
3875
|
+
const dash = words.indexOf("--", from);
|
|
3876
|
+
const own = dash < 0 ? words : words.slice(0, dash);
|
|
3877
|
+
const pinned = exactVersion(flagValues(own, from, ["-v", "--version"])[0]);
|
|
3878
|
+
for (const raw of operands(own, from, GEM_VALUE_FLAGS)) {
|
|
3879
|
+
if (NON_REGISTRY_RE.test(raw) || /\.gem$/i.test(raw)) continue;
|
|
3880
|
+
const colon = raw.indexOf(":");
|
|
3881
|
+
const name = colon < 0 ? raw : raw.slice(0, colon);
|
|
3882
|
+
if (!GEM_NAME_RE.test(name)) continue;
|
|
3883
|
+
push(
|
|
3884
|
+
out,
|
|
3885
|
+
"RubyGems",
|
|
3886
|
+
"gem",
|
|
3887
|
+
name,
|
|
3888
|
+
colon < 0 ? pinned : exactVersion(raw.slice(colon + 1)),
|
|
3889
|
+
raw
|
|
3890
|
+
);
|
|
3891
|
+
}
|
|
3892
|
+
}
|
|
3893
|
+
function cargoCrates(words, from, manager, out) {
|
|
3894
|
+
const flags = manager === "cargo add" ? CARGO_ADD_VALUE_FLAGS : CARGO_INSTALL_VALUE_FLAGS;
|
|
3895
|
+
if (flagValues(words, from, ["--git", "--path", "--registry", "--index"]).length > 0) return;
|
|
3896
|
+
if (words.some((w) => w === "--git" || w === "--path")) return;
|
|
3897
|
+
const flagVersion = manager === "cargo install" ? exactVersion(flagValues(words, from, ["--version", "--vers"])[0]) : void 0;
|
|
3898
|
+
for (const raw of operands(words, from, flags)) {
|
|
3899
|
+
const at = raw.indexOf("@");
|
|
3900
|
+
const name = at < 0 ? raw : raw.slice(0, at);
|
|
3901
|
+
if (!CRATE_NAME_RE.test(name)) continue;
|
|
3902
|
+
push(
|
|
3903
|
+
out,
|
|
3904
|
+
"crates.io",
|
|
3905
|
+
manager,
|
|
3906
|
+
name,
|
|
3907
|
+
at < 0 ? flagVersion : exactVersion(raw.slice(at + 1)),
|
|
3908
|
+
raw
|
|
3909
|
+
);
|
|
3910
|
+
}
|
|
3911
|
+
}
|
|
3912
|
+
function goModules(words, from, manager, out) {
|
|
3913
|
+
let specs = operands(words, from, GO_VALUE_FLAGS);
|
|
3914
|
+
if (manager === "go run") specs = specs.slice(0, 1).filter((s) => s.includes("@"));
|
|
3915
|
+
for (const raw of specs) {
|
|
3916
|
+
const at = raw.indexOf("@");
|
|
3917
|
+
const pathPart = (at < 0 ? raw : raw.slice(0, at)).replace(/\/\.\.\.$/, "");
|
|
3918
|
+
const ver = at < 0 ? void 0 : raw.slice(at + 1);
|
|
3919
|
+
if (ver === "none") continue;
|
|
3920
|
+
if (!GO_PATH_RE.test(pathPart) || /\.go$/i.test(pathPart)) continue;
|
|
3921
|
+
const exact = ver && /^v\d/.test(ver) ? exactVersion(ver) : void 0;
|
|
3922
|
+
push(out, "Go", manager, pathPart, exact, raw);
|
|
3923
|
+
}
|
|
3924
|
+
}
|
|
3925
|
+
function dotnetAdd(words, from, out) {
|
|
3926
|
+
let k = -1;
|
|
3927
|
+
for (let i = from; i < Math.min(words.length, from + 2); i++) {
|
|
3928
|
+
if (lower(words[i]) === "package") {
|
|
3929
|
+
k = i;
|
|
3930
|
+
break;
|
|
3931
|
+
}
|
|
3932
|
+
}
|
|
3933
|
+
if (k < 0) return;
|
|
3934
|
+
const name = operands(words, k + 1, DOTNET_ADD_VALUE_FLAGS)[0];
|
|
3935
|
+
if (!name || !NUGET_NAME_RE.test(name)) return;
|
|
3936
|
+
const version = exactVersion(flagValues(words, k + 1, ["-v", "--version"])[0]);
|
|
3937
|
+
push(out, "NuGet", "dotnet add package", name, version, name);
|
|
3938
|
+
}
|
|
3939
|
+
function dotnetTool(words, from, out) {
|
|
3940
|
+
const name = operands(words, from, DOTNET_TOOL_VALUE_FLAGS)[0];
|
|
3941
|
+
if (!name || !NUGET_NAME_RE.test(name)) return;
|
|
3942
|
+
const version = exactVersion(flagValues(words, from, ["--version"])[0]);
|
|
3943
|
+
push(out, "NuGet", "dotnet tool", name, version, name);
|
|
3944
|
+
}
|
|
3945
|
+
function nugetInstall(words, from, out) {
|
|
3946
|
+
const lowered = words.map((w) => w !== null && w.startsWith("-") ? w.toLowerCase() : w);
|
|
3947
|
+
const name = operands(lowered, from, NUGET_VALUE_FLAGS)[0];
|
|
3948
|
+
if (!name || !NUGET_NAME_RE.test(name) || /\.config$/i.test(name)) return;
|
|
3949
|
+
const version = exactVersion(flagValues(lowered, from, ["-version"])[0]);
|
|
3950
|
+
push(out, "NuGet", "nuget install", name, version, name);
|
|
3951
|
+
}
|
|
3952
|
+
function mavenGet(words, from, out) {
|
|
3953
|
+
const goal = words.slice(from).some(
|
|
3954
|
+
(w) => w !== null && /(?:^|:)dependency:get$|^org\.apache\.maven\.plugins:maven-dependency-plugin:(?:[\w.-]+:)?get$/.test(
|
|
3955
|
+
w
|
|
3956
|
+
)
|
|
3957
|
+
);
|
|
3958
|
+
if (!goal) return;
|
|
3959
|
+
const prop = (key) => {
|
|
3960
|
+
for (let i = from; i < words.length; i++) {
|
|
3961
|
+
const w = words[i];
|
|
3962
|
+
if (w === null) continue;
|
|
3963
|
+
if (w.startsWith(`-D${key}=`)) return w.slice(key.length + 3);
|
|
3964
|
+
if (w === "-D" && words[i + 1]?.startsWith(`${key}=`))
|
|
3965
|
+
return words[i + 1].slice(key.length + 1);
|
|
3966
|
+
}
|
|
3967
|
+
return void 0;
|
|
3968
|
+
};
|
|
3969
|
+
const artifact = prop("artifact");
|
|
3970
|
+
let coord;
|
|
3971
|
+
let ver;
|
|
3972
|
+
if (artifact) {
|
|
3973
|
+
const parts = artifact.split(":");
|
|
3974
|
+
if (parts.length < 2) return;
|
|
3975
|
+
coord = `${parts[0]}:${parts[1]}`;
|
|
3976
|
+
ver = parts[2];
|
|
3977
|
+
} else {
|
|
3978
|
+
const g = prop("groupId");
|
|
3979
|
+
const a = prop("artifactId");
|
|
3980
|
+
if (!g || !a) return;
|
|
3981
|
+
coord = `${g}:${a}`;
|
|
3982
|
+
ver = prop("version");
|
|
3983
|
+
}
|
|
3984
|
+
if (!MAVEN_COORD_RE.test(coord)) return;
|
|
3985
|
+
const exact = ver && !/^(?:LATEST|RELEASE)$/i.test(ver) ? exactVersion(ver) : void 0;
|
|
3986
|
+
push(out, "Maven", "mvn dependency:get", coord, exact, artifact ?? coord);
|
|
3987
|
+
}
|
|
3988
|
+
function composerPackages(words, from, manager, out) {
|
|
3989
|
+
const ops = operands(words, from, COMPOSER_VALUE_FLAGS);
|
|
3990
|
+
const specs = manager === "composer create-project" ? ops.slice(0, 1) : ops;
|
|
3991
|
+
for (const raw of specs) {
|
|
3992
|
+
const sep = raw.search(/[:=]/);
|
|
3993
|
+
const name = sep < 0 ? raw : raw.slice(0, sep);
|
|
3994
|
+
if (!COMPOSER_NAME_RE.test(name)) continue;
|
|
3995
|
+
let ver = sep < 0 ? void 0 : raw.slice(sep + 1);
|
|
3996
|
+
if (manager === "composer create-project" && ver === void 0) ver = ops[2] ?? void 0;
|
|
3997
|
+
push(out, "Packagist", manager, name.toLowerCase(), exactVersion(ver), raw);
|
|
3998
|
+
}
|
|
3999
|
+
}
|
|
4000
|
+
function editorExtensions(words, from, out) {
|
|
4001
|
+
for (const raw of flagValues(words, from, ["--install-extension"])) {
|
|
4002
|
+
if (/\.vsix$/i.test(raw) || NON_REGISTRY_RE.test(raw)) continue;
|
|
4003
|
+
const at = raw.lastIndexOf("@");
|
|
4004
|
+
const name = at <= 0 ? raw : raw.slice(0, at);
|
|
4005
|
+
if (!EXTENSION_ID_RE.test(name)) continue;
|
|
4006
|
+
push(
|
|
4007
|
+
out,
|
|
4008
|
+
"VSCode",
|
|
4009
|
+
"code --install-extension",
|
|
4010
|
+
name,
|
|
4011
|
+
at <= 0 ? void 0 : exactVersion(raw.slice(at + 1)),
|
|
4012
|
+
raw
|
|
4013
|
+
);
|
|
4014
|
+
}
|
|
4015
|
+
}
|
|
3535
4016
|
function basename(w) {
|
|
3536
4017
|
return (w ?? "").toLowerCase().split("/").pop() ?? "";
|
|
3537
4018
|
}
|
|
@@ -3763,7 +4244,63 @@ function fromCall(words, out, depth) {
|
|
|
3763
4244
|
pushPy(out, "pipenv", operands(words, v + 1, PIPENV_VALUE_FLAGS));
|
|
3764
4245
|
return;
|
|
3765
4246
|
}
|
|
4247
|
+
case "gem": {
|
|
4248
|
+
const v = verbIndex(words, start + 1, GEM_VALUE_FLAGS);
|
|
4249
|
+
if (v >= 0 && ["install", "i"].includes(lower(words[v]))) gemInstall(words, v + 1, out);
|
|
4250
|
+
return;
|
|
4251
|
+
}
|
|
4252
|
+
case "bundle": {
|
|
4253
|
+
const v = verbIndex(words, start + 1, BUNDLE_VALUE_FLAGS);
|
|
4254
|
+
if (v < 0 || lower(words[v]) !== "add") return;
|
|
4255
|
+
if (flagValues(words, v + 1, ["--git", "--github", "--path"]).length > 0) return;
|
|
4256
|
+
const version = exactVersion(flagValues(words, v + 1, ["-v", "--version"])[0]);
|
|
4257
|
+
for (const raw of operands(words, v + 1, BUNDLE_VALUE_FLAGS))
|
|
4258
|
+
if (GEM_NAME_RE.test(raw)) push(out, "RubyGems", "bundle add", raw, version, raw);
|
|
4259
|
+
return;
|
|
4260
|
+
}
|
|
4261
|
+
case "cargo": {
|
|
4262
|
+
const from = words[start + 1]?.startsWith("+") ? start + 2 : start + 1;
|
|
4263
|
+
const v = verbIndex(words, from, CARGO_ADD_VALUE_FLAGS);
|
|
4264
|
+
const verb = lower(words[v]);
|
|
4265
|
+
if (verb === "add") cargoCrates(words, v + 1, "cargo add", out);
|
|
4266
|
+
else if (verb === "install") cargoCrates(words, v + 1, "cargo install", out);
|
|
4267
|
+
return;
|
|
4268
|
+
}
|
|
4269
|
+
case "go": {
|
|
4270
|
+
const v = verbIndex(words, start + 1, GO_VALUE_FLAGS);
|
|
4271
|
+
const verb = lower(words[v]);
|
|
4272
|
+
if (verb === "get" || verb === "install" || verb === "run")
|
|
4273
|
+
goModules(words, v + 1, `go ${verb}`, out);
|
|
4274
|
+
return;
|
|
4275
|
+
}
|
|
4276
|
+
case "dotnet": {
|
|
4277
|
+
const v = verbIndex(words, start + 1, DOTNET_ADD_VALUE_FLAGS);
|
|
4278
|
+
const verb = lower(words[v]);
|
|
4279
|
+
if (verb === "add") dotnetAdd(words, v + 1, out);
|
|
4280
|
+
else if (verb === "tool" && ["install", "update"].includes(lower(words[v + 1])))
|
|
4281
|
+
dotnetTool(words, v + 2, out);
|
|
4282
|
+
return;
|
|
4283
|
+
}
|
|
4284
|
+
case "nuget": {
|
|
4285
|
+
if (lower(words[start + 1]) === "install") nugetInstall(words, start + 2, out);
|
|
4286
|
+
return;
|
|
4287
|
+
}
|
|
4288
|
+
case "mvn":
|
|
4289
|
+
case "mvnw":
|
|
4290
|
+
mavenGet(words, start + 1, out);
|
|
4291
|
+
return;
|
|
4292
|
+
case "composer": {
|
|
4293
|
+
let v = verbIndex(words, start + 1, COMPOSER_VALUE_FLAGS);
|
|
4294
|
+
if (v >= 0 && lower(words[v]) === "global") v = verbIndex(words, v + 1, COMPOSER_VALUE_FLAGS);
|
|
4295
|
+
const verb = lower(words[v]);
|
|
4296
|
+
if (verb === "require" || verb === "req" || verb === "r")
|
|
4297
|
+
composerPackages(words, v + 1, "composer require", out);
|
|
4298
|
+
else if (verb === "create-project")
|
|
4299
|
+
composerPackages(words, v + 1, "composer create-project", out);
|
|
4300
|
+
return;
|
|
4301
|
+
}
|
|
3766
4302
|
default:
|
|
4303
|
+
if (EDITOR_HEADS.includes(family)) editorExtensions(words, start + 1, out);
|
|
3767
4304
|
return;
|
|
3768
4305
|
}
|
|
3769
4306
|
}
|
|
@@ -5230,12 +5767,13 @@ function gitRepoPath(url) {
|
|
|
5230
5767
|
function isDestructiveToolName(bareTool) {
|
|
5231
5768
|
return DESTRUCTIVE_VERB_RE.test(bareTool);
|
|
5232
5769
|
}
|
|
5233
|
-
function* validCardRuns(text, base2) {
|
|
5770
|
+
function* validCardRuns(text, base2, exemptFractions = true) {
|
|
5234
5771
|
const re = new RegExp(base2.source, "g");
|
|
5235
5772
|
let m;
|
|
5236
5773
|
while ((m = re.exec(text)) !== null) {
|
|
5237
5774
|
const digits = m[0].replace(/\D/g, "");
|
|
5238
|
-
|
|
5775
|
+
const fraction = exemptFractions && isDecimalFraction(text, m.index, digits.length === m[0].length);
|
|
5776
|
+
if (!fraction && validateLuhn(digits)) {
|
|
5239
5777
|
yield { index: m.index, length: m[0].length, digits };
|
|
5240
5778
|
} else {
|
|
5241
5779
|
re.lastIndex = m.index + 1;
|
|
@@ -5248,12 +5786,12 @@ function hasValidCard(text) {
|
|
|
5248
5786
|
}
|
|
5249
5787
|
return false;
|
|
5250
5788
|
}
|
|
5251
|
-
function replaceValidCards(text, replace) {
|
|
5789
|
+
function replaceValidCards(text, replace, { exemptFractions = true } = {}) {
|
|
5252
5790
|
let out = text;
|
|
5253
5791
|
for (const base2 of [PII_CC16_RE, PII_CC15_RE]) {
|
|
5254
5792
|
let res = "";
|
|
5255
5793
|
let last = 0;
|
|
5256
|
-
for (const run of validCardRuns(out, base2)) {
|
|
5794
|
+
for (const run of validCardRuns(out, base2, exemptFractions)) {
|
|
5257
5795
|
res += out.slice(last, run.index) + replace(run.digits);
|
|
5258
5796
|
last = run.index + run.length;
|
|
5259
5797
|
}
|
|
@@ -5267,11 +5805,11 @@ function isDecimalFraction(text, index, unseparated) {
|
|
|
5267
5805
|
const a = text[index - 2];
|
|
5268
5806
|
if (sep === ".") {
|
|
5269
5807
|
if (a !== void 0 && a >= "0" && a <= "9") return true;
|
|
5270
|
-
return a === void 0 ||
|
|
5808
|
+
return a === void 0 || (a === "-" || a === "+") && index === 2;
|
|
5271
5809
|
}
|
|
5272
5810
|
if (sep === "," && a === "0") {
|
|
5273
5811
|
const b = text[index - 3];
|
|
5274
|
-
return b === void 0 ||
|
|
5812
|
+
return b === void 0 || b === ";";
|
|
5275
5813
|
}
|
|
5276
5814
|
return false;
|
|
5277
5815
|
}
|
|
@@ -5407,6 +5945,9 @@ function normalizeTrustedEntry(raw) {
|
|
|
5407
5945
|
return full === null ? null : trustedEntryOf(full);
|
|
5408
5946
|
}
|
|
5409
5947
|
function maskPersonalData(text) {
|
|
5948
|
+
return maskText(text, false);
|
|
5949
|
+
}
|
|
5950
|
+
function maskText(text, exemptFractions) {
|
|
5410
5951
|
if (!text) return text;
|
|
5411
5952
|
try {
|
|
5412
5953
|
let out = text;
|
|
@@ -5414,7 +5955,9 @@ function maskPersonalData(text) {
|
|
|
5414
5955
|
const v = m.replace(/[ -]/g, "").toUpperCase();
|
|
5415
5956
|
return validateIban(v) ? `${v.slice(0, 4)} **** **** ${v.slice(-4)}` : m;
|
|
5416
5957
|
});
|
|
5417
|
-
out = replaceValidCards(out, (digits) => `**** **** **** ${digits.slice(-4)}
|
|
5958
|
+
out = replaceValidCards(out, (digits) => `**** **** **** ${digits.slice(-4)}`, {
|
|
5959
|
+
exemptFractions
|
|
5960
|
+
});
|
|
5418
5961
|
out = out.replace(
|
|
5419
5962
|
ACCOUNT_VALUE_RE,
|
|
5420
5963
|
(m, v) => IBAN_LENGTH[v.slice(0, 2).toUpperCase()] === v.length ? `"****${v.slice(-4)}"` : m
|
|
@@ -5437,7 +5980,7 @@ function maskLeaf(value, key, depth) {
|
|
|
5437
5980
|
if (depth > MAX_MASK_DEPTH) return "[REDACTED]";
|
|
5438
5981
|
if (typeof value === "string") {
|
|
5439
5982
|
try {
|
|
5440
|
-
const json = `{${JSON.stringify(key)}:${JSON.stringify(value)}}`;
|
|
5983
|
+
const json = `{${JSON.stringify(key)}:${JSON.stringify(maskPersonalData(value))}}`;
|
|
5441
5984
|
return Object.values(JSON.parse(maskPersonalData(json)))[0];
|
|
5442
5985
|
} catch {
|
|
5443
5986
|
return "[REDACTED]";
|
|
@@ -5445,13 +5988,20 @@ function maskLeaf(value, key, depth) {
|
|
|
5445
5988
|
}
|
|
5446
5989
|
if (typeof value === "number") {
|
|
5447
5990
|
const text = String(value);
|
|
5448
|
-
const masked =
|
|
5991
|
+
const masked = maskText(text, true);
|
|
5449
5992
|
return masked === text ? value : masked;
|
|
5450
5993
|
}
|
|
5451
5994
|
if (Array.isArray(value)) return value.map((v) => maskLeaf(v, key, depth + 1));
|
|
5452
5995
|
if (value && typeof value === "object") {
|
|
5996
|
+
const used = /* @__PURE__ */ new Set();
|
|
5453
5997
|
return Object.fromEntries(
|
|
5454
|
-
Object.entries(value).map(([k, v]) =>
|
|
5998
|
+
Object.entries(value).map(([k, v]) => {
|
|
5999
|
+
const base2 = maskPersonalData(k);
|
|
6000
|
+
let name = base2;
|
|
6001
|
+
for (let n = 2; used.has(name); n++) name = `${base2}#${n}`;
|
|
6002
|
+
used.add(name);
|
|
6003
|
+
return [name, maskLeaf(v, k, depth + 1)];
|
|
6004
|
+
})
|
|
5455
6005
|
);
|
|
5456
6006
|
}
|
|
5457
6007
|
return value;
|
|
@@ -5933,7 +6483,7 @@ function matchCanaryArgs(args, values) {
|
|
|
5933
6483
|
return null;
|
|
5934
6484
|
}
|
|
5935
6485
|
}
|
|
5936
|
-
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, ALL, BUILTIN_DANGEROUS_WORDS, PRODUCT_CHECKS, SHIELD_RULE_NAME, SHIELD_RULES_FOLDED_INTO_PRODUCT, CHECKS, CHECK_BY_ID, LEGACY_TO_ID, PROJECT_JAIL_BLOCK_READ, OVERRIDE_PREFIX, LABEL_WRAPPERS, LEGACY_READERS, syntax, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, AST_CACHE_MAX, astCache, PARSE_FAIL, defaultExpansionOps, VARIABLE_READERS, FS_READ_TOOLS, GREP_SHAPE, PATTERN_VERBS, PATTERN_VERB_NAMES, GREP_FILE_OPERANDS, READER_VALUE_LETTERS, FILE_OPERAND_FLAGS, SCP_VALUE_FLAGS, TAR_VALUE_LETTERS, ZIP_VALUE_LETTERS, RSYNC_VALUE_LETTERS, RSYNC_SKIP, CP_VALUE_LETTERS, INSTALL_VALUE_LETTERS, COPY_VERBS, TAR_MODE_WORD, COPY_VERB_HEADS, FS_OP_PRESCREEN_RE, HOME_CACHE_ALLOWLIST, SENSITIVE_PATH_RULES, AST_FS_REGEX_RULES, SQL_DB_CLIS, SQL_DDL_RE, CHMOD_OPEN_PERM_TOKENS, COMMAND_WRAPPERS, INLINE_INTERPRETER, RUNNER_WRAPPERS, _redirStdinOps, _listOps, WRAPPER_TAKES_TARGET, FIND_EXEC_FLAGS, FS_MUTATION_TOOLS, INTERP_LEADING_TARGET, NET_BINARIES, VALUE_FLAGS, HOME_VARIABLES, assignmentTable, currentStmtOffset, PAYLOAD_BUDGET, payloadBudget, seenPayloads, UNANALYSABLE_NESTING, ASSIGNMENT_HEADS, AND_OP, OR_OP, AND_OR_OPS, FS_OP_CACHE_MAX, fsOpCache, stripDotSlash, REDIR_TRUNCATE_OPS, REDIR_FILE_IN_OPS, REDIR_HEREDOC_OPS, FIND_OPTIONS, COPY_RULE_OF, isReaderWord, positionalAfter, NONE, UNKNOWN, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, FORBIDDEN_PATH_SEGMENTS, BASH_TOOL_NAMES, MCP_EXECUTORS, BUILTIN_FIELDS, ARGV_FIELDS, BARE_WORD, MAX_ARGV_CHARACTERS, syntax2, MAX_ALTERNATIVES, MAX_DEPTH, MAX_PAYLOAD_DEPTH, unique, clone, base, quoteGlob, asWord, WRITES, READS, DUP_OUT, HERE_STRING, AND, OR, DEFAULT_OPS, SYSTEM_AUTH_FILES, SYSTEM_AUTH_DIRS, PASSWORD_HASH_FILES, STARTUP_NAMES, AGENT_INSTRUCTION_FILES, AGENT_INSTRUCTION_DIRS, LEGACY_SYSTEM_PATH_RE, RANK, BIP39_ENGLISH, BIP39_PUBLIC_VECTORS, indices, lengths, hash, MAX_MNEMONIC_HASHES, IBAN_LENGTH, B58, B58_INDEX, XPRV_VERSIONS, B62, CRC32_TABLE, GITHUB_TOKEN_RE, B64URL, B64URL_INDEX, CASK_TWO_ZERO_SUFFIX, CASK_FOUR_ZERO_SUFFIX, INVISIBLE_RE, INVISIBLE_ONE, HOMOGLYPHS, HOMOGLYPH_RE, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, REFERENCE_VALUE_RE, IDENTIFIER_VALUE_RE, PATH_VALUE_RE, DOTTED_REFERENCE_RE, EMAIL_VALUE_RE, FIXTURE_NAME_RE, DERIVATIVE_NAME_RE, QUERY_CREDENTIAL_VALUE_RE, OTP_SECRET_RE, ASSIGNED_SECRET_VALUE_RE, ASSIGNED_SECRET_NAME_RE, NAME_KEYWORD_RE, ARN_CONTEXT_RE, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, SENSITIVE_PATH_REGEXES, MAX_DEPTH2, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, BATCH_SEPARATOR, syntax3, NPM_INSTALL_VERBS, NPM_EXEC_VERBS, PNPM_INSTALL_VERBS, YARN_INSTALL_VERBS, BUN_INSTALL_VERBS, DLX_VERBS, NON_REGISTRY_RE, NPM_NAME_RE, EXACT_SEMVER_RE, NPM_VALUE_FLAGS, PNPM_VALUE_FLAGS, YARN_VALUE_FLAGS, BUN_VALUE_FLAGS, PIP_VALUE_FLAGS, UV_VALUE_FLAGS, POETRY_VALUE_FLAGS, PIPENV_VALUE_FLAGS, PIPX_VALUE_FLAGS, PY_SPEC_RE, PY_EXACT_RE, MANAGER_HEADS, SHELLS, MAX_NESTING, SHELL_VALUE_FLAGS, TUNING_FIELDS, MAP_GOVERNED, KNOB_GOVERNED, CONFIGURABLE_CHECK_IDS, DEFAULT_EGRESS_ALLOWLIST, PRIVATE_HOST_SUFFIXES, SOURCE_COMMANDS, SINK_COMMANDS, OBFUSCATORS, SENSITIVE_PATTERNS, FLAGS_WITH_VALUES, DESTINATION_ARGS, SECRET_VIEW_LIMITS, ARGUMENT_SCAN_LIMITS, RUN_CHAR, runChar, VERDICT_RANK, SQL_DML_KEYWORDS, MAX_DESTINATIONS, MAX_SCAN_CHARS, EMAIL_RE, URL_RE, IBAN_RE, RECIPIENT_KEYS, DESTRUCTIVE_VERB_RE, PII_EMAIL_RE, PII_SSN_RE, PII_PHONE_RE, PII_CC16_RE, PII_CC15_RE, PII_IBAN_RE,
|
|
6486
|
+
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, ALL, BUILTIN_DANGEROUS_WORDS, PRODUCT_CHECKS, SHIELD_RULE_NAME, SHIELD_RULES_FOLDED_INTO_PRODUCT, CHECKS, CHECK_BY_ID, LEGACY_TO_ID, PROJECT_JAIL_BLOCK_READ, OVERRIDE_PREFIX, LABEL_WRAPPERS, LEGACY_READERS, syntax, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, AST_CACHE_MAX, astCache, PARSE_FAIL, defaultExpansionOps, VARIABLE_READERS, FS_READ_TOOLS, GREP_SHAPE, PATTERN_VERBS, PATTERN_VERB_NAMES, GREP_FILE_OPERANDS, READER_VALUE_LETTERS, FILE_OPERAND_FLAGS, SCP_VALUE_FLAGS, TAR_VALUE_LETTERS, ZIP_VALUE_LETTERS, RSYNC_VALUE_LETTERS, RSYNC_SKIP, CP_VALUE_LETTERS, INSTALL_VALUE_LETTERS, COPY_VERBS, TAR_MODE_WORD, COPY_VERB_HEADS, FS_OP_PRESCREEN_RE, HOME_CACHE_ALLOWLIST, SENSITIVE_PATH_RULES, AST_FS_REGEX_RULES, SQL_DB_CLIS, SQL_DDL_RE, CHMOD_OPEN_PERM_TOKENS, COMMAND_WRAPPERS, INLINE_INTERPRETER, RUNNER_WRAPPERS, _redirStdinOps, _listOps, WRAPPER_TAKES_TARGET, FIND_EXEC_FLAGS, FS_MUTATION_TOOLS, INTERP_LEADING_TARGET, NET_BINARIES, VALUE_FLAGS, HOME_VARIABLES, assignmentTable, currentStmtOffset, PAYLOAD_BUDGET, payloadBudget, seenPayloads, UNANALYSABLE_NESTING, ASSIGNMENT_HEADS, AND_OP, OR_OP, AND_OR_OPS, MAX_CALL_DEPTH, MAX_CALL_WALKS, callDepth, callWalks, FS_OP_CACHE_MAX, fsOpCache, stripDotSlash, REDIR_TRUNCATE_OPS, REDIR_FILE_IN_OPS, REDIR_HEREDOC_OPS, FIND_OPTIONS, COPY_RULE_OF, isReaderWord, positionalAfter, NONE, UNKNOWN, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, FORBIDDEN_PATH_SEGMENTS, BASH_TOOL_NAMES, MCP_EXECUTORS, BUILTIN_FIELDS, ARGV_FIELDS, BARE_WORD, MAX_ARGV_CHARACTERS, syntax2, MAX_ALTERNATIVES, MAX_CALL_DEPTH2, MAX_CALL_WALKS2, POSITIONAL, MAX_DEPTH, MAX_PAYLOAD_DEPTH, unique, clone, SENDING_STDIN, DOWNLOAD_SHORT_VALUE, DOWNLOAD_LONG_VALUE, DOWNLOAD_OUTPUT_FLAGS, DOWNLOAD_DIR_FLAGS, base, quoteGlob, asWord, WRITES, READS, DUP_OUT, HERE_STRING, AND, OR, DEFAULT_OPS, SYSTEM_AUTH_FILES, SYSTEM_AUTH_DIRS, PASSWORD_HASH_FILES, STARTUP_NAMES, SYSTEM_DIRS, SYSTEM_DIRS_EXEMPT, UPLOAD_SYSTEM_DIRS, AGENT_INSTRUCTION_FILES, AGENT_INSTRUCTION_DIRS, LEGACY_SYSTEM_PATH_RE, exempt, RANK, BIP39_ENGLISH, BIP39_PUBLIC_VECTORS, indices, lengths, hash, MAX_MNEMONIC_HASHES, IBAN_LENGTH, B58, B58_INDEX, XPRV_VERSIONS, B62, CRC32_TABLE, GITHUB_TOKEN_RE, B64URL, B64URL_INDEX, CASK_TWO_ZERO_SUFFIX, CASK_FOUR_ZERO_SUFFIX, INVISIBLE_RE, INVISIBLE_ONE, HOMOGLYPHS, HOMOGLYPH_RE, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, REFERENCE_VALUE_RE, IDENTIFIER_VALUE_RE, PATH_VALUE_RE, DOTTED_REFERENCE_RE, CONCATENATION_RE, EMAIL_VALUE_RE, FIXTURE_NAME_RE, DERIVATIVE_NAME_RE, QUERY_CREDENTIAL_VALUE_RE, OTP_SECRET_RE, OTP_DOC_SECRETS, ASSIGNED_SECRET_VALUE_RE, ASSIGNED_SECRET_NAME_RE, NAME_KEYWORD_RE, ARN_CONTEXT_RE, BASIC_VALUE_RE, BASIC_PAIR_RE, OTP_CAPTURE_RE, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, SENSITIVE_PATH_REGEXES, MAX_DEPTH2, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, BATCH_SEPARATOR, syntax3, NPM_INSTALL_VERBS, NPM_EXEC_VERBS, PNPM_INSTALL_VERBS, YARN_INSTALL_VERBS, BUN_INSTALL_VERBS, DLX_VERBS, NON_REGISTRY_RE, NPM_NAME_RE, EXACT_SEMVER_RE, NPM_VALUE_FLAGS, PNPM_VALUE_FLAGS, YARN_VALUE_FLAGS, BUN_VALUE_FLAGS, PIP_VALUE_FLAGS, UV_VALUE_FLAGS, POETRY_VALUE_FLAGS, PIPENV_VALUE_FLAGS, PIPX_VALUE_FLAGS, PY_SPEC_RE, PY_EXACT_RE, EXACT_VERSION_RE, GEM_NAME_RE, GEM_VALUE_FLAGS, BUNDLE_VALUE_FLAGS, CRATE_NAME_RE, CARGO_ADD_VALUE_FLAGS, CARGO_INSTALL_VALUE_FLAGS, GO_VALUE_FLAGS, GO_PATH_RE, NUGET_NAME_RE, DOTNET_ADD_VALUE_FLAGS, DOTNET_TOOL_VALUE_FLAGS, NUGET_VALUE_FLAGS, MAVEN_COORD_RE, COMPOSER_NAME_RE, COMPOSER_VALUE_FLAGS, EDITOR_HEADS, EXTENSION_ID_RE, MANAGER_HEADS, SHELLS, MAX_NESTING, SHELL_VALUE_FLAGS, TUNING_FIELDS, MAP_GOVERNED, KNOB_GOVERNED, CONFIGURABLE_CHECK_IDS, DEFAULT_EGRESS_ALLOWLIST, PRIVATE_HOST_SUFFIXES, SOURCE_COMMANDS, SINK_COMMANDS, OBFUSCATORS, SENSITIVE_PATTERNS, FLAGS_WITH_VALUES, DESTINATION_ARGS, SECRET_VIEW_LIMITS, ARGUMENT_SCAN_LIMITS, RUN_CHAR, runChar, VERDICT_RANK, SQL_DML_KEYWORDS, MAX_DESTINATIONS, MAX_SCAN_CHARS, EMAIL_RE, URL_RE, IBAN_RE, RECIPIENT_KEYS, DESTRUCTIVE_VERB_RE, PII_EMAIL_RE, PII_SSN_RE, PII_PHONE_RE, PII_CC16_RE, PII_CC15_RE, PII_IBAN_RE, REALTIME_PII_PATTERNS, MAX_PII_SCAN_BYTES, MASKED_KINDS, ENTRY_RE, IBAN_TEXT_RE, EMAIL_TEXT_RE, ACCOUNT_KEY_RE, ACCOUNT_VALUE_RE, MAX_MASK_DEPTH, syntax4, EMPTY, CURL_SEND_FLAGS, WGET_SEND_FLAGS, IWR_SEND_FLAGS, WRITE_METHODS, GH_READ, GH_WRITE, GH_HOSTNAME_FLAGS, GH_REPO_VALUE_FLAGS, GH_API_VALUE_FLAGS, GIT_FETCH, PUBLISH, SHELLS2, OWN_HEADS, MAX_DEPTH3, CWD_CHANGERS, TERMINAL_ESCAPE_RE, LOOP_MAX_RECORDS, CANARY_MIN_LENGTH, MAX_TEXT, MAX_DEPTH4, MAX_JSON_PARSE, URL_DEPTH, B64_DEPTH, MIN_SEGMENT, SEPARATORS, stripSeparators, looksText, CANARY_DECODERS, VIEWS, LONG_OUTPUT_THRESHOLD_BYTES;
|
|
5937
6487
|
var init_dist = __esm({
|
|
5938
6488
|
"packages/policy-engine/dist/index.mjs"() {
|
|
5939
6489
|
"use strict";
|
|
@@ -6747,6 +7297,11 @@ var init_dist = __esm({
|
|
|
6747
7297
|
example: "cat /etc/shadow",
|
|
6748
7298
|
advice: "Keep Review, or choose Block when agents must never read system password hashes."
|
|
6749
7299
|
},
|
|
7300
|
+
"files.system-dirs": {
|
|
7301
|
+
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.",
|
|
7302
|
+
example: "wget -O /usr/bin/tool https://example.com/tool",
|
|
7303
|
+
advice: "Keep Review on a workstation. A container image build that installs into /usr can set it to Log."
|
|
7304
|
+
},
|
|
6750
7305
|
// ── Commands ──────────────────────────────────────────────────────────────
|
|
6751
7306
|
"commands.inline-exec": {
|
|
6752
7307
|
plain: "The agent runs a small program written inside the command itself.",
|
|
@@ -6943,7 +7498,7 @@ var init_dist = __esm({
|
|
|
6943
7498
|
advice: "Fixed at Block. A person accepts the change on the machine (node9 mcp pin update)."
|
|
6944
7499
|
},
|
|
6945
7500
|
"loading.malicious-package": {
|
|
6946
|
-
plain: "The agent installs a package that the public malicious-package database (OSV) lists, or one published minutes ago.",
|
|
7501
|
+
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.",
|
|
6947
7502
|
example: "npm install of a package flagged as malware",
|
|
6948
7503
|
advice: "Keep Block for known-malicious packages; brand-new packages already stop for Review."
|
|
6949
7504
|
}
|
|
@@ -6981,6 +7536,15 @@ var init_dist = __esm({
|
|
|
6981
7536
|
defaultValue: "review",
|
|
6982
7537
|
legacy: ["review-password-hash-read"]
|
|
6983
7538
|
},
|
|
7539
|
+
{
|
|
7540
|
+
id: "files.system-dirs",
|
|
7541
|
+
group: "files",
|
|
7542
|
+
title: "System directories",
|
|
7543
|
+
catches: "Writes, new files and removals under /usr, /etc, /boot, /bin, /sbin, /lib and /var/log, and uploads of system files",
|
|
7544
|
+
values: ALL,
|
|
7545
|
+
defaultValue: "review",
|
|
7546
|
+
legacy: ["review-system-dir-mutation", "review-system-file-upload"]
|
|
7547
|
+
},
|
|
6984
7548
|
// ── Commands ────────────────────────────────────────────────────────────
|
|
6985
7549
|
{
|
|
6986
7550
|
id: "commands.inline-exec",
|
|
@@ -8278,6 +8842,10 @@ var init_dist = __esm({
|
|
|
8278
8842
|
AND_OP = probeBinOp("a && b");
|
|
8279
8843
|
OR_OP = probeBinOp("a || b");
|
|
8280
8844
|
AND_OR_OPS = new Set([AND_OP, OR_OP].filter((o) => o !== null));
|
|
8845
|
+
MAX_CALL_DEPTH = 4;
|
|
8846
|
+
MAX_CALL_WALKS = 256;
|
|
8847
|
+
callDepth = 0;
|
|
8848
|
+
callWalks = 0;
|
|
8281
8849
|
FS_OP_CACHE_MAX = 5e3;
|
|
8282
8850
|
fsOpCache = /* @__PURE__ */ new Map();
|
|
8283
8851
|
stripDotSlash = (p) => p.replace(/^\.\//, "");
|
|
@@ -8330,10 +8898,79 @@ var init_dist = __esm({
|
|
|
8330
8898
|
MAX_ARGV_CHARACTERS = 262144;
|
|
8331
8899
|
({ syntax: syntax2 } = import_mvdan_sh2.default);
|
|
8332
8900
|
MAX_ALTERNATIVES = 64;
|
|
8901
|
+
MAX_CALL_DEPTH2 = 4;
|
|
8902
|
+
MAX_CALL_WALKS2 = 256;
|
|
8903
|
+
POSITIONAL = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "@", "*", "#"];
|
|
8333
8904
|
MAX_DEPTH = 64;
|
|
8334
8905
|
MAX_PAYLOAD_DEPTH = 24;
|
|
8335
8906
|
unique = (a) => [...new Set(a)].slice(0, MAX_ALTERNATIVES);
|
|
8336
|
-
clone = (s) => ({
|
|
8907
|
+
clone = (s) => ({
|
|
8908
|
+
cwd: [...s.cwd],
|
|
8909
|
+
vars: new Map(s.vars),
|
|
8910
|
+
...s.callDepth ? { callDepth: s.callDepth } : {}
|
|
8911
|
+
});
|
|
8912
|
+
SENDING_STDIN = /* @__PURE__ */ new Set(["nc", "ncat", "netcat", "socat", "telnet", "curl", "wget"]);
|
|
8913
|
+
DOWNLOAD_SHORT_VALUE = {
|
|
8914
|
+
curl: "odDeEFHKmPrtTuUwxXyYzAbcCQ",
|
|
8915
|
+
wget: "OoaPiBetTwQlARDXIUY",
|
|
8916
|
+
aria2c: "odisx"
|
|
8917
|
+
};
|
|
8918
|
+
DOWNLOAD_LONG_VALUE = {
|
|
8919
|
+
curl: [
|
|
8920
|
+
"--output",
|
|
8921
|
+
"--output-dir",
|
|
8922
|
+
"--dump-header",
|
|
8923
|
+
"--cookie-jar",
|
|
8924
|
+
"--data",
|
|
8925
|
+
"--data-binary",
|
|
8926
|
+
"--data-raw",
|
|
8927
|
+
"--data-ascii",
|
|
8928
|
+
"--data-urlencode",
|
|
8929
|
+
"--form",
|
|
8930
|
+
"--header",
|
|
8931
|
+
"--request",
|
|
8932
|
+
"--user",
|
|
8933
|
+
"--user-agent",
|
|
8934
|
+
"--referer",
|
|
8935
|
+
"--cookie",
|
|
8936
|
+
"--config",
|
|
8937
|
+
"--json",
|
|
8938
|
+
"--upload-file",
|
|
8939
|
+
"--max-time",
|
|
8940
|
+
"--proxy",
|
|
8941
|
+
"--write-out",
|
|
8942
|
+
"--url"
|
|
8943
|
+
],
|
|
8944
|
+
wget: [
|
|
8945
|
+
"--output-document",
|
|
8946
|
+
"--output-file",
|
|
8947
|
+
"--append-output",
|
|
8948
|
+
"--directory-prefix",
|
|
8949
|
+
"--input-file",
|
|
8950
|
+
"--post-data",
|
|
8951
|
+
"--post-file",
|
|
8952
|
+
"--body-data",
|
|
8953
|
+
"--body-file",
|
|
8954
|
+
"--method",
|
|
8955
|
+
"--header",
|
|
8956
|
+
"--user-agent",
|
|
8957
|
+
"--user",
|
|
8958
|
+
"--password",
|
|
8959
|
+
"--tries",
|
|
8960
|
+
"--timeout"
|
|
8961
|
+
],
|
|
8962
|
+
aria2c: ["--out", "--dir", "--input-file"]
|
|
8963
|
+
};
|
|
8964
|
+
DOWNLOAD_OUTPUT_FLAGS = {
|
|
8965
|
+
curl: ["-o", "--output", "-D", "--dump-header", "-c", "--cookie-jar"],
|
|
8966
|
+
wget: ["-O", "--output-document", "-o", "--output-file", "-a", "--append-output"],
|
|
8967
|
+
aria2c: ["-o", "--out"]
|
|
8968
|
+
};
|
|
8969
|
+
DOWNLOAD_DIR_FLAGS = {
|
|
8970
|
+
curl: ["--output-dir"],
|
|
8971
|
+
wget: ["-P", "--directory-prefix"],
|
|
8972
|
+
aria2c: ["-d", "--dir"]
|
|
8973
|
+
};
|
|
8337
8974
|
base = (s) => (s ?? "").replace(/\/+$/, "").split("/").pop() ?? "";
|
|
8338
8975
|
quoteGlob = (s) => s.replace(/[\\*?\[\]{}()!+@]/g, "\\$&");
|
|
8339
8976
|
asWord = (p) => ({ value: p.value, ...p.glob ? { pattern: p.pattern } : {} });
|
|
@@ -8358,6 +8995,18 @@ var init_dist = __esm({
|
|
|
8358
8995
|
SYSTEM_AUTH_DIRS = ["/etc/security", "/etc/pam.d", "/etc/sudoers.d"];
|
|
8359
8996
|
PASSWORD_HASH_FILES = ["/etc/shadow", "/etc/gshadow"];
|
|
8360
8997
|
STARTUP_NAMES = [".bashrc", ".zshrc", ".profile", ".bash_profile"];
|
|
8998
|
+
SYSTEM_DIRS = [
|
|
8999
|
+
"/usr",
|
|
9000
|
+
"/etc",
|
|
9001
|
+
"/boot",
|
|
9002
|
+
"/bin",
|
|
9003
|
+
"/sbin",
|
|
9004
|
+
"/lib",
|
|
9005
|
+
"/lib64",
|
|
9006
|
+
"/var/log"
|
|
9007
|
+
];
|
|
9008
|
+
SYSTEM_DIRS_EXEMPT = ["/usr/local", "/usr/src"];
|
|
9009
|
+
UPLOAD_SYSTEM_DIRS = [...SYSTEM_DIRS, "/proc", "/sys", "/root"];
|
|
8361
9010
|
AGENT_INSTRUCTION_FILES = [
|
|
8362
9011
|
".gitlab-ci.yml",
|
|
8363
9012
|
"CLAUDE.md",
|
|
@@ -8375,6 +9024,7 @@ var init_dist = __esm({
|
|
|
8375
9024
|
".husky"
|
|
8376
9025
|
];
|
|
8377
9026
|
LEGACY_SYSTEM_PATH_RE = /^(?:[a-zA-Z]:)?\/etc\/(?:passwd|shadow|sudoers)$/;
|
|
9027
|
+
exempt = (p) => SYSTEM_DIRS_EXEMPT.some((d) => within(p, d));
|
|
8378
9028
|
RANK = { allow: 0, review: 1, block: 2 };
|
|
8379
9029
|
BIP39_ENGLISH = `abandon
|
|
8380
9030
|
ability
|
|
@@ -10660,15 +11310,20 @@ zoo`.split("\n");
|
|
|
10660
11310
|
IDENTIFIER_VALUE_RE = /^[A-Za-z_.-]+$/;
|
|
10661
11311
|
PATH_VALUE_RE = /^(?:\/|\.{1,2}\/|~\/)/;
|
|
10662
11312
|
DOTTED_REFERENCE_RE = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)+$/;
|
|
11313
|
+
CONCATENATION_RE = /^\+[A-Za-z_$][\w$]*\./;
|
|
10663
11314
|
EMAIL_VALUE_RE = /^[^@\s]+@[^@\s]+\.[A-Za-z]{2,}$/;
|
|
10664
11315
|
FIXTURE_NAME_RE = /(?:fake|dummy|example|sample|mock|stub|placeholder|test)/i;
|
|
10665
|
-
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;
|
|
11316
|
+
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;
|
|
10666
11317
|
QUERY_CREDENTIAL_VALUE_RE = /=([A-Za-z0-9%+/_.~-]{8,512}={0,2})$/;
|
|
10667
11318
|
OTP_SECRET_RE = /^[A-Z2-7]{16,}=*$/i;
|
|
10668
|
-
|
|
11319
|
+
OTP_DOC_SECRETS = /* @__PURE__ */ new Set(["JBSWY3DPEHPK3PXP", "GEZDGNBVGY3TQOJQ"]);
|
|
11320
|
+
ASSIGNED_SECRET_VALUE_RE = /(?::=|=>|[:=])[ \t]{0,8}\\?["'`]?([A-Za-z0-9+/_.!@#$%^*~-]{12,512}={0,2})$/;
|
|
10669
11321
|
ASSIGNED_SECRET_NAME_RE = /^([A-Za-z0-9_]+)/;
|
|
10670
|
-
NAME_KEYWORD_RE = /^(?:password|passphrase|passwd|pwd|secret|token|api[_-]?key|auth[_-]?key|private[_-]?key|access[_-]?key|client[_-]?secret)/i;
|
|
11322
|
+
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;
|
|
10671
11323
|
ARN_CONTEXT_RE = /\barn:[A-Za-z0-9-]*:[^\s"'`]*$/i;
|
|
11324
|
+
BASIC_VALUE_RE = /([A-Za-z0-9+/]{8,512}={0,2})$/;
|
|
11325
|
+
BASIC_PAIR_RE = /^([^:\s]{1,128}):(\S{8,256})$/;
|
|
11326
|
+
OTP_CAPTURE_RE = /["']([A-Z2-7]{16,128}=*)["']$/i;
|
|
10672
11327
|
DLP_PATTERNS = [
|
|
10673
11328
|
// ── AWS ───────────────────────────────────────────────────────────────────
|
|
10674
11329
|
{
|
|
@@ -11006,6 +11661,22 @@ zoo`.split("\n");
|
|
|
11006
11661
|
contextBoost: true,
|
|
11007
11662
|
minEntropy: 3
|
|
11008
11663
|
},
|
|
11664
|
+
// ── HTTP Basic credential ─────────────────────────────────────────────────
|
|
11665
|
+
// `Authorization: Basic <base64 of user:password>`, or the bare scheme in a
|
|
11666
|
+
// config list. The validator decodes the value and accepts only a
|
|
11667
|
+
// `user:password` whose password looks generated (letters and digits, 8+
|
|
11668
|
+
// characters, the query-credential rule), so prose ("Basic authentication")
|
|
11669
|
+
// and documentation pairs (`admin:changeme`, `user:pass1234`) stay allowed.
|
|
11670
|
+
// review: the same weight as a bare Bearer token.
|
|
11671
|
+
{
|
|
11672
|
+
name: "HTTP Basic Credential",
|
|
11673
|
+
regex: /\bBasic[ \t]{1,8}([A-Za-z0-9+/]{8,512}={0,2})/i,
|
|
11674
|
+
severity: "review",
|
|
11675
|
+
keywords: ["basic"],
|
|
11676
|
+
validate: validateBasicCredential,
|
|
11677
|
+
redactGroup: 1,
|
|
11678
|
+
argumentsOnly: true
|
|
11679
|
+
},
|
|
11009
11680
|
// ── Resend ────────────────────────────────────────────────────────────────
|
|
11010
11681
|
{
|
|
11011
11682
|
name: "Resend API Key",
|
|
@@ -11147,6 +11818,19 @@ zoo`.split("\n");
|
|
|
11147
11818
|
validate: validateQueryCredential,
|
|
11148
11819
|
argumentsOnly: true
|
|
11149
11820
|
},
|
|
11821
|
+
// ── OTP secret in a constructor ───────────────────────────────────────────
|
|
11822
|
+
// `ROTP::TOTP.new('<base32>')`, `pyotp.TOTP("<base32>")`, `new HOTP('...')`:
|
|
11823
|
+
// the shared secret of a one-time-password generator written as a literal.
|
|
11824
|
+
// The otpauth URI form is `Credential in URL Query`. review.
|
|
11825
|
+
{
|
|
11826
|
+
name: "OTP Secret",
|
|
11827
|
+
regex: /\b(?:TOTP|HOTP|OTP)(?:\.new|\.create)?[ \t]{0,8}\(?[ \t]{0,8}["']([A-Z2-7]{16,128}=*)["']/i,
|
|
11828
|
+
severity: "review",
|
|
11829
|
+
keywords: ["otp"],
|
|
11830
|
+
validate: validateOtpSecret,
|
|
11831
|
+
redactGroup: 1,
|
|
11832
|
+
argumentsOnly: true
|
|
11833
|
+
},
|
|
11150
11834
|
// ── Secret assigned to a telling name ─────────────────────────────────────
|
|
11151
11835
|
// `password: ...`, `JWT_SECRET=...`, `api_key = "..."`. The name alone is
|
|
11152
11836
|
// not a finding: most such lines hold a reference (`process.env.X`), a
|
|
@@ -11154,12 +11838,14 @@ zoo`.split("\n");
|
|
|
11154
11838
|
// generated: 12+ characters, not an identifier, Shannon entropy of 3.5
|
|
11155
11839
|
// bits per character or more. review, never block (node9Firewall#348,
|
|
11156
11840
|
// item 4). Tuned on CredData's public split; its look-alike lines are the
|
|
11157
|
-
// negative control.
|
|
11841
|
+
// negative control. A quote may be escaped (`\"access_token\":\"...\"`):
|
|
11842
|
+
// JSON written inside a string literal. The two optional quotes after the
|
|
11843
|
+
// name are written out, not as a counted group, to keep star height 1.
|
|
11158
11844
|
{
|
|
11159
11845
|
name: "Assigned Secret",
|
|
11160
|
-
regex: /(?:password|passphrase|passwd|pwd|secret|token|api[_-]?key|auth[_-]?key|private[_-]?key|access[_-]?key|client[_-]?secret)[A-Za-z0-9_]{0,40}["'\]]
|
|
11846
|
+
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,
|
|
11161
11847
|
severity: "review",
|
|
11162
|
-
keywords: ["
|
|
11848
|
+
keywords: ["pass", "pwd", "secret", "token", "key", "ticket"],
|
|
11163
11849
|
validate: validateAssignedSecret,
|
|
11164
11850
|
argumentsOnly: true
|
|
11165
11851
|
}
|
|
@@ -11420,7 +12106,169 @@ zoo`.split("\n");
|
|
|
11420
12106
|
]);
|
|
11421
12107
|
PY_SPEC_RE = /^([A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?)(?:\[[^\]]*\])?\s*(.*)$/;
|
|
11422
12108
|
PY_EXACT_RE = /^===?\s*v?(\d+(?:\.\d+)*(?:[a-zA-Z0-9.+!-]*))$/;
|
|
12109
|
+
EXACT_VERSION_RE = /^=?v?(\d+(?:\.\d+)*(?:[-+][0-9A-Za-z][0-9A-Za-z.+-]*)?)$/;
|
|
12110
|
+
GEM_NAME_RE = /^[\p{L}\p{N}][\p{L}\p{N}_.-]*$/u;
|
|
12111
|
+
GEM_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
12112
|
+
"-v",
|
|
12113
|
+
"--version",
|
|
12114
|
+
"-i",
|
|
12115
|
+
"--install-dir",
|
|
12116
|
+
"-n",
|
|
12117
|
+
"--bindir",
|
|
12118
|
+
"--document",
|
|
12119
|
+
"--build-root",
|
|
12120
|
+
"-P",
|
|
12121
|
+
"--trust-policy",
|
|
12122
|
+
"-s",
|
|
12123
|
+
"--source",
|
|
12124
|
+
"-p",
|
|
12125
|
+
"--http-proxy",
|
|
12126
|
+
"--platform",
|
|
12127
|
+
"-g",
|
|
12128
|
+
"--file",
|
|
12129
|
+
"--without"
|
|
12130
|
+
]);
|
|
12131
|
+
BUNDLE_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
12132
|
+
"-v",
|
|
12133
|
+
"--version",
|
|
12134
|
+
"-g",
|
|
12135
|
+
"--group",
|
|
12136
|
+
"-s",
|
|
12137
|
+
"--source",
|
|
12138
|
+
"-r",
|
|
12139
|
+
"--require",
|
|
12140
|
+
"--path",
|
|
12141
|
+
"--git",
|
|
12142
|
+
"--github",
|
|
12143
|
+
"--branch",
|
|
12144
|
+
"--ref",
|
|
12145
|
+
"--glob"
|
|
12146
|
+
]);
|
|
12147
|
+
CRATE_NAME_RE = /^[\p{L}\p{N}][\p{L}\p{N}_-]*$/u;
|
|
12148
|
+
CARGO_ADD_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
12149
|
+
"-F",
|
|
12150
|
+
"--features",
|
|
12151
|
+
"--rename",
|
|
12152
|
+
"--registry",
|
|
12153
|
+
"--manifest-path",
|
|
12154
|
+
"-p",
|
|
12155
|
+
"--package",
|
|
12156
|
+
"--git",
|
|
12157
|
+
"--branch",
|
|
12158
|
+
"--tag",
|
|
12159
|
+
"--rev",
|
|
12160
|
+
"--path",
|
|
12161
|
+
"--base",
|
|
12162
|
+
"--target",
|
|
12163
|
+
"--color",
|
|
12164
|
+
"--config",
|
|
12165
|
+
"-Z",
|
|
12166
|
+
"--lockfile-path"
|
|
12167
|
+
]);
|
|
12168
|
+
CARGO_INSTALL_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
12169
|
+
"--version",
|
|
12170
|
+
"--vers",
|
|
12171
|
+
"--git",
|
|
12172
|
+
"--branch",
|
|
12173
|
+
"--tag",
|
|
12174
|
+
"--rev",
|
|
12175
|
+
"--path",
|
|
12176
|
+
"--root",
|
|
12177
|
+
"--index",
|
|
12178
|
+
"--registry",
|
|
12179
|
+
"-F",
|
|
12180
|
+
"--features",
|
|
12181
|
+
"--profile",
|
|
12182
|
+
"--target",
|
|
12183
|
+
"--target-dir",
|
|
12184
|
+
"-j",
|
|
12185
|
+
"--jobs",
|
|
12186
|
+
"--bin",
|
|
12187
|
+
"--example",
|
|
12188
|
+
"--color",
|
|
12189
|
+
"--config",
|
|
12190
|
+
"-Z"
|
|
12191
|
+
]);
|
|
12192
|
+
GO_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
12193
|
+
"-C",
|
|
12194
|
+
"-tags",
|
|
12195
|
+
"-ldflags",
|
|
12196
|
+
"-gcflags",
|
|
12197
|
+
"-asmflags",
|
|
12198
|
+
"-mod",
|
|
12199
|
+
"-modfile",
|
|
12200
|
+
"-o",
|
|
12201
|
+
"-p",
|
|
12202
|
+
"-pkgdir",
|
|
12203
|
+
"-toolexec",
|
|
12204
|
+
"-exec",
|
|
12205
|
+
"-overlay",
|
|
12206
|
+
"-pgo",
|
|
12207
|
+
"-buildmode",
|
|
12208
|
+
"-compiler",
|
|
12209
|
+
"-gccgoflags",
|
|
12210
|
+
"-installsuffix",
|
|
12211
|
+
"-covermode",
|
|
12212
|
+
"-coverpkg"
|
|
12213
|
+
]);
|
|
12214
|
+
GO_PATH_RE = /^[\p{L}\p{N}-]+(?:\.[\p{L}\p{N}-]+)+(?:\/[\p{L}\p{N}_.~+-]+)*$/u;
|
|
12215
|
+
NUGET_NAME_RE = /^[\p{L}\p{N}_][\p{L}\p{N}_.-]*$/u;
|
|
12216
|
+
DOTNET_ADD_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
12217
|
+
"-v",
|
|
12218
|
+
"--version",
|
|
12219
|
+
"-s",
|
|
12220
|
+
"--source",
|
|
12221
|
+
"-f",
|
|
12222
|
+
"--framework",
|
|
12223
|
+
"--package-directory"
|
|
12224
|
+
]);
|
|
12225
|
+
DOTNET_TOOL_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
12226
|
+
"--version",
|
|
12227
|
+
"--add-source",
|
|
12228
|
+
"--configfile",
|
|
12229
|
+
"--framework",
|
|
12230
|
+
"--tool-path",
|
|
12231
|
+
"-a",
|
|
12232
|
+
"--arch",
|
|
12233
|
+
"--tool-manifest",
|
|
12234
|
+
"-v",
|
|
12235
|
+
"--verbosity"
|
|
12236
|
+
]);
|
|
12237
|
+
NUGET_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
12238
|
+
"-version",
|
|
12239
|
+
"-outputdirectory",
|
|
12240
|
+
"-o",
|
|
12241
|
+
"-source",
|
|
12242
|
+
"-configfile",
|
|
12243
|
+
"-fallbacksource",
|
|
12244
|
+
"-framework",
|
|
12245
|
+
"-packagesavemode",
|
|
12246
|
+
"-dependencyversion",
|
|
12247
|
+
"-solutiondirectory",
|
|
12248
|
+
"-verbosity"
|
|
12249
|
+
]);
|
|
12250
|
+
MAVEN_COORD_RE = /^[\p{L}\p{N}_.-]+:[\p{L}\p{N}_.-]+$/u;
|
|
12251
|
+
COMPOSER_NAME_RE = /^[\p{L}\p{N}][\p{L}\p{N}_.-]*\/[\p{L}\p{N}][\p{L}\p{N}_.-]*$/u;
|
|
12252
|
+
COMPOSER_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
12253
|
+
"-d",
|
|
12254
|
+
"--working-dir",
|
|
12255
|
+
"--stability",
|
|
12256
|
+
"--repository",
|
|
12257
|
+
"--add-repository"
|
|
12258
|
+
]);
|
|
12259
|
+
EDITOR_HEADS = ["code", "code-insiders", "codium", "vscodium", "cursor"];
|
|
12260
|
+
EXTENSION_ID_RE = /^[\p{L}\p{N}][\p{L}\p{N}_-]*\.[\p{L}\p{N}][\p{L}\p{N}_.-]*$/u;
|
|
11423
12261
|
MANAGER_HEADS = /* @__PURE__ */ new Set([
|
|
12262
|
+
"gem",
|
|
12263
|
+
"bundle",
|
|
12264
|
+
"cargo",
|
|
12265
|
+
"go",
|
|
12266
|
+
"dotnet",
|
|
12267
|
+
"nuget",
|
|
12268
|
+
"mvn",
|
|
12269
|
+
"mvnw",
|
|
12270
|
+
"composer",
|
|
12271
|
+
...EDITOR_HEADS,
|
|
11424
12272
|
"npm",
|
|
11425
12273
|
"npx",
|
|
11426
12274
|
"pnpm",
|
|
@@ -11499,6 +12347,7 @@ zoo`.split("\n");
|
|
|
11499
12347
|
MAP_GOVERNED = /* @__PURE__ */ new Set([
|
|
11500
12348
|
"files.shell-startup",
|
|
11501
12349
|
"files.password-hashes",
|
|
12350
|
+
"files.system-dirs",
|
|
11502
12351
|
"commands.inline-exec",
|
|
11503
12352
|
"commands.eval-dynamic",
|
|
11504
12353
|
"commands.curl-pipe-shell",
|
|
@@ -11767,7 +12616,6 @@ zoo`.split("\n");
|
|
|
11767
12616
|
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/;
|
|
11768
12617
|
PII_CC15_RE = /\b3[47]\d{2}[-\s]?\d{6}[-\s]?\d{5}\b/;
|
|
11769
12618
|
PII_IBAN_RE = /\b[A-Z]{2}\d{2}(?:[A-Z0-9]|[ -][A-Z0-9]){11,30}\b/;
|
|
11770
|
-
NUMBER_LEAD_RE = /[\s:=;([{"'+-]/;
|
|
11771
12619
|
REALTIME_PII_PATTERNS = ["SSN", "Credit Card", "IBAN"];
|
|
11772
12620
|
MAX_PII_SCAN_BYTES = 1e5;
|
|
11773
12621
|
MASKED_KINDS = /* @__PURE__ */ new Set(["email", "iban", "account"]);
|
|
@@ -14629,14 +15477,23 @@ async function registerDaemonEntry(toolName, args, meta, riskMetadata, activityI
|
|
|
14629
15477
|
clearTimeout(timer);
|
|
14630
15478
|
}
|
|
14631
15479
|
}
|
|
14632
|
-
async function waitForDaemonDecision(id, signal) {
|
|
15480
|
+
async function waitForDaemonDecision(id, signal, maxWaitMs = 12e4) {
|
|
14633
15481
|
const base2 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
|
|
14634
15482
|
const waitCtrl = new AbortController();
|
|
14635
|
-
|
|
15483
|
+
let capped = false;
|
|
15484
|
+
const waitTimer = maxWaitMs > 0 ? setTimeout(() => {
|
|
15485
|
+
capped = true;
|
|
15486
|
+
waitCtrl.abort();
|
|
15487
|
+
}, maxWaitMs) : void 0;
|
|
14636
15488
|
const onAbort = () => waitCtrl.abort();
|
|
14637
15489
|
if (signal) signal.addEventListener("abort", onAbort);
|
|
14638
15490
|
try {
|
|
14639
|
-
const waitRes = await fetch(`${base2}/wait/${id}`, { signal: waitCtrl.signal })
|
|
15491
|
+
const waitRes = await fetch(`${base2}/wait/${id}`, { signal: waitCtrl.signal }).catch(
|
|
15492
|
+
(err) => {
|
|
15493
|
+
if (capped && !signal?.aborted) throw new Error("node9 daemon did not answer in time");
|
|
15494
|
+
throw err;
|
|
15495
|
+
}
|
|
15496
|
+
);
|
|
14640
15497
|
if (!waitRes.ok) return { decision: "deny" };
|
|
14641
15498
|
const { decision, source, reason } = await waitRes.json();
|
|
14642
15499
|
if (decision === "allow") return { decision: "allow", source };
|
|
@@ -15677,13 +16534,13 @@ function maskRiskMetadataForCloud(rm) {
|
|
|
15677
16534
|
const r = rm;
|
|
15678
16535
|
const dests = Array.isArray(r.exposureDestinations) ? r.exposureDestinations.filter((d) => typeof d === "string") : [];
|
|
15679
16536
|
const swaps = dests.filter(isMaskedKind).map((d) => [destinationValue(d), destinationValue(maskDestination(d))]).filter(([full]) => full.length > 0).sort((a, b) => b[0].length - a[0].length);
|
|
15680
|
-
const
|
|
16537
|
+
const maskText2 = (text) => {
|
|
15681
16538
|
let out2 = text;
|
|
15682
16539
|
for (const [full, masked] of swaps) out2 = out2.split(full).join(masked);
|
|
15683
16540
|
return maskPersonalData(out2);
|
|
15684
16541
|
};
|
|
15685
16542
|
const out = {};
|
|
15686
|
-
for (const [k, v] of Object.entries(r)) out[k] = typeof v === "string" ?
|
|
16543
|
+
for (const [k, v] of Object.entries(r)) out[k] = typeof v === "string" ? maskText2(v) : v;
|
|
15687
16544
|
if (dests.length > 0) {
|
|
15688
16545
|
out.exposureDestinations = dests.map(maskDestination);
|
|
15689
16546
|
out.exposureDestinationHashes = dests.map((d) => isMaskedKind(d) ? hashDestination(d) : "");
|
|
@@ -15886,7 +16743,17 @@ function ecoDir(eco) {
|
|
|
15886
16743
|
return import_path23.default.join(osvRoot(), eco);
|
|
15887
16744
|
}
|
|
15888
16745
|
function indexKey(eco, name) {
|
|
15889
|
-
|
|
16746
|
+
switch (eco) {
|
|
16747
|
+
case "PyPI":
|
|
16748
|
+
return normalizePyPiName(name);
|
|
16749
|
+
case "crates.io":
|
|
16750
|
+
return name.toLowerCase().replace(/_/g, "-");
|
|
16751
|
+
case "Go":
|
|
16752
|
+
case "Maven":
|
|
16753
|
+
return name;
|
|
16754
|
+
default:
|
|
16755
|
+
return name.toLowerCase();
|
|
16756
|
+
}
|
|
15890
16757
|
}
|
|
15891
16758
|
function shardOf(key) {
|
|
15892
16759
|
return (0, import_crypto9.createHash)("sha1").update(key).digest("hex").slice(0, 2);
|
|
@@ -16097,8 +16964,11 @@ async function pypiInfo(name, version) {
|
|
|
16097
16964
|
publishedAtMs: times.length > 0 ? Math.min(...times) : void 0
|
|
16098
16965
|
};
|
|
16099
16966
|
}
|
|
16967
|
+
var HAS_REGISTRY_CLIENT = /* @__PURE__ */ new Set(["npm", "PyPI"]);
|
|
16100
16968
|
async function registryInfo(eco, name, version, freshWindowMs, raw) {
|
|
16101
|
-
|
|
16969
|
+
if (eco === "npm") return npmInfo(name, version, freshWindowMs, raw);
|
|
16970
|
+
if (eco === "PyPI") return pypiInfo(name, version);
|
|
16971
|
+
return null;
|
|
16102
16972
|
}
|
|
16103
16973
|
|
|
16104
16974
|
// src/supply-chain/local-resolve.ts
|
|
@@ -16137,26 +17007,51 @@ function commandOf(toolName, args) {
|
|
|
16137
17007
|
const a = args;
|
|
16138
17008
|
return typeof a.command === "string" ? a.command : typeof a.cmd === "string" ? a.cmd : null;
|
|
16139
17009
|
}
|
|
17010
|
+
var KIND = {
|
|
17011
|
+
npm: "npm package",
|
|
17012
|
+
PyPI: "PyPI package",
|
|
17013
|
+
RubyGems: "RubyGems package",
|
|
17014
|
+
"crates.io": "crates.io package",
|
|
17015
|
+
Go: "Go module",
|
|
17016
|
+
NuGet: "NuGet package",
|
|
17017
|
+
Maven: "Maven artifact",
|
|
17018
|
+
Packagist: "Packagist package",
|
|
17019
|
+
VSCode: "VS Code extension"
|
|
17020
|
+
};
|
|
16140
17021
|
function label(p, version) {
|
|
16141
|
-
return `${p.ecosystem
|
|
17022
|
+
return `${KIND[p.ecosystem]} "${p.name}${version ? `@${version}` : ""}"`;
|
|
17023
|
+
}
|
|
17024
|
+
function osvNames(p) {
|
|
17025
|
+
if (p.ecosystem !== "Go") return [p.name];
|
|
17026
|
+
const parts = p.name.split("/");
|
|
17027
|
+
const out = [];
|
|
17028
|
+
for (let n = parts.length; n >= 2 && out.length < 6; n--) out.push(parts.slice(0, n).join("/"));
|
|
17029
|
+
return out;
|
|
16142
17030
|
}
|
|
16143
17031
|
async function maliciousFor(p, version, cfg, misses, networkCapped) {
|
|
16144
|
-
const
|
|
16145
|
-
const
|
|
16146
|
-
|
|
17032
|
+
const names = osvNames(p);
|
|
17033
|
+
const local = names.map((n) => lookupIndex(p.ecosystem, n));
|
|
17034
|
+
const hits2 = local.flatMap((l) => l.status === "hit" ? l.entries : []);
|
|
17035
|
+
const entries = hits2.length > 0 ? hits2 : null;
|
|
17036
|
+
const freshClean = local.every((l) => l.status === "clean" && l.fresh);
|
|
17037
|
+
const status = local.some((l) => l.status === "unavailable") ? "unavailable" : local[0].status;
|
|
17038
|
+
if (!entries && !freshClean) {
|
|
16147
17039
|
if (!cfg.onlineFallback || networkCapped) {
|
|
16148
17040
|
misses.push(
|
|
16149
|
-
`${label(p, version)}: local index ${
|
|
17041
|
+
`${label(p, version)}: local index ${status}, no online lookup (${networkCapped ? "over the per-command network cap" : "online fallback off"})`
|
|
16150
17042
|
);
|
|
16151
17043
|
return null;
|
|
16152
17044
|
}
|
|
16153
|
-
let
|
|
17045
|
+
let perName = null;
|
|
16154
17046
|
try {
|
|
16155
|
-
|
|
17047
|
+
perName = await queryOsvMalicious(
|
|
17048
|
+
names.map((name) => ({ ecosystem: p.ecosystem, name, version }))
|
|
17049
|
+
);
|
|
16156
17050
|
} catch (err) {
|
|
16157
17051
|
misses.push(`${label(p, version)}: OSV online ${err.message}`);
|
|
16158
17052
|
return null;
|
|
16159
17053
|
}
|
|
17054
|
+
const ids = perName ? [[...new Set(perName.flat())]] : null;
|
|
16160
17055
|
if (!ids) {
|
|
16161
17056
|
misses.push(`${label(p, version)}: OSV online unavailable`);
|
|
16162
17057
|
return null;
|
|
@@ -16226,7 +17121,7 @@ async function checkOne(p, cfg, misses, networkCapped, cwd) {
|
|
|
16226
17121
|
}
|
|
16227
17122
|
const findings = [];
|
|
16228
17123
|
let info = null;
|
|
16229
|
-
const wantsRegistry = cfg.newPackage !== "off" || cfg.installScript !== "off";
|
|
17124
|
+
const wantsRegistry = (cfg.newPackage !== "off" || cfg.installScript !== "off") && HAS_REGISTRY_CLIENT.has(p.ecosystem);
|
|
16230
17125
|
if (wantsRegistry && !networkCapped) {
|
|
16231
17126
|
try {
|
|
16232
17127
|
const windowMs = cfg.newPackage === "off" ? 0 : cfg.maxAgeHours * 36e5;
|
|
@@ -16307,6 +17202,36 @@ async function runPackageCheck(toolName, args, cfg, cwd) {
|
|
|
16307
17202
|
}
|
|
16308
17203
|
}
|
|
16309
17204
|
|
|
17205
|
+
// src/policy/negotiation.ts
|
|
17206
|
+
var BLOCKED_BY = " is blocked by ";
|
|
17207
|
+
var DASH_BLOCKED_BY = " \u2014 blocked by ";
|
|
17208
|
+
var SHIELD = " shield";
|
|
17209
|
+
function asApprovalRequest(description) {
|
|
17210
|
+
const is = description.lastIndexOf(BLOCKED_BY);
|
|
17211
|
+
if (is > 0) {
|
|
17212
|
+
const by = description.slice(is + BLOCKED_BY.length).replace(/\.$/, "");
|
|
17213
|
+
if (by.endsWith(SHIELD)) return `${description.slice(0, is)} needs your approval (${by})`;
|
|
17214
|
+
}
|
|
17215
|
+
const dash = description.lastIndexOf(DASH_BLOCKED_BY);
|
|
17216
|
+
if (dash > 0) {
|
|
17217
|
+
const by = description.slice(dash + DASH_BLOCKED_BY.length).replace(/\.$/, "");
|
|
17218
|
+
if (by.endsWith(SHIELD)) return `${description.slice(0, dash)}. Needs your approval (${by})`;
|
|
17219
|
+
}
|
|
17220
|
+
return description;
|
|
17221
|
+
}
|
|
17222
|
+
|
|
17223
|
+
// src/auth/decision-sources.ts
|
|
17224
|
+
var HUMAN_DECISION_SOURCES = /* @__PURE__ */ new Set([
|
|
17225
|
+
"terminal",
|
|
17226
|
+
"browser",
|
|
17227
|
+
"native"
|
|
17228
|
+
]);
|
|
17229
|
+
var REDIRECT_SOURCE = "terminal-redirect";
|
|
17230
|
+
var DECISION_ROUTE_SOURCES = /* @__PURE__ */ new Set([
|
|
17231
|
+
...HUMAN_DECISION_SOURCES,
|
|
17232
|
+
REDIRECT_SOURCE
|
|
17233
|
+
]);
|
|
17234
|
+
|
|
16310
17235
|
// src/auth/orchestrator.ts
|
|
16311
17236
|
var SENSITIVE_PATH_PATTERN = sensitivePathMatch("").patternName;
|
|
16312
17237
|
function isProtectedPathRule(ruleName) {
|
|
@@ -16434,6 +17359,10 @@ async function authorizeHeadless(toolName, args, meta, options) {
|
|
|
16434
17359
|
}
|
|
16435
17360
|
return _authorizeHeadlessCore(toolName, args, meta, options);
|
|
16436
17361
|
}
|
|
17362
|
+
function daemonWaitCapMs(approvalTimeoutMs) {
|
|
17363
|
+
if (approvalTimeoutMs === 0) return 0;
|
|
17364
|
+
return Math.max(approvalTimeoutMs ?? 0, 12e4) + 5e3;
|
|
17365
|
+
}
|
|
16437
17366
|
function approvalTimeoutReason(approvalTimeoutMs) {
|
|
16438
17367
|
return `No human response within ${approvalTimeoutMs / 1e3}s \u2014 auto-denied by timeout policy. Approve from your phone next time: run \`node9 login\`.`;
|
|
16439
17368
|
}
|
|
@@ -16469,6 +17398,7 @@ async function _authorizeHeadlessCore(toolName, args, metaArg, options) {
|
|
|
16469
17398
|
let statefulRecoveryCommand;
|
|
16470
17399
|
let localSmartRuleMatched = false;
|
|
16471
17400
|
let hardBlockDowngraded = false;
|
|
17401
|
+
let downgradedBlock = null;
|
|
16472
17402
|
let argScan;
|
|
16473
17403
|
const argumentScan = () => argScan ??= scanArgumentSecrets(args);
|
|
16474
17404
|
let fileAccess;
|
|
@@ -17088,6 +18018,16 @@ async function _authorizeHeadlessCore(toolName, args, metaArg, options) {
|
|
|
17088
18018
|
humanApproverReachable,
|
|
17089
18019
|
overridable: policyResult.overridable
|
|
17090
18020
|
});
|
|
18021
|
+
const blocked = {
|
|
18022
|
+
approved: false,
|
|
18023
|
+
reason: policyResult.reason ?? "Action explicitly blocked by Smart Policy.",
|
|
18024
|
+
blockedBy: "local-config",
|
|
18025
|
+
blockedByLabel: policyResult.blockedByLabel,
|
|
18026
|
+
ruleHit: policyResult.ruleName,
|
|
18027
|
+
...policyResult.recoveryCommand && { recoveryCommand: policyResult.recoveryCommand },
|
|
18028
|
+
...policyResult.ruleDescription && { ruleDescription: policyResult.ruleDescription },
|
|
18029
|
+
...protectedPathFields(policyResult.ruleName, toolName, args, dlpReviewFlagged)
|
|
18030
|
+
};
|
|
17091
18031
|
const hardBlock = () => {
|
|
17092
18032
|
if (!isManual)
|
|
17093
18033
|
appendLocalAudit(
|
|
@@ -17098,17 +18038,9 @@ async function _authorizeHeadlessCore(toolName, args, metaArg, options) {
|
|
|
17098
18038
|
{ ...meta, ruleName: policyResult.ruleName },
|
|
17099
18039
|
hashAuditArgs
|
|
17100
18040
|
);
|
|
17101
|
-
return
|
|
17102
|
-
approved: false,
|
|
17103
|
-
reason: policyResult.reason ?? "Action explicitly blocked by Smart Policy.",
|
|
17104
|
-
blockedBy: "local-config",
|
|
17105
|
-
blockedByLabel: policyResult.blockedByLabel,
|
|
17106
|
-
ruleHit: policyResult.ruleName,
|
|
17107
|
-
...policyResult.recoveryCommand && { recoveryCommand: policyResult.recoveryCommand },
|
|
17108
|
-
...policyResult.ruleDescription && { ruleDescription: policyResult.ruleDescription },
|
|
17109
|
-
...protectedPathFields(policyResult.ruleName, toolName, args, dlpReviewFlagged)
|
|
17110
|
-
};
|
|
18041
|
+
return blocked;
|
|
17111
18042
|
};
|
|
18043
|
+
downgradedBlock = blocked;
|
|
17112
18044
|
if (policyResult.dependsOnStatePredicates?.length) {
|
|
17113
18045
|
const stateResults = await checkStatePredicates(policyResult.dependsOnStatePredicates);
|
|
17114
18046
|
const predicatesMet = stateResults !== null && policyResult.dependsOnStatePredicates.every((p) => stateResults[p] === true);
|
|
@@ -17165,7 +18097,8 @@ async function _authorizeHeadlessCore(toolName, args, metaArg, options) {
|
|
|
17165
18097
|
policyMatchedWord,
|
|
17166
18098
|
policyResult.ruleName
|
|
17167
18099
|
);
|
|
17168
|
-
if (policyRuleDescription)
|
|
18100
|
+
if (policyRuleDescription)
|
|
18101
|
+
riskMetadata.ruleDescription = (hardBlockDowngraded ? asApprovalRequest(policyRuleDescription) : policyRuleDescription).slice(0, 200);
|
|
17169
18102
|
const persistent = policyResult.ruleName || policyResult.tier === 7 || hardBlockDowngraded ? null : getPersistentDecision(toolName);
|
|
17170
18103
|
if (persistent === "allow" && !callSpecificReview()) {
|
|
17171
18104
|
if (!isManual)
|
|
@@ -17329,7 +18262,7 @@ ${packageReview}`;
|
|
|
17329
18262
|
}
|
|
17330
18263
|
}
|
|
17331
18264
|
if (initResult.pending) cloudRequestId = initResult.requestId || null;
|
|
17332
|
-
if (!
|
|
18265
|
+
if (!reviewDemanded()) explainableLabel = "Organization Policy (SaaS)";
|
|
17333
18266
|
} catch {
|
|
17334
18267
|
}
|
|
17335
18268
|
}
|
|
@@ -17471,10 +18404,54 @@ ${packageReview}`;
|
|
|
17471
18404
|
decision: daemonDecision,
|
|
17472
18405
|
source: decisionSource,
|
|
17473
18406
|
reason: daemonReason
|
|
17474
|
-
} = await waitForDaemonDecision(
|
|
18407
|
+
} = await waitForDaemonDecision(
|
|
18408
|
+
daemonEntryId,
|
|
18409
|
+
signal,
|
|
18410
|
+
daemonWaitCapMs(config.settings.approvalTimeoutMs)
|
|
18411
|
+
);
|
|
17475
18412
|
if (daemonDecision === "abandoned") throw new Error("Abandoned");
|
|
17476
18413
|
const isApproved = daemonDecision === "allow";
|
|
17477
|
-
const isRedirect = decisionSource ===
|
|
18414
|
+
const isRedirect = decisionSource === REDIRECT_SOURCE;
|
|
18415
|
+
if (!isApproved && !isRedirect && !(decisionSource && HUMAN_DECISION_SOURCES.has(decisionSource))) {
|
|
18416
|
+
if (decisionSource === "cloud")
|
|
18417
|
+
return {
|
|
18418
|
+
approved: false,
|
|
18419
|
+
reason: daemonReason || "Action rejected by organization administrator via Slack.",
|
|
18420
|
+
blockedBy: "team-policy",
|
|
18421
|
+
blockedByLabel: "Organization Policy (SaaS)",
|
|
18422
|
+
decisionSource: "cloud"
|
|
18423
|
+
};
|
|
18424
|
+
const timedOut = decisionSource === "timeout";
|
|
18425
|
+
if (downgradedBlock)
|
|
18426
|
+
return {
|
|
18427
|
+
...downgradedBlock,
|
|
18428
|
+
blockedBy: timedOut ? "timeout" : "local-config"
|
|
18429
|
+
};
|
|
18430
|
+
if (timedOut)
|
|
18431
|
+
return {
|
|
18432
|
+
approved: false,
|
|
18433
|
+
reason: daemonReason || "No response: auto-denied after the approval timeout.",
|
|
18434
|
+
blockedBy: "timeout",
|
|
18435
|
+
blockedByLabel: "Approval Timeout"
|
|
18436
|
+
};
|
|
18437
|
+
if (decisionSource === "policy")
|
|
18438
|
+
return {
|
|
18439
|
+
approved: false,
|
|
18440
|
+
reason: daemonReason || "Action blocked by policy.",
|
|
18441
|
+
blockedBy: "local-config",
|
|
18442
|
+
blockedByLabel: explainableLabel
|
|
18443
|
+
};
|
|
18444
|
+
return {
|
|
18445
|
+
approved: false,
|
|
18446
|
+
reason: daemonReason || "node9 could not get an approval decision for this call.",
|
|
18447
|
+
blockedBy: "no-approval-mechanism",
|
|
18448
|
+
blockedByLabel: "Approval Unavailable"
|
|
18449
|
+
};
|
|
18450
|
+
}
|
|
18451
|
+
if (isApproved && decisionSource === "cloud")
|
|
18452
|
+
return { approved: true, checkedBy: "cloud", decisionSource: "cloud" };
|
|
18453
|
+
if (isApproved && decisionSource === "policy")
|
|
18454
|
+
return { approved: true, checkedBy: "local-policy" };
|
|
17478
18455
|
const via = "Terminal (node9 tail)";
|
|
17479
18456
|
return {
|
|
17480
18457
|
approved: isApproved,
|