@node9/proxy 2.13.1 → 2.14.1
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 +830 -33
- package/dist/cli.mjs +830 -33
- package/dist/dashboard.mjs +814 -31
- package/dist/index.js +787 -28
- package/dist/index.mjs +787 -28
- package/dist/scan-ink.mjs +372 -1
- package/package.json +1 -1
package/dist/dashboard.mjs
CHANGED
|
@@ -343,6 +343,28 @@ function codexSessionCost(model, tokens, request) {
|
|
|
343
343
|
const tierMultiplier = request?.serviceTier === "flex" ? 0.5 : codexModel(model) === "gpt-6-astra" && (request?.serviceTier === "fast" || request?.serviceTier === "priority") ? 2 : 1;
|
|
344
344
|
return (((input - cached - written) * pin + cached * pcr + written * (pcw || pin)) * inputMultiplier + tokenNumber(tokens.output) * pout * outputMultiplier) * tierMultiplier;
|
|
345
345
|
}
|
|
346
|
+
function statAndFirstLine(file) {
|
|
347
|
+
const CAP = 4 * 1024 * 1024;
|
|
348
|
+
const CHUNK = 64 * 1024;
|
|
349
|
+
const fd = fs2.openSync(file, "r");
|
|
350
|
+
try {
|
|
351
|
+
const stat = fs2.fstatSync(fd);
|
|
352
|
+
const limit = Math.min(stat.size, CAP);
|
|
353
|
+
const parts = [];
|
|
354
|
+
for (let pos = 0; pos < limit; pos += CHUNK) {
|
|
355
|
+
const buf = Buffer.alloc(Math.min(CHUNK, limit - pos));
|
|
356
|
+
const read = fs2.readSync(fd, buf, 0, buf.length, pos);
|
|
357
|
+
if (read <= 0) break;
|
|
358
|
+
const slice = buf.subarray(0, read);
|
|
359
|
+
const nl = slice.indexOf(10);
|
|
360
|
+
parts.push(nl >= 0 ? slice.subarray(0, nl) : slice);
|
|
361
|
+
if (nl >= 0) break;
|
|
362
|
+
}
|
|
363
|
+
return { stat, first: Buffer.concat(parts).toString("utf8") };
|
|
364
|
+
} finally {
|
|
365
|
+
fs2.closeSync(fd);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
346
368
|
function listCodexSessionFiles(base = codexSessionsDir()) {
|
|
347
369
|
const files = [];
|
|
348
370
|
const walk = (dir) => {
|
|
@@ -360,10 +382,10 @@ function listCodexSessionFiles(base = codexSessionsDir()) {
|
|
|
360
382
|
const sessions = /* @__PURE__ */ new Map();
|
|
361
383
|
for (const file of files.sort()) {
|
|
362
384
|
try {
|
|
363
|
-
const stat =
|
|
385
|
+
const { stat, first: head } = statAndFirstLine(file);
|
|
364
386
|
let id = "";
|
|
365
387
|
try {
|
|
366
|
-
const first = JSON.parse(
|
|
388
|
+
const first = JSON.parse(head);
|
|
367
389
|
if (first?.type === "session_meta" && typeof first.payload?.id === "string")
|
|
368
390
|
id = first.payload.id;
|
|
369
391
|
} catch {
|
|
@@ -826,6 +848,19 @@ function parseShared(command) {
|
|
|
826
848
|
astCache.set(command, parsed);
|
|
827
849
|
return parsed;
|
|
828
850
|
}
|
|
851
|
+
function byteOffsetToCharIndex(command) {
|
|
852
|
+
if (!/[^\u0000-\u007F]/.test(command)) return null;
|
|
853
|
+
const map = /* @__PURE__ */ new Map();
|
|
854
|
+
let byte = 0;
|
|
855
|
+
for (let i = 0; i < command.length; ) {
|
|
856
|
+
map.set(byte, i);
|
|
857
|
+
const cp = command.codePointAt(i);
|
|
858
|
+
byte += cp < 128 ? 1 : cp < 2048 ? 2 : cp < 65536 ? 3 : 4;
|
|
859
|
+
i += cp > 65535 ? 2 : 1;
|
|
860
|
+
}
|
|
861
|
+
map.set(byte, command.length);
|
|
862
|
+
return (b) => map.get(b) ?? -1;
|
|
863
|
+
}
|
|
829
864
|
function cachedNormalize(command, compute) {
|
|
830
865
|
const hit = normalizeCache.get(command);
|
|
831
866
|
if (hit !== void 0) {
|
|
@@ -855,6 +890,8 @@ function normalizeCommandForPolicyImpl(command) {
|
|
|
855
890
|
const f = parseShared(command);
|
|
856
891
|
if (f === PARSE_FAIL) return { posix: command, separator: command };
|
|
857
892
|
try {
|
|
893
|
+
const toCharIndex = byteOffsetToCharIndex(command);
|
|
894
|
+
const at = (byteOffset) => toCharIndex === null ? byteOffset : toCharIndex(byteOffset);
|
|
858
895
|
const strips = [];
|
|
859
896
|
const rewrites = [];
|
|
860
897
|
const quoteOnlyRewrites = [];
|
|
@@ -875,8 +912,9 @@ function normalizeCommandForPolicyImpl(command) {
|
|
|
875
912
|
const quotedNode = nextParts[0];
|
|
876
913
|
const nt = syntax.NodeType(quotedNode);
|
|
877
914
|
const markStrip = () => {
|
|
878
|
-
const s = next.Pos().Offset();
|
|
879
|
-
const e = next.End().Offset();
|
|
915
|
+
const s = at(next.Pos().Offset());
|
|
916
|
+
const e = at(next.End().Offset());
|
|
917
|
+
if (s < 0 || e < 0) return;
|
|
880
918
|
strips.push([s, e]);
|
|
881
919
|
msgSpans.add(`${s}:${e}`);
|
|
882
920
|
};
|
|
@@ -893,8 +931,9 @@ function normalizeCommandForPolicyImpl(command) {
|
|
|
893
931
|
}
|
|
894
932
|
}
|
|
895
933
|
for (const arg of args) {
|
|
896
|
-
const s = arg.Pos().Offset();
|
|
897
|
-
const e = arg.End().Offset();
|
|
934
|
+
const s = at(arg.Pos().Offset());
|
|
935
|
+
const e = at(arg.End().Offset());
|
|
936
|
+
if (s < 0 || e < 0) continue;
|
|
898
937
|
if (msgSpans.has(`${s}:${e}`)) continue;
|
|
899
938
|
const resolved = resolveWordLiteral(arg);
|
|
900
939
|
if (resolved === null) continue;
|
|
@@ -1067,20 +1106,32 @@ function isProtectedHomePath(rawPath) {
|
|
|
1067
1106
|
}
|
|
1068
1107
|
return true;
|
|
1069
1108
|
}
|
|
1070
|
-
function
|
|
1071
|
-
const
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
const name = (words[0] ?? "").toLowerCase();
|
|
1075
|
-
const flags = [];
|
|
1076
|
-
const paths = [];
|
|
1077
|
-
for (let i = 1; i < words.length; i++) {
|
|
1109
|
+
function positionedArgs(words, from = 1, to = words.length) {
|
|
1110
|
+
const out = [];
|
|
1111
|
+
let afterFlag = null;
|
|
1112
|
+
for (let i = from; i < to; i++) {
|
|
1078
1113
|
const v = words[i];
|
|
1079
|
-
if (v === null)
|
|
1080
|
-
|
|
1081
|
-
|
|
1114
|
+
if (v === null) {
|
|
1115
|
+
afterFlag = null;
|
|
1116
|
+
continue;
|
|
1117
|
+
}
|
|
1118
|
+
if (v.startsWith("-")) {
|
|
1119
|
+
afterFlag = v;
|
|
1120
|
+
continue;
|
|
1121
|
+
}
|
|
1122
|
+
out.push({ value: v, index: out.length, argv: i, afterFlag });
|
|
1123
|
+
afterFlag = null;
|
|
1082
1124
|
}
|
|
1083
|
-
return
|
|
1125
|
+
return out;
|
|
1126
|
+
}
|
|
1127
|
+
function extractLiteralArgs(callExpr) {
|
|
1128
|
+
const rawArgs = callExpr.Args || [];
|
|
1129
|
+
if (rawArgs.length === 0) return { name: "", flags: [], paths: [], words: [], args: [] };
|
|
1130
|
+
const words = rawArgs.map((a) => resolveWordLiteral(a));
|
|
1131
|
+
const name = baseWord(words[0]);
|
|
1132
|
+
const flags = words.slice(1).filter((w) => w !== null && w.startsWith("-"));
|
|
1133
|
+
const args = positionedArgs(words);
|
|
1134
|
+
return { name, flags, paths: args.map((a) => a.value), words, args };
|
|
1084
1135
|
}
|
|
1085
1136
|
function resolveWordLiteral(w) {
|
|
1086
1137
|
const parts = w?.Parts || [];
|
|
@@ -1144,7 +1195,7 @@ function analyzeFsOperationImpl(command, depth = 0) {
|
|
|
1144
1195
|
return result?.verdict !== "block";
|
|
1145
1196
|
}
|
|
1146
1197
|
if (nodeType !== "CallExpr") return true;
|
|
1147
|
-
const { name, flags, paths, words } = extractLiteralArgs(n);
|
|
1198
|
+
const { name, flags, paths, words, args } = extractLiteralArgs(n);
|
|
1148
1199
|
if (!name) return true;
|
|
1149
1200
|
if (name === "rm") {
|
|
1150
1201
|
const flagStr = flags.join("").toLowerCase();
|
|
@@ -1184,13 +1235,16 @@ function analyzeFsOperationImpl(command, depth = 0) {
|
|
|
1184
1235
|
return true;
|
|
1185
1236
|
}
|
|
1186
1237
|
}
|
|
1187
|
-
const readPaths = FS_READ_TOOLS.has(name) ?
|
|
1238
|
+
const readPaths = FS_READ_TOOLS.has(name) ? [...readTargets(name, args, flags, words, 1), ...flagOperandFiles(name, words, 1)] : wrappedReadPaths(words, name);
|
|
1188
1239
|
if (readPaths) {
|
|
1189
1240
|
for (const p of readPaths) {
|
|
1190
1241
|
result = stricter(result, matchSensitivePath2(p));
|
|
1191
1242
|
if (result?.verdict === "block") return false;
|
|
1192
1243
|
}
|
|
1193
1244
|
}
|
|
1245
|
+
for (const p of copySourcePaths(words)) {
|
|
1246
|
+
result = stricter(result, copyVerdictOf(matchSensitivePath2(p)));
|
|
1247
|
+
}
|
|
1194
1248
|
return true;
|
|
1195
1249
|
});
|
|
1196
1250
|
return result;
|
|
@@ -1203,6 +1257,184 @@ function stricter(a, b) {
|
|
|
1203
1257
|
if (!b) return a;
|
|
1204
1258
|
return b.verdict === "block" && a.verdict !== "block" ? b : a;
|
|
1205
1259
|
}
|
|
1260
|
+
function flagInfo(w) {
|
|
1261
|
+
if (w.startsWith("--")) {
|
|
1262
|
+
const eq = w.indexOf("=");
|
|
1263
|
+
return eq < 0 ? { letter: null, long: w, attached: null } : { letter: null, long: w.slice(0, eq), attached: w.slice(eq + 1) };
|
|
1264
|
+
}
|
|
1265
|
+
const m = /^-([a-zA-Z]+)(.*)$/.exec(w);
|
|
1266
|
+
if (!m) return { letter: null, long: null, attached: null };
|
|
1267
|
+
return { letter: m[1][m[1].length - 1], long: null, attached: m[2] === "" ? null : m[2] };
|
|
1268
|
+
}
|
|
1269
|
+
function flagIs(w, names) {
|
|
1270
|
+
if (w === null) return false;
|
|
1271
|
+
const f = flagInfo(w);
|
|
1272
|
+
return names.some((n) => n.startsWith("--") ? f.long === n : f.letter === n);
|
|
1273
|
+
}
|
|
1274
|
+
function operandOf(a, names, valueLetters) {
|
|
1275
|
+
if (!names || a.afterFlag === null) return false;
|
|
1276
|
+
const w = a.afterFlag;
|
|
1277
|
+
if (!w.startsWith("--") && valueLetters) {
|
|
1278
|
+
const letters = w.slice(1);
|
|
1279
|
+
for (let i = 0; i < letters.length; i++) {
|
|
1280
|
+
if (!valueLetters.includes(letters[i])) continue;
|
|
1281
|
+
return i === letters.length - 1 && names.includes(letters[i]);
|
|
1282
|
+
}
|
|
1283
|
+
return false;
|
|
1284
|
+
}
|
|
1285
|
+
if (w.startsWith("--") && !w.includes("=")) {
|
|
1286
|
+
const longs = names.filter((n) => n.startsWith("--"));
|
|
1287
|
+
if (longs.some((n) => n === w || w.length >= 3 && n.startsWith(w))) return true;
|
|
1288
|
+
}
|
|
1289
|
+
return flagIs(w, names) && flagInfo(w).attached === null;
|
|
1290
|
+
}
|
|
1291
|
+
function resolveCopyShape(words, h) {
|
|
1292
|
+
const verb = baseWord(words[h]);
|
|
1293
|
+
if (!verb) return null;
|
|
1294
|
+
const direct = COPY_VERBS[verb];
|
|
1295
|
+
if (direct) return { shape: direct, last: h };
|
|
1296
|
+
const slots = positionedArgs(words, h + 1);
|
|
1297
|
+
for (let i = 0; i < slots.length; i++) {
|
|
1298
|
+
for (let n = 3; n >= 1; n--) {
|
|
1299
|
+
const part = slots.slice(i, i + n);
|
|
1300
|
+
if (part.length < n) continue;
|
|
1301
|
+
const key = [verb, ...part.map((a) => a.value.toLowerCase())].join(" ");
|
|
1302
|
+
const shape = COPY_VERBS[key];
|
|
1303
|
+
if (shape) return { shape, last: part[n - 1].argv };
|
|
1304
|
+
}
|
|
1305
|
+
if (slots[i].afterFlag === null) return null;
|
|
1306
|
+
}
|
|
1307
|
+
return null;
|
|
1308
|
+
}
|
|
1309
|
+
function findStartPoints(words, h) {
|
|
1310
|
+
const k = words.findIndex((w, i) => i > h && w !== null && FIND_EXEC_FLAGS.has(w));
|
|
1311
|
+
if (k < 0) return { k, starts: [] };
|
|
1312
|
+
const firstPredicate = words.findIndex(
|
|
1313
|
+
(w, i) => i > h && w !== null && w !== "--" && w.startsWith("-") && !FIND_OPTIONS.has(w)
|
|
1314
|
+
);
|
|
1315
|
+
const end = firstPredicate > h ? firstPredicate : k;
|
|
1316
|
+
return { k, starts: positionalAfter(words, h + 1, end) };
|
|
1317
|
+
}
|
|
1318
|
+
function copySourcePaths(words) {
|
|
1319
|
+
const h = unwrapCommandHead(words);
|
|
1320
|
+
const fi = words.findIndex((w, i) => i <= h && baseWord(w) === "find");
|
|
1321
|
+
if (fi >= 0) {
|
|
1322
|
+
const { k, starts } = findStartPoints(words, fi);
|
|
1323
|
+
if (k < 0) return [];
|
|
1324
|
+
const action = unwrapCommandHead(words.slice(k + 1));
|
|
1325
|
+
return resolveCopyShape(words.slice(k + 1), action) ? starts : [];
|
|
1326
|
+
}
|
|
1327
|
+
if (!COPY_VERB_HEADS.has(baseWord(words[h]))) return [];
|
|
1328
|
+
const r = resolveCopyShape(words, h);
|
|
1329
|
+
if (!r) return [];
|
|
1330
|
+
const { shape, last } = r;
|
|
1331
|
+
const args = positionedArgs(words, last + 1);
|
|
1332
|
+
const tail = words.slice(last + 1);
|
|
1333
|
+
const copyOptionsEnd = tail.findIndex((w) => w === "--");
|
|
1334
|
+
const copyPastOptions = (a) => copyOptionsEnd >= 0 && a.argv > last + 1 + copyOptionsEnd;
|
|
1335
|
+
const skipped = (a) => !copyPastOptions(a) && operandOf(a, shape.skipFlags, shape.valueLetters);
|
|
1336
|
+
const firstValueLetter = (w) => {
|
|
1337
|
+
const letters = w.slice(1);
|
|
1338
|
+
for (let i = 0; i < letters.length; i++) {
|
|
1339
|
+
if ((shape.valueLetters ?? []).includes(letters[i]))
|
|
1340
|
+
return { letter: letters[i], last: i === letters.length - 1 };
|
|
1341
|
+
}
|
|
1342
|
+
return null;
|
|
1343
|
+
};
|
|
1344
|
+
const isTargetDirFlag = (w) => {
|
|
1345
|
+
if (w.startsWith("--")) {
|
|
1346
|
+
const name = w.includes("=") ? w.slice(0, w.indexOf("=")) : w;
|
|
1347
|
+
return name.length >= 3 && "--target-directory".startsWith(name);
|
|
1348
|
+
}
|
|
1349
|
+
return firstValueLetter(w)?.letter === "t";
|
|
1350
|
+
};
|
|
1351
|
+
const targetDir = shape.targetDirFlag === true && tail.some((w) => w !== null && w.startsWith("-") && isTargetDirFlag(w));
|
|
1352
|
+
const targetTakesNextWord = (w) => {
|
|
1353
|
+
if (w.startsWith("--"))
|
|
1354
|
+
return !w.includes("=") && w.length >= 3 && "--target-directory".startsWith(w);
|
|
1355
|
+
const f = firstValueLetter(w);
|
|
1356
|
+
return f !== null && f.letter === "t" && f.last;
|
|
1357
|
+
};
|
|
1358
|
+
const targetOperand = (a) => targetDir && a.afterFlag !== null && !copyPastOptions(a) && targetTakesNextWord(a.afterFlag);
|
|
1359
|
+
const lastOperand = [...tail].reverse().find((w) => w === null || !w.startsWith("-"));
|
|
1360
|
+
const dynamicDest = lastOperand === null;
|
|
1361
|
+
const destIsLastOperand = (shape.source === "allButLast" || shape.source === "first") && !targetDir && !dynamicDest;
|
|
1362
|
+
let src;
|
|
1363
|
+
switch (shape.source) {
|
|
1364
|
+
case "all":
|
|
1365
|
+
src = args;
|
|
1366
|
+
break;
|
|
1367
|
+
case "first":
|
|
1368
|
+
src = targetDir ? args : args.slice(0, 1);
|
|
1369
|
+
break;
|
|
1370
|
+
case "flagOperand": {
|
|
1371
|
+
const namesSource = (f) => {
|
|
1372
|
+
const names = shape.sourceFlags ?? [];
|
|
1373
|
+
if (f.long !== null)
|
|
1374
|
+
return names.some(
|
|
1375
|
+
(n) => n.startsWith("--") && (n === f.long || f.long.length >= 3 && n.startsWith(f.long))
|
|
1376
|
+
);
|
|
1377
|
+
return f.letter !== null && names.includes(f.letter);
|
|
1378
|
+
};
|
|
1379
|
+
const inline = tail.filter((w) => w !== null && w.startsWith("-")).map((w) => flagInfo(w)).filter((f) => f.attached !== null && namesSource(f)).map((f) => f.attached);
|
|
1380
|
+
return [
|
|
1381
|
+
...args.filter((a) => !copyPastOptions(a) && operandOf(a, shape.sourceFlags, shape.valueLetters)).map((a) => a.value),
|
|
1382
|
+
...inline
|
|
1383
|
+
];
|
|
1384
|
+
}
|
|
1385
|
+
case "archive":
|
|
1386
|
+
src = archiveInputs(shape.archive, args, tail);
|
|
1387
|
+
break;
|
|
1388
|
+
case "allButLast":
|
|
1389
|
+
src = targetDir || dynamicDest ? args : args.slice(0, -1);
|
|
1390
|
+
break;
|
|
1391
|
+
}
|
|
1392
|
+
const sources = src.filter((a) => !skipped(a) && !targetOperand(a)).map((a) => a.value);
|
|
1393
|
+
if (destIsLastOperand && typeof lastOperand === "string" && matchSensitivePath2(lastOperand)) {
|
|
1394
|
+
const jailedSources = sources.filter((p) => matchSensitivePath2(p));
|
|
1395
|
+
const dirOf = (p) => /[\\/]/.test(p) ? p.replace(/[\\/][^\\/]*$/, "") : "";
|
|
1396
|
+
if (jailedSources.length === 0) return [];
|
|
1397
|
+
if (jailedSources.every((p) => dirOf(p) === dirOf(lastOperand))) return [];
|
|
1398
|
+
}
|
|
1399
|
+
return sources;
|
|
1400
|
+
}
|
|
1401
|
+
function archiveInputs(kind, args, tail) {
|
|
1402
|
+
const first = args[0];
|
|
1403
|
+
const bareKey = first && first.afterFlag === null && TAR_MODE_WORD.test(first.value);
|
|
1404
|
+
if (kind === "tar") {
|
|
1405
|
+
const flagsText = tail.filter((w) => w !== null && w.startsWith("-")).join(" ");
|
|
1406
|
+
const mode = (bareKey ? first.value : "") + flagsText;
|
|
1407
|
+
const extracting = /x|t/.test(bareKey ? first.value.replace(/f/g, "") : "") || /(^|\s)-[a-zA-Z]*[xt]|--extract|--list|--get/.test(flagsText);
|
|
1408
|
+
const writing = /[cruA]/.test(bareKey ? first.value : "") || /(^|\s)-[a-zA-Z]*[cruA]|--create|--append|--update|--concatenate/.test(flagsText);
|
|
1409
|
+
if (extracting && !writing) return [];
|
|
1410
|
+
void mode;
|
|
1411
|
+
if (!bareKey) return args;
|
|
1412
|
+
let i = 1;
|
|
1413
|
+
const fromDirs = [];
|
|
1414
|
+
for (const ch of first.value) {
|
|
1415
|
+
if (!TAR_VALUE_LETTERS.includes(ch)) continue;
|
|
1416
|
+
const operand = args[i];
|
|
1417
|
+
if (!operand || operand.afterFlag !== null) break;
|
|
1418
|
+
i += 1;
|
|
1419
|
+
if (ch === "C") fromDirs.push(operand);
|
|
1420
|
+
}
|
|
1421
|
+
return [...fromDirs, ...args.slice(i)];
|
|
1422
|
+
}
|
|
1423
|
+
if (kind === "zip") return first && first.afterFlag === "-" ? args : args.slice(1);
|
|
1424
|
+
if (kind === "ar") return bareKey ? args.slice(2) : args.slice(1);
|
|
1425
|
+
return args.slice(2);
|
|
1426
|
+
}
|
|
1427
|
+
function copyVerdictOf(hit) {
|
|
1428
|
+
if (!hit) return null;
|
|
1429
|
+
const ruleName = COPY_RULE_OF[hit.ruleName];
|
|
1430
|
+
if (!ruleName) return null;
|
|
1431
|
+
return {
|
|
1432
|
+
ruleName,
|
|
1433
|
+
verdict: "review",
|
|
1434
|
+
reason: `Copying ${hit.path} moves a credential out of its jail (project-jail shield)`,
|
|
1435
|
+
path: hit.path
|
|
1436
|
+
};
|
|
1437
|
+
}
|
|
1206
1438
|
function matchSensitivePath2(p) {
|
|
1207
1439
|
for (const sp of SENSITIVE_PATH_RULES) {
|
|
1208
1440
|
if (sp.match(p))
|
|
@@ -1210,16 +1442,156 @@ function matchSensitivePath2(p) {
|
|
|
1210
1442
|
}
|
|
1211
1443
|
return null;
|
|
1212
1444
|
}
|
|
1445
|
+
function baseWord(w) {
|
|
1446
|
+
return (w ?? "").split("/").pop()?.toLowerCase() ?? "";
|
|
1447
|
+
}
|
|
1448
|
+
function namesFlag(name, candidates, known) {
|
|
1449
|
+
if (candidates.has(name)) return true;
|
|
1450
|
+
if (!name.startsWith("--") || name.length < 3) return false;
|
|
1451
|
+
if (known?.has(name)) return false;
|
|
1452
|
+
for (const c of candidates) if (c.startsWith(name)) return true;
|
|
1453
|
+
return false;
|
|
1454
|
+
}
|
|
1455
|
+
function knownLongFlags(verb) {
|
|
1456
|
+
const out = /* @__PURE__ */ new Set();
|
|
1457
|
+
const shape = PATTERN_VERBS[verb];
|
|
1458
|
+
if (shape) {
|
|
1459
|
+
for (const set of [shape.takesValue, shape.noValue, shape.patternFlags, shape.noPatternFlags])
|
|
1460
|
+
for (const f of set) if (f.startsWith("--")) out.add(f);
|
|
1461
|
+
}
|
|
1462
|
+
for (const f of FILE_OPERAND_FLAGS[verb] ?? []) if (f.startsWith("--")) out.add(f);
|
|
1463
|
+
return out;
|
|
1464
|
+
}
|
|
1465
|
+
function flagNamesOf(token, shape) {
|
|
1466
|
+
if (token.startsWith("--")) {
|
|
1467
|
+
const eq = token.indexOf("=");
|
|
1468
|
+
return [eq > 0 ? token.slice(0, eq) : token];
|
|
1469
|
+
}
|
|
1470
|
+
const out = [];
|
|
1471
|
+
for (const c of token.slice(1)) {
|
|
1472
|
+
const name = `-${c}`;
|
|
1473
|
+
out.push(name);
|
|
1474
|
+
if (shape?.takesValue.has(name)) break;
|
|
1475
|
+
}
|
|
1476
|
+
return out;
|
|
1477
|
+
}
|
|
1478
|
+
function flagEffect(token, shape, known) {
|
|
1479
|
+
if (token === "--") return NONE;
|
|
1480
|
+
if (/^-+$/.test(token)) return UNKNOWN;
|
|
1481
|
+
if (/^-\d+$/.test(token)) return NONE;
|
|
1482
|
+
if (token.startsWith("--")) {
|
|
1483
|
+
if (token.includes("=")) return NONE;
|
|
1484
|
+
const takes = namesFlag(token, shape.takesValue, known);
|
|
1485
|
+
const none = namesFlag(token, shape.noValue, known);
|
|
1486
|
+
if (takes && none) return UNKNOWN;
|
|
1487
|
+
if (takes) return { kind: "takes", flag: token };
|
|
1488
|
+
if (none) return NONE;
|
|
1489
|
+
return UNKNOWN;
|
|
1490
|
+
}
|
|
1491
|
+
const letters = token.slice(1);
|
|
1492
|
+
if (!letters) return NONE;
|
|
1493
|
+
for (let i = 0; i < letters.length; i++) {
|
|
1494
|
+
const name = `-${letters[i]}`;
|
|
1495
|
+
if (shape.takesValue.has(name)) {
|
|
1496
|
+
return i === letters.length - 1 ? { kind: "takes", flag: name } : NONE;
|
|
1497
|
+
}
|
|
1498
|
+
if (!shape.noValue.has(name)) return UNKNOWN;
|
|
1499
|
+
}
|
|
1500
|
+
return NONE;
|
|
1501
|
+
}
|
|
1502
|
+
function readTargets(verb, args, flags, words = [], from = 1) {
|
|
1503
|
+
const shape = PATTERN_VERBS[verb];
|
|
1504
|
+
if (!shape) return args.map((a) => a.value);
|
|
1505
|
+
const known = knownLongFlags(verb);
|
|
1506
|
+
const names = flags.flatMap((f) => flagNamesOf(f, shape));
|
|
1507
|
+
const patternElsewhere = names.some(
|
|
1508
|
+
(n) => namesFlag(n, shape.patternFlags, known) || namesFlag(n, shape.noPatternFlags, known)
|
|
1509
|
+
);
|
|
1510
|
+
const excused = /* @__PURE__ */ new Set();
|
|
1511
|
+
const fileFlags = FILE_OPERAND_FLAGS[verb];
|
|
1512
|
+
const optionsEnd = words.findIndex((w, i) => i >= from && w === "--");
|
|
1513
|
+
const pastOptions = (a) => optionsEnd >= 0 && a.argv > optionsEnd;
|
|
1514
|
+
for (const a of args) {
|
|
1515
|
+
if (a.afterFlag === null || pastOptions(a)) continue;
|
|
1516
|
+
const e = flagEffect(a.afterFlag, shape, known);
|
|
1517
|
+
if (e.kind !== "takes") continue;
|
|
1518
|
+
if (fileFlags && namesFlag(e.flag, fileFlags, known)) continue;
|
|
1519
|
+
excused.add(a);
|
|
1520
|
+
}
|
|
1521
|
+
const patternArgv = (() => {
|
|
1522
|
+
for (let i = from; i < words.length; i++) {
|
|
1523
|
+
const w = words[i];
|
|
1524
|
+
if (w === null) return -1;
|
|
1525
|
+
if (w === "--") return i + 1;
|
|
1526
|
+
if (w.startsWith("-")) {
|
|
1527
|
+
const e = flagEffect(w, shape, known);
|
|
1528
|
+
if (e.kind === "takes") i += 1;
|
|
1529
|
+
else if (e.kind === "unknown") return -1;
|
|
1530
|
+
continue;
|
|
1531
|
+
}
|
|
1532
|
+
return i;
|
|
1533
|
+
}
|
|
1534
|
+
return -1;
|
|
1535
|
+
})();
|
|
1536
|
+
if (!patternElsewhere && patternArgv >= 0) {
|
|
1537
|
+
const a = args.find((x) => x.argv === patternArgv);
|
|
1538
|
+
if (a) excused.add(a);
|
|
1539
|
+
}
|
|
1540
|
+
return args.filter((a) => !excused.has(a)).map((a) => a.value);
|
|
1541
|
+
}
|
|
1542
|
+
function flagOperandFiles(verb, words, from) {
|
|
1543
|
+
const flags = FILE_OPERAND_FLAGS[verb];
|
|
1544
|
+
if (!flags) return [];
|
|
1545
|
+
const known = knownLongFlags(verb);
|
|
1546
|
+
const out = [];
|
|
1547
|
+
for (let i = from; i < words.length; i++) {
|
|
1548
|
+
const w = words[i];
|
|
1549
|
+
if (w === null || !w.startsWith("-") || w === "--") continue;
|
|
1550
|
+
if (w.startsWith("--")) {
|
|
1551
|
+
const eq = w.indexOf("=");
|
|
1552
|
+
if (eq <= 0) continue;
|
|
1553
|
+
const name = w.slice(0, eq);
|
|
1554
|
+
const value = w.slice(eq + 1);
|
|
1555
|
+
if (!value) continue;
|
|
1556
|
+
if (namesFlag(name, flags, known)) {
|
|
1557
|
+
out.push(value);
|
|
1558
|
+
continue;
|
|
1559
|
+
}
|
|
1560
|
+
const shape2 = PATTERN_VERBS[verb];
|
|
1561
|
+
if (!shape2) continue;
|
|
1562
|
+
const recognised = namesFlag(name, shape2.takesValue, known) || namesFlag(name, shape2.noValue, known) || namesFlag(name, shape2.patternFlags, known) || namesFlag(name, shape2.noPatternFlags, known);
|
|
1563
|
+
if (!recognised) out.push(value);
|
|
1564
|
+
continue;
|
|
1565
|
+
}
|
|
1566
|
+
const shape = PATTERN_VERBS[verb];
|
|
1567
|
+
const letters = w.slice(1);
|
|
1568
|
+
for (let j = 0; j < letters.length; j++) {
|
|
1569
|
+
const name = `-${letters[j]}`;
|
|
1570
|
+
const isFileFlag = flags.has(name);
|
|
1571
|
+
const argTaking = isFileFlag || (shape?.takesValue.has(name) ?? false) || (READER_VALUE_LETTERS[verb] ?? []).includes(letters[j]);
|
|
1572
|
+
if (!argTaking) continue;
|
|
1573
|
+
const attached = letters.slice(j + 1);
|
|
1574
|
+
if (isFileFlag && attached) out.push(attached);
|
|
1575
|
+
break;
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
return out;
|
|
1579
|
+
}
|
|
1213
1580
|
function wrappedReadPaths(words, name) {
|
|
1214
1581
|
if (name === "find") {
|
|
1215
|
-
const k = words
|
|
1216
|
-
|
|
1217
|
-
const firstPredicate = words.findIndex((w, i) => i > 0 && w !== null && w.startsWith("-"));
|
|
1218
|
-
return positionalAfter(words, 1, firstPredicate > 0 ? firstPredicate : k);
|
|
1582
|
+
const { k, starts } = findStartPoints(words, 0);
|
|
1583
|
+
return k > 0 && isReaderWord(words[k + 1] ?? null) ? starts : null;
|
|
1219
1584
|
}
|
|
1220
1585
|
if (!COMMAND_WRAPPERS.has(name) && !RUNNER_WRAPPERS.has(name)) return null;
|
|
1221
1586
|
const h = unwrapCommandHead(words);
|
|
1222
|
-
|
|
1587
|
+
if (h <= 0 || !isReaderWord(words[h] ?? null)) return null;
|
|
1588
|
+
const head = baseWord(words[h]);
|
|
1589
|
+
const rest = words.slice(h + 1);
|
|
1590
|
+
const restFlags = rest.filter((w) => w !== null && w.startsWith("-"));
|
|
1591
|
+
return [
|
|
1592
|
+
...readTargets(head, positionedArgs(words, h + 1), restFlags, words, h + 1),
|
|
1593
|
+
...flagOperandFiles(head, words, h + 1)
|
|
1594
|
+
];
|
|
1223
1595
|
}
|
|
1224
1596
|
function literalShellPayload(words, name) {
|
|
1225
1597
|
const h = COMMAND_WRAPPERS.has(name) || RUNNER_WRAPPERS.has(name) ? unwrapCommandHead(words) : 0;
|
|
@@ -1564,7 +1936,7 @@ function matchCanaryArgs(args, values) {
|
|
|
1564
1936
|
return null;
|
|
1565
1937
|
}
|
|
1566
1938
|
}
|
|
1567
|
-
var B58, B58_INDEX, XPRV_VERSIONS, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, MAX_DEPTH, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, FORBIDDEN_PATH_SEGMENTS, syntax, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, AST_CACHE_MAX, astCache, PARSE_FAIL, FS_READ_TOOLS, FS_OP_PRESCREEN_RE, HOME_CACHE_ALLOWLIST, SENSITIVE_PATH_RULES, BASH_TOOL_NAMES, AST_FS_REGEX_RULES, COMMAND_WRAPPERS, INLINE_INTERPRETER, RUNNER_WRAPPERS, WRAPPER_TAKES_TARGET, FIND_EXEC_FLAGS, FS_OP_CACHE_MAX, fsOpCache, REDIR_TRUNCATE_OPS, REDIR_FILE_IN_OPS, REDIR_HEREDOC_OPS, isReaderWord, positionalAfter, SOURCE_COMMANDS, SSRF_MAX_HOST, METADATA_ADDRESSES, METADATA_HOSTNAMES, v4Octets, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, BUILTIN_SHIELDS, CANARY_MIN_LENGTH, MAX_TEXT, MAX_DEPTH2, MAX_JSON_PARSE, URL_DEPTH, B64_DEPTH, MIN_SEGMENT, SEPARATORS, stripSeparators, looksText, CANARY_DECODERS, VIEWS, LONG_OUTPUT_THRESHOLD_BYTES;
|
|
1939
|
+
var B58, B58_INDEX, XPRV_VERSIONS, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, MAX_DEPTH, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, FORBIDDEN_PATH_SEGMENTS, syntax, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, AST_CACHE_MAX, astCache, PARSE_FAIL, 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, BASH_TOOL_NAMES, AST_FS_REGEX_RULES, COMMAND_WRAPPERS, INLINE_INTERPRETER, RUNNER_WRAPPERS, WRAPPER_TAKES_TARGET, FIND_EXEC_FLAGS, FS_OP_CACHE_MAX, fsOpCache, REDIR_TRUNCATE_OPS, REDIR_FILE_IN_OPS, REDIR_HEREDOC_OPS, FIND_OPTIONS, COPY_RULE_OF, isReaderWord, positionalAfter, NONE, UNKNOWN, SOURCE_COMMANDS, SSRF_MAX_HOST, METADATA_ADDRESSES, METADATA_HOSTNAMES, v4Octets, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, BUILTIN_SHIELDS, CANARY_MIN_LENGTH, MAX_TEXT, MAX_DEPTH2, MAX_JSON_PARSE, URL_DEPTH, B64_DEPTH, MIN_SEGMENT, SEPARATORS, stripSeparators, looksText, CANARY_DECODERS, VIEWS, LONG_OUTPUT_THRESHOLD_BYTES;
|
|
1568
1940
|
var init_dist = __esm({
|
|
1569
1941
|
"packages/policy-engine/dist/index.mjs"() {
|
|
1570
1942
|
"use strict";
|
|
@@ -2165,12 +2537,408 @@ var init_dist = __esm({
|
|
|
2165
2537
|
"nl",
|
|
2166
2538
|
"dd"
|
|
2167
2539
|
]);
|
|
2540
|
+
GREP_SHAPE = {
|
|
2541
|
+
takesValue: /* @__PURE__ */ new Set([
|
|
2542
|
+
"-A",
|
|
2543
|
+
"-B",
|
|
2544
|
+
"-C",
|
|
2545
|
+
"-D",
|
|
2546
|
+
"-d",
|
|
2547
|
+
"-e",
|
|
2548
|
+
"-f",
|
|
2549
|
+
"-m",
|
|
2550
|
+
"--after-context",
|
|
2551
|
+
"--before-context",
|
|
2552
|
+
"--binary-files",
|
|
2553
|
+
"--context",
|
|
2554
|
+
"--devices",
|
|
2555
|
+
"--directories",
|
|
2556
|
+
"--exclude",
|
|
2557
|
+
"--exclude-dir",
|
|
2558
|
+
"--exclude-from",
|
|
2559
|
+
"--file",
|
|
2560
|
+
"--include",
|
|
2561
|
+
"--label",
|
|
2562
|
+
"--max-count",
|
|
2563
|
+
"--regexp"
|
|
2564
|
+
]),
|
|
2565
|
+
noValue: /* @__PURE__ */ new Set([
|
|
2566
|
+
"-E",
|
|
2567
|
+
"-F",
|
|
2568
|
+
"-G",
|
|
2569
|
+
"-P",
|
|
2570
|
+
"-i",
|
|
2571
|
+
"-y",
|
|
2572
|
+
"-v",
|
|
2573
|
+
"-V",
|
|
2574
|
+
"-w",
|
|
2575
|
+
"-x",
|
|
2576
|
+
"-c",
|
|
2577
|
+
"-l",
|
|
2578
|
+
"-L",
|
|
2579
|
+
"-o",
|
|
2580
|
+
"-q",
|
|
2581
|
+
"-s",
|
|
2582
|
+
"-b",
|
|
2583
|
+
"-H",
|
|
2584
|
+
"-h",
|
|
2585
|
+
"-n",
|
|
2586
|
+
"-T",
|
|
2587
|
+
"-Z",
|
|
2588
|
+
"-z",
|
|
2589
|
+
"-R",
|
|
2590
|
+
"-r",
|
|
2591
|
+
"-U",
|
|
2592
|
+
"-u",
|
|
2593
|
+
"-I",
|
|
2594
|
+
"-a",
|
|
2595
|
+
"--basic-regexp",
|
|
2596
|
+
"--binary",
|
|
2597
|
+
"--byte-offset",
|
|
2598
|
+
"--color",
|
|
2599
|
+
"--colour",
|
|
2600
|
+
"--count",
|
|
2601
|
+
"--dereference-recursive",
|
|
2602
|
+
"--extended-regexp",
|
|
2603
|
+
"--files-with-matches",
|
|
2604
|
+
"--files-without-match",
|
|
2605
|
+
"--fixed-strings",
|
|
2606
|
+
"--help",
|
|
2607
|
+
"--ignore-case",
|
|
2608
|
+
"--initial-tab",
|
|
2609
|
+
"--invert-match",
|
|
2610
|
+
"--line-buffered",
|
|
2611
|
+
"--line-number",
|
|
2612
|
+
"--line-regexp",
|
|
2613
|
+
"--no-filename",
|
|
2614
|
+
"--no-group-separator",
|
|
2615
|
+
"--no-ignore-case",
|
|
2616
|
+
"--no-messages",
|
|
2617
|
+
"--null",
|
|
2618
|
+
"--null-data",
|
|
2619
|
+
"--only-matching",
|
|
2620
|
+
"--perl-regexp",
|
|
2621
|
+
"--quiet",
|
|
2622
|
+
"--recursive",
|
|
2623
|
+
"--silent",
|
|
2624
|
+
"--text",
|
|
2625
|
+
"--version",
|
|
2626
|
+
"--with-filename",
|
|
2627
|
+
"--word-regexp"
|
|
2628
|
+
]),
|
|
2629
|
+
patternFlags: /* @__PURE__ */ new Set(["-e", "--regexp"]),
|
|
2630
|
+
noPatternFlags: /* @__PURE__ */ new Set(["-f", "--file"])
|
|
2631
|
+
};
|
|
2632
|
+
PATTERN_VERBS = {
|
|
2633
|
+
grep: GREP_SHAPE,
|
|
2634
|
+
// /usr/bin/egrep and /usr/bin/fgrep are 41-byte shell wrappers that exec
|
|
2635
|
+
// `grep -E` and `grep -F`. Read in full, not assumed.
|
|
2636
|
+
egrep: GREP_SHAPE,
|
|
2637
|
+
fgrep: GREP_SHAPE,
|
|
2638
|
+
// ripgrep 14.1.1, complete from its own --help. An earlier comment here said rg
|
|
2639
|
+
// was not installed on the measuring machine; it is, and that stale claim is why
|
|
2640
|
+
// this table shipped incomplete for two rounds, blocking ordinary searches like
|
|
2641
|
+
// `rg --sort-files .env src` (/code-review round 4).
|
|
2642
|
+
rg: {
|
|
2643
|
+
takesValue: /* @__PURE__ */ new Set([
|
|
2644
|
+
"-A",
|
|
2645
|
+
"-B",
|
|
2646
|
+
"-C",
|
|
2647
|
+
"-d",
|
|
2648
|
+
"-E",
|
|
2649
|
+
"-e",
|
|
2650
|
+
"-f",
|
|
2651
|
+
"-g",
|
|
2652
|
+
"-j",
|
|
2653
|
+
"-M",
|
|
2654
|
+
"-m",
|
|
2655
|
+
"-r",
|
|
2656
|
+
"-t",
|
|
2657
|
+
"-T",
|
|
2658
|
+
"--after-context",
|
|
2659
|
+
"--before-context",
|
|
2660
|
+
"--color",
|
|
2661
|
+
"--colors",
|
|
2662
|
+
"--context",
|
|
2663
|
+
"--context-separator",
|
|
2664
|
+
"--dfa-size-limit",
|
|
2665
|
+
"--encoding",
|
|
2666
|
+
"--engine",
|
|
2667
|
+
"--field-context-separator",
|
|
2668
|
+
"--field-match-separator",
|
|
2669
|
+
"--file",
|
|
2670
|
+
"--generate",
|
|
2671
|
+
"--glob",
|
|
2672
|
+
"--hostname-bin",
|
|
2673
|
+
"--hyperlink-format",
|
|
2674
|
+
"--iglob",
|
|
2675
|
+
"--ignore-file",
|
|
2676
|
+
"--max-columns",
|
|
2677
|
+
"--max-count",
|
|
2678
|
+
"--max-depth",
|
|
2679
|
+
// An ALIAS of --max-depth, and it takes a value. The round-5 extraction read
|
|
2680
|
+
// alias lines as plain switches and swept it into noValue, which hard-blocked
|
|
2681
|
+
// `rg --maxdepth 2 .env src` while `--max-depth 2` ran (/code-review round 7).
|
|
2682
|
+
"--maxdepth",
|
|
2683
|
+
"--max-filesize",
|
|
2684
|
+
"--path-separator",
|
|
2685
|
+
"--pre",
|
|
2686
|
+
"--pre-glob",
|
|
2687
|
+
"--regexp",
|
|
2688
|
+
"--regex-size-limit",
|
|
2689
|
+
"--replace",
|
|
2690
|
+
"--sort",
|
|
2691
|
+
"--sortr",
|
|
2692
|
+
"--threads",
|
|
2693
|
+
"--type",
|
|
2694
|
+
"--type-add",
|
|
2695
|
+
"--type-clear",
|
|
2696
|
+
"--type-not"
|
|
2697
|
+
]),
|
|
2698
|
+
noValue: /* @__PURE__ */ new Set([
|
|
2699
|
+
"-.",
|
|
2700
|
+
"-0",
|
|
2701
|
+
"-a",
|
|
2702
|
+
"-b",
|
|
2703
|
+
"-c",
|
|
2704
|
+
"-F",
|
|
2705
|
+
"-h",
|
|
2706
|
+
"-H",
|
|
2707
|
+
"-i",
|
|
2708
|
+
"-I",
|
|
2709
|
+
"-l",
|
|
2710
|
+
"-L",
|
|
2711
|
+
"-n",
|
|
2712
|
+
"-N",
|
|
2713
|
+
"-o",
|
|
2714
|
+
"-p",
|
|
2715
|
+
"-P",
|
|
2716
|
+
"-q",
|
|
2717
|
+
"-s",
|
|
2718
|
+
"-S",
|
|
2719
|
+
"-u",
|
|
2720
|
+
"-U",
|
|
2721
|
+
"-v",
|
|
2722
|
+
"-V",
|
|
2723
|
+
"-w",
|
|
2724
|
+
"-x",
|
|
2725
|
+
"-z",
|
|
2726
|
+
"--auto-hybrid-regex",
|
|
2727
|
+
"--binary",
|
|
2728
|
+
"--block-buffered",
|
|
2729
|
+
"--byte-offset",
|
|
2730
|
+
"--case-sensitive",
|
|
2731
|
+
"--column",
|
|
2732
|
+
"--count",
|
|
2733
|
+
"--count-matches",
|
|
2734
|
+
"--crlf",
|
|
2735
|
+
"--debug",
|
|
2736
|
+
"--files",
|
|
2737
|
+
"--files-with-matches",
|
|
2738
|
+
"--files-without-match",
|
|
2739
|
+
"--fixed-strings",
|
|
2740
|
+
"--follow",
|
|
2741
|
+
"--glob-case-insensitive",
|
|
2742
|
+
"--heading",
|
|
2743
|
+
"--help",
|
|
2744
|
+
"--hidden",
|
|
2745
|
+
"--ignore-case",
|
|
2746
|
+
"--ignore-file-case-insensitive",
|
|
2747
|
+
"--include-zero",
|
|
2748
|
+
"--invert-match",
|
|
2749
|
+
"--json",
|
|
2750
|
+
"--line-buffered",
|
|
2751
|
+
"--line-number",
|
|
2752
|
+
"--line-regexp",
|
|
2753
|
+
"--max-columns-preview",
|
|
2754
|
+
"--mmap",
|
|
2755
|
+
"--multiline",
|
|
2756
|
+
"--multiline-dotall",
|
|
2757
|
+
"--no-column",
|
|
2758
|
+
"--no-config",
|
|
2759
|
+
"--no-context-separator",
|
|
2760
|
+
"--no-encoding",
|
|
2761
|
+
"--no-filename",
|
|
2762
|
+
"--no-ignore",
|
|
2763
|
+
"--no-ignore-dot",
|
|
2764
|
+
"--no-ignore-exclude",
|
|
2765
|
+
"--no-ignore-files",
|
|
2766
|
+
"--no-ignore-global",
|
|
2767
|
+
"--no-ignore-messages",
|
|
2768
|
+
"--no-ignore-parent",
|
|
2769
|
+
"--no-ignore-vcs",
|
|
2770
|
+
"--no-line-number",
|
|
2771
|
+
"--no-messages",
|
|
2772
|
+
"--no-pcre2-unicode",
|
|
2773
|
+
"--no-pre",
|
|
2774
|
+
"--no-require-git",
|
|
2775
|
+
"--no-unicode",
|
|
2776
|
+
"--null",
|
|
2777
|
+
"--null-data",
|
|
2778
|
+
"--one-file-system",
|
|
2779
|
+
"--only-matching",
|
|
2780
|
+
"--passthru",
|
|
2781
|
+
"--pcre2",
|
|
2782
|
+
"--pcre2-version",
|
|
2783
|
+
"--pretty",
|
|
2784
|
+
"--print0",
|
|
2785
|
+
"--quiet",
|
|
2786
|
+
"--search-zip",
|
|
2787
|
+
"--smart-case",
|
|
2788
|
+
"--sort-files",
|
|
2789
|
+
"--stats",
|
|
2790
|
+
"--stop-on-nonmatch",
|
|
2791
|
+
"--text",
|
|
2792
|
+
"--trace",
|
|
2793
|
+
"--trim",
|
|
2794
|
+
"--type-list",
|
|
2795
|
+
"--unrestricted",
|
|
2796
|
+
"--version",
|
|
2797
|
+
"--vimgrep",
|
|
2798
|
+
"--with-filename",
|
|
2799
|
+
"--word-regexp",
|
|
2800
|
+
// The COMPLETE remainder of `rg --help`, added after /code-review round 5
|
|
2801
|
+
// found 40 documented switches in neither set. The table is now the whole
|
|
2802
|
+
// option list: `rg --help` yields 149 long options, 35 of them value-taking.
|
|
2803
|
+
"--ignore",
|
|
2804
|
+
"--ignore-dot",
|
|
2805
|
+
"--ignore-exclude",
|
|
2806
|
+
"--ignore-files",
|
|
2807
|
+
"--ignore-global",
|
|
2808
|
+
"--ignore-messages",
|
|
2809
|
+
"--ignore-parent",
|
|
2810
|
+
"--ignore-vcs",
|
|
2811
|
+
"--messages",
|
|
2812
|
+
"--no-auto-hybrid-regex",
|
|
2813
|
+
"--no-binary",
|
|
2814
|
+
"--no-block-buffered",
|
|
2815
|
+
"--no-byte-offset",
|
|
2816
|
+
"--no-crlf",
|
|
2817
|
+
"--no-fixed-strings",
|
|
2818
|
+
"--no-follow",
|
|
2819
|
+
"--no-glob-case-insensitive",
|
|
2820
|
+
"--no-heading",
|
|
2821
|
+
"--no-hidden",
|
|
2822
|
+
"--no-ignore-file-case-insensitive",
|
|
2823
|
+
"--no-include-zero",
|
|
2824
|
+
"--no-invert-match",
|
|
2825
|
+
"--no-json",
|
|
2826
|
+
"--no-line-buffered",
|
|
2827
|
+
"--no-max-columns-preview",
|
|
2828
|
+
"--no-mmap",
|
|
2829
|
+
"--no-multiline",
|
|
2830
|
+
"--no-multiline-dotall",
|
|
2831
|
+
"--no-one-file-system",
|
|
2832
|
+
"--no-pcre2",
|
|
2833
|
+
"--no-search-zip",
|
|
2834
|
+
"--no-sort-files",
|
|
2835
|
+
"--no-stats",
|
|
2836
|
+
"--no-text",
|
|
2837
|
+
"--no-trim",
|
|
2838
|
+
"--passthrough",
|
|
2839
|
+
"--pcre2-unicode",
|
|
2840
|
+
"--require-git",
|
|
2841
|
+
"--unicode"
|
|
2842
|
+
]),
|
|
2843
|
+
patternFlags: /* @__PURE__ */ new Set(["-e", "--regexp"]),
|
|
2844
|
+
// `--files` and `--type-list` list or enumerate without a pattern. Founder
|
|
2845
|
+
// decision 2026-09-13: `rg --files ~/.ssh` stays BLOCKED, which this achieves
|
|
2846
|
+
// by leaving the directory in the judged list.
|
|
2847
|
+
noPatternFlags: /* @__PURE__ */ new Set(["-f", "--file", "--files", "--type-list", "--pcre2-version"])
|
|
2848
|
+
}
|
|
2849
|
+
};
|
|
2850
|
+
PATTERN_VERB_NAMES = Object.keys(PATTERN_VERBS);
|
|
2851
|
+
GREP_FILE_OPERANDS = /* @__PURE__ */ new Set(["-f", "--file", "--exclude-from", "--include"]);
|
|
2852
|
+
READER_VALUE_LETTERS = {
|
|
2853
|
+
awk: ["F", "v", "f"],
|
|
2854
|
+
gawk: ["F", "v", "f", "e", "E", "i", "l", "o", "p", "D"],
|
|
2855
|
+
sed: ["e", "f", "i", "l"],
|
|
2856
|
+
sort: ["C", "k", "o", "S", "t", "T"]
|
|
2857
|
+
};
|
|
2858
|
+
FILE_OPERAND_FLAGS = {
|
|
2859
|
+
grep: GREP_FILE_OPERANDS,
|
|
2860
|
+
egrep: GREP_FILE_OPERANDS,
|
|
2861
|
+
fgrep: GREP_FILE_OPERANDS,
|
|
2862
|
+
// rg and gawk are NOT installed on the measuring machine: these two rows are
|
|
2863
|
+
// from the shipped documentation (ripgrep `-f/--file`, `--ignore-file`; gawk
|
|
2864
|
+
// `-f/--file`) and are marked as such in the spec.
|
|
2865
|
+
// `-g/--glob/--iglob` name which files ripgrep SEARCHES, so an operand naming
|
|
2866
|
+
// a credential makes rg open it -- the same reason grep's `--include` is here.
|
|
2867
|
+
// Measured (/code-review round 3): `rg --hidden -g .env AWS tree` opened .env
|
|
2868
|
+
// and printed its contents.
|
|
2869
|
+
rg: /* @__PURE__ */ new Set(["-f", "--file", "--ignore-file", "-g", "--glob", "--iglob"]),
|
|
2870
|
+
sed: /* @__PURE__ */ new Set(["-f", "--file"]),
|
|
2871
|
+
awk: /* @__PURE__ */ new Set(["-f", "--file"]),
|
|
2872
|
+
gawk: /* @__PURE__ */ new Set(["-f", "--file"]),
|
|
2873
|
+
sort: /* @__PURE__ */ new Set(["--files0-from"])
|
|
2874
|
+
};
|
|
2875
|
+
SCP_VALUE_FLAGS = ["i", "F", "o", "c", "S", "P", "J", "D", "W", "l"];
|
|
2876
|
+
TAR_VALUE_LETTERS = ["b", "C", "f", "F", "g", "H", "I", "K", "L", "N", "T", "V", "X"];
|
|
2877
|
+
ZIP_VALUE_LETTERS = ["b", "n", "P", "t", "s", "O", "x", "i"];
|
|
2878
|
+
RSYNC_VALUE_LETTERS = ["e", "f", "T", "B", "M"];
|
|
2879
|
+
RSYNC_SKIP = [
|
|
2880
|
+
"e",
|
|
2881
|
+
"--rsh",
|
|
2882
|
+
"--exclude",
|
|
2883
|
+
"--exclude-from",
|
|
2884
|
+
"--include",
|
|
2885
|
+
"--include-from",
|
|
2886
|
+
"--files-from",
|
|
2887
|
+
"f",
|
|
2888
|
+
"--filter"
|
|
2889
|
+
];
|
|
2890
|
+
CP_VALUE_LETTERS = ["S", "t"];
|
|
2891
|
+
INSTALL_VALUE_LETTERS = ["S", "t", "g", "m", "o"];
|
|
2892
|
+
COPY_VERBS = {
|
|
2893
|
+
cp: { source: "allButLast", targetDirFlag: true, valueLetters: CP_VALUE_LETTERS },
|
|
2894
|
+
mv: { source: "allButLast", targetDirFlag: true, valueLetters: CP_VALUE_LETTERS },
|
|
2895
|
+
install: { source: "allButLast", targetDirFlag: true, valueLetters: INSTALL_VALUE_LETTERS },
|
|
2896
|
+
ln: { source: "first", targetDirFlag: true, valueLetters: CP_VALUE_LETTERS },
|
|
2897
|
+
scp: { source: "allButLast", skipFlags: SCP_VALUE_FLAGS, valueLetters: SCP_VALUE_FLAGS },
|
|
2898
|
+
rsync: { source: "allButLast", skipFlags: RSYNC_SKIP, valueLetters: RSYNC_VALUE_LETTERS },
|
|
2899
|
+
tar: {
|
|
2900
|
+
source: "archive",
|
|
2901
|
+
archive: "tar",
|
|
2902
|
+
skipFlags: ["f", "X", "T", "--file", "--exclude", "--exclude-from", "--files-from"],
|
|
2903
|
+
valueLetters: TAR_VALUE_LETTERS
|
|
2904
|
+
},
|
|
2905
|
+
zip: {
|
|
2906
|
+
source: "archive",
|
|
2907
|
+
archive: "zip",
|
|
2908
|
+
skipFlags: ["x", "i", "--exclude", "--include"],
|
|
2909
|
+
valueLetters: ZIP_VALUE_LETTERS
|
|
2910
|
+
},
|
|
2911
|
+
ar: { source: "archive", archive: "ar" },
|
|
2912
|
+
// 7z switches are INLINE only (`-mx9`, `-px`, `-x!pat`), so no following word is
|
|
2913
|
+
// ever a switch's operand -- which is why the short `x` is NOT a skipFlag here
|
|
2914
|
+
// and valueLetters is empty. Keeping the short `x` made `7z a out.7z -mx KEY`
|
|
2915
|
+
// drop the credential as an exclusion operand (/code-review round 8), and the
|
|
2916
|
+
// derived "every skipped letter is a value letter" row in jail-copy.spec.ts is
|
|
2917
|
+
// what keeps the two tables honest about it.
|
|
2918
|
+
"7z": { source: "archive", archive: "7z", skipFlags: ["--exclude"], valueLetters: [] },
|
|
2919
|
+
gzip: { source: "all" },
|
|
2920
|
+
bzip2: { source: "all" },
|
|
2921
|
+
xz: { source: "all" },
|
|
2922
|
+
"docker cp": { source: "allButLast" },
|
|
2923
|
+
"kubectl cp": { source: "allButLast" },
|
|
2924
|
+
"gsutil cp": { source: "allButLast" },
|
|
2925
|
+
"gsutil rsync": { source: "allButLast" },
|
|
2926
|
+
"rclone copy": { source: "allButLast" },
|
|
2927
|
+
"rclone sync": { source: "allButLast" },
|
|
2928
|
+
"aws s3 cp": { source: "allButLast" },
|
|
2929
|
+
"aws s3 mv": { source: "allButLast" },
|
|
2930
|
+
"aws s3 sync": { source: "allButLast" },
|
|
2931
|
+
"gcloud storage cp": { source: "allButLast" },
|
|
2932
|
+
"az storage blob upload": { source: "flagOperand", sourceFlags: ["f", "--file"] }
|
|
2933
|
+
};
|
|
2934
|
+
TAR_MODE_WORD = /^[a-zA-Z]+$/;
|
|
2935
|
+
COPY_VERB_HEADS = new Set(Object.keys(COPY_VERBS).map((k) => k.split(" ")[0]));
|
|
2168
2936
|
FS_OP_PRESCREEN_RE = new RegExp(
|
|
2169
2937
|
// A quote is a separator too: `eval "cat X"` and `sh -c 'cat X'` put the
|
|
2170
2938
|
// reader right after `"` / `'`, and without these two characters the
|
|
2171
2939
|
// prescreen rejected every string-wrapped read before the parser ran.
|
|
2172
2940
|
// Found 2026-09-11 by instrumenting the walk -- no CallExpr was ever visited.
|
|
2173
|
-
`(?:^|[\\s|;&("'\`\\n])(?:rm|${[...FS_READ_TOOLS].map((t) => t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})\\b|(?<!<)<(?!<)`
|
|
2941
|
+
`(?:^|[\\s|;&("'\`\\n/])(?:rm|${[...FS_READ_TOOLS, ...COPY_VERB_HEADS].map((t) => t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})\\b|(?<!<)<(?!<)`
|
|
2174
2942
|
);
|
|
2175
2943
|
HOME_CACHE_ALLOWLIST = [
|
|
2176
2944
|
".cache",
|
|
@@ -2349,8 +3117,17 @@ var init_dist = __esm({
|
|
|
2349
3117
|
deriveRedirOp("cat <<X\nX"),
|
|
2350
3118
|
deriveRedirOp("cat <<-X\nX")
|
|
2351
3119
|
]);
|
|
2352
|
-
|
|
2353
|
-
|
|
3120
|
+
FIND_OPTIONS = /* @__PURE__ */ new Set(["-H", "-L", "-P"]);
|
|
3121
|
+
COPY_RULE_OF = {
|
|
3122
|
+
"shield:project-jail:block-read-ssh": "shield:project-jail:review-copy-ssh",
|
|
3123
|
+
"shield:project-jail:block-read-aws": "shield:project-jail:review-copy-aws",
|
|
3124
|
+
"shield:project-jail:block-read-env": "shield:project-jail:review-copy-env",
|
|
3125
|
+
"shield:project-jail:review-read-credentials": "shield:project-jail:review-copy-credentials"
|
|
3126
|
+
};
|
|
3127
|
+
isReaderWord = (w) => w !== null && FS_READ_TOOLS.has(baseWord(w));
|
|
3128
|
+
positionalAfter = (words, from, to = words.length) => positionedArgs(words, from, to).map((a) => a.value);
|
|
3129
|
+
NONE = { kind: "none" };
|
|
3130
|
+
UNKNOWN = { kind: "unknown" };
|
|
2354
3131
|
SOURCE_COMMANDS = /* @__PURE__ */ new Set([...FS_READ_TOOLS, "tee"]);
|
|
2355
3132
|
SSRF_MAX_HOST = 253;
|
|
2356
3133
|
METADATA_ADDRESSES = /* @__PURE__ */ new Set([
|
|
@@ -7278,10 +8055,16 @@ function buildRuleToShieldMap() {
|
|
|
7278
8055
|
}
|
|
7279
8056
|
return map;
|
|
7280
8057
|
}
|
|
8058
|
+
function shieldOfRule(map, rule) {
|
|
8059
|
+
const exact = map.get(rule);
|
|
8060
|
+
if (exact) return exact;
|
|
8061
|
+
const m = /^shield:([^:]+):/.exec(rule);
|
|
8062
|
+
return m && SHIELDS[m[1]] ? m[1] : void 0;
|
|
8063
|
+
}
|
|
7281
8064
|
function applyActivityToShields(agg, e, ruleToShield) {
|
|
7282
8065
|
if (e.kind !== "tool" || !e.checkedBy) return agg;
|
|
7283
8066
|
if (e.verdict !== "block" && e.verdict !== "review") return agg;
|
|
7284
|
-
const shieldName = ruleToShield
|
|
8067
|
+
const shieldName = shieldOfRule(ruleToShield, e.checkedBy);
|
|
7285
8068
|
if (!shieldName) return agg;
|
|
7286
8069
|
const current = agg.byShield[shieldName] ?? { blocks: 0, reviews: 0 };
|
|
7287
8070
|
const updated = {
|
|
@@ -8277,7 +9060,7 @@ function PeriodShields({
|
|
|
8277
9060
|
const byShield = /* @__PURE__ */ new Map();
|
|
8278
9061
|
if (data) {
|
|
8279
9062
|
for (const [rule, count] of data.ruleMap) {
|
|
8280
|
-
const shield = ruleToShield
|
|
9063
|
+
const shield = shieldOfRule(ruleToShield, rule);
|
|
8281
9064
|
if (!shield) continue;
|
|
8282
9065
|
byShield.set(shield, (byShield.get(shield) ?? 0) + count);
|
|
8283
9066
|
}
|