@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.mjs
CHANGED
|
@@ -1173,6 +1173,120 @@ function expandPlainParam(p) {
|
|
|
1173
1173
|
if (recorded !== void 0) return recorded;
|
|
1174
1174
|
return HOME_VARIABLES.has(p.Param?.Value) ? "~" : void 0;
|
|
1175
1175
|
}
|
|
1176
|
+
function functionBodies(f) {
|
|
1177
|
+
const out = /* @__PURE__ */ new Map();
|
|
1178
|
+
syntax.Walk(f, (node) => {
|
|
1179
|
+
const n = node;
|
|
1180
|
+
if (n && syntax.NodeType(n) === "FuncDecl" && n.Name?.Value && n.Body) {
|
|
1181
|
+
const list = out.get(n.Name.Value) ?? [];
|
|
1182
|
+
list.push(n.Body);
|
|
1183
|
+
out.set(n.Name.Value, list);
|
|
1184
|
+
}
|
|
1185
|
+
return true;
|
|
1186
|
+
});
|
|
1187
|
+
return out;
|
|
1188
|
+
}
|
|
1189
|
+
function rebindsPositionals(body) {
|
|
1190
|
+
let found = false;
|
|
1191
|
+
syntax.Walk(body, (node) => {
|
|
1192
|
+
const n = node;
|
|
1193
|
+
if (found || !n) return false;
|
|
1194
|
+
if (syntax.NodeType(n) === "CallExpr") {
|
|
1195
|
+
const words = (n.Args || []).map((a) => resolveWordLiteral(a));
|
|
1196
|
+
if (words[0] === "shift") found = true;
|
|
1197
|
+
else if (words[0] === "set" && words.slice(1).some((w) => w === null || w === "--" || !/^[-+]/.test(w)))
|
|
1198
|
+
found = true;
|
|
1199
|
+
}
|
|
1200
|
+
return !found;
|
|
1201
|
+
});
|
|
1202
|
+
return found;
|
|
1203
|
+
}
|
|
1204
|
+
function tableBindings() {
|
|
1205
|
+
if (!assignmentTable) return "";
|
|
1206
|
+
const out = [];
|
|
1207
|
+
for (const [name, rec] of assignmentTable)
|
|
1208
|
+
if (rec.at < currentStmtOffset) out.push(`${name}=${rec.value ?? "\0"}`);
|
|
1209
|
+
return out.sort().join("");
|
|
1210
|
+
}
|
|
1211
|
+
function walkWithCalls(f, visit) {
|
|
1212
|
+
const bodies = functionBodies(f);
|
|
1213
|
+
if (bodies.size === 0) {
|
|
1214
|
+
syntax.Walk(f, visit);
|
|
1215
|
+
return;
|
|
1216
|
+
}
|
|
1217
|
+
const seenCalls = /* @__PURE__ */ new Set();
|
|
1218
|
+
const inline = (call) => {
|
|
1219
|
+
const words = (call.Args || []).map((a) => resolveWordLiteral(a));
|
|
1220
|
+
const list = words[0] ? bodies.get(words[0]) : void 0;
|
|
1221
|
+
if (!list || callDepth >= MAX_CALL_DEPTH) return;
|
|
1222
|
+
const args = words.slice(1);
|
|
1223
|
+
const key = `${words[0]}\0${JSON.stringify(args)}\0${tableBindings()}`;
|
|
1224
|
+
if (seenCalls.has(key)) return;
|
|
1225
|
+
seenCalls.add(key);
|
|
1226
|
+
const outerTable = assignmentTable;
|
|
1227
|
+
const outerOffset = currentStmtOffset;
|
|
1228
|
+
for (const body of list) {
|
|
1229
|
+
if (callWalks >= MAX_CALL_WALKS) return;
|
|
1230
|
+
callWalks++;
|
|
1231
|
+
const table = /* @__PURE__ */ new Map();
|
|
1232
|
+
for (const [name, rec] of outerTable ?? [])
|
|
1233
|
+
if (rec.at < outerOffset) table.set(name, { value: rec.value, at: -1 });
|
|
1234
|
+
for (const name of ["1", "2", "3", "4", "5", "6", "7", "8", "9", "@", "*", "#"])
|
|
1235
|
+
table.delete(name);
|
|
1236
|
+
if (!rebindsPositionals(body)) {
|
|
1237
|
+
args.slice(0, 9).forEach((value, i) => table.set(String(i + 1), { value, at: -1 }));
|
|
1238
|
+
if (args.length === 1) {
|
|
1239
|
+
table.set("@", { value: args[0], at: -1 });
|
|
1240
|
+
table.set("*", { value: args[0], at: -1 });
|
|
1241
|
+
}
|
|
1242
|
+
table.set("#", { value: String(args.length), at: -1 });
|
|
1243
|
+
}
|
|
1244
|
+
assignmentTable = table;
|
|
1245
|
+
currentStmtOffset = Number.MAX_SAFE_INTEGER;
|
|
1246
|
+
callDepth++;
|
|
1247
|
+
try {
|
|
1248
|
+
if (body.Cmd && syntax.NodeType(body.Cmd) === "Block") recordTopLevelAssignments(body.Cmd);
|
|
1249
|
+
syntax.Walk(body, step);
|
|
1250
|
+
} finally {
|
|
1251
|
+
callDepth--;
|
|
1252
|
+
assignmentTable = outerTable;
|
|
1253
|
+
currentStmtOffset = outerOffset;
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
};
|
|
1257
|
+
const step = (node) => {
|
|
1258
|
+
if (!node) return false;
|
|
1259
|
+
const go = visit(node);
|
|
1260
|
+
if (go !== false && syntax.NodeType(node) === "CallExpr") inline(node);
|
|
1261
|
+
return go;
|
|
1262
|
+
};
|
|
1263
|
+
syntax.Walk(f, step);
|
|
1264
|
+
}
|
|
1265
|
+
function withAssignments(f, body) {
|
|
1266
|
+
const outerTable = assignmentTable;
|
|
1267
|
+
const outerOffset = currentStmtOffset;
|
|
1268
|
+
const outerWalks = callWalks;
|
|
1269
|
+
callWalks = 0;
|
|
1270
|
+
assignmentTable = new Map(
|
|
1271
|
+
[...outerTable ?? []].map(([k, r]) => [k, { value: r.value, at: -1 }])
|
|
1272
|
+
);
|
|
1273
|
+
currentStmtOffset = Number.MAX_SAFE_INTEGER;
|
|
1274
|
+
try {
|
|
1275
|
+
recordTopLevelAssignments(f);
|
|
1276
|
+
return body();
|
|
1277
|
+
} finally {
|
|
1278
|
+
assignmentTable = outerTable;
|
|
1279
|
+
currentStmtOffset = outerOffset;
|
|
1280
|
+
callWalks = outerWalks;
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
function trackStatement(n) {
|
|
1284
|
+
try {
|
|
1285
|
+
currentStmtOffset = n.Pos().Offset();
|
|
1286
|
+
} catch {
|
|
1287
|
+
currentStmtOffset = Number.MAX_SAFE_INTEGER;
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1176
1290
|
function resolveWordLiteral(w) {
|
|
1177
1291
|
const parts = w?.Parts || [];
|
|
1178
1292
|
let s = "";
|
|
@@ -1293,27 +1407,31 @@ function extractShellDestinations(command) {
|
|
|
1293
1407
|
const out = [];
|
|
1294
1408
|
const seen = /* @__PURE__ */ new Set();
|
|
1295
1409
|
try {
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
const
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1410
|
+
withAssignments(
|
|
1411
|
+
f,
|
|
1412
|
+
() => walkWithCalls(f, (node) => {
|
|
1413
|
+
if (!node) return false;
|
|
1414
|
+
const n = node;
|
|
1415
|
+
if (syntax.NodeType(n) === "Stmt") trackStatement(n);
|
|
1416
|
+
if (syntax.NodeType(n) !== "CallExpr") return true;
|
|
1417
|
+
const callArgs = n.Args || [];
|
|
1418
|
+
if (callArgs.length === 0) return true;
|
|
1419
|
+
const words = callArgs.map((a) => resolveWordLiteral(a));
|
|
1420
|
+
const at = unwrapCommandHead(words);
|
|
1421
|
+
const name = (words[at] || "").toLowerCase().split("/").pop() ?? "";
|
|
1422
|
+
if (!NET_BINARIES.has(name)) return true;
|
|
1423
|
+
const rest = words.slice(at + 1);
|
|
1424
|
+
for (const raw of destTokensForBinary(name, rest)) {
|
|
1425
|
+
const host2 = parseDestHost(raw);
|
|
1426
|
+
if (!host2) continue;
|
|
1427
|
+
const key = `${name}:${host2}`;
|
|
1428
|
+
if (seen.has(key)) continue;
|
|
1429
|
+
seen.add(key);
|
|
1430
|
+
out.push({ host: host2, binary: name, raw });
|
|
1431
|
+
}
|
|
1432
|
+
return true;
|
|
1433
|
+
})
|
|
1434
|
+
);
|
|
1317
1435
|
} catch {
|
|
1318
1436
|
return out;
|
|
1319
1437
|
}
|
|
@@ -1325,39 +1443,45 @@ function extractShellDestTokens(command) {
|
|
|
1325
1443
|
const out = [];
|
|
1326
1444
|
const seen = /* @__PURE__ */ new Set();
|
|
1327
1445
|
try {
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
const
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
tok = tok.
|
|
1446
|
+
withAssignments(
|
|
1447
|
+
f,
|
|
1448
|
+
() => walkWithCalls(f, (node) => {
|
|
1449
|
+
if (!node) return false;
|
|
1450
|
+
const n = node;
|
|
1451
|
+
if (syntax.NodeType(n) === "Stmt") trackStatement(n);
|
|
1452
|
+
if (syntax.NodeType(n) !== "CallExpr") return true;
|
|
1453
|
+
const callArgs = n.Args || [];
|
|
1454
|
+
if (callArgs.length === 0) return true;
|
|
1455
|
+
const words = callArgs.map((a) => resolveWordLiteral(a));
|
|
1456
|
+
const head = unwrapCommandHead(words);
|
|
1457
|
+
const name = (words[head] || "").toLowerCase().split("/").pop() ?? "";
|
|
1458
|
+
if (!NET_BINARIES.has(name)) return true;
|
|
1459
|
+
const rest = words.slice(head + 1);
|
|
1460
|
+
for (const raw of destTokensForBinary(name, rest)) {
|
|
1461
|
+
if (!raw) continue;
|
|
1462
|
+
let tok = raw.trim();
|
|
1463
|
+
const scheme = /^[a-z][a-z0-9+.-]*:\/\//i.exec(tok);
|
|
1464
|
+
const hasScheme = scheme !== null;
|
|
1465
|
+
if (hasScheme) tok = tok.slice(scheme[0].length);
|
|
1466
|
+
tok = tok.split(/[/?#]/)[0];
|
|
1467
|
+
const at = tok.lastIndexOf("@");
|
|
1468
|
+
if (at >= 0) tok = tok.slice(at + 1);
|
|
1469
|
+
if (tok.startsWith("[")) {
|
|
1470
|
+
const close = tok.indexOf("]");
|
|
1471
|
+
if (close > 0) tok = tok.slice(0, close + 1);
|
|
1472
|
+
} else {
|
|
1473
|
+
tok = tok.split(":")[0];
|
|
1474
|
+
}
|
|
1475
|
+
if (!tok) continue;
|
|
1476
|
+
if (!hasScheme && /^\d+$/.test(tok) && Number(tok) < 16777216) continue;
|
|
1477
|
+
const key = `${name}:${tok}`;
|
|
1478
|
+
if (seen.has(key)) continue;
|
|
1479
|
+
seen.add(key);
|
|
1480
|
+
out.push({ token: tok, binary: name });
|
|
1351
1481
|
}
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
if (seen.has(key)) continue;
|
|
1356
|
-
seen.add(key);
|
|
1357
|
-
out.push({ token: tok, binary: name });
|
|
1358
|
-
}
|
|
1359
|
-
return true;
|
|
1360
|
-
});
|
|
1482
|
+
return true;
|
|
1483
|
+
})
|
|
1484
|
+
);
|
|
1361
1485
|
} catch {
|
|
1362
1486
|
return out;
|
|
1363
1487
|
}
|
|
@@ -1476,6 +1600,7 @@ function analyzeFsOperationImpl(command, depth = 0) {
|
|
|
1476
1600
|
if (depth === 0) {
|
|
1477
1601
|
seenPayloads = /* @__PURE__ */ new Set();
|
|
1478
1602
|
payloadBudget = PAYLOAD_BUDGET;
|
|
1603
|
+
if (callDepth === 0) callWalks = 0;
|
|
1479
1604
|
}
|
|
1480
1605
|
assignmentTable = new Map(
|
|
1481
1606
|
[...outerTable ?? []].map(([k, r]) => [k, { value: r.value, at: -1 }])
|
|
@@ -1483,7 +1608,7 @@ function analyzeFsOperationImpl(command, depth = 0) {
|
|
|
1483
1608
|
currentStmtOffset = Number.MAX_SAFE_INTEGER;
|
|
1484
1609
|
try {
|
|
1485
1610
|
recordTopLevelAssignments(f);
|
|
1486
|
-
|
|
1611
|
+
walkWithCalls(f, (node) => {
|
|
1487
1612
|
if (!node || result?.verdict === "block") return false;
|
|
1488
1613
|
const n = node;
|
|
1489
1614
|
const nodeType = syntax.NodeType(n);
|
|
@@ -2272,6 +2397,19 @@ function runnablePrefix(command) {
|
|
|
2272
2397
|
}
|
|
2273
2398
|
return null;
|
|
2274
2399
|
}
|
|
2400
|
+
function uploadedFile(tool, flag, value) {
|
|
2401
|
+
const v = value.value;
|
|
2402
|
+
const file2 = (f) => f && f !== "-" && f !== "." ? { value: f } : null;
|
|
2403
|
+
if (tool === "curl") {
|
|
2404
|
+
if (["-d", "--data", "--data-ascii", "--data-binary", "--json"].includes(flag))
|
|
2405
|
+
return v.startsWith("@") ? file2(v.slice(1)) : null;
|
|
2406
|
+
if (flag === "--data-urlencode") return file2(/^[^=@]*@(.+)$/.exec(v)?.[1]);
|
|
2407
|
+
if (flag === "-F" || flag === "--form") return file2(/^[^=]*=[@<]([^;]+)/.exec(v)?.[1]);
|
|
2408
|
+
if (flag === "-T" || flag === "--upload-file") return file2(v);
|
|
2409
|
+
}
|
|
2410
|
+
if (tool === "wget" && ["--post-file", "--body-file"].includes(flag)) return file2(v);
|
|
2411
|
+
return null;
|
|
2412
|
+
}
|
|
2275
2413
|
function merge(a, b) {
|
|
2276
2414
|
a.cwd = unique([...a.cwd, ...b.cwd]);
|
|
2277
2415
|
for (const key of /* @__PURE__ */ new Set([...a.vars.keys(), ...b.vars.keys()])) {
|
|
@@ -2345,6 +2483,9 @@ function wordValues(word, state) {
|
|
|
2345
2483
|
function collectShellFileAccesses(command, context = {}) {
|
|
2346
2484
|
const out = [];
|
|
2347
2485
|
let payloads = 256;
|
|
2486
|
+
const functions = /* @__PURE__ */ new Map();
|
|
2487
|
+
const seenCalls = /* @__PURE__ */ new Set();
|
|
2488
|
+
let callWalks2 = MAX_CALL_WALKS2;
|
|
2348
2489
|
const initial = {
|
|
2349
2490
|
cwd: [context.cwd ?? ""],
|
|
2350
2491
|
vars: /* @__PURE__ */ new Map([["HOME", [context.homeDirs?.[0] ?? "~"]]])
|
|
@@ -2602,8 +2743,98 @@ function collectShellFileAccesses(command, context = {}) {
|
|
|
2602
2743
|
}
|
|
2603
2744
|
return;
|
|
2604
2745
|
}
|
|
2746
|
+
if (name === "wget" || name === "curl" || name === "aria2c") {
|
|
2747
|
+
downloadOutputs(name, args, state);
|
|
2748
|
+
return;
|
|
2749
|
+
}
|
|
2750
|
+
if (["http", "https", "xh", "xhs"].includes(name)) {
|
|
2751
|
+
for (const token of args) {
|
|
2752
|
+
if (!token || token.dynamic || /^[a-z][a-z0-9+.-]*:\/\//i.test(token.value)) continue;
|
|
2753
|
+
const m = /^(?:@|[^=:@\s]+@|[^=:@\s]*=@)(.+)$/.exec(token.value);
|
|
2754
|
+
if (m && m[1] !== "-") add("read", { value: m[1] }, state, { upload: true });
|
|
2755
|
+
}
|
|
2756
|
+
return;
|
|
2757
|
+
}
|
|
2758
|
+
if (name === "scp" || name === "rsync") {
|
|
2759
|
+
const operands2 = strings.filter((s) => s && !s.startsWith("-"));
|
|
2760
|
+
const remote = operands2.slice(1).some((o) => /^[^/\s]+:/.test(o));
|
|
2761
|
+
for (const p of copySourcePaths(words))
|
|
2762
|
+
add("read", locate(p), state, remote ? { upload: true } : {});
|
|
2763
|
+
return;
|
|
2764
|
+
}
|
|
2605
2765
|
for (const p of copySourcePaths(words)) add("read", locate(p), state);
|
|
2606
2766
|
}
|
|
2767
|
+
function downloadOutputs(name, args, state) {
|
|
2768
|
+
const valueShort = DOWNLOAD_SHORT_VALUE[name];
|
|
2769
|
+
const outputs = [];
|
|
2770
|
+
const urls = [];
|
|
2771
|
+
let dir2 = null;
|
|
2772
|
+
let remoteName = false;
|
|
2773
|
+
let document = false;
|
|
2774
|
+
let serverNamed = false;
|
|
2775
|
+
const take = (flag, value) => {
|
|
2776
|
+
if (!value) return;
|
|
2777
|
+
const sent = uploadedFile(name, flag, value);
|
|
2778
|
+
if (sent) add("read", sent, state, { upload: true });
|
|
2779
|
+
if (DOWNLOAD_OUTPUT_FLAGS[name].includes(flag)) {
|
|
2780
|
+
if (["-O", "--output-document", "--out"].includes(flag) || name === "aria2c" && flag === "-o")
|
|
2781
|
+
document = true;
|
|
2782
|
+
if (value.value !== "-" || value.pattern || value.dynamic) outputs.push(value);
|
|
2783
|
+
} else if (DOWNLOAD_DIR_FLAGS[name].includes(flag)) dir2 = value;
|
|
2784
|
+
else if (name === "wget" && (flag === "-i" || flag === "--input-file")) serverNamed = true;
|
|
2785
|
+
};
|
|
2786
|
+
for (let i = 0; i < args.length; i++) {
|
|
2787
|
+
const token = args[i];
|
|
2788
|
+
if (!token) continue;
|
|
2789
|
+
const v = token.value;
|
|
2790
|
+
if (v.startsWith("--")) {
|
|
2791
|
+
const eq = v.indexOf("=");
|
|
2792
|
+
const flag = eq < 0 ? v : v.slice(0, eq);
|
|
2793
|
+
if (["--remote-name", "--remote-name-all"].includes(flag)) remoteName = true;
|
|
2794
|
+
if (["--remote-header-name", "--recursive", "--mirror", "--page-requisites"].includes(flag))
|
|
2795
|
+
serverNamed = true;
|
|
2796
|
+
if (DOWNLOAD_LONG_VALUE[name].includes(flag))
|
|
2797
|
+
take(flag, eq < 0 ? args[++i] ?? null : { ...token, value: v.slice(eq + 1) });
|
|
2798
|
+
continue;
|
|
2799
|
+
}
|
|
2800
|
+
if (v.startsWith("-") && v.length > 1 && !token.dynamic) {
|
|
2801
|
+
for (let j = 1; j < v.length; j++) {
|
|
2802
|
+
const c = v[j];
|
|
2803
|
+
if (name === "wget" && c === "n") {
|
|
2804
|
+
j++;
|
|
2805
|
+
continue;
|
|
2806
|
+
}
|
|
2807
|
+
if (name === "curl" && c === "O") remoteName = true;
|
|
2808
|
+
if (name === "curl" && c === "J") serverNamed = true;
|
|
2809
|
+
if (name === "wget" && (c === "r" || c === "m" || c === "p")) serverNamed = true;
|
|
2810
|
+
if (valueShort.includes(c)) {
|
|
2811
|
+
const rest = v.slice(j + 1);
|
|
2812
|
+
take(`-${c}`, rest ? { value: rest } : args[++i] ?? null);
|
|
2813
|
+
break;
|
|
2814
|
+
}
|
|
2815
|
+
}
|
|
2816
|
+
continue;
|
|
2817
|
+
}
|
|
2818
|
+
if (/^(?:https?|ftps?):\/\//i.test(v)) urls.push(v);
|
|
2819
|
+
}
|
|
2820
|
+
for (const out2 of outputs) {
|
|
2821
|
+
const spelled = name === "aria2c" && dir2?.value && !out2.value.startsWith("/") ? { ...out2, value: `${dir2.value.replace(/\/+$/, "")}/${out2.value}` } : out2;
|
|
2822
|
+
if (name === "curl" && /#\d/.test(spelled.value))
|
|
2823
|
+
add("write", { value: "", pattern: spelled.value.replace(/#\d+/g, "*"), dot: true }, state);
|
|
2824
|
+
else add("write", spelled, state);
|
|
2825
|
+
}
|
|
2826
|
+
const remote = name === "curl" ? remoteName || serverNamed : !document || serverNamed;
|
|
2827
|
+
if (!remote) return;
|
|
2828
|
+
const into = dir2?.value ? dir2.value.replace(/\/+$/, "") : ".";
|
|
2829
|
+
if (serverNamed || urls.length === 0) {
|
|
2830
|
+
add("write", { value: "", pattern: `${quoteGlob(into)}/*`, dot: true }, state);
|
|
2831
|
+
return;
|
|
2832
|
+
}
|
|
2833
|
+
for (const url of urls) {
|
|
2834
|
+
const file2 = url.replace(/[?#].*$/, "").replace(/^[a-z]+:\/\/[^/]*/i, "").split("/").pop();
|
|
2835
|
+
add("write", { value: `${into}/${file2 || "index.html"}` }, state);
|
|
2836
|
+
}
|
|
2837
|
+
}
|
|
2607
2838
|
function findActions2(tokens, words, head, state, nested, depth) {
|
|
2608
2839
|
const starts = findStartPoints(words, head).starts;
|
|
2609
2840
|
const roots = (starts.length ? starts : ["."]).slice(0, MAX_ALTERNATIVES);
|
|
@@ -2675,7 +2906,35 @@ function collectShellFileAccesses(command, context = {}) {
|
|
|
2675
2906
|
const words = wordValues(node, state);
|
|
2676
2907
|
alternatives = alternatives.flatMap((prefix) => (words.length ? words : [null]).map((w) => [...prefix, w])).slice(0, MAX_ALTERNATIVES);
|
|
2677
2908
|
}
|
|
2678
|
-
for (const tokens of alternatives)
|
|
2909
|
+
for (const tokens of alternatives) {
|
|
2910
|
+
fromWords(tokens, state, nested, depth);
|
|
2911
|
+
inlineCall(tokens, state, depth);
|
|
2912
|
+
}
|
|
2913
|
+
}
|
|
2914
|
+
function inlineCall(tokens, state, depth) {
|
|
2915
|
+
const head = tokens[0];
|
|
2916
|
+
if (!head || head.dynamic || head.pattern) return;
|
|
2917
|
+
const bodies = functions.get(head.value);
|
|
2918
|
+
if (!bodies || (state.callDepth ?? 0) >= MAX_CALL_DEPTH2) return;
|
|
2919
|
+
const args = tokens.slice(1).map((a) => a && !a.dynamic ? a.value : null);
|
|
2920
|
+
const vars = JSON.stringify([...state.vars].sort(([a], [b]) => a < b ? -1 : 1));
|
|
2921
|
+
const key = `${head.value}\0${JSON.stringify(args)}\0${state.cwd.join("")}\0${vars}`;
|
|
2922
|
+
if (seenCalls.has(key)) return;
|
|
2923
|
+
seenCalls.add(key);
|
|
2924
|
+
for (const body of bodies) {
|
|
2925
|
+
if (callWalks2-- <= 0) return;
|
|
2926
|
+
const local = clone(state);
|
|
2927
|
+
local.callDepth = (state.callDepth ?? 0) + 1;
|
|
2928
|
+
for (const name of POSITIONAL) local.vars.delete(name);
|
|
2929
|
+
if (!rebindsPositionals(body)) {
|
|
2930
|
+
args.slice(0, 9).forEach((v, i) => local.vars.set(String(i + 1), v === null ? null : [v]));
|
|
2931
|
+
const all = args.every((v) => v !== null) && args.length ? unique(args) : null;
|
|
2932
|
+
local.vars.set("@", all);
|
|
2933
|
+
local.vars.set("*", all);
|
|
2934
|
+
local.vars.set("#", [String(args.length)]);
|
|
2935
|
+
}
|
|
2936
|
+
statement(body, local, depth + 1);
|
|
2937
|
+
}
|
|
2679
2938
|
}
|
|
2680
2939
|
function readsScriptFromStdin(stmt) {
|
|
2681
2940
|
const cmd = stmt?.Cmd;
|
|
@@ -2712,11 +2971,13 @@ function collectShellFileAccesses(command, context = {}) {
|
|
|
2712
2971
|
if (!stmt || depth > MAX_DEPTH) return;
|
|
2713
2972
|
state.onSuccess = void 0;
|
|
2714
2973
|
if (stmt.Background) state = clone(state);
|
|
2974
|
+
const headWord = stmt.Cmd && syntax2.NodeType(stmt.Cmd) === "CallExpr" ? base(resolveWordLiteral(stmt.Cmd.Args?.[0])) : "";
|
|
2975
|
+
const sends = SENDING_STDIN.has(headWord);
|
|
2715
2976
|
for (const redir of stmt.Redirs ?? []) {
|
|
2716
2977
|
substitutions(redir.Word, state, depth);
|
|
2717
2978
|
for (const word of wordValues(redir.Word, state)) {
|
|
2718
2979
|
if (WRITES.has(redir.Op)) add("write", word, state);
|
|
2719
|
-
if (READS.has(redir.Op)) add("read", word, state);
|
|
2980
|
+
if (READS.has(redir.Op)) add("read", word, state, sends ? { upload: true } : {});
|
|
2720
2981
|
if (redir.Op === DUP_OUT && word.value && !/^\d+$/.test(word.value))
|
|
2721
2982
|
add("write", word, state);
|
|
2722
2983
|
}
|
|
@@ -2825,7 +3086,10 @@ function collectShellFileAccesses(command, context = {}) {
|
|
|
2825
3086
|
}
|
|
2826
3087
|
const parsed = parseShared(command);
|
|
2827
3088
|
const program = typeof parsed !== "symbol" ? parsed : runnablePrefix(command);
|
|
2828
|
-
if (program)
|
|
3089
|
+
if (program) {
|
|
3090
|
+
for (const [name, bodies] of functionBodies(program)) functions.set(name, bodies);
|
|
3091
|
+
statements(program.Stmts ?? [], initial, 0);
|
|
3092
|
+
}
|
|
2829
3093
|
return out;
|
|
2830
3094
|
}
|
|
2831
3095
|
function isAgentInstructionPath(absPath, root) {
|
|
@@ -2941,6 +3205,33 @@ function hits(access, paths, directories, context) {
|
|
|
2941
3205
|
}
|
|
2942
3206
|
return false;
|
|
2943
3207
|
}
|
|
3208
|
+
function hitsSystemDir(access, context, dirs = SYSTEM_DIRS) {
|
|
3209
|
+
const home = context.homeDirs?.[0] ?? "~";
|
|
3210
|
+
const candidates = access.candidates ?? [
|
|
3211
|
+
normalizeFilePath(access.path, access.cwd ?? context.cwd, home)
|
|
3212
|
+
];
|
|
3213
|
+
for (const p of candidates) {
|
|
3214
|
+
if (dirs.some((d) => within(p, d)) && !exempt(p)) return true;
|
|
3215
|
+
if (access.recursive && dirs.some((d) => within(d, p))) return true;
|
|
3216
|
+
}
|
|
3217
|
+
if (!access.glob) return false;
|
|
3218
|
+
const pattern = normalizeFilePath(access.glob, access.cwd ?? context.cwd, home);
|
|
3219
|
+
const magic = pattern.search(/[*?[{]/);
|
|
3220
|
+
const literal = magic < 0 ? pattern : pattern.slice(0, pattern.lastIndexOf("/", magic) + 1);
|
|
3221
|
+
if (literal && exempt(literal.replace(/\/$/, "") || "/")) return false;
|
|
3222
|
+
const options = { dot: access.dotGlob === true, nonegate: true };
|
|
3223
|
+
const components = pattern.split("/");
|
|
3224
|
+
for (const root of dirs) {
|
|
3225
|
+
const count = root.split("/").length;
|
|
3226
|
+
if (components.length > count && picomatch(components.slice(0, count).join("/"), options)(root))
|
|
3227
|
+
return true;
|
|
3228
|
+
if (access.recursive && picomatch(pattern, options)(root)) return true;
|
|
3229
|
+
}
|
|
3230
|
+
return false;
|
|
3231
|
+
}
|
|
3232
|
+
function uploadDirs(context) {
|
|
3233
|
+
return context.homeDirs?.[0] === "/root" ? UPLOAD_SYSTEM_DIRS.filter((d) => d !== "/root") : UPLOAD_SYSTEM_DIRS;
|
|
3234
|
+
}
|
|
2944
3235
|
function evaluateProtectedFileAccess(accesses, context, settings) {
|
|
2945
3236
|
const homes = [.../* @__PURE__ */ new Set([...context.homeDirs ?? ["~"], "/root"])];
|
|
2946
3237
|
const startup = homes.flatMap(
|
|
@@ -2962,6 +3253,14 @@ function evaluateProtectedFileAccess(accesses, context, settings) {
|
|
|
2962
3253
|
"files.shell-startup",
|
|
2963
3254
|
"review-shell-startup-mutation",
|
|
2964
3255
|
"Changing shell startup files requires approval"
|
|
3256
|
+
] : mutation && hitsSystemDir(access, context) ? [
|
|
3257
|
+
"files.system-dirs",
|
|
3258
|
+
"review-system-dir-mutation",
|
|
3259
|
+
"Changing files in a system directory requires approval"
|
|
3260
|
+
] : !mutation && access.upload && hitsSystemDir(access, context, uploadDirs(context)) ? [
|
|
3261
|
+
"files.system-dirs",
|
|
3262
|
+
"review-system-file-upload",
|
|
3263
|
+
"Uploading a system file requires approval"
|
|
2965
3264
|
] : !mutation && hits(access, PASSWORD_HASH_FILES, [], context) ? [
|
|
2966
3265
|
"files.password-hashes",
|
|
2967
3266
|
"review-password-hash-read",
|
|
@@ -3222,16 +3521,21 @@ function looksGenerated(value, minLength, minEntropy, lettersOnlyAllowed = false
|
|
|
3222
3521
|
if (EMAIL_VALUE_RE.test(value)) return false;
|
|
3223
3522
|
if (looksLikeJoinedWords(value) || hasSequentialRun(value)) return false;
|
|
3224
3523
|
if (PATH_VALUE_RE.test(value) || DOTTED_REFERENCE_RE.test(value)) return false;
|
|
3524
|
+
if (CONCATENATION_RE.test(value)) return false;
|
|
3225
3525
|
if (rejectedByStructuredValidator(value)) return false;
|
|
3226
3526
|
return shannonEntropy(value) >= minEntropy;
|
|
3227
3527
|
}
|
|
3228
3528
|
function nameHoldsCredential(name) {
|
|
3229
3529
|
return !FIXTURE_NAME_RE.test(name) && !DERIVATIVE_NAME_RE.test(name);
|
|
3230
3530
|
}
|
|
3531
|
+
function isOtpDocSecret(value) {
|
|
3532
|
+
return OTP_DOC_SECRETS.has(value.replace(/=+$/, "").toUpperCase());
|
|
3533
|
+
}
|
|
3231
3534
|
function validateQueryCredential(raw) {
|
|
3232
3535
|
const value = capturedValue(raw, QUERY_CREDENTIAL_VALUE_RE);
|
|
3233
3536
|
if (value === null) return false;
|
|
3234
|
-
if (OTP_SECRET_RE.test(value))
|
|
3537
|
+
if (OTP_SECRET_RE.test(value))
|
|
3538
|
+
return !isOtpDocSecret(value) && looksGenerated(value, 16, 3, true);
|
|
3235
3539
|
if (!/[0-9]/.test(value) || !/[A-Za-z]/.test(value)) return false;
|
|
3236
3540
|
return looksGenerated(value, 12, 3);
|
|
3237
3541
|
}
|
|
@@ -3245,13 +3549,40 @@ function keywordEndsAPart(name) {
|
|
|
3245
3549
|
}
|
|
3246
3550
|
function validateAssignedSecret(raw, before = "") {
|
|
3247
3551
|
const value = capturedValue(raw, ASSIGNED_SECRET_VALUE_RE);
|
|
3248
|
-
const name = capturedValue(raw, ASSIGNED_SECRET_NAME_RE);
|
|
3552
|
+
const name = capturedValue(raw, ASSIGNED_SECRET_NAME_RE)?.replace(/^_+/, "") ?? null;
|
|
3249
3553
|
if (value === null || name === null) return false;
|
|
3250
|
-
if (!keywordEndsAPart(name)
|
|
3251
|
-
|
|
3554
|
+
if (!keywordEndsAPart(name)) return false;
|
|
3555
|
+
if (ARN_CONTEXT_RE.test(before)) return false;
|
|
3556
|
+
const prefix = (before.match(/[A-Za-z0-9_]*$/)?.[0] ?? "") + (raw.startsWith("_") ? "_" : "");
|
|
3252
3557
|
if (!nameHoldsCredential(prefix + name)) return false;
|
|
3253
3558
|
return looksGenerated(value, 12, 3.5);
|
|
3254
3559
|
}
|
|
3560
|
+
function validateBasicCredential(raw) {
|
|
3561
|
+
const b64 = capturedValue(raw, BASIC_VALUE_RE);
|
|
3562
|
+
if (b64 === null) return false;
|
|
3563
|
+
const bare = b64.replace(/=+$/, "");
|
|
3564
|
+
if (bare.length % 4 === 1) return false;
|
|
3565
|
+
const bytes = Buffer.from(b64, "base64");
|
|
3566
|
+
if (bytes.toString("base64").replace(/=+$/, "") !== bare) return false;
|
|
3567
|
+
const text = bytes.toString("utf8");
|
|
3568
|
+
if (!Buffer.from(text, "utf8").equals(bytes)) return false;
|
|
3569
|
+
for (let i = 0; i < text.length; i++) {
|
|
3570
|
+
const c = text.charCodeAt(i);
|
|
3571
|
+
if (c < 32 || c === 127) return false;
|
|
3572
|
+
}
|
|
3573
|
+
const pair = BASIC_PAIR_RE.exec(text);
|
|
3574
|
+
if (!pair) return false;
|
|
3575
|
+
const password = pair[2];
|
|
3576
|
+
if (!/[0-9]/.test(password) || !/[A-Za-z]/.test(password)) return false;
|
|
3577
|
+
const lower2 = password.toLowerCase();
|
|
3578
|
+
if (DLP_STOPWORDS.some((sw) => lower2.includes(sw)) || hasSequentialRun(password)) return false;
|
|
3579
|
+
return shannonEntropy(password) >= 3;
|
|
3580
|
+
}
|
|
3581
|
+
function validateOtpSecret(raw) {
|
|
3582
|
+
const secret = capturedValue(raw, OTP_CAPTURE_RE);
|
|
3583
|
+
if (secret === null || isOtpDocSecret(secret)) return false;
|
|
3584
|
+
return looksGenerated(secret.replace(/=+$/, ""), 16, 3, true);
|
|
3585
|
+
}
|
|
3255
3586
|
function matchSensitivePath2(resolvedPath, originalPath, options = {}) {
|
|
3256
3587
|
const normalised = resolvedPath.replace(/\\/g, "/");
|
|
3257
3588
|
if (!options.systemAccessHandled && isLegacySystemPath(normalised))
|
|
@@ -3522,6 +3853,156 @@ function parsePySpec(raw) {
|
|
|
3522
3853
|
const exact = PY_EXACT_RE.exec(m[2].trim());
|
|
3523
3854
|
return { name, version: exact ? exact[1] : void 0 };
|
|
3524
3855
|
}
|
|
3856
|
+
function exactVersion(v) {
|
|
3857
|
+
if (!v) return void 0;
|
|
3858
|
+
const m = EXACT_VERSION_RE.exec(v.trim());
|
|
3859
|
+
return m ? m[1] : void 0;
|
|
3860
|
+
}
|
|
3861
|
+
function push(out, ecosystem, manager, name, version, raw) {
|
|
3862
|
+
out.push({ ecosystem, manager, name, ...version ? { version } : {}, raw });
|
|
3863
|
+
}
|
|
3864
|
+
function gemInstall(words, from, out) {
|
|
3865
|
+
const dash = words.indexOf("--", from);
|
|
3866
|
+
const own = dash < 0 ? words : words.slice(0, dash);
|
|
3867
|
+
const pinned = exactVersion(flagValues(own, from, ["-v", "--version"])[0]);
|
|
3868
|
+
for (const raw of operands(own, from, GEM_VALUE_FLAGS)) {
|
|
3869
|
+
if (NON_REGISTRY_RE.test(raw) || /\.gem$/i.test(raw)) continue;
|
|
3870
|
+
const colon = raw.indexOf(":");
|
|
3871
|
+
const name = colon < 0 ? raw : raw.slice(0, colon);
|
|
3872
|
+
if (!GEM_NAME_RE.test(name)) continue;
|
|
3873
|
+
push(
|
|
3874
|
+
out,
|
|
3875
|
+
"RubyGems",
|
|
3876
|
+
"gem",
|
|
3877
|
+
name,
|
|
3878
|
+
colon < 0 ? pinned : exactVersion(raw.slice(colon + 1)),
|
|
3879
|
+
raw
|
|
3880
|
+
);
|
|
3881
|
+
}
|
|
3882
|
+
}
|
|
3883
|
+
function cargoCrates(words, from, manager, out) {
|
|
3884
|
+
const flags = manager === "cargo add" ? CARGO_ADD_VALUE_FLAGS : CARGO_INSTALL_VALUE_FLAGS;
|
|
3885
|
+
if (flagValues(words, from, ["--git", "--path", "--registry", "--index"]).length > 0) return;
|
|
3886
|
+
if (words.some((w) => w === "--git" || w === "--path")) return;
|
|
3887
|
+
const flagVersion = manager === "cargo install" ? exactVersion(flagValues(words, from, ["--version", "--vers"])[0]) : void 0;
|
|
3888
|
+
for (const raw of operands(words, from, flags)) {
|
|
3889
|
+
const at = raw.indexOf("@");
|
|
3890
|
+
const name = at < 0 ? raw : raw.slice(0, at);
|
|
3891
|
+
if (!CRATE_NAME_RE.test(name)) continue;
|
|
3892
|
+
push(
|
|
3893
|
+
out,
|
|
3894
|
+
"crates.io",
|
|
3895
|
+
manager,
|
|
3896
|
+
name,
|
|
3897
|
+
at < 0 ? flagVersion : exactVersion(raw.slice(at + 1)),
|
|
3898
|
+
raw
|
|
3899
|
+
);
|
|
3900
|
+
}
|
|
3901
|
+
}
|
|
3902
|
+
function goModules(words, from, manager, out) {
|
|
3903
|
+
let specs = operands(words, from, GO_VALUE_FLAGS);
|
|
3904
|
+
if (manager === "go run") specs = specs.slice(0, 1).filter((s) => s.includes("@"));
|
|
3905
|
+
for (const raw of specs) {
|
|
3906
|
+
const at = raw.indexOf("@");
|
|
3907
|
+
const pathPart = (at < 0 ? raw : raw.slice(0, at)).replace(/\/\.\.\.$/, "");
|
|
3908
|
+
const ver = at < 0 ? void 0 : raw.slice(at + 1);
|
|
3909
|
+
if (ver === "none") continue;
|
|
3910
|
+
if (!GO_PATH_RE.test(pathPart) || /\.go$/i.test(pathPart)) continue;
|
|
3911
|
+
const exact = ver && /^v\d/.test(ver) ? exactVersion(ver) : void 0;
|
|
3912
|
+
push(out, "Go", manager, pathPart, exact, raw);
|
|
3913
|
+
}
|
|
3914
|
+
}
|
|
3915
|
+
function dotnetAdd(words, from, out) {
|
|
3916
|
+
let k = -1;
|
|
3917
|
+
for (let i = from; i < Math.min(words.length, from + 2); i++) {
|
|
3918
|
+
if (lower(words[i]) === "package") {
|
|
3919
|
+
k = i;
|
|
3920
|
+
break;
|
|
3921
|
+
}
|
|
3922
|
+
}
|
|
3923
|
+
if (k < 0) return;
|
|
3924
|
+
const name = operands(words, k + 1, DOTNET_ADD_VALUE_FLAGS)[0];
|
|
3925
|
+
if (!name || !NUGET_NAME_RE.test(name)) return;
|
|
3926
|
+
const version = exactVersion(flagValues(words, k + 1, ["-v", "--version"])[0]);
|
|
3927
|
+
push(out, "NuGet", "dotnet add package", name, version, name);
|
|
3928
|
+
}
|
|
3929
|
+
function dotnetTool(words, from, out) {
|
|
3930
|
+
const name = operands(words, from, DOTNET_TOOL_VALUE_FLAGS)[0];
|
|
3931
|
+
if (!name || !NUGET_NAME_RE.test(name)) return;
|
|
3932
|
+
const version = exactVersion(flagValues(words, from, ["--version"])[0]);
|
|
3933
|
+
push(out, "NuGet", "dotnet tool", name, version, name);
|
|
3934
|
+
}
|
|
3935
|
+
function nugetInstall(words, from, out) {
|
|
3936
|
+
const lowered = words.map((w) => w !== null && w.startsWith("-") ? w.toLowerCase() : w);
|
|
3937
|
+
const name = operands(lowered, from, NUGET_VALUE_FLAGS)[0];
|
|
3938
|
+
if (!name || !NUGET_NAME_RE.test(name) || /\.config$/i.test(name)) return;
|
|
3939
|
+
const version = exactVersion(flagValues(lowered, from, ["-version"])[0]);
|
|
3940
|
+
push(out, "NuGet", "nuget install", name, version, name);
|
|
3941
|
+
}
|
|
3942
|
+
function mavenGet(words, from, out) {
|
|
3943
|
+
const goal = words.slice(from).some(
|
|
3944
|
+
(w) => w !== null && /(?:^|:)dependency:get$|^org\.apache\.maven\.plugins:maven-dependency-plugin:(?:[\w.-]+:)?get$/.test(
|
|
3945
|
+
w
|
|
3946
|
+
)
|
|
3947
|
+
);
|
|
3948
|
+
if (!goal) return;
|
|
3949
|
+
const prop = (key) => {
|
|
3950
|
+
for (let i = from; i < words.length; i++) {
|
|
3951
|
+
const w = words[i];
|
|
3952
|
+
if (w === null) continue;
|
|
3953
|
+
if (w.startsWith(`-D${key}=`)) return w.slice(key.length + 3);
|
|
3954
|
+
if (w === "-D" && words[i + 1]?.startsWith(`${key}=`))
|
|
3955
|
+
return words[i + 1].slice(key.length + 1);
|
|
3956
|
+
}
|
|
3957
|
+
return void 0;
|
|
3958
|
+
};
|
|
3959
|
+
const artifact = prop("artifact");
|
|
3960
|
+
let coord;
|
|
3961
|
+
let ver;
|
|
3962
|
+
if (artifact) {
|
|
3963
|
+
const parts = artifact.split(":");
|
|
3964
|
+
if (parts.length < 2) return;
|
|
3965
|
+
coord = `${parts[0]}:${parts[1]}`;
|
|
3966
|
+
ver = parts[2];
|
|
3967
|
+
} else {
|
|
3968
|
+
const g = prop("groupId");
|
|
3969
|
+
const a = prop("artifactId");
|
|
3970
|
+
if (!g || !a) return;
|
|
3971
|
+
coord = `${g}:${a}`;
|
|
3972
|
+
ver = prop("version");
|
|
3973
|
+
}
|
|
3974
|
+
if (!MAVEN_COORD_RE.test(coord)) return;
|
|
3975
|
+
const exact = ver && !/^(?:LATEST|RELEASE)$/i.test(ver) ? exactVersion(ver) : void 0;
|
|
3976
|
+
push(out, "Maven", "mvn dependency:get", coord, exact, artifact ?? coord);
|
|
3977
|
+
}
|
|
3978
|
+
function composerPackages(words, from, manager, out) {
|
|
3979
|
+
const ops = operands(words, from, COMPOSER_VALUE_FLAGS);
|
|
3980
|
+
const specs = manager === "composer create-project" ? ops.slice(0, 1) : ops;
|
|
3981
|
+
for (const raw of specs) {
|
|
3982
|
+
const sep = raw.search(/[:=]/);
|
|
3983
|
+
const name = sep < 0 ? raw : raw.slice(0, sep);
|
|
3984
|
+
if (!COMPOSER_NAME_RE.test(name)) continue;
|
|
3985
|
+
let ver = sep < 0 ? void 0 : raw.slice(sep + 1);
|
|
3986
|
+
if (manager === "composer create-project" && ver === void 0) ver = ops[2] ?? void 0;
|
|
3987
|
+
push(out, "Packagist", manager, name.toLowerCase(), exactVersion(ver), raw);
|
|
3988
|
+
}
|
|
3989
|
+
}
|
|
3990
|
+
function editorExtensions(words, from, out) {
|
|
3991
|
+
for (const raw of flagValues(words, from, ["--install-extension"])) {
|
|
3992
|
+
if (/\.vsix$/i.test(raw) || NON_REGISTRY_RE.test(raw)) continue;
|
|
3993
|
+
const at = raw.lastIndexOf("@");
|
|
3994
|
+
const name = at <= 0 ? raw : raw.slice(0, at);
|
|
3995
|
+
if (!EXTENSION_ID_RE.test(name)) continue;
|
|
3996
|
+
push(
|
|
3997
|
+
out,
|
|
3998
|
+
"VSCode",
|
|
3999
|
+
"code --install-extension",
|
|
4000
|
+
name,
|
|
4001
|
+
at <= 0 ? void 0 : exactVersion(raw.slice(at + 1)),
|
|
4002
|
+
raw
|
|
4003
|
+
);
|
|
4004
|
+
}
|
|
4005
|
+
}
|
|
3525
4006
|
function basename(w) {
|
|
3526
4007
|
return (w ?? "").toLowerCase().split("/").pop() ?? "";
|
|
3527
4008
|
}
|
|
@@ -3753,7 +4234,63 @@ function fromCall(words, out, depth) {
|
|
|
3753
4234
|
pushPy(out, "pipenv", operands(words, v + 1, PIPENV_VALUE_FLAGS));
|
|
3754
4235
|
return;
|
|
3755
4236
|
}
|
|
4237
|
+
case "gem": {
|
|
4238
|
+
const v = verbIndex(words, start + 1, GEM_VALUE_FLAGS);
|
|
4239
|
+
if (v >= 0 && ["install", "i"].includes(lower(words[v]))) gemInstall(words, v + 1, out);
|
|
4240
|
+
return;
|
|
4241
|
+
}
|
|
4242
|
+
case "bundle": {
|
|
4243
|
+
const v = verbIndex(words, start + 1, BUNDLE_VALUE_FLAGS);
|
|
4244
|
+
if (v < 0 || lower(words[v]) !== "add") return;
|
|
4245
|
+
if (flagValues(words, v + 1, ["--git", "--github", "--path"]).length > 0) return;
|
|
4246
|
+
const version = exactVersion(flagValues(words, v + 1, ["-v", "--version"])[0]);
|
|
4247
|
+
for (const raw of operands(words, v + 1, BUNDLE_VALUE_FLAGS))
|
|
4248
|
+
if (GEM_NAME_RE.test(raw)) push(out, "RubyGems", "bundle add", raw, version, raw);
|
|
4249
|
+
return;
|
|
4250
|
+
}
|
|
4251
|
+
case "cargo": {
|
|
4252
|
+
const from = words[start + 1]?.startsWith("+") ? start + 2 : start + 1;
|
|
4253
|
+
const v = verbIndex(words, from, CARGO_ADD_VALUE_FLAGS);
|
|
4254
|
+
const verb = lower(words[v]);
|
|
4255
|
+
if (verb === "add") cargoCrates(words, v + 1, "cargo add", out);
|
|
4256
|
+
else if (verb === "install") cargoCrates(words, v + 1, "cargo install", out);
|
|
4257
|
+
return;
|
|
4258
|
+
}
|
|
4259
|
+
case "go": {
|
|
4260
|
+
const v = verbIndex(words, start + 1, GO_VALUE_FLAGS);
|
|
4261
|
+
const verb = lower(words[v]);
|
|
4262
|
+
if (verb === "get" || verb === "install" || verb === "run")
|
|
4263
|
+
goModules(words, v + 1, `go ${verb}`, out);
|
|
4264
|
+
return;
|
|
4265
|
+
}
|
|
4266
|
+
case "dotnet": {
|
|
4267
|
+
const v = verbIndex(words, start + 1, DOTNET_ADD_VALUE_FLAGS);
|
|
4268
|
+
const verb = lower(words[v]);
|
|
4269
|
+
if (verb === "add") dotnetAdd(words, v + 1, out);
|
|
4270
|
+
else if (verb === "tool" && ["install", "update"].includes(lower(words[v + 1])))
|
|
4271
|
+
dotnetTool(words, v + 2, out);
|
|
4272
|
+
return;
|
|
4273
|
+
}
|
|
4274
|
+
case "nuget": {
|
|
4275
|
+
if (lower(words[start + 1]) === "install") nugetInstall(words, start + 2, out);
|
|
4276
|
+
return;
|
|
4277
|
+
}
|
|
4278
|
+
case "mvn":
|
|
4279
|
+
case "mvnw":
|
|
4280
|
+
mavenGet(words, start + 1, out);
|
|
4281
|
+
return;
|
|
4282
|
+
case "composer": {
|
|
4283
|
+
let v = verbIndex(words, start + 1, COMPOSER_VALUE_FLAGS);
|
|
4284
|
+
if (v >= 0 && lower(words[v]) === "global") v = verbIndex(words, v + 1, COMPOSER_VALUE_FLAGS);
|
|
4285
|
+
const verb = lower(words[v]);
|
|
4286
|
+
if (verb === "require" || verb === "req" || verb === "r")
|
|
4287
|
+
composerPackages(words, v + 1, "composer require", out);
|
|
4288
|
+
else if (verb === "create-project")
|
|
4289
|
+
composerPackages(words, v + 1, "composer create-project", out);
|
|
4290
|
+
return;
|
|
4291
|
+
}
|
|
3756
4292
|
default:
|
|
4293
|
+
if (EDITOR_HEADS.includes(family)) editorExtensions(words, start + 1, out);
|
|
3757
4294
|
return;
|
|
3758
4295
|
}
|
|
3759
4296
|
}
|
|
@@ -5220,12 +5757,13 @@ function gitRepoPath(url) {
|
|
|
5220
5757
|
function isDestructiveToolName(bareTool) {
|
|
5221
5758
|
return DESTRUCTIVE_VERB_RE.test(bareTool);
|
|
5222
5759
|
}
|
|
5223
|
-
function* validCardRuns(text, base2) {
|
|
5760
|
+
function* validCardRuns(text, base2, exemptFractions = true) {
|
|
5224
5761
|
const re = new RegExp(base2.source, "g");
|
|
5225
5762
|
let m;
|
|
5226
5763
|
while ((m = re.exec(text)) !== null) {
|
|
5227
5764
|
const digits = m[0].replace(/\D/g, "");
|
|
5228
|
-
|
|
5765
|
+
const fraction = exemptFractions && isDecimalFraction(text, m.index, digits.length === m[0].length);
|
|
5766
|
+
if (!fraction && validateLuhn(digits)) {
|
|
5229
5767
|
yield { index: m.index, length: m[0].length, digits };
|
|
5230
5768
|
} else {
|
|
5231
5769
|
re.lastIndex = m.index + 1;
|
|
@@ -5238,12 +5776,12 @@ function hasValidCard(text) {
|
|
|
5238
5776
|
}
|
|
5239
5777
|
return false;
|
|
5240
5778
|
}
|
|
5241
|
-
function replaceValidCards(text, replace) {
|
|
5779
|
+
function replaceValidCards(text, replace, { exemptFractions = true } = {}) {
|
|
5242
5780
|
let out = text;
|
|
5243
5781
|
for (const base2 of [PII_CC16_RE, PII_CC15_RE]) {
|
|
5244
5782
|
let res = "";
|
|
5245
5783
|
let last = 0;
|
|
5246
|
-
for (const run of validCardRuns(out, base2)) {
|
|
5784
|
+
for (const run of validCardRuns(out, base2, exemptFractions)) {
|
|
5247
5785
|
res += out.slice(last, run.index) + replace(run.digits);
|
|
5248
5786
|
last = run.index + run.length;
|
|
5249
5787
|
}
|
|
@@ -5257,11 +5795,11 @@ function isDecimalFraction(text, index, unseparated) {
|
|
|
5257
5795
|
const a = text[index - 2];
|
|
5258
5796
|
if (sep === ".") {
|
|
5259
5797
|
if (a !== void 0 && a >= "0" && a <= "9") return true;
|
|
5260
|
-
return a === void 0 ||
|
|
5798
|
+
return a === void 0 || (a === "-" || a === "+") && index === 2;
|
|
5261
5799
|
}
|
|
5262
5800
|
if (sep === "," && a === "0") {
|
|
5263
5801
|
const b = text[index - 3];
|
|
5264
|
-
return b === void 0 ||
|
|
5802
|
+
return b === void 0 || b === ";";
|
|
5265
5803
|
}
|
|
5266
5804
|
return false;
|
|
5267
5805
|
}
|
|
@@ -5397,6 +5935,9 @@ function normalizeTrustedEntry(raw) {
|
|
|
5397
5935
|
return full === null ? null : trustedEntryOf(full);
|
|
5398
5936
|
}
|
|
5399
5937
|
function maskPersonalData(text) {
|
|
5938
|
+
return maskText(text, false);
|
|
5939
|
+
}
|
|
5940
|
+
function maskText(text, exemptFractions) {
|
|
5400
5941
|
if (!text) return text;
|
|
5401
5942
|
try {
|
|
5402
5943
|
let out = text;
|
|
@@ -5404,7 +5945,9 @@ function maskPersonalData(text) {
|
|
|
5404
5945
|
const v = m.replace(/[ -]/g, "").toUpperCase();
|
|
5405
5946
|
return validateIban(v) ? `${v.slice(0, 4)} **** **** ${v.slice(-4)}` : m;
|
|
5406
5947
|
});
|
|
5407
|
-
out = replaceValidCards(out, (digits) => `**** **** **** ${digits.slice(-4)}
|
|
5948
|
+
out = replaceValidCards(out, (digits) => `**** **** **** ${digits.slice(-4)}`, {
|
|
5949
|
+
exemptFractions
|
|
5950
|
+
});
|
|
5408
5951
|
out = out.replace(
|
|
5409
5952
|
ACCOUNT_VALUE_RE,
|
|
5410
5953
|
(m, v) => IBAN_LENGTH[v.slice(0, 2).toUpperCase()] === v.length ? `"****${v.slice(-4)}"` : m
|
|
@@ -5427,7 +5970,7 @@ function maskLeaf(value, key, depth) {
|
|
|
5427
5970
|
if (depth > MAX_MASK_DEPTH) return "[REDACTED]";
|
|
5428
5971
|
if (typeof value === "string") {
|
|
5429
5972
|
try {
|
|
5430
|
-
const json = `{${JSON.stringify(key)}:${JSON.stringify(value)}}`;
|
|
5973
|
+
const json = `{${JSON.stringify(key)}:${JSON.stringify(maskPersonalData(value))}}`;
|
|
5431
5974
|
return Object.values(JSON.parse(maskPersonalData(json)))[0];
|
|
5432
5975
|
} catch {
|
|
5433
5976
|
return "[REDACTED]";
|
|
@@ -5435,13 +5978,20 @@ function maskLeaf(value, key, depth) {
|
|
|
5435
5978
|
}
|
|
5436
5979
|
if (typeof value === "number") {
|
|
5437
5980
|
const text = String(value);
|
|
5438
|
-
const masked =
|
|
5981
|
+
const masked = maskText(text, true);
|
|
5439
5982
|
return masked === text ? value : masked;
|
|
5440
5983
|
}
|
|
5441
5984
|
if (Array.isArray(value)) return value.map((v) => maskLeaf(v, key, depth + 1));
|
|
5442
5985
|
if (value && typeof value === "object") {
|
|
5986
|
+
const used = /* @__PURE__ */ new Set();
|
|
5443
5987
|
return Object.fromEntries(
|
|
5444
|
-
Object.entries(value).map(([k, v]) =>
|
|
5988
|
+
Object.entries(value).map(([k, v]) => {
|
|
5989
|
+
const base2 = maskPersonalData(k);
|
|
5990
|
+
let name = base2;
|
|
5991
|
+
for (let n = 2; used.has(name); n++) name = `${base2}#${n}`;
|
|
5992
|
+
used.add(name);
|
|
5993
|
+
return [name, maskLeaf(v, k, depth + 1)];
|
|
5994
|
+
})
|
|
5445
5995
|
);
|
|
5446
5996
|
}
|
|
5447
5997
|
return value;
|
|
@@ -5923,7 +6473,7 @@ function matchCanaryArgs(args, values) {
|
|
|
5923
6473
|
return null;
|
|
5924
6474
|
}
|
|
5925
6475
|
}
|
|
5926
|
-
var aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, BUILTIN_SHIELDS, SSRF_MAX_HOST, METADATA_ADDRESSES, STRICT_TIERS, METADATA_HOSTNAMES, v4Octets, TIER_REASON, CHECK_TEXT, VERDICTS, CHECK_VERDICT_RANK, ALL, BUILTIN_DANGEROUS_WORDS, PRODUCT_CHECKS, SHIELD_RULE_NAME, SHIELD_RULES_FOLDED_INTO_PRODUCT, CHECKS, CHECK_BY_ID, LEGACY_TO_ID, PROJECT_JAIL_BLOCK_READ, OVERRIDE_PREFIX, LABEL_WRAPPERS, LEGACY_READERS, syntax, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, AST_CACHE_MAX, astCache, PARSE_FAIL, defaultExpansionOps, VARIABLE_READERS, FS_READ_TOOLS, GREP_SHAPE, PATTERN_VERBS, PATTERN_VERB_NAMES, GREP_FILE_OPERANDS, READER_VALUE_LETTERS, FILE_OPERAND_FLAGS, SCP_VALUE_FLAGS, TAR_VALUE_LETTERS, ZIP_VALUE_LETTERS, RSYNC_VALUE_LETTERS, RSYNC_SKIP, CP_VALUE_LETTERS, INSTALL_VALUE_LETTERS, COPY_VERBS, TAR_MODE_WORD, COPY_VERB_HEADS, FS_OP_PRESCREEN_RE, HOME_CACHE_ALLOWLIST, SENSITIVE_PATH_RULES, AST_FS_REGEX_RULES, SQL_DB_CLIS, SQL_DDL_RE, CHMOD_OPEN_PERM_TOKENS, COMMAND_WRAPPERS, INLINE_INTERPRETER, RUNNER_WRAPPERS, _redirStdinOps, _listOps, WRAPPER_TAKES_TARGET, FIND_EXEC_FLAGS, FS_MUTATION_TOOLS, INTERP_LEADING_TARGET, NET_BINARIES, VALUE_FLAGS, HOME_VARIABLES, assignmentTable, currentStmtOffset, PAYLOAD_BUDGET, payloadBudget, seenPayloads, UNANALYSABLE_NESTING, ASSIGNMENT_HEADS, AND_OP, OR_OP, AND_OR_OPS, FS_OP_CACHE_MAX, fsOpCache, stripDotSlash, REDIR_TRUNCATE_OPS, REDIR_FILE_IN_OPS, REDIR_HEREDOC_OPS, FIND_OPTIONS, COPY_RULE_OF, isReaderWord, positionalAfter, NONE, UNKNOWN, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, FORBIDDEN_PATH_SEGMENTS, BASH_TOOL_NAMES, MCP_EXECUTORS, BUILTIN_FIELDS, ARGV_FIELDS, BARE_WORD, MAX_ARGV_CHARACTERS, syntax2, MAX_ALTERNATIVES, MAX_DEPTH, MAX_PAYLOAD_DEPTH, unique, clone, base, quoteGlob, asWord, WRITES, READS, DUP_OUT, HERE_STRING, AND, OR, DEFAULT_OPS, SYSTEM_AUTH_FILES, SYSTEM_AUTH_DIRS, PASSWORD_HASH_FILES, STARTUP_NAMES, AGENT_INSTRUCTION_FILES, AGENT_INSTRUCTION_DIRS, LEGACY_SYSTEM_PATH_RE, RANK, BIP39_ENGLISH, BIP39_PUBLIC_VECTORS, indices, lengths, hash, MAX_MNEMONIC_HASHES, IBAN_LENGTH, B58, B58_INDEX, XPRV_VERSIONS, B62, CRC32_TABLE, GITHUB_TOKEN_RE, B64URL, B64URL_INDEX, CASK_TWO_ZERO_SUFFIX, CASK_FOUR_ZERO_SUFFIX, INVISIBLE_RE, INVISIBLE_ONE, HOMOGLYPHS, HOMOGLYPH_RE, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, REFERENCE_VALUE_RE, IDENTIFIER_VALUE_RE, PATH_VALUE_RE, DOTTED_REFERENCE_RE, EMAIL_VALUE_RE, FIXTURE_NAME_RE, DERIVATIVE_NAME_RE, QUERY_CREDENTIAL_VALUE_RE, OTP_SECRET_RE, ASSIGNED_SECRET_VALUE_RE, ASSIGNED_SECRET_NAME_RE, NAME_KEYWORD_RE, ARN_CONTEXT_RE, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, SENSITIVE_PATH_REGEXES, MAX_DEPTH2, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, BATCH_SEPARATOR, syntax3, NPM_INSTALL_VERBS, NPM_EXEC_VERBS, PNPM_INSTALL_VERBS, YARN_INSTALL_VERBS, BUN_INSTALL_VERBS, DLX_VERBS, NON_REGISTRY_RE, NPM_NAME_RE, EXACT_SEMVER_RE, NPM_VALUE_FLAGS, PNPM_VALUE_FLAGS, YARN_VALUE_FLAGS, BUN_VALUE_FLAGS, PIP_VALUE_FLAGS, UV_VALUE_FLAGS, POETRY_VALUE_FLAGS, PIPENV_VALUE_FLAGS, PIPX_VALUE_FLAGS, PY_SPEC_RE, PY_EXACT_RE, MANAGER_HEADS, SHELLS, MAX_NESTING, SHELL_VALUE_FLAGS, TUNING_FIELDS, MAP_GOVERNED, KNOB_GOVERNED, CONFIGURABLE_CHECK_IDS, DEFAULT_EGRESS_ALLOWLIST, PRIVATE_HOST_SUFFIXES, SOURCE_COMMANDS, SINK_COMMANDS, OBFUSCATORS, SENSITIVE_PATTERNS, FLAGS_WITH_VALUES, DESTINATION_ARGS, SECRET_VIEW_LIMITS, ARGUMENT_SCAN_LIMITS, RUN_CHAR, runChar, VERDICT_RANK, SQL_DML_KEYWORDS, MAX_DESTINATIONS, MAX_SCAN_CHARS, EMAIL_RE, URL_RE, IBAN_RE, RECIPIENT_KEYS, DESTRUCTIVE_VERB_RE, PII_EMAIL_RE, PII_SSN_RE, PII_PHONE_RE, PII_CC16_RE, PII_CC15_RE, PII_IBAN_RE,
|
|
6476
|
+
var aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, BUILTIN_SHIELDS, SSRF_MAX_HOST, METADATA_ADDRESSES, STRICT_TIERS, METADATA_HOSTNAMES, v4Octets, TIER_REASON, CHECK_TEXT, VERDICTS, CHECK_VERDICT_RANK, ALL, BUILTIN_DANGEROUS_WORDS, PRODUCT_CHECKS, SHIELD_RULE_NAME, SHIELD_RULES_FOLDED_INTO_PRODUCT, CHECKS, CHECK_BY_ID, LEGACY_TO_ID, PROJECT_JAIL_BLOCK_READ, OVERRIDE_PREFIX, LABEL_WRAPPERS, LEGACY_READERS, syntax, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, AST_CACHE_MAX, astCache, PARSE_FAIL, defaultExpansionOps, VARIABLE_READERS, FS_READ_TOOLS, GREP_SHAPE, PATTERN_VERBS, PATTERN_VERB_NAMES, GREP_FILE_OPERANDS, READER_VALUE_LETTERS, FILE_OPERAND_FLAGS, SCP_VALUE_FLAGS, TAR_VALUE_LETTERS, ZIP_VALUE_LETTERS, RSYNC_VALUE_LETTERS, RSYNC_SKIP, CP_VALUE_LETTERS, INSTALL_VALUE_LETTERS, COPY_VERBS, TAR_MODE_WORD, COPY_VERB_HEADS, FS_OP_PRESCREEN_RE, HOME_CACHE_ALLOWLIST, SENSITIVE_PATH_RULES, AST_FS_REGEX_RULES, SQL_DB_CLIS, SQL_DDL_RE, CHMOD_OPEN_PERM_TOKENS, COMMAND_WRAPPERS, INLINE_INTERPRETER, RUNNER_WRAPPERS, _redirStdinOps, _listOps, WRAPPER_TAKES_TARGET, FIND_EXEC_FLAGS, FS_MUTATION_TOOLS, INTERP_LEADING_TARGET, NET_BINARIES, VALUE_FLAGS, HOME_VARIABLES, assignmentTable, currentStmtOffset, PAYLOAD_BUDGET, payloadBudget, seenPayloads, UNANALYSABLE_NESTING, ASSIGNMENT_HEADS, AND_OP, OR_OP, AND_OR_OPS, MAX_CALL_DEPTH, MAX_CALL_WALKS, callDepth, callWalks, FS_OP_CACHE_MAX, fsOpCache, stripDotSlash, REDIR_TRUNCATE_OPS, REDIR_FILE_IN_OPS, REDIR_HEREDOC_OPS, FIND_OPTIONS, COPY_RULE_OF, isReaderWord, positionalAfter, NONE, UNKNOWN, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, FORBIDDEN_PATH_SEGMENTS, BASH_TOOL_NAMES, MCP_EXECUTORS, BUILTIN_FIELDS, ARGV_FIELDS, BARE_WORD, MAX_ARGV_CHARACTERS, syntax2, MAX_ALTERNATIVES, MAX_CALL_DEPTH2, MAX_CALL_WALKS2, POSITIONAL, MAX_DEPTH, MAX_PAYLOAD_DEPTH, unique, clone, SENDING_STDIN, DOWNLOAD_SHORT_VALUE, DOWNLOAD_LONG_VALUE, DOWNLOAD_OUTPUT_FLAGS, DOWNLOAD_DIR_FLAGS, base, quoteGlob, asWord, WRITES, READS, DUP_OUT, HERE_STRING, AND, OR, DEFAULT_OPS, SYSTEM_AUTH_FILES, SYSTEM_AUTH_DIRS, PASSWORD_HASH_FILES, STARTUP_NAMES, SYSTEM_DIRS, SYSTEM_DIRS_EXEMPT, UPLOAD_SYSTEM_DIRS, AGENT_INSTRUCTION_FILES, AGENT_INSTRUCTION_DIRS, LEGACY_SYSTEM_PATH_RE, exempt, RANK, BIP39_ENGLISH, BIP39_PUBLIC_VECTORS, indices, lengths, hash, MAX_MNEMONIC_HASHES, IBAN_LENGTH, B58, B58_INDEX, XPRV_VERSIONS, B62, CRC32_TABLE, GITHUB_TOKEN_RE, B64URL, B64URL_INDEX, CASK_TWO_ZERO_SUFFIX, CASK_FOUR_ZERO_SUFFIX, INVISIBLE_RE, INVISIBLE_ONE, HOMOGLYPHS, HOMOGLYPH_RE, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, REFERENCE_VALUE_RE, IDENTIFIER_VALUE_RE, PATH_VALUE_RE, DOTTED_REFERENCE_RE, CONCATENATION_RE, EMAIL_VALUE_RE, FIXTURE_NAME_RE, DERIVATIVE_NAME_RE, QUERY_CREDENTIAL_VALUE_RE, OTP_SECRET_RE, OTP_DOC_SECRETS, ASSIGNED_SECRET_VALUE_RE, ASSIGNED_SECRET_NAME_RE, NAME_KEYWORD_RE, ARN_CONTEXT_RE, BASIC_VALUE_RE, BASIC_PAIR_RE, OTP_CAPTURE_RE, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, SENSITIVE_PATH_REGEXES, MAX_DEPTH2, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, BATCH_SEPARATOR, syntax3, NPM_INSTALL_VERBS, NPM_EXEC_VERBS, PNPM_INSTALL_VERBS, YARN_INSTALL_VERBS, BUN_INSTALL_VERBS, DLX_VERBS, NON_REGISTRY_RE, NPM_NAME_RE, EXACT_SEMVER_RE, NPM_VALUE_FLAGS, PNPM_VALUE_FLAGS, YARN_VALUE_FLAGS, BUN_VALUE_FLAGS, PIP_VALUE_FLAGS, UV_VALUE_FLAGS, POETRY_VALUE_FLAGS, PIPENV_VALUE_FLAGS, PIPX_VALUE_FLAGS, PY_SPEC_RE, PY_EXACT_RE, EXACT_VERSION_RE, GEM_NAME_RE, GEM_VALUE_FLAGS, BUNDLE_VALUE_FLAGS, CRATE_NAME_RE, CARGO_ADD_VALUE_FLAGS, CARGO_INSTALL_VALUE_FLAGS, GO_VALUE_FLAGS, GO_PATH_RE, NUGET_NAME_RE, DOTNET_ADD_VALUE_FLAGS, DOTNET_TOOL_VALUE_FLAGS, NUGET_VALUE_FLAGS, MAVEN_COORD_RE, COMPOSER_NAME_RE, COMPOSER_VALUE_FLAGS, EDITOR_HEADS, EXTENSION_ID_RE, MANAGER_HEADS, SHELLS, MAX_NESTING, SHELL_VALUE_FLAGS, TUNING_FIELDS, MAP_GOVERNED, KNOB_GOVERNED, CONFIGURABLE_CHECK_IDS, DEFAULT_EGRESS_ALLOWLIST, PRIVATE_HOST_SUFFIXES, SOURCE_COMMANDS, SINK_COMMANDS, OBFUSCATORS, SENSITIVE_PATTERNS, FLAGS_WITH_VALUES, DESTINATION_ARGS, SECRET_VIEW_LIMITS, ARGUMENT_SCAN_LIMITS, RUN_CHAR, runChar, VERDICT_RANK, SQL_DML_KEYWORDS, MAX_DESTINATIONS, MAX_SCAN_CHARS, EMAIL_RE, URL_RE, IBAN_RE, RECIPIENT_KEYS, DESTRUCTIVE_VERB_RE, PII_EMAIL_RE, PII_SSN_RE, PII_PHONE_RE, PII_CC16_RE, PII_CC15_RE, PII_IBAN_RE, REALTIME_PII_PATTERNS, MAX_PII_SCAN_BYTES, MASKED_KINDS, ENTRY_RE, IBAN_TEXT_RE, EMAIL_TEXT_RE, ACCOUNT_KEY_RE, ACCOUNT_VALUE_RE, MAX_MASK_DEPTH, syntax4, EMPTY, CURL_SEND_FLAGS, WGET_SEND_FLAGS, IWR_SEND_FLAGS, WRITE_METHODS, GH_READ, GH_WRITE, GH_HOSTNAME_FLAGS, GH_REPO_VALUE_FLAGS, GH_API_VALUE_FLAGS, GIT_FETCH, PUBLISH, SHELLS2, OWN_HEADS, MAX_DEPTH3, CWD_CHANGERS, TERMINAL_ESCAPE_RE, LOOP_MAX_RECORDS, CANARY_MIN_LENGTH, MAX_TEXT, MAX_DEPTH4, MAX_JSON_PARSE, URL_DEPTH, B64_DEPTH, MIN_SEGMENT, SEPARATORS, stripSeparators, looksText, CANARY_DECODERS, VIEWS, LONG_OUTPUT_THRESHOLD_BYTES;
|
|
5927
6477
|
var init_dist = __esm({
|
|
5928
6478
|
"packages/policy-engine/dist/index.mjs"() {
|
|
5929
6479
|
"use strict";
|
|
@@ -6724,6 +7274,11 @@ var init_dist = __esm({
|
|
|
6724
7274
|
example: "cat /etc/shadow",
|
|
6725
7275
|
advice: "Keep Review, or choose Block when agents must never read system password hashes."
|
|
6726
7276
|
},
|
|
7277
|
+
"files.system-dirs": {
|
|
7278
|
+
plain: "The agent writes, creates or deletes a file in a system directory, or sends a system file in a request. /usr/local and a container working directory under /usr/src are ordinary project space and are not included.",
|
|
7279
|
+
example: "wget -O /usr/bin/tool https://example.com/tool",
|
|
7280
|
+
advice: "Keep Review on a workstation. A container image build that installs into /usr can set it to Log."
|
|
7281
|
+
},
|
|
6727
7282
|
// ── Commands ──────────────────────────────────────────────────────────────
|
|
6728
7283
|
"commands.inline-exec": {
|
|
6729
7284
|
plain: "The agent runs a small program written inside the command itself.",
|
|
@@ -6920,7 +7475,7 @@ var init_dist = __esm({
|
|
|
6920
7475
|
advice: "Fixed at Block. A person accepts the change on the machine (node9 mcp pin update)."
|
|
6921
7476
|
},
|
|
6922
7477
|
"loading.malicious-package": {
|
|
6923
|
-
plain: "The agent installs a package that the public malicious-package database (OSV) lists, or one published minutes ago.",
|
|
7478
|
+
plain: "The agent installs a package that the public malicious-package database (OSV) lists, or one published minutes ago. Covers npm, PyPI, RubyGems, crates.io, Go, NuGet, Maven, Packagist and VS Code extensions; the publish-age and install-script signals are npm and PyPI only.",
|
|
6924
7479
|
example: "npm install of a package flagged as malware",
|
|
6925
7480
|
advice: "Keep Block for known-malicious packages; brand-new packages already stop for Review."
|
|
6926
7481
|
}
|
|
@@ -6958,6 +7513,15 @@ var init_dist = __esm({
|
|
|
6958
7513
|
defaultValue: "review",
|
|
6959
7514
|
legacy: ["review-password-hash-read"]
|
|
6960
7515
|
},
|
|
7516
|
+
{
|
|
7517
|
+
id: "files.system-dirs",
|
|
7518
|
+
group: "files",
|
|
7519
|
+
title: "System directories",
|
|
7520
|
+
catches: "Writes, new files and removals under /usr, /etc, /boot, /bin, /sbin, /lib and /var/log, and uploads of system files",
|
|
7521
|
+
values: ALL,
|
|
7522
|
+
defaultValue: "review",
|
|
7523
|
+
legacy: ["review-system-dir-mutation", "review-system-file-upload"]
|
|
7524
|
+
},
|
|
6961
7525
|
// ── Commands ────────────────────────────────────────────────────────────
|
|
6962
7526
|
{
|
|
6963
7527
|
id: "commands.inline-exec",
|
|
@@ -8255,6 +8819,10 @@ var init_dist = __esm({
|
|
|
8255
8819
|
AND_OP = probeBinOp("a && b");
|
|
8256
8820
|
OR_OP = probeBinOp("a || b");
|
|
8257
8821
|
AND_OR_OPS = new Set([AND_OP, OR_OP].filter((o) => o !== null));
|
|
8822
|
+
MAX_CALL_DEPTH = 4;
|
|
8823
|
+
MAX_CALL_WALKS = 256;
|
|
8824
|
+
callDepth = 0;
|
|
8825
|
+
callWalks = 0;
|
|
8258
8826
|
FS_OP_CACHE_MAX = 5e3;
|
|
8259
8827
|
fsOpCache = /* @__PURE__ */ new Map();
|
|
8260
8828
|
stripDotSlash = (p) => p.replace(/^\.\//, "");
|
|
@@ -8307,10 +8875,79 @@ var init_dist = __esm({
|
|
|
8307
8875
|
MAX_ARGV_CHARACTERS = 262144;
|
|
8308
8876
|
({ syntax: syntax2 } = mvdanSh2);
|
|
8309
8877
|
MAX_ALTERNATIVES = 64;
|
|
8878
|
+
MAX_CALL_DEPTH2 = 4;
|
|
8879
|
+
MAX_CALL_WALKS2 = 256;
|
|
8880
|
+
POSITIONAL = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "@", "*", "#"];
|
|
8310
8881
|
MAX_DEPTH = 64;
|
|
8311
8882
|
MAX_PAYLOAD_DEPTH = 24;
|
|
8312
8883
|
unique = (a) => [...new Set(a)].slice(0, MAX_ALTERNATIVES);
|
|
8313
|
-
clone = (s) => ({
|
|
8884
|
+
clone = (s) => ({
|
|
8885
|
+
cwd: [...s.cwd],
|
|
8886
|
+
vars: new Map(s.vars),
|
|
8887
|
+
...s.callDepth ? { callDepth: s.callDepth } : {}
|
|
8888
|
+
});
|
|
8889
|
+
SENDING_STDIN = /* @__PURE__ */ new Set(["nc", "ncat", "netcat", "socat", "telnet", "curl", "wget"]);
|
|
8890
|
+
DOWNLOAD_SHORT_VALUE = {
|
|
8891
|
+
curl: "odDeEFHKmPrtTuUwxXyYzAbcCQ",
|
|
8892
|
+
wget: "OoaPiBetTwQlARDXIUY",
|
|
8893
|
+
aria2c: "odisx"
|
|
8894
|
+
};
|
|
8895
|
+
DOWNLOAD_LONG_VALUE = {
|
|
8896
|
+
curl: [
|
|
8897
|
+
"--output",
|
|
8898
|
+
"--output-dir",
|
|
8899
|
+
"--dump-header",
|
|
8900
|
+
"--cookie-jar",
|
|
8901
|
+
"--data",
|
|
8902
|
+
"--data-binary",
|
|
8903
|
+
"--data-raw",
|
|
8904
|
+
"--data-ascii",
|
|
8905
|
+
"--data-urlencode",
|
|
8906
|
+
"--form",
|
|
8907
|
+
"--header",
|
|
8908
|
+
"--request",
|
|
8909
|
+
"--user",
|
|
8910
|
+
"--user-agent",
|
|
8911
|
+
"--referer",
|
|
8912
|
+
"--cookie",
|
|
8913
|
+
"--config",
|
|
8914
|
+
"--json",
|
|
8915
|
+
"--upload-file",
|
|
8916
|
+
"--max-time",
|
|
8917
|
+
"--proxy",
|
|
8918
|
+
"--write-out",
|
|
8919
|
+
"--url"
|
|
8920
|
+
],
|
|
8921
|
+
wget: [
|
|
8922
|
+
"--output-document",
|
|
8923
|
+
"--output-file",
|
|
8924
|
+
"--append-output",
|
|
8925
|
+
"--directory-prefix",
|
|
8926
|
+
"--input-file",
|
|
8927
|
+
"--post-data",
|
|
8928
|
+
"--post-file",
|
|
8929
|
+
"--body-data",
|
|
8930
|
+
"--body-file",
|
|
8931
|
+
"--method",
|
|
8932
|
+
"--header",
|
|
8933
|
+
"--user-agent",
|
|
8934
|
+
"--user",
|
|
8935
|
+
"--password",
|
|
8936
|
+
"--tries",
|
|
8937
|
+
"--timeout"
|
|
8938
|
+
],
|
|
8939
|
+
aria2c: ["--out", "--dir", "--input-file"]
|
|
8940
|
+
};
|
|
8941
|
+
DOWNLOAD_OUTPUT_FLAGS = {
|
|
8942
|
+
curl: ["-o", "--output", "-D", "--dump-header", "-c", "--cookie-jar"],
|
|
8943
|
+
wget: ["-O", "--output-document", "-o", "--output-file", "-a", "--append-output"],
|
|
8944
|
+
aria2c: ["-o", "--out"]
|
|
8945
|
+
};
|
|
8946
|
+
DOWNLOAD_DIR_FLAGS = {
|
|
8947
|
+
curl: ["--output-dir"],
|
|
8948
|
+
wget: ["-P", "--directory-prefix"],
|
|
8949
|
+
aria2c: ["-d", "--dir"]
|
|
8950
|
+
};
|
|
8314
8951
|
base = (s) => (s ?? "").replace(/\/+$/, "").split("/").pop() ?? "";
|
|
8315
8952
|
quoteGlob = (s) => s.replace(/[\\*?\[\]{}()!+@]/g, "\\$&");
|
|
8316
8953
|
asWord = (p) => ({ value: p.value, ...p.glob ? { pattern: p.pattern } : {} });
|
|
@@ -8335,6 +8972,18 @@ var init_dist = __esm({
|
|
|
8335
8972
|
SYSTEM_AUTH_DIRS = ["/etc/security", "/etc/pam.d", "/etc/sudoers.d"];
|
|
8336
8973
|
PASSWORD_HASH_FILES = ["/etc/shadow", "/etc/gshadow"];
|
|
8337
8974
|
STARTUP_NAMES = [".bashrc", ".zshrc", ".profile", ".bash_profile"];
|
|
8975
|
+
SYSTEM_DIRS = [
|
|
8976
|
+
"/usr",
|
|
8977
|
+
"/etc",
|
|
8978
|
+
"/boot",
|
|
8979
|
+
"/bin",
|
|
8980
|
+
"/sbin",
|
|
8981
|
+
"/lib",
|
|
8982
|
+
"/lib64",
|
|
8983
|
+
"/var/log"
|
|
8984
|
+
];
|
|
8985
|
+
SYSTEM_DIRS_EXEMPT = ["/usr/local", "/usr/src"];
|
|
8986
|
+
UPLOAD_SYSTEM_DIRS = [...SYSTEM_DIRS, "/proc", "/sys", "/root"];
|
|
8338
8987
|
AGENT_INSTRUCTION_FILES = [
|
|
8339
8988
|
".gitlab-ci.yml",
|
|
8340
8989
|
"CLAUDE.md",
|
|
@@ -8352,6 +9001,7 @@ var init_dist = __esm({
|
|
|
8352
9001
|
".husky"
|
|
8353
9002
|
];
|
|
8354
9003
|
LEGACY_SYSTEM_PATH_RE = /^(?:[a-zA-Z]:)?\/etc\/(?:passwd|shadow|sudoers)$/;
|
|
9004
|
+
exempt = (p) => SYSTEM_DIRS_EXEMPT.some((d) => within(p, d));
|
|
8355
9005
|
RANK = { allow: 0, review: 1, block: 2 };
|
|
8356
9006
|
BIP39_ENGLISH = `abandon
|
|
8357
9007
|
ability
|
|
@@ -10637,15 +11287,20 @@ zoo`.split("\n");
|
|
|
10637
11287
|
IDENTIFIER_VALUE_RE = /^[A-Za-z_.-]+$/;
|
|
10638
11288
|
PATH_VALUE_RE = /^(?:\/|\.{1,2}\/|~\/)/;
|
|
10639
11289
|
DOTTED_REFERENCE_RE = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)+$/;
|
|
11290
|
+
CONCATENATION_RE = /^\+[A-Za-z_$][\w$]*\./;
|
|
10640
11291
|
EMAIL_VALUE_RE = /^[^@\s]+@[^@\s]+\.[A-Za-z]{2,}$/;
|
|
10641
11292
|
FIXTURE_NAME_RE = /(?:fake|dummy|example|sample|mock|stub|placeholder|test)/i;
|
|
10642
|
-
DERIVATIVE_NAME_RE = /(?:hash|digest|id|type|name|url|uri|path|file|len|length|count|size|ttl|expires?|expiry|age|date|time|format|field|header|param)$/i;
|
|
11293
|
+
DERIVATIVE_NAME_RE = /(?:hash|digest|fingerprint|thumbprint|checksum|id|type|name|url|uri|path|file|len|length|count|size|ttl|expires?|expiry|age|date|time|format|field|header|param)$/i;
|
|
10643
11294
|
QUERY_CREDENTIAL_VALUE_RE = /=([A-Za-z0-9%+/_.~-]{8,512}={0,2})$/;
|
|
10644
11295
|
OTP_SECRET_RE = /^[A-Z2-7]{16,}=*$/i;
|
|
10645
|
-
|
|
11296
|
+
OTP_DOC_SECRETS = /* @__PURE__ */ new Set(["JBSWY3DPEHPK3PXP", "GEZDGNBVGY3TQOJQ"]);
|
|
11297
|
+
ASSIGNED_SECRET_VALUE_RE = /(?::=|=>|[:=])[ \t]{0,8}\\?["'`]?([A-Za-z0-9+/_.!@#$%^*~-]{12,512}={0,2})$/;
|
|
10646
11298
|
ASSIGNED_SECRET_NAME_RE = /^([A-Za-z0-9_]+)/;
|
|
10647
|
-
NAME_KEYWORD_RE = /^(?:password|passphrase|passwd|pwd|secret|token|api[_-]?key|auth[_-]?key|private[_-]?key|access[_-]?key|client[_-]?secret)/i;
|
|
11299
|
+
NAME_KEYWORD_RE = /^(?:password|passphrase|passwd|pwd|secret|token|api[_-]?key|auth[_-]?key|private[_-]?key|access[_-]?key|client[_-]?secret|consumer[_-]?key|app[_-]?key|signing[_-]?key|encryption[_-]?key|master[_-]?key|jsapi[_-]?ticket|pass)/i;
|
|
10648
11300
|
ARN_CONTEXT_RE = /\barn:[A-Za-z0-9-]*:[^\s"'`]*$/i;
|
|
11301
|
+
BASIC_VALUE_RE = /([A-Za-z0-9+/]{8,512}={0,2})$/;
|
|
11302
|
+
BASIC_PAIR_RE = /^([^:\s]{1,128}):(\S{8,256})$/;
|
|
11303
|
+
OTP_CAPTURE_RE = /["']([A-Z2-7]{16,128}=*)["']$/i;
|
|
10649
11304
|
DLP_PATTERNS = [
|
|
10650
11305
|
// ── AWS ───────────────────────────────────────────────────────────────────
|
|
10651
11306
|
{
|
|
@@ -10983,6 +11638,22 @@ zoo`.split("\n");
|
|
|
10983
11638
|
contextBoost: true,
|
|
10984
11639
|
minEntropy: 3
|
|
10985
11640
|
},
|
|
11641
|
+
// ── HTTP Basic credential ─────────────────────────────────────────────────
|
|
11642
|
+
// `Authorization: Basic <base64 of user:password>`, or the bare scheme in a
|
|
11643
|
+
// config list. The validator decodes the value and accepts only a
|
|
11644
|
+
// `user:password` whose password looks generated (letters and digits, 8+
|
|
11645
|
+
// characters, the query-credential rule), so prose ("Basic authentication")
|
|
11646
|
+
// and documentation pairs (`admin:changeme`, `user:pass1234`) stay allowed.
|
|
11647
|
+
// review: the same weight as a bare Bearer token.
|
|
11648
|
+
{
|
|
11649
|
+
name: "HTTP Basic Credential",
|
|
11650
|
+
regex: /\bBasic[ \t]{1,8}([A-Za-z0-9+/]{8,512}={0,2})/i,
|
|
11651
|
+
severity: "review",
|
|
11652
|
+
keywords: ["basic"],
|
|
11653
|
+
validate: validateBasicCredential,
|
|
11654
|
+
redactGroup: 1,
|
|
11655
|
+
argumentsOnly: true
|
|
11656
|
+
},
|
|
10986
11657
|
// ── Resend ────────────────────────────────────────────────────────────────
|
|
10987
11658
|
{
|
|
10988
11659
|
name: "Resend API Key",
|
|
@@ -11124,6 +11795,19 @@ zoo`.split("\n");
|
|
|
11124
11795
|
validate: validateQueryCredential,
|
|
11125
11796
|
argumentsOnly: true
|
|
11126
11797
|
},
|
|
11798
|
+
// ── OTP secret in a constructor ───────────────────────────────────────────
|
|
11799
|
+
// `ROTP::TOTP.new('<base32>')`, `pyotp.TOTP("<base32>")`, `new HOTP('...')`:
|
|
11800
|
+
// the shared secret of a one-time-password generator written as a literal.
|
|
11801
|
+
// The otpauth URI form is `Credential in URL Query`. review.
|
|
11802
|
+
{
|
|
11803
|
+
name: "OTP Secret",
|
|
11804
|
+
regex: /\b(?:TOTP|HOTP|OTP)(?:\.new|\.create)?[ \t]{0,8}\(?[ \t]{0,8}["']([A-Z2-7]{16,128}=*)["']/i,
|
|
11805
|
+
severity: "review",
|
|
11806
|
+
keywords: ["otp"],
|
|
11807
|
+
validate: validateOtpSecret,
|
|
11808
|
+
redactGroup: 1,
|
|
11809
|
+
argumentsOnly: true
|
|
11810
|
+
},
|
|
11127
11811
|
// ── Secret assigned to a telling name ─────────────────────────────────────
|
|
11128
11812
|
// `password: ...`, `JWT_SECRET=...`, `api_key = "..."`. The name alone is
|
|
11129
11813
|
// not a finding: most such lines hold a reference (`process.env.X`), a
|
|
@@ -11131,12 +11815,14 @@ zoo`.split("\n");
|
|
|
11131
11815
|
// generated: 12+ characters, not an identifier, Shannon entropy of 3.5
|
|
11132
11816
|
// bits per character or more. review, never block (node9Firewall#348,
|
|
11133
11817
|
// item 4). Tuned on CredData's public split; its look-alike lines are the
|
|
11134
|
-
// negative control.
|
|
11818
|
+
// negative control. A quote may be escaped (`\"access_token\":\"...\"`):
|
|
11819
|
+
// JSON written inside a string literal. The two optional quotes after the
|
|
11820
|
+
// name are written out, not as a counted group, to keep star height 1.
|
|
11135
11821
|
{
|
|
11136
11822
|
name: "Assigned Secret",
|
|
11137
|
-
regex: /(?:password|passphrase|passwd|pwd|secret|token|api[_-]?key|auth[_-]?key|private[_-]?key|access[_-]?key|client[_-]?secret)[A-Za-z0-9_]{0,40}["'\]]
|
|
11823
|
+
regex: /(?:password|passphrase|passwd|pwd|secret|token|api[_-]?key|auth[_-]?key|private[_-]?key|access[_-]?key|client[_-]?secret|consumer[_-]?key|app[_-]?key|signing[_-]?key|encryption[_-]?key|master[_-]?key|jsapi[_-]?ticket|\bpass(?![A-Za-z0-9])|_pass(?![A-Za-z0-9]))[A-Za-z0-9_]{0,40}\\?["'\]]?\\?["'\]]?[ \t]{0,8}(?::=|=>|[:=])[ \t]{0,8}\\?["'`]?([A-Za-z0-9+/_.!@#$%^*~-]{12,512}={0,2})/i,
|
|
11138
11824
|
severity: "review",
|
|
11139
|
-
keywords: ["
|
|
11825
|
+
keywords: ["pass", "pwd", "secret", "token", "key", "ticket"],
|
|
11140
11826
|
validate: validateAssignedSecret,
|
|
11141
11827
|
argumentsOnly: true
|
|
11142
11828
|
}
|
|
@@ -11397,7 +12083,169 @@ zoo`.split("\n");
|
|
|
11397
12083
|
]);
|
|
11398
12084
|
PY_SPEC_RE = /^([A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?)(?:\[[^\]]*\])?\s*(.*)$/;
|
|
11399
12085
|
PY_EXACT_RE = /^===?\s*v?(\d+(?:\.\d+)*(?:[a-zA-Z0-9.+!-]*))$/;
|
|
12086
|
+
EXACT_VERSION_RE = /^=?v?(\d+(?:\.\d+)*(?:[-+][0-9A-Za-z][0-9A-Za-z.+-]*)?)$/;
|
|
12087
|
+
GEM_NAME_RE = /^[\p{L}\p{N}][\p{L}\p{N}_.-]*$/u;
|
|
12088
|
+
GEM_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
12089
|
+
"-v",
|
|
12090
|
+
"--version",
|
|
12091
|
+
"-i",
|
|
12092
|
+
"--install-dir",
|
|
12093
|
+
"-n",
|
|
12094
|
+
"--bindir",
|
|
12095
|
+
"--document",
|
|
12096
|
+
"--build-root",
|
|
12097
|
+
"-P",
|
|
12098
|
+
"--trust-policy",
|
|
12099
|
+
"-s",
|
|
12100
|
+
"--source",
|
|
12101
|
+
"-p",
|
|
12102
|
+
"--http-proxy",
|
|
12103
|
+
"--platform",
|
|
12104
|
+
"-g",
|
|
12105
|
+
"--file",
|
|
12106
|
+
"--without"
|
|
12107
|
+
]);
|
|
12108
|
+
BUNDLE_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
12109
|
+
"-v",
|
|
12110
|
+
"--version",
|
|
12111
|
+
"-g",
|
|
12112
|
+
"--group",
|
|
12113
|
+
"-s",
|
|
12114
|
+
"--source",
|
|
12115
|
+
"-r",
|
|
12116
|
+
"--require",
|
|
12117
|
+
"--path",
|
|
12118
|
+
"--git",
|
|
12119
|
+
"--github",
|
|
12120
|
+
"--branch",
|
|
12121
|
+
"--ref",
|
|
12122
|
+
"--glob"
|
|
12123
|
+
]);
|
|
12124
|
+
CRATE_NAME_RE = /^[\p{L}\p{N}][\p{L}\p{N}_-]*$/u;
|
|
12125
|
+
CARGO_ADD_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
12126
|
+
"-F",
|
|
12127
|
+
"--features",
|
|
12128
|
+
"--rename",
|
|
12129
|
+
"--registry",
|
|
12130
|
+
"--manifest-path",
|
|
12131
|
+
"-p",
|
|
12132
|
+
"--package",
|
|
12133
|
+
"--git",
|
|
12134
|
+
"--branch",
|
|
12135
|
+
"--tag",
|
|
12136
|
+
"--rev",
|
|
12137
|
+
"--path",
|
|
12138
|
+
"--base",
|
|
12139
|
+
"--target",
|
|
12140
|
+
"--color",
|
|
12141
|
+
"--config",
|
|
12142
|
+
"-Z",
|
|
12143
|
+
"--lockfile-path"
|
|
12144
|
+
]);
|
|
12145
|
+
CARGO_INSTALL_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
12146
|
+
"--version",
|
|
12147
|
+
"--vers",
|
|
12148
|
+
"--git",
|
|
12149
|
+
"--branch",
|
|
12150
|
+
"--tag",
|
|
12151
|
+
"--rev",
|
|
12152
|
+
"--path",
|
|
12153
|
+
"--root",
|
|
12154
|
+
"--index",
|
|
12155
|
+
"--registry",
|
|
12156
|
+
"-F",
|
|
12157
|
+
"--features",
|
|
12158
|
+
"--profile",
|
|
12159
|
+
"--target",
|
|
12160
|
+
"--target-dir",
|
|
12161
|
+
"-j",
|
|
12162
|
+
"--jobs",
|
|
12163
|
+
"--bin",
|
|
12164
|
+
"--example",
|
|
12165
|
+
"--color",
|
|
12166
|
+
"--config",
|
|
12167
|
+
"-Z"
|
|
12168
|
+
]);
|
|
12169
|
+
GO_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
12170
|
+
"-C",
|
|
12171
|
+
"-tags",
|
|
12172
|
+
"-ldflags",
|
|
12173
|
+
"-gcflags",
|
|
12174
|
+
"-asmflags",
|
|
12175
|
+
"-mod",
|
|
12176
|
+
"-modfile",
|
|
12177
|
+
"-o",
|
|
12178
|
+
"-p",
|
|
12179
|
+
"-pkgdir",
|
|
12180
|
+
"-toolexec",
|
|
12181
|
+
"-exec",
|
|
12182
|
+
"-overlay",
|
|
12183
|
+
"-pgo",
|
|
12184
|
+
"-buildmode",
|
|
12185
|
+
"-compiler",
|
|
12186
|
+
"-gccgoflags",
|
|
12187
|
+
"-installsuffix",
|
|
12188
|
+
"-covermode",
|
|
12189
|
+
"-coverpkg"
|
|
12190
|
+
]);
|
|
12191
|
+
GO_PATH_RE = /^[\p{L}\p{N}-]+(?:\.[\p{L}\p{N}-]+)+(?:\/[\p{L}\p{N}_.~+-]+)*$/u;
|
|
12192
|
+
NUGET_NAME_RE = /^[\p{L}\p{N}_][\p{L}\p{N}_.-]*$/u;
|
|
12193
|
+
DOTNET_ADD_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
12194
|
+
"-v",
|
|
12195
|
+
"--version",
|
|
12196
|
+
"-s",
|
|
12197
|
+
"--source",
|
|
12198
|
+
"-f",
|
|
12199
|
+
"--framework",
|
|
12200
|
+
"--package-directory"
|
|
12201
|
+
]);
|
|
12202
|
+
DOTNET_TOOL_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
12203
|
+
"--version",
|
|
12204
|
+
"--add-source",
|
|
12205
|
+
"--configfile",
|
|
12206
|
+
"--framework",
|
|
12207
|
+
"--tool-path",
|
|
12208
|
+
"-a",
|
|
12209
|
+
"--arch",
|
|
12210
|
+
"--tool-manifest",
|
|
12211
|
+
"-v",
|
|
12212
|
+
"--verbosity"
|
|
12213
|
+
]);
|
|
12214
|
+
NUGET_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
12215
|
+
"-version",
|
|
12216
|
+
"-outputdirectory",
|
|
12217
|
+
"-o",
|
|
12218
|
+
"-source",
|
|
12219
|
+
"-configfile",
|
|
12220
|
+
"-fallbacksource",
|
|
12221
|
+
"-framework",
|
|
12222
|
+
"-packagesavemode",
|
|
12223
|
+
"-dependencyversion",
|
|
12224
|
+
"-solutiondirectory",
|
|
12225
|
+
"-verbosity"
|
|
12226
|
+
]);
|
|
12227
|
+
MAVEN_COORD_RE = /^[\p{L}\p{N}_.-]+:[\p{L}\p{N}_.-]+$/u;
|
|
12228
|
+
COMPOSER_NAME_RE = /^[\p{L}\p{N}][\p{L}\p{N}_.-]*\/[\p{L}\p{N}][\p{L}\p{N}_.-]*$/u;
|
|
12229
|
+
COMPOSER_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
12230
|
+
"-d",
|
|
12231
|
+
"--working-dir",
|
|
12232
|
+
"--stability",
|
|
12233
|
+
"--repository",
|
|
12234
|
+
"--add-repository"
|
|
12235
|
+
]);
|
|
12236
|
+
EDITOR_HEADS = ["code", "code-insiders", "codium", "vscodium", "cursor"];
|
|
12237
|
+
EXTENSION_ID_RE = /^[\p{L}\p{N}][\p{L}\p{N}_-]*\.[\p{L}\p{N}][\p{L}\p{N}_.-]*$/u;
|
|
11400
12238
|
MANAGER_HEADS = /* @__PURE__ */ new Set([
|
|
12239
|
+
"gem",
|
|
12240
|
+
"bundle",
|
|
12241
|
+
"cargo",
|
|
12242
|
+
"go",
|
|
12243
|
+
"dotnet",
|
|
12244
|
+
"nuget",
|
|
12245
|
+
"mvn",
|
|
12246
|
+
"mvnw",
|
|
12247
|
+
"composer",
|
|
12248
|
+
...EDITOR_HEADS,
|
|
11401
12249
|
"npm",
|
|
11402
12250
|
"npx",
|
|
11403
12251
|
"pnpm",
|
|
@@ -11476,6 +12324,7 @@ zoo`.split("\n");
|
|
|
11476
12324
|
MAP_GOVERNED = /* @__PURE__ */ new Set([
|
|
11477
12325
|
"files.shell-startup",
|
|
11478
12326
|
"files.password-hashes",
|
|
12327
|
+
"files.system-dirs",
|
|
11479
12328
|
"commands.inline-exec",
|
|
11480
12329
|
"commands.eval-dynamic",
|
|
11481
12330
|
"commands.curl-pipe-shell",
|
|
@@ -11744,7 +12593,6 @@ zoo`.split("\n");
|
|
|
11744
12593
|
PII_CC16_RE = /\b(?:4\d{3}|5[1-5]\d{2}|6\d{3})[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}\b/;
|
|
11745
12594
|
PII_CC15_RE = /\b3[47]\d{2}[-\s]?\d{6}[-\s]?\d{5}\b/;
|
|
11746
12595
|
PII_IBAN_RE = /\b[A-Z]{2}\d{2}(?:[A-Z0-9]|[ -][A-Z0-9]){11,30}\b/;
|
|
11747
|
-
NUMBER_LEAD_RE = /[\s:=;([{"'+-]/;
|
|
11748
12596
|
REALTIME_PII_PATTERNS = ["SSN", "Credit Card", "IBAN"];
|
|
11749
12597
|
MAX_PII_SCAN_BYTES = 1e5;
|
|
11750
12598
|
MASKED_KINDS = /* @__PURE__ */ new Set(["email", "iban", "account"]);
|
|
@@ -14599,14 +15447,23 @@ async function registerDaemonEntry(toolName, args, meta, riskMetadata, activityI
|
|
|
14599
15447
|
clearTimeout(timer);
|
|
14600
15448
|
}
|
|
14601
15449
|
}
|
|
14602
|
-
async function waitForDaemonDecision(id, signal) {
|
|
15450
|
+
async function waitForDaemonDecision(id, signal, maxWaitMs = 12e4) {
|
|
14603
15451
|
const base2 = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
|
|
14604
15452
|
const waitCtrl = new AbortController();
|
|
14605
|
-
|
|
15453
|
+
let capped = false;
|
|
15454
|
+
const waitTimer = maxWaitMs > 0 ? setTimeout(() => {
|
|
15455
|
+
capped = true;
|
|
15456
|
+
waitCtrl.abort();
|
|
15457
|
+
}, maxWaitMs) : void 0;
|
|
14606
15458
|
const onAbort = () => waitCtrl.abort();
|
|
14607
15459
|
if (signal) signal.addEventListener("abort", onAbort);
|
|
14608
15460
|
try {
|
|
14609
|
-
const waitRes = await fetch(`${base2}/wait/${id}`, { signal: waitCtrl.signal })
|
|
15461
|
+
const waitRes = await fetch(`${base2}/wait/${id}`, { signal: waitCtrl.signal }).catch(
|
|
15462
|
+
(err) => {
|
|
15463
|
+
if (capped && !signal?.aborted) throw new Error("node9 daemon did not answer in time");
|
|
15464
|
+
throw err;
|
|
15465
|
+
}
|
|
15466
|
+
);
|
|
14610
15467
|
if (!waitRes.ok) return { decision: "deny" };
|
|
14611
15468
|
const { decision, source, reason } = await waitRes.json();
|
|
14612
15469
|
if (decision === "allow") return { decision: "allow", source };
|
|
@@ -15647,13 +16504,13 @@ function maskRiskMetadataForCloud(rm) {
|
|
|
15647
16504
|
const r = rm;
|
|
15648
16505
|
const dests = Array.isArray(r.exposureDestinations) ? r.exposureDestinations.filter((d) => typeof d === "string") : [];
|
|
15649
16506
|
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);
|
|
15650
|
-
const
|
|
16507
|
+
const maskText2 = (text) => {
|
|
15651
16508
|
let out2 = text;
|
|
15652
16509
|
for (const [full, masked] of swaps) out2 = out2.split(full).join(masked);
|
|
15653
16510
|
return maskPersonalData(out2);
|
|
15654
16511
|
};
|
|
15655
16512
|
const out = {};
|
|
15656
|
-
for (const [k, v] of Object.entries(r)) out[k] = typeof v === "string" ?
|
|
16513
|
+
for (const [k, v] of Object.entries(r)) out[k] = typeof v === "string" ? maskText2(v) : v;
|
|
15657
16514
|
if (dests.length > 0) {
|
|
15658
16515
|
out.exposureDestinations = dests.map(maskDestination);
|
|
15659
16516
|
out.exposureDestinationHashes = dests.map((d) => isMaskedKind(d) ? hashDestination(d) : "");
|
|
@@ -15856,7 +16713,17 @@ function ecoDir(eco) {
|
|
|
15856
16713
|
return path23.join(osvRoot(), eco);
|
|
15857
16714
|
}
|
|
15858
16715
|
function indexKey(eco, name) {
|
|
15859
|
-
|
|
16716
|
+
switch (eco) {
|
|
16717
|
+
case "PyPI":
|
|
16718
|
+
return normalizePyPiName(name);
|
|
16719
|
+
case "crates.io":
|
|
16720
|
+
return name.toLowerCase().replace(/_/g, "-");
|
|
16721
|
+
case "Go":
|
|
16722
|
+
case "Maven":
|
|
16723
|
+
return name;
|
|
16724
|
+
default:
|
|
16725
|
+
return name.toLowerCase();
|
|
16726
|
+
}
|
|
15860
16727
|
}
|
|
15861
16728
|
function shardOf(key) {
|
|
15862
16729
|
return createHash4("sha1").update(key).digest("hex").slice(0, 2);
|
|
@@ -16067,8 +16934,11 @@ async function pypiInfo(name, version) {
|
|
|
16067
16934
|
publishedAtMs: times.length > 0 ? Math.min(...times) : void 0
|
|
16068
16935
|
};
|
|
16069
16936
|
}
|
|
16937
|
+
var HAS_REGISTRY_CLIENT = /* @__PURE__ */ new Set(["npm", "PyPI"]);
|
|
16070
16938
|
async function registryInfo(eco, name, version, freshWindowMs, raw) {
|
|
16071
|
-
|
|
16939
|
+
if (eco === "npm") return npmInfo(name, version, freshWindowMs, raw);
|
|
16940
|
+
if (eco === "PyPI") return pypiInfo(name, version);
|
|
16941
|
+
return null;
|
|
16072
16942
|
}
|
|
16073
16943
|
|
|
16074
16944
|
// src/supply-chain/local-resolve.ts
|
|
@@ -16107,26 +16977,51 @@ function commandOf(toolName, args) {
|
|
|
16107
16977
|
const a = args;
|
|
16108
16978
|
return typeof a.command === "string" ? a.command : typeof a.cmd === "string" ? a.cmd : null;
|
|
16109
16979
|
}
|
|
16980
|
+
var KIND = {
|
|
16981
|
+
npm: "npm package",
|
|
16982
|
+
PyPI: "PyPI package",
|
|
16983
|
+
RubyGems: "RubyGems package",
|
|
16984
|
+
"crates.io": "crates.io package",
|
|
16985
|
+
Go: "Go module",
|
|
16986
|
+
NuGet: "NuGet package",
|
|
16987
|
+
Maven: "Maven artifact",
|
|
16988
|
+
Packagist: "Packagist package",
|
|
16989
|
+
VSCode: "VS Code extension"
|
|
16990
|
+
};
|
|
16110
16991
|
function label(p, version) {
|
|
16111
|
-
return `${p.ecosystem
|
|
16992
|
+
return `${KIND[p.ecosystem]} "${p.name}${version ? `@${version}` : ""}"`;
|
|
16993
|
+
}
|
|
16994
|
+
function osvNames(p) {
|
|
16995
|
+
if (p.ecosystem !== "Go") return [p.name];
|
|
16996
|
+
const parts = p.name.split("/");
|
|
16997
|
+
const out = [];
|
|
16998
|
+
for (let n = parts.length; n >= 2 && out.length < 6; n--) out.push(parts.slice(0, n).join("/"));
|
|
16999
|
+
return out;
|
|
16112
17000
|
}
|
|
16113
17001
|
async function maliciousFor(p, version, cfg, misses, networkCapped) {
|
|
16114
|
-
const
|
|
16115
|
-
const
|
|
16116
|
-
|
|
17002
|
+
const names = osvNames(p);
|
|
17003
|
+
const local = names.map((n) => lookupIndex(p.ecosystem, n));
|
|
17004
|
+
const hits2 = local.flatMap((l) => l.status === "hit" ? l.entries : []);
|
|
17005
|
+
const entries = hits2.length > 0 ? hits2 : null;
|
|
17006
|
+
const freshClean = local.every((l) => l.status === "clean" && l.fresh);
|
|
17007
|
+
const status = local.some((l) => l.status === "unavailable") ? "unavailable" : local[0].status;
|
|
17008
|
+
if (!entries && !freshClean) {
|
|
16117
17009
|
if (!cfg.onlineFallback || networkCapped) {
|
|
16118
17010
|
misses.push(
|
|
16119
|
-
`${label(p, version)}: local index ${
|
|
17011
|
+
`${label(p, version)}: local index ${status}, no online lookup (${networkCapped ? "over the per-command network cap" : "online fallback off"})`
|
|
16120
17012
|
);
|
|
16121
17013
|
return null;
|
|
16122
17014
|
}
|
|
16123
|
-
let
|
|
17015
|
+
let perName = null;
|
|
16124
17016
|
try {
|
|
16125
|
-
|
|
17017
|
+
perName = await queryOsvMalicious(
|
|
17018
|
+
names.map((name) => ({ ecosystem: p.ecosystem, name, version }))
|
|
17019
|
+
);
|
|
16126
17020
|
} catch (err) {
|
|
16127
17021
|
misses.push(`${label(p, version)}: OSV online ${err.message}`);
|
|
16128
17022
|
return null;
|
|
16129
17023
|
}
|
|
17024
|
+
const ids = perName ? [[...new Set(perName.flat())]] : null;
|
|
16130
17025
|
if (!ids) {
|
|
16131
17026
|
misses.push(`${label(p, version)}: OSV online unavailable`);
|
|
16132
17027
|
return null;
|
|
@@ -16196,7 +17091,7 @@ async function checkOne(p, cfg, misses, networkCapped, cwd) {
|
|
|
16196
17091
|
}
|
|
16197
17092
|
const findings = [];
|
|
16198
17093
|
let info = null;
|
|
16199
|
-
const wantsRegistry = cfg.newPackage !== "off" || cfg.installScript !== "off";
|
|
17094
|
+
const wantsRegistry = (cfg.newPackage !== "off" || cfg.installScript !== "off") && HAS_REGISTRY_CLIENT.has(p.ecosystem);
|
|
16200
17095
|
if (wantsRegistry && !networkCapped) {
|
|
16201
17096
|
try {
|
|
16202
17097
|
const windowMs = cfg.newPackage === "off" ? 0 : cfg.maxAgeHours * 36e5;
|
|
@@ -16277,6 +17172,36 @@ async function runPackageCheck(toolName, args, cfg, cwd) {
|
|
|
16277
17172
|
}
|
|
16278
17173
|
}
|
|
16279
17174
|
|
|
17175
|
+
// src/policy/negotiation.ts
|
|
17176
|
+
var BLOCKED_BY = " is blocked by ";
|
|
17177
|
+
var DASH_BLOCKED_BY = " \u2014 blocked by ";
|
|
17178
|
+
var SHIELD = " shield";
|
|
17179
|
+
function asApprovalRequest(description) {
|
|
17180
|
+
const is = description.lastIndexOf(BLOCKED_BY);
|
|
17181
|
+
if (is > 0) {
|
|
17182
|
+
const by = description.slice(is + BLOCKED_BY.length).replace(/\.$/, "");
|
|
17183
|
+
if (by.endsWith(SHIELD)) return `${description.slice(0, is)} needs your approval (${by})`;
|
|
17184
|
+
}
|
|
17185
|
+
const dash = description.lastIndexOf(DASH_BLOCKED_BY);
|
|
17186
|
+
if (dash > 0) {
|
|
17187
|
+
const by = description.slice(dash + DASH_BLOCKED_BY.length).replace(/\.$/, "");
|
|
17188
|
+
if (by.endsWith(SHIELD)) return `${description.slice(0, dash)}. Needs your approval (${by})`;
|
|
17189
|
+
}
|
|
17190
|
+
return description;
|
|
17191
|
+
}
|
|
17192
|
+
|
|
17193
|
+
// src/auth/decision-sources.ts
|
|
17194
|
+
var HUMAN_DECISION_SOURCES = /* @__PURE__ */ new Set([
|
|
17195
|
+
"terminal",
|
|
17196
|
+
"browser",
|
|
17197
|
+
"native"
|
|
17198
|
+
]);
|
|
17199
|
+
var REDIRECT_SOURCE = "terminal-redirect";
|
|
17200
|
+
var DECISION_ROUTE_SOURCES = /* @__PURE__ */ new Set([
|
|
17201
|
+
...HUMAN_DECISION_SOURCES,
|
|
17202
|
+
REDIRECT_SOURCE
|
|
17203
|
+
]);
|
|
17204
|
+
|
|
16280
17205
|
// src/auth/orchestrator.ts
|
|
16281
17206
|
var SENSITIVE_PATH_PATTERN = sensitivePathMatch("").patternName;
|
|
16282
17207
|
function isProtectedPathRule(ruleName) {
|
|
@@ -16404,6 +17329,10 @@ async function authorizeHeadless(toolName, args, meta, options) {
|
|
|
16404
17329
|
}
|
|
16405
17330
|
return _authorizeHeadlessCore(toolName, args, meta, options);
|
|
16406
17331
|
}
|
|
17332
|
+
function daemonWaitCapMs(approvalTimeoutMs) {
|
|
17333
|
+
if (approvalTimeoutMs === 0) return 0;
|
|
17334
|
+
return Math.max(approvalTimeoutMs ?? 0, 12e4) + 5e3;
|
|
17335
|
+
}
|
|
16407
17336
|
function approvalTimeoutReason(approvalTimeoutMs) {
|
|
16408
17337
|
return `No human response within ${approvalTimeoutMs / 1e3}s \u2014 auto-denied by timeout policy. Approve from your phone next time: run \`node9 login\`.`;
|
|
16409
17338
|
}
|
|
@@ -16439,6 +17368,7 @@ async function _authorizeHeadlessCore(toolName, args, metaArg, options) {
|
|
|
16439
17368
|
let statefulRecoveryCommand;
|
|
16440
17369
|
let localSmartRuleMatched = false;
|
|
16441
17370
|
let hardBlockDowngraded = false;
|
|
17371
|
+
let downgradedBlock = null;
|
|
16442
17372
|
let argScan;
|
|
16443
17373
|
const argumentScan = () => argScan ??= scanArgumentSecrets(args);
|
|
16444
17374
|
let fileAccess;
|
|
@@ -17058,6 +17988,16 @@ async function _authorizeHeadlessCore(toolName, args, metaArg, options) {
|
|
|
17058
17988
|
humanApproverReachable,
|
|
17059
17989
|
overridable: policyResult.overridable
|
|
17060
17990
|
});
|
|
17991
|
+
const blocked = {
|
|
17992
|
+
approved: false,
|
|
17993
|
+
reason: policyResult.reason ?? "Action explicitly blocked by Smart Policy.",
|
|
17994
|
+
blockedBy: "local-config",
|
|
17995
|
+
blockedByLabel: policyResult.blockedByLabel,
|
|
17996
|
+
ruleHit: policyResult.ruleName,
|
|
17997
|
+
...policyResult.recoveryCommand && { recoveryCommand: policyResult.recoveryCommand },
|
|
17998
|
+
...policyResult.ruleDescription && { ruleDescription: policyResult.ruleDescription },
|
|
17999
|
+
...protectedPathFields(policyResult.ruleName, toolName, args, dlpReviewFlagged)
|
|
18000
|
+
};
|
|
17061
18001
|
const hardBlock = () => {
|
|
17062
18002
|
if (!isManual)
|
|
17063
18003
|
appendLocalAudit(
|
|
@@ -17068,17 +18008,9 @@ async function _authorizeHeadlessCore(toolName, args, metaArg, options) {
|
|
|
17068
18008
|
{ ...meta, ruleName: policyResult.ruleName },
|
|
17069
18009
|
hashAuditArgs
|
|
17070
18010
|
);
|
|
17071
|
-
return
|
|
17072
|
-
approved: false,
|
|
17073
|
-
reason: policyResult.reason ?? "Action explicitly blocked by Smart Policy.",
|
|
17074
|
-
blockedBy: "local-config",
|
|
17075
|
-
blockedByLabel: policyResult.blockedByLabel,
|
|
17076
|
-
ruleHit: policyResult.ruleName,
|
|
17077
|
-
...policyResult.recoveryCommand && { recoveryCommand: policyResult.recoveryCommand },
|
|
17078
|
-
...policyResult.ruleDescription && { ruleDescription: policyResult.ruleDescription },
|
|
17079
|
-
...protectedPathFields(policyResult.ruleName, toolName, args, dlpReviewFlagged)
|
|
17080
|
-
};
|
|
18011
|
+
return blocked;
|
|
17081
18012
|
};
|
|
18013
|
+
downgradedBlock = blocked;
|
|
17082
18014
|
if (policyResult.dependsOnStatePredicates?.length) {
|
|
17083
18015
|
const stateResults = await checkStatePredicates(policyResult.dependsOnStatePredicates);
|
|
17084
18016
|
const predicatesMet = stateResults !== null && policyResult.dependsOnStatePredicates.every((p) => stateResults[p] === true);
|
|
@@ -17135,7 +18067,8 @@ async function _authorizeHeadlessCore(toolName, args, metaArg, options) {
|
|
|
17135
18067
|
policyMatchedWord,
|
|
17136
18068
|
policyResult.ruleName
|
|
17137
18069
|
);
|
|
17138
|
-
if (policyRuleDescription)
|
|
18070
|
+
if (policyRuleDescription)
|
|
18071
|
+
riskMetadata.ruleDescription = (hardBlockDowngraded ? asApprovalRequest(policyRuleDescription) : policyRuleDescription).slice(0, 200);
|
|
17139
18072
|
const persistent = policyResult.ruleName || policyResult.tier === 7 || hardBlockDowngraded ? null : getPersistentDecision(toolName);
|
|
17140
18073
|
if (persistent === "allow" && !callSpecificReview()) {
|
|
17141
18074
|
if (!isManual)
|
|
@@ -17299,7 +18232,7 @@ ${packageReview}`;
|
|
|
17299
18232
|
}
|
|
17300
18233
|
}
|
|
17301
18234
|
if (initResult.pending) cloudRequestId = initResult.requestId || null;
|
|
17302
|
-
if (!
|
|
18235
|
+
if (!reviewDemanded()) explainableLabel = "Organization Policy (SaaS)";
|
|
17303
18236
|
} catch {
|
|
17304
18237
|
}
|
|
17305
18238
|
}
|
|
@@ -17441,10 +18374,54 @@ ${packageReview}`;
|
|
|
17441
18374
|
decision: daemonDecision,
|
|
17442
18375
|
source: decisionSource,
|
|
17443
18376
|
reason: daemonReason
|
|
17444
|
-
} = await waitForDaemonDecision(
|
|
18377
|
+
} = await waitForDaemonDecision(
|
|
18378
|
+
daemonEntryId,
|
|
18379
|
+
signal,
|
|
18380
|
+
daemonWaitCapMs(config.settings.approvalTimeoutMs)
|
|
18381
|
+
);
|
|
17445
18382
|
if (daemonDecision === "abandoned") throw new Error("Abandoned");
|
|
17446
18383
|
const isApproved = daemonDecision === "allow";
|
|
17447
|
-
const isRedirect = decisionSource ===
|
|
18384
|
+
const isRedirect = decisionSource === REDIRECT_SOURCE;
|
|
18385
|
+
if (!isApproved && !isRedirect && !(decisionSource && HUMAN_DECISION_SOURCES.has(decisionSource))) {
|
|
18386
|
+
if (decisionSource === "cloud")
|
|
18387
|
+
return {
|
|
18388
|
+
approved: false,
|
|
18389
|
+
reason: daemonReason || "Action rejected by organization administrator via Slack.",
|
|
18390
|
+
blockedBy: "team-policy",
|
|
18391
|
+
blockedByLabel: "Organization Policy (SaaS)",
|
|
18392
|
+
decisionSource: "cloud"
|
|
18393
|
+
};
|
|
18394
|
+
const timedOut = decisionSource === "timeout";
|
|
18395
|
+
if (downgradedBlock)
|
|
18396
|
+
return {
|
|
18397
|
+
...downgradedBlock,
|
|
18398
|
+
blockedBy: timedOut ? "timeout" : "local-config"
|
|
18399
|
+
};
|
|
18400
|
+
if (timedOut)
|
|
18401
|
+
return {
|
|
18402
|
+
approved: false,
|
|
18403
|
+
reason: daemonReason || "No response: auto-denied after the approval timeout.",
|
|
18404
|
+
blockedBy: "timeout",
|
|
18405
|
+
blockedByLabel: "Approval Timeout"
|
|
18406
|
+
};
|
|
18407
|
+
if (decisionSource === "policy")
|
|
18408
|
+
return {
|
|
18409
|
+
approved: false,
|
|
18410
|
+
reason: daemonReason || "Action blocked by policy.",
|
|
18411
|
+
blockedBy: "local-config",
|
|
18412
|
+
blockedByLabel: explainableLabel
|
|
18413
|
+
};
|
|
18414
|
+
return {
|
|
18415
|
+
approved: false,
|
|
18416
|
+
reason: daemonReason || "node9 could not get an approval decision for this call.",
|
|
18417
|
+
blockedBy: "no-approval-mechanism",
|
|
18418
|
+
blockedByLabel: "Approval Unavailable"
|
|
18419
|
+
};
|
|
18420
|
+
}
|
|
18421
|
+
if (isApproved && decisionSource === "cloud")
|
|
18422
|
+
return { approved: true, checkedBy: "cloud", decisionSource: "cloud" };
|
|
18423
|
+
if (isApproved && decisionSource === "policy")
|
|
18424
|
+
return { approved: true, checkedBy: "local-policy" };
|
|
17448
18425
|
const via = "Terminal (node9 tail)";
|
|
17449
18426
|
return {
|
|
17450
18427
|
approved: isApproved,
|