@node9/proxy 2.22.0 → 2.23.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 +1283 -949
- package/dist/cli.mjs +1254 -920
- package/dist/dashboard.mjs +458 -88
- package/dist/index.js +293 -24
- package/dist/index.mjs +293 -24
- package/dist/scan-ink.mjs +12 -0
- package/package.json +1 -1
package/dist/dashboard.mjs
CHANGED
|
@@ -741,9 +741,9 @@ function matchesPattern(text, patterns) {
|
|
|
741
741
|
const withoutDotSlash = text.replace(/^\.\//, "");
|
|
742
742
|
return isMatch(withoutDotSlash) || isMatch(`./${withoutDotSlash}`);
|
|
743
743
|
}
|
|
744
|
-
function getNestedValue(obj,
|
|
744
|
+
function getNestedValue(obj, path17) {
|
|
745
745
|
if (!obj || typeof obj !== "object") return null;
|
|
746
|
-
const segments2 =
|
|
746
|
+
const segments2 = path17.split(".");
|
|
747
747
|
for (const seg of segments2) {
|
|
748
748
|
if (FORBIDDEN_PATH_SEGMENTS.has(seg)) return null;
|
|
749
749
|
}
|
|
@@ -945,6 +945,7 @@ function normalizeCommandForPolicyImpl(command) {
|
|
|
945
945
|
const source = command.slice(s, e);
|
|
946
946
|
if (resolved === source) continue;
|
|
947
947
|
if (resolved === "" || /\s/.test(resolved)) continue;
|
|
948
|
+
if (/^[;&|()<>]+$/.test(resolved)) continue;
|
|
948
949
|
rewrites.push([s, e, resolved]);
|
|
949
950
|
const quoteOnly = source.replace(/['"]/g, "");
|
|
950
951
|
if (quoteOnly !== source) quoteOnlyRewrites.push([s, e, quoteOnly]);
|
|
@@ -1138,23 +1139,147 @@ function extractLiteralArgs(callExpr) {
|
|
|
1138
1139
|
const args = positionedArgs(words);
|
|
1139
1140
|
return { name, flags, paths: args.map((a) => a.value), words, args };
|
|
1140
1141
|
}
|
|
1142
|
+
function payloadKey(payload) {
|
|
1143
|
+
if (!assignmentTable || assignmentTable.size === 0) return payload;
|
|
1144
|
+
const bindings = [];
|
|
1145
|
+
for (const [name, rec] of assignmentTable) {
|
|
1146
|
+
if (rec.value !== null) bindings.push(`${name}=${rec.value}`);
|
|
1147
|
+
}
|
|
1148
|
+
return `${payload}\0${bindings.sort().join("")}`;
|
|
1149
|
+
}
|
|
1150
|
+
function claimPayload(payload) {
|
|
1151
|
+
if (!seenPayloads) return "ok";
|
|
1152
|
+
const key = payloadKey(payload);
|
|
1153
|
+
if (seenPayloads.has(key)) return "seen";
|
|
1154
|
+
if (payloadBudget <= 0) return "exhausted";
|
|
1155
|
+
payloadBudget--;
|
|
1156
|
+
seenPayloads.add(key);
|
|
1157
|
+
return "ok";
|
|
1158
|
+
}
|
|
1159
|
+
function recordTopLevelAssignments(f) {
|
|
1160
|
+
const stmts = Array.isArray(f?.Stmts) ? f.Stmts : [];
|
|
1161
|
+
for (const stmt of stmts) recordTopLevelStmt(stmt);
|
|
1162
|
+
}
|
|
1163
|
+
function probeBinOp(src) {
|
|
1164
|
+
try {
|
|
1165
|
+
const cmd = syntax.NewParser().Parse(src, "probe")?.Stmts?.[0]?.Cmd;
|
|
1166
|
+
if (cmd && syntax.NodeType(cmd) === "BinaryCmd") return cmd.Op;
|
|
1167
|
+
} catch {
|
|
1168
|
+
}
|
|
1169
|
+
return null;
|
|
1170
|
+
}
|
|
1171
|
+
function recordTopLevelStmt(stmt) {
|
|
1172
|
+
if (!stmt || !stmt.Cmd) return false;
|
|
1173
|
+
const t = syntax.NodeType(stmt.Cmd);
|
|
1174
|
+
if (t === "BinaryCmd") {
|
|
1175
|
+
if (!AND_OR_OPS.has(stmt.Cmd.Op)) return false;
|
|
1176
|
+
if (!recordTopLevelStmt(stmt.Cmd.X)) return false;
|
|
1177
|
+
if (stmt.Cmd.Op === AND_OP) return recordTopLevelStmt(stmt.Cmd.Y);
|
|
1178
|
+
return true;
|
|
1179
|
+
}
|
|
1180
|
+
if (t !== "CallExpr" && t !== "DeclClause") return false;
|
|
1181
|
+
let at = 0;
|
|
1182
|
+
try {
|
|
1183
|
+
at = stmt.Pos().Offset();
|
|
1184
|
+
} catch {
|
|
1185
|
+
at = 0;
|
|
1186
|
+
}
|
|
1187
|
+
recordAssignments(stmt.Cmd, at);
|
|
1188
|
+
if (stmt.Negated) return false;
|
|
1189
|
+
if (t === "DeclClause") return ASSIGNMENT_HEADS.has(stmt.Cmd.Variant?.Value ?? "");
|
|
1190
|
+
return (stmt.Cmd.Args || []).length === 0 && (stmt.Cmd.Assigns || []).length > 0;
|
|
1191
|
+
}
|
|
1192
|
+
function recordAssignments(n, at) {
|
|
1193
|
+
if (!assignmentTable) return;
|
|
1194
|
+
const t = syntax.NodeType(n);
|
|
1195
|
+
let assigns = [];
|
|
1196
|
+
if (t === "CallExpr") {
|
|
1197
|
+
if ((n.Args || []).length > 0) return;
|
|
1198
|
+
assigns = n.Assigns || [];
|
|
1199
|
+
} else if (t === "DeclClause") {
|
|
1200
|
+
if (!ASSIGNMENT_HEADS.has(n.Variant?.Value ?? "")) return;
|
|
1201
|
+
assigns = (n.Args || []).filter((a) => syntax.NodeType(a) === "Assign");
|
|
1202
|
+
} else return;
|
|
1203
|
+
for (const a of assigns) {
|
|
1204
|
+
const name = a?.Name?.Value;
|
|
1205
|
+
if (!name || !a.Value || a.Append) continue;
|
|
1206
|
+
assignmentTable.set(name, { value: resolveWordLiteral(a.Value), at });
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
function resolveTrivialSubst(part) {
|
|
1210
|
+
if (syntax.NodeType(part) !== "CmdSubst") return void 0;
|
|
1211
|
+
const stmts = part.Stmts || [];
|
|
1212
|
+
if (stmts.length !== 1) return void 0;
|
|
1213
|
+
const st = stmts[0];
|
|
1214
|
+
if ((st.Redirs || []).length > 0 || st.Negated || st.Background) return void 0;
|
|
1215
|
+
const cmd = st.Cmd;
|
|
1216
|
+
if (!cmd || syntax.NodeType(cmd) !== "CallExpr") return void 0;
|
|
1217
|
+
if ((cmd.Assigns || []).length > 0) return void 0;
|
|
1218
|
+
const words = (cmd.Args || []).map((a) => resolveWordLiteral(a));
|
|
1219
|
+
if (words.length === 0 || words.some((w) => w === null)) return void 0;
|
|
1220
|
+
const head = baseWord(words[0]);
|
|
1221
|
+
const rest = words.slice(1);
|
|
1222
|
+
if (head === "echo") {
|
|
1223
|
+
let i = 0;
|
|
1224
|
+
while (i < rest.length && /^-[neE]+$/.test(rest[i])) i++;
|
|
1225
|
+
return rest.slice(i).join(" ");
|
|
1226
|
+
}
|
|
1227
|
+
if (head === "printf") {
|
|
1228
|
+
if (rest.length !== 2) return void 0;
|
|
1229
|
+
if (!/^%s(\\n)?$/.test(rest[0])) return void 0;
|
|
1230
|
+
return rest[1];
|
|
1231
|
+
}
|
|
1232
|
+
return void 0;
|
|
1233
|
+
}
|
|
1234
|
+
function recordedExpansion(name) {
|
|
1235
|
+
if (!assignmentTable || !name) return void 0;
|
|
1236
|
+
const rec = assignmentTable.get(name);
|
|
1237
|
+
if (rec === void 0 || rec.at >= currentStmtOffset) return void 0;
|
|
1238
|
+
return rec.value;
|
|
1239
|
+
}
|
|
1240
|
+
function isPlainParam(p) {
|
|
1241
|
+
if (syntax.NodeType(p) !== "ParamExp") return false;
|
|
1242
|
+
return !(p.Excl || p.Length || p.Width || p.Index || p.Slice || p.Repl || p.Exp);
|
|
1243
|
+
}
|
|
1244
|
+
function expandPlainParam(p) {
|
|
1245
|
+
if (!assignmentTable) return void 0;
|
|
1246
|
+
if (!isPlainParam(p)) return void 0;
|
|
1247
|
+
const recorded = recordedExpansion(p.Param?.Value);
|
|
1248
|
+
if (recorded !== void 0) return recorded;
|
|
1249
|
+
return HOME_VARIABLES.has(p.Param?.Value) ? "~" : void 0;
|
|
1250
|
+
}
|
|
1141
1251
|
function resolveWordLiteral(w) {
|
|
1142
1252
|
const parts = w?.Parts || [];
|
|
1143
1253
|
let s = "";
|
|
1144
1254
|
for (const p of parts) {
|
|
1145
|
-
const
|
|
1146
|
-
if (
|
|
1147
|
-
|
|
1148
|
-
else if (t === "DblQuoted") {
|
|
1149
|
-
const inner = p.Parts || [];
|
|
1150
|
-
if (!inner.every((ip) => syntax.NodeType(ip) === "Lit")) return null;
|
|
1151
|
-
s += inner.map((ip) => ip.Value ?? "").join("");
|
|
1152
|
-
} else {
|
|
1153
|
-
return null;
|
|
1154
|
-
}
|
|
1255
|
+
const piece = resolvePart(p, false);
|
|
1256
|
+
if (piece === void 0 || piece === null) return null;
|
|
1257
|
+
s += piece;
|
|
1155
1258
|
}
|
|
1156
1259
|
return s;
|
|
1157
1260
|
}
|
|
1261
|
+
function resolvePart(p, inQuotes) {
|
|
1262
|
+
const t = syntax.NodeType(p);
|
|
1263
|
+
if (t === "Lit") {
|
|
1264
|
+
const raw = p.Value ?? "";
|
|
1265
|
+
if (!inQuotes) return raw.replace(/\\(.)/g, "$1");
|
|
1266
|
+
return assignmentTable ? raw.replace(/\\([$`"\\])/g, "$1") : raw;
|
|
1267
|
+
}
|
|
1268
|
+
if (t === "SglQuoted") return p.Value ?? "";
|
|
1269
|
+
if (t === "ParamExp") return expandPlainParam(p);
|
|
1270
|
+
if (t === "CmdSubst") return assignmentTable ? resolveTrivialSubst(p) : void 0;
|
|
1271
|
+
if (t === "DblQuoted" && !inQuotes) {
|
|
1272
|
+
const inner = p.Parts || [];
|
|
1273
|
+
let out = "";
|
|
1274
|
+
for (const ip of inner) {
|
|
1275
|
+
const piece = resolvePart(ip, true);
|
|
1276
|
+
if (piece === void 0 || piece === null) return piece;
|
|
1277
|
+
out += piece;
|
|
1278
|
+
}
|
|
1279
|
+
return out;
|
|
1280
|
+
}
|
|
1281
|
+
return void 0;
|
|
1282
|
+
}
|
|
1158
1283
|
function analyzeFsOperation(command) {
|
|
1159
1284
|
const normalized = normalizeCommandForPolicy(command);
|
|
1160
1285
|
if (!FS_OP_PRESCREEN_RE.test(normalized)) return null;
|
|
@@ -1190,17 +1315,35 @@ function analyzeFsOperationImpl(command, depth = 0) {
|
|
|
1190
1315
|
const f = parseShared(command);
|
|
1191
1316
|
if (f === PARSE_FAIL) return null;
|
|
1192
1317
|
let result = null;
|
|
1318
|
+
const outerTable = assignmentTable;
|
|
1319
|
+
const outerOffset = currentStmtOffset;
|
|
1320
|
+
const outerSeen = seenPayloads;
|
|
1321
|
+
const outerBudget = payloadBudget;
|
|
1322
|
+
if (depth === 0) {
|
|
1323
|
+
seenPayloads = /* @__PURE__ */ new Set();
|
|
1324
|
+
payloadBudget = PAYLOAD_BUDGET;
|
|
1325
|
+
}
|
|
1326
|
+
assignmentTable = new Map(
|
|
1327
|
+
[...outerTable ?? []].map(([k, r]) => [k, { value: r.value, at: -1 }])
|
|
1328
|
+
);
|
|
1329
|
+
currentStmtOffset = Number.MAX_SAFE_INTEGER;
|
|
1193
1330
|
try {
|
|
1331
|
+
recordTopLevelAssignments(f);
|
|
1194
1332
|
syntax.Walk(f, (node) => {
|
|
1195
1333
|
if (!node || result?.verdict === "block") return false;
|
|
1196
1334
|
const n = node;
|
|
1197
1335
|
const nodeType = syntax.NodeType(n);
|
|
1198
1336
|
if (nodeType === "Stmt") {
|
|
1337
|
+
try {
|
|
1338
|
+
currentStmtOffset = n.Pos().Offset();
|
|
1339
|
+
} catch {
|
|
1340
|
+
currentStmtOffset = Number.MAX_SAFE_INTEGER;
|
|
1341
|
+
}
|
|
1199
1342
|
result = stricter(result, jailedRedirectRead(n));
|
|
1200
1343
|
return result?.verdict !== "block";
|
|
1201
1344
|
}
|
|
1202
1345
|
if (nodeType !== "CallExpr") return true;
|
|
1203
|
-
const { name, flags, paths, words
|
|
1346
|
+
const { name, flags, paths, words } = extractLiteralArgs(n);
|
|
1204
1347
|
if (!name) return true;
|
|
1205
1348
|
if (name === "rm") {
|
|
1206
1349
|
const flagStr = flags.join("").toLowerCase();
|
|
@@ -1229,9 +1372,30 @@ function analyzeFsOperationImpl(command, depth = 0) {
|
|
|
1229
1372
|
}
|
|
1230
1373
|
}
|
|
1231
1374
|
}
|
|
1232
|
-
if (depth <
|
|
1375
|
+
if (depth < 24 && name === "find") {
|
|
1376
|
+
for (const action of findActions(words, 0)) {
|
|
1377
|
+
const h = unwrapCommandHead(action);
|
|
1378
|
+
const inner = literalShellPayload(action.slice(h), baseWord(action[h]));
|
|
1379
|
+
if (inner === null) continue;
|
|
1380
|
+
const claim = claimPayload(inner);
|
|
1381
|
+
if (claim === "exhausted") {
|
|
1382
|
+
result = stricter(result, UNANALYSABLE_NESTING);
|
|
1383
|
+
continue;
|
|
1384
|
+
}
|
|
1385
|
+
if (claim === "seen") continue;
|
|
1386
|
+
const v = analyzeFsOperationImpl(inner, depth + 1);
|
|
1387
|
+
result = stricter(result, v);
|
|
1388
|
+
if (result?.verdict === "block") return false;
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
if (depth < 24) {
|
|
1233
1392
|
const payload = literalShellPayload(words, name);
|
|
1234
|
-
|
|
1393
|
+
const claim = payload === null ? "seen" : claimPayload(payload);
|
|
1394
|
+
if (claim === "exhausted") {
|
|
1395
|
+
result = stricter(result, UNANALYSABLE_NESTING);
|
|
1396
|
+
return true;
|
|
1397
|
+
}
|
|
1398
|
+
if (payload !== null && claim === "ok") {
|
|
1235
1399
|
const inner = analyzeFsOperationImpl(payload, depth + 1);
|
|
1236
1400
|
if (inner) {
|
|
1237
1401
|
result = inner;
|
|
@@ -1240,7 +1404,7 @@ function analyzeFsOperationImpl(command, depth = 0) {
|
|
|
1240
1404
|
return true;
|
|
1241
1405
|
}
|
|
1242
1406
|
}
|
|
1243
|
-
const readPaths = FS_READ_TOOLS.has(name) ?
|
|
1407
|
+
const readPaths = FS_READ_TOOLS.has(name) ? readerPaths(words, 0) : wrappedReadPaths(words, name);
|
|
1244
1408
|
if (readPaths) {
|
|
1245
1409
|
for (const p of readPaths) {
|
|
1246
1410
|
result = stricter(result, matchSensitivePath2(p));
|
|
@@ -1255,6 +1419,11 @@ function analyzeFsOperationImpl(command, depth = 0) {
|
|
|
1255
1419
|
return result;
|
|
1256
1420
|
} catch {
|
|
1257
1421
|
return null;
|
|
1422
|
+
} finally {
|
|
1423
|
+
assignmentTable = outerTable;
|
|
1424
|
+
currentStmtOffset = outerOffset;
|
|
1425
|
+
seenPayloads = outerSeen;
|
|
1426
|
+
if (depth === 0) payloadBudget = outerBudget;
|
|
1258
1427
|
}
|
|
1259
1428
|
}
|
|
1260
1429
|
function stricter(a, b) {
|
|
@@ -1311,6 +1480,18 @@ function resolveCopyShape(words, h) {
|
|
|
1311
1480
|
}
|
|
1312
1481
|
return null;
|
|
1313
1482
|
}
|
|
1483
|
+
function findAction(words, k) {
|
|
1484
|
+
const end = words.findIndex((w, i) => i > k && (w === ";" || w === "+"));
|
|
1485
|
+
return words.slice(k + 1, end < 0 ? words.length : end);
|
|
1486
|
+
}
|
|
1487
|
+
function findActions(words, h) {
|
|
1488
|
+
const out = [];
|
|
1489
|
+
for (let i = h + 1; i < words.length; i++) {
|
|
1490
|
+
const w = words[i];
|
|
1491
|
+
if (w !== null && FIND_EXEC_FLAGS.has(w)) out.push(findAction(words, i));
|
|
1492
|
+
}
|
|
1493
|
+
return out;
|
|
1494
|
+
}
|
|
1314
1495
|
function findStartPoints(words, h) {
|
|
1315
1496
|
const k = words.findIndex((w, i) => i > h && w !== null && FIND_EXEC_FLAGS.has(w));
|
|
1316
1497
|
if (k < 0) return { k, starts: [] };
|
|
@@ -1326,8 +1507,13 @@ function copySourcePaths(words) {
|
|
|
1326
1507
|
if (fi >= 0) {
|
|
1327
1508
|
const { k, starts } = findStartPoints(words, fi);
|
|
1328
1509
|
if (k < 0) return [];
|
|
1329
|
-
const
|
|
1330
|
-
|
|
1510
|
+
const out = [];
|
|
1511
|
+
for (const action of findActions(words, fi)) {
|
|
1512
|
+
const h2 = unwrapCommandHead(action);
|
|
1513
|
+
if (!resolveCopyShape(action, h2)) continue;
|
|
1514
|
+
out.push(...starts, ...copySourcePaths(action));
|
|
1515
|
+
}
|
|
1516
|
+
return out;
|
|
1331
1517
|
}
|
|
1332
1518
|
if (!COPY_VERB_HEADS.has(baseWord(words[h]))) return [];
|
|
1333
1519
|
const r = resolveCopyShape(words, h);
|
|
@@ -1504,6 +1690,15 @@ function flagEffect(token, shape, known) {
|
|
|
1504
1690
|
}
|
|
1505
1691
|
return NONE;
|
|
1506
1692
|
}
|
|
1693
|
+
function readerPaths(words, h) {
|
|
1694
|
+
const head = baseWord(words[h]);
|
|
1695
|
+
const from = h + 1;
|
|
1696
|
+
const flags = words.slice(from).filter((w) => w !== null && w.startsWith("-"));
|
|
1697
|
+
return [
|
|
1698
|
+
...readTargets(head, positionedArgs(words, from), flags, words, from),
|
|
1699
|
+
...flagOperandFiles(head, words, from)
|
|
1700
|
+
];
|
|
1701
|
+
}
|
|
1507
1702
|
function readTargets(verb, args, flags, words = [], from = 1) {
|
|
1508
1703
|
const shape = PATTERN_VERBS[verb];
|
|
1509
1704
|
if (!shape) return args.map((a) => a.value);
|
|
@@ -1585,18 +1780,21 @@ function flagOperandFiles(verb, words, from) {
|
|
|
1585
1780
|
function wrappedReadPaths(words, name) {
|
|
1586
1781
|
if (name === "find") {
|
|
1587
1782
|
const { k, starts } = findStartPoints(words, 0);
|
|
1588
|
-
|
|
1783
|
+
if (k < 0) return null;
|
|
1784
|
+
const paths = [];
|
|
1785
|
+
let reads = false;
|
|
1786
|
+
for (const action of findActions(words, 0)) {
|
|
1787
|
+
const h2 = unwrapCommandHead(action);
|
|
1788
|
+
if (!isReaderWord(action[h2] ?? null)) continue;
|
|
1789
|
+
reads = true;
|
|
1790
|
+
paths.push(...readerPaths(action, h2));
|
|
1791
|
+
}
|
|
1792
|
+
return reads ? [...starts, ...paths] : paths;
|
|
1589
1793
|
}
|
|
1590
1794
|
if (!COMMAND_WRAPPERS.has(name) && !RUNNER_WRAPPERS.has(name)) return null;
|
|
1591
1795
|
const h = unwrapCommandHead(words);
|
|
1592
1796
|
if (h <= 0 || !isReaderWord(words[h] ?? null)) return null;
|
|
1593
|
-
|
|
1594
|
-
const rest = words.slice(h + 1);
|
|
1595
|
-
const restFlags = rest.filter((w) => w !== null && w.startsWith("-"));
|
|
1596
|
-
return [
|
|
1597
|
-
...readTargets(head, positionedArgs(words, h + 1), restFlags, words, h + 1),
|
|
1598
|
-
...flagOperandFiles(head, words, h + 1)
|
|
1599
|
-
];
|
|
1797
|
+
return readerPaths(words, h);
|
|
1600
1798
|
}
|
|
1601
1799
|
function literalShellPayload(words, name) {
|
|
1602
1800
|
const h = COMMAND_WRAPPERS.has(name) || RUNNER_WRAPPERS.has(name) ? unwrapCommandHead(words) : 0;
|
|
@@ -1944,7 +2142,7 @@ function matchCanaryArgs(args, values) {
|
|
|
1944
2142
|
return null;
|
|
1945
2143
|
}
|
|
1946
2144
|
}
|
|
1947
|
-
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, SSRF_MAX_HOST, METADATA_ADDRESSES, METADATA_HOSTNAMES, v4Octets, SOURCE_COMMANDS, TERMINAL_ESCAPE_RE, 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;
|
|
2145
|
+
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, HOME_VARIABLES, assignmentTable, currentStmtOffset, PAYLOAD_BUDGET, payloadBudget, seenPayloads, UNANALYSABLE_NESTING, ASSIGNMENT_HEADS, AND_OP, OR_OP, AND_OR_OPS, FS_OP_CACHE_MAX, fsOpCache, REDIR_TRUNCATE_OPS, REDIR_FILE_IN_OPS, REDIR_HEREDOC_OPS, FIND_OPTIONS, COPY_RULE_OF, isReaderWord, positionalAfter, NONE, UNKNOWN, SSRF_MAX_HOST, METADATA_ADDRESSES, METADATA_HOSTNAMES, v4Octets, SOURCE_COMMANDS, TERMINAL_ESCAPE_RE, 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;
|
|
1948
2146
|
var init_dist = __esm({
|
|
1949
2147
|
"packages/policy-engine/dist/index.mjs"() {
|
|
1950
2148
|
"use strict";
|
|
@@ -3154,6 +3352,22 @@ var init_dist = __esm({
|
|
|
3154
3352
|
]);
|
|
3155
3353
|
WRAPPER_TAKES_TARGET = /* @__PURE__ */ new Set(["chroot"]);
|
|
3156
3354
|
FIND_EXEC_FLAGS = /* @__PURE__ */ new Set(["-exec", "-execdir", "-ok", "-okdir"]);
|
|
3355
|
+
HOME_VARIABLES = /* @__PURE__ */ new Set(["HOME", "USERPROFILE"]);
|
|
3356
|
+
assignmentTable = null;
|
|
3357
|
+
currentStmtOffset = Number.MAX_SAFE_INTEGER;
|
|
3358
|
+
PAYLOAD_BUDGET = 256;
|
|
3359
|
+
payloadBudget = 0;
|
|
3360
|
+
seenPayloads = null;
|
|
3361
|
+
UNANALYSABLE_NESTING = {
|
|
3362
|
+
ruleName: "review-unanalysable-nesting",
|
|
3363
|
+
verdict: "review",
|
|
3364
|
+
reason: "This command nests more wrapped shell payloads than the policy engine will unwrap, so some of what it runs was not read.",
|
|
3365
|
+
path: ""
|
|
3366
|
+
};
|
|
3367
|
+
ASSIGNMENT_HEADS = /* @__PURE__ */ new Set(["export", "declare", "local", "readonly", "typeset"]);
|
|
3368
|
+
AND_OP = probeBinOp("a && b");
|
|
3369
|
+
OR_OP = probeBinOp("a || b");
|
|
3370
|
+
AND_OR_OPS = new Set([AND_OP, OR_OP].filter((o) => o !== null));
|
|
3157
3371
|
FS_OP_CACHE_MAX = 5e3;
|
|
3158
3372
|
fsOpCache = /* @__PURE__ */ new Map();
|
|
3159
3373
|
REDIR_TRUNCATE_OPS = /* @__PURE__ */ new Set([deriveRedirOp(">_f")]);
|
|
@@ -4170,8 +4384,8 @@ var init_daemon = __esm({
|
|
|
4170
4384
|
import { z } from "zod";
|
|
4171
4385
|
function formatIssues(issues) {
|
|
4172
4386
|
const lines = issues.map((issue) => {
|
|
4173
|
-
const
|
|
4174
|
-
return ` \u2022 ${
|
|
4387
|
+
const path17 = issue.path.length > 0 ? issue.path.map(String).join(".") : "root";
|
|
4388
|
+
return ` \u2022 ${path17}: ${issue.message}`;
|
|
4175
4389
|
});
|
|
4176
4390
|
return `Invalid config:
|
|
4177
4391
|
${lines.join("\n")}`;
|
|
@@ -4184,9 +4398,9 @@ function prunePaths(root, paths) {
|
|
|
4184
4398
|
const yi = y[y.length - 1];
|
|
4185
4399
|
return typeof xi === "number" && typeof yi === "number" ? yi - xi : 0;
|
|
4186
4400
|
});
|
|
4187
|
-
for (const
|
|
4401
|
+
for (const path17 of ordered) {
|
|
4188
4402
|
let cur = root;
|
|
4189
|
-
for (const key of
|
|
4403
|
+
for (const key of path17.slice(0, -1)) {
|
|
4190
4404
|
if (cur === null || typeof cur !== "object") {
|
|
4191
4405
|
cur = void 0;
|
|
4192
4406
|
break;
|
|
@@ -4194,7 +4408,7 @@ function prunePaths(root, paths) {
|
|
|
4194
4408
|
cur = cur[key];
|
|
4195
4409
|
}
|
|
4196
4410
|
if (cur === null || typeof cur !== "object") continue;
|
|
4197
|
-
const last =
|
|
4411
|
+
const last = path17[path17.length - 1];
|
|
4198
4412
|
if (Array.isArray(cur)) {
|
|
4199
4413
|
const i = Number(last);
|
|
4200
4414
|
if (Number.isInteger(i) && i >= 0 && i < cur.length) {
|
|
@@ -4230,7 +4444,7 @@ function sanitizeConfig(raw) {
|
|
|
4230
4444
|
return issue.keys.map((k) => [...at, k]);
|
|
4231
4445
|
}
|
|
4232
4446
|
return [at.slice(0, -level || void 0)];
|
|
4233
|
-
}).filter((
|
|
4447
|
+
}).filter((path17) => path17.length > 0);
|
|
4234
4448
|
if (paths.length === 0 || !prunePaths(working, paths)) break;
|
|
4235
4449
|
}
|
|
4236
4450
|
if (ConfigFileSchema.safeParse(working).success) break;
|
|
@@ -5233,13 +5447,13 @@ function getConfig(cwd) {
|
|
|
5233
5447
|
}
|
|
5234
5448
|
if (Array.isArray(mc.jailPaths)) {
|
|
5235
5449
|
for (const jp of mc.jailPaths) {
|
|
5236
|
-
const
|
|
5237
|
-
if (!
|
|
5450
|
+
const path17 = typeof jp?.path === "string" ? jp.path.trim() : "";
|
|
5451
|
+
if (!path17) continue;
|
|
5238
5452
|
const verdict = jp?.verdict === "review" ? "review" : "block";
|
|
5239
|
-
for (const r of pathRules(
|
|
5453
|
+
for (const r of pathRules(path17, verdict, "org-managed jail")) {
|
|
5240
5454
|
mergedPolicy.smartRules.push({ ...r, name: `org:${r.name}` });
|
|
5241
5455
|
}
|
|
5242
|
-
mergedPolicy.managedJailPaths.push({ path:
|
|
5456
|
+
mergedPolicy.managedJailPaths.push({ path: path17, verdict });
|
|
5243
5457
|
}
|
|
5244
5458
|
}
|
|
5245
5459
|
if (Array.isArray(mc.trustedHosts)) {
|
|
@@ -6693,19 +6907,129 @@ var init_scan_summary = __esm({
|
|
|
6693
6907
|
}
|
|
6694
6908
|
});
|
|
6695
6909
|
|
|
6696
|
-
// src/
|
|
6697
|
-
|
|
6698
|
-
|
|
6910
|
+
// src/agent-wiring.ts
|
|
6911
|
+
import fs10 from "fs";
|
|
6912
|
+
import path12 from "path";
|
|
6913
|
+
import * as yaml from "yaml";
|
|
6914
|
+
import { parse as parseToml } from "smol-toml";
|
|
6915
|
+
var exists, ck, lg, AGENT_SPECS;
|
|
6916
|
+
var init_agent_wiring = __esm({
|
|
6917
|
+
"src/agent-wiring.ts"() {
|
|
6699
6918
|
"use strict";
|
|
6919
|
+
init_setup();
|
|
6920
|
+
exists = (p) => {
|
|
6921
|
+
try {
|
|
6922
|
+
return fs10.existsSync(p);
|
|
6923
|
+
} catch {
|
|
6924
|
+
return false;
|
|
6925
|
+
}
|
|
6926
|
+
};
|
|
6927
|
+
ck = (key) => ({ key, kind: "check" });
|
|
6928
|
+
lg = (key) => ({ key, kind: "log" });
|
|
6929
|
+
AGENT_SPECS = [
|
|
6930
|
+
{
|
|
6931
|
+
id: "claude",
|
|
6932
|
+
label: "Claude Code",
|
|
6933
|
+
setupCommand: "node9 agents add claude",
|
|
6934
|
+
hookFile: (h) => path12.join(h, ".claude", "settings.json"),
|
|
6935
|
+
hookFormat: "matcher",
|
|
6936
|
+
// UserPromptSubmit is prompt DLP. setup.ts has written it for Claude since
|
|
6937
|
+
// that shipped; the spec must name it too, or status/doctor never show the
|
|
6938
|
+
// row and heal (which repairs via setupAgent) has no signal it is missing.
|
|
6939
|
+
hookEvents: [ck("PreToolUse"), lg("PostToolUse"), ck("UserPromptSubmit")],
|
|
6940
|
+
mcpFile: (h) => path12.join(h, ".claude.json"),
|
|
6941
|
+
present: (h) => exists(path12.join(h, ".claude", "settings.json")) || exists(path12.join(h, ".claude.json"))
|
|
6942
|
+
},
|
|
6943
|
+
{
|
|
6944
|
+
id: "gemini",
|
|
6945
|
+
label: "Gemini CLI",
|
|
6946
|
+
setupCommand: "node9 agents add gemini",
|
|
6947
|
+
hookFile: (h) => path12.join(h, ".gemini", "settings.json"),
|
|
6948
|
+
hookFormat: "matcher",
|
|
6949
|
+
hookEvents: [ck("BeforeTool"), lg("AfterTool")],
|
|
6950
|
+
mcpFile: (h) => path12.join(h, ".gemini", "settings.json"),
|
|
6951
|
+
present: (h) => exists(path12.join(h, ".gemini", "settings.json"))
|
|
6952
|
+
},
|
|
6953
|
+
{
|
|
6954
|
+
id: "codex",
|
|
6955
|
+
label: "Codex",
|
|
6956
|
+
setupCommand: "node9 agents add codex",
|
|
6957
|
+
hookFile: (h) => path12.join(h, ".codex", "hooks.json"),
|
|
6958
|
+
hookFormat: "matcher",
|
|
6959
|
+
hookEvents: [ck("PreToolUse"), ck("UserPromptSubmit")],
|
|
6960
|
+
mcpFile: (h) => path12.join(h, ".codex", "config.toml"),
|
|
6961
|
+
mcpFormat: "toml",
|
|
6962
|
+
present: (h) => exists(path12.join(h, ".codex"))
|
|
6963
|
+
},
|
|
6964
|
+
{
|
|
6965
|
+
id: "antigravity",
|
|
6966
|
+
label: "Antigravity",
|
|
6967
|
+
setupCommand: "node9 agents add antigravity",
|
|
6968
|
+
hookFile: (h) => path12.join(h, ".gemini", "config", "hooks.json"),
|
|
6969
|
+
hookFormat: "matcher",
|
|
6970
|
+
hookEvents: [ck("PreToolUse"), lg("PostToolUse")],
|
|
6971
|
+
mcpFile: (h) => path12.join(h, ".gemini", "config", "mcp_config.json"),
|
|
6972
|
+
present: (h) => exists(path12.join(h, ".gemini", "config", "hooks.json")) || exists(path12.join(h, ".gemini", "antigravity-cli")) || exists(path12.join(h, ".gemini", "antigravity-ide"))
|
|
6973
|
+
},
|
|
6974
|
+
{
|
|
6975
|
+
id: "copilot",
|
|
6976
|
+
label: "GitHub Copilot",
|
|
6977
|
+
setupCommand: "node9 agents add copilot",
|
|
6978
|
+
hookFile: (h) => path12.join(h, ".copilot", "hooks", "node9.json"),
|
|
6979
|
+
hookFormat: "flat",
|
|
6980
|
+
hookEvents: [ck("PreToolUse"), lg("PostToolUse"), ck("UserPromptSubmit")],
|
|
6981
|
+
mcpFile: (h) => path12.join(h, ".copilot", "mcp-config.json"),
|
|
6982
|
+
present: (h) => exists(path12.join(h, ".copilot"))
|
|
6983
|
+
},
|
|
6984
|
+
{
|
|
6985
|
+
id: "cursor",
|
|
6986
|
+
label: "Cursor",
|
|
6987
|
+
setupCommand: "node9 agents add cursor",
|
|
6988
|
+
// MCP-only — no hook file (see note above).
|
|
6989
|
+
hookFormat: "flat",
|
|
6990
|
+
hookEvents: [],
|
|
6991
|
+
mcpFile: (h) => path12.join(h, ".cursor", "mcp.json"),
|
|
6992
|
+
present: (h) => exists(path12.join(h, ".cursor", "mcp.json"))
|
|
6993
|
+
},
|
|
6994
|
+
{
|
|
6995
|
+
id: "hermes",
|
|
6996
|
+
label: "Hermes Agent",
|
|
6997
|
+
setupCommand: "node9 agents add hermes",
|
|
6998
|
+
hookFile: (h) => hermesConfigPath(h),
|
|
6999
|
+
hookFormat: "yaml",
|
|
7000
|
+
hookEvents: [ck("pre_tool_call"), lg("post_tool_call")],
|
|
7001
|
+
labelPad: 14,
|
|
7002
|
+
// 'post_tool_call' is wider than the default
|
|
7003
|
+
present: (h) => exists(hermesConfigPath(h))
|
|
7004
|
+
},
|
|
7005
|
+
{
|
|
7006
|
+
// Plugin-shim agents — protected by a node9-authored plugin/extension file
|
|
7007
|
+
// (no hooks, no MCP). hookFormat is unused for these (shimFile drives it).
|
|
7008
|
+
id: "opencode",
|
|
7009
|
+
label: "OpenCode",
|
|
7010
|
+
setupCommand: "node9 agents add opencode",
|
|
7011
|
+
hookFormat: "flat",
|
|
7012
|
+
hookEvents: [],
|
|
7013
|
+
shimFile: (h) => path12.join(opencodeConfigDir(h), "plugins", "node9.js"),
|
|
7014
|
+
present: (h) => exists(opencodeConfigDir(h)) || exists(path12.join(opencodeConfigDir(h), "plugins", "node9.js"))
|
|
7015
|
+
},
|
|
7016
|
+
{
|
|
7017
|
+
id: "pi",
|
|
7018
|
+
label: "Pi",
|
|
7019
|
+
setupCommand: "node9 agents add pi",
|
|
7020
|
+
hookFormat: "flat",
|
|
7021
|
+
hookEvents: [],
|
|
7022
|
+
shimFile: (h) => path12.join(h, ".pi", "agent", "extensions", "node9.js"),
|
|
7023
|
+
present: (h) => exists(path12.join(h, ".pi", "agent")) || exists(path12.join(h, ".pi", "agent", "extensions", "node9.js"))
|
|
7024
|
+
}
|
|
7025
|
+
];
|
|
6700
7026
|
}
|
|
6701
7027
|
});
|
|
6702
7028
|
|
|
6703
|
-
// src/
|
|
6704
|
-
|
|
6705
|
-
|
|
6706
|
-
"src/codex-trust.ts"() {
|
|
7029
|
+
// src/mcp-cmd.ts
|
|
7030
|
+
var init_mcp_cmd = __esm({
|
|
7031
|
+
"src/mcp-cmd.ts"() {
|
|
6707
7032
|
"use strict";
|
|
6708
|
-
init_platform_shell();
|
|
6709
7033
|
}
|
|
6710
7034
|
});
|
|
6711
7035
|
|
|
@@ -6716,15 +7040,43 @@ var init_mcp_pin = __esm({
|
|
|
6716
7040
|
}
|
|
6717
7041
|
});
|
|
6718
7042
|
|
|
7043
|
+
// src/mcp-wrap.ts
|
|
7044
|
+
import { parse as parseToml2, stringify as stringifyToml } from "smol-toml";
|
|
7045
|
+
var init_mcp_wrap = __esm({
|
|
7046
|
+
"src/mcp-wrap.ts"() {
|
|
7047
|
+
"use strict";
|
|
7048
|
+
init_agent_wiring();
|
|
7049
|
+
init_mcp_cmd();
|
|
7050
|
+
init_mcp_pin();
|
|
7051
|
+
init_mcp_cmd();
|
|
7052
|
+
}
|
|
7053
|
+
});
|
|
7054
|
+
|
|
7055
|
+
// src/utils/platform-shell.ts
|
|
7056
|
+
var init_platform_shell = __esm({
|
|
7057
|
+
"src/utils/platform-shell.ts"() {
|
|
7058
|
+
"use strict";
|
|
7059
|
+
}
|
|
7060
|
+
});
|
|
7061
|
+
|
|
7062
|
+
// src/codex-trust.ts
|
|
7063
|
+
import { parse as parseToml3 } from "smol-toml";
|
|
7064
|
+
var init_codex_trust = __esm({
|
|
7065
|
+
"src/codex-trust.ts"() {
|
|
7066
|
+
"use strict";
|
|
7067
|
+
init_platform_shell();
|
|
7068
|
+
}
|
|
7069
|
+
});
|
|
7070
|
+
|
|
6719
7071
|
// src/daemon/hook-baseline.ts
|
|
6720
|
-
import
|
|
7072
|
+
import path13 from "path";
|
|
6721
7073
|
import os11 from "os";
|
|
6722
7074
|
var BASELINE_FILE, NOTIFIED_FILE;
|
|
6723
7075
|
var init_hook_baseline = __esm({
|
|
6724
7076
|
"src/daemon/hook-baseline.ts"() {
|
|
6725
7077
|
"use strict";
|
|
6726
|
-
BASELINE_FILE =
|
|
6727
|
-
NOTIFIED_FILE =
|
|
7078
|
+
BASELINE_FILE = path13.join(os11.homedir(), ".node9", "hooks-baseline.json");
|
|
7079
|
+
NOTIFIED_FILE = path13.join(os11.homedir(), ".node9", "hook-heal-notified.json");
|
|
6728
7080
|
}
|
|
6729
7081
|
});
|
|
6730
7082
|
|
|
@@ -6750,19 +7102,37 @@ var init_atomic_write = __esm({
|
|
|
6750
7102
|
});
|
|
6751
7103
|
|
|
6752
7104
|
// src/setup.ts
|
|
7105
|
+
import path14 from "path";
|
|
7106
|
+
import os12 from "os";
|
|
6753
7107
|
import chalk3 from "chalk";
|
|
6754
7108
|
import { confirm as rawConfirm } from "@inquirer/prompts";
|
|
6755
|
-
import { parse as
|
|
6756
|
-
import * as
|
|
7109
|
+
import { parse as parseToml4, stringify as stringifyToml2 } from "smol-toml";
|
|
7110
|
+
import * as yaml2 from "yaml";
|
|
7111
|
+
function opencodeConfigDir(home = os12.homedir()) {
|
|
7112
|
+
const xdg = process.env.XDG_CONFIG_HOME;
|
|
7113
|
+
const base = xdg && path14.isAbsolute(xdg) ? xdg : path14.join(home, ".config");
|
|
7114
|
+
return path14.join(base, "opencode");
|
|
7115
|
+
}
|
|
7116
|
+
function hermesHomeDir(homeDir = os12.homedir()) {
|
|
7117
|
+
const env = process.env.HERMES_HOME?.trim();
|
|
7118
|
+
if (env && path14.isAbsolute(env)) return env;
|
|
7119
|
+
return path14.join(homeDir, ".hermes");
|
|
7120
|
+
}
|
|
7121
|
+
function hermesConfigPath(homeDir = os12.homedir()) {
|
|
7122
|
+
return path14.join(hermesHomeDir(homeDir), HERMES_CONFIG_FILENAME);
|
|
7123
|
+
}
|
|
7124
|
+
var HERMES_CONFIG_FILENAME;
|
|
6757
7125
|
var init_setup = __esm({
|
|
6758
7126
|
"src/setup.ts"() {
|
|
6759
7127
|
"use strict";
|
|
7128
|
+
init_mcp_wrap();
|
|
6760
7129
|
init_codex_trust();
|
|
6761
7130
|
init_mcp_pin();
|
|
6762
7131
|
init_hook_baseline();
|
|
6763
7132
|
init_setup_opencode_shim();
|
|
6764
7133
|
init_setup_pi_shim();
|
|
6765
7134
|
init_atomic_write();
|
|
7135
|
+
HERMES_CONFIG_FILENAME = "config.yaml";
|
|
6766
7136
|
}
|
|
6767
7137
|
});
|
|
6768
7138
|
|
|
@@ -6793,9 +7163,9 @@ var init_scan_history = __esm({
|
|
|
6793
7163
|
|
|
6794
7164
|
// src/cli/commands/scan.ts
|
|
6795
7165
|
import chalk5 from "chalk";
|
|
6796
|
-
import
|
|
6797
|
-
import
|
|
6798
|
-
import
|
|
7166
|
+
import fs11 from "fs";
|
|
7167
|
+
import path15 from "path";
|
|
7168
|
+
import os13 from "os";
|
|
6799
7169
|
import stringWidth2 from "string-width";
|
|
6800
7170
|
function claudeModelPrice2(model) {
|
|
6801
7171
|
const t = pricingFor(model);
|
|
@@ -7003,7 +7373,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
|
|
|
7003
7373
|
const session = { sessionId, costUSD: 0, toolCalls: 0 };
|
|
7004
7374
|
let raw;
|
|
7005
7375
|
try {
|
|
7006
|
-
raw =
|
|
7376
|
+
raw = fs11.readFileSync(path15.join(projPath, file), "utf-8");
|
|
7007
7377
|
} catch {
|
|
7008
7378
|
return;
|
|
7009
7379
|
}
|
|
@@ -7066,7 +7436,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
|
|
|
7066
7436
|
if (block.type !== "tool_result") continue;
|
|
7067
7437
|
const filePath = block.tool_use_id ? toolUseFilePaths.get(block.tool_use_id) : void 0;
|
|
7068
7438
|
if (filePath) {
|
|
7069
|
-
const ext =
|
|
7439
|
+
const ext = path15.extname(filePath).toLowerCase();
|
|
7070
7440
|
if (CODE_EXTENSIONS.has(ext)) continue;
|
|
7071
7441
|
}
|
|
7072
7442
|
const resultText = typeof block.content === "string" ? block.content : Array.isArray(block.content) ? block.content.map((c) => c.text ?? "").join("\n") : null;
|
|
@@ -7136,7 +7506,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
|
|
|
7136
7506
|
const rawCmd = String(input.command ?? "").trimStart();
|
|
7137
7507
|
if (/^node9\s+(scan|explain|report|tail|dlp|status|sessions|audit)\b/.test(rawCmd)) continue;
|
|
7138
7508
|
const inputFilePath = typeof input.file_path === "string" ? input.file_path : "";
|
|
7139
|
-
const inputFileExt = inputFilePath ?
|
|
7509
|
+
const inputFileExt = inputFilePath ? path15.extname(inputFilePath).toLowerCase() : "";
|
|
7140
7510
|
const canaryInInput = recordCanaries(
|
|
7141
7511
|
input,
|
|
7142
7512
|
toolName,
|
|
@@ -7245,13 +7615,13 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
|
|
|
7245
7615
|
result.perSession.push(session);
|
|
7246
7616
|
}
|
|
7247
7617
|
async function processClaudeProjectAsync(proj, projectsDir, ruleSources, startDate, result, dedup, canaryVals, onProgress, onLine) {
|
|
7248
|
-
const projPath =
|
|
7618
|
+
const projPath = path15.join(projectsDir, proj);
|
|
7249
7619
|
try {
|
|
7250
|
-
if (!
|
|
7620
|
+
if (!fs11.statSync(projPath).isDirectory()) return;
|
|
7251
7621
|
} catch {
|
|
7252
7622
|
return;
|
|
7253
7623
|
}
|
|
7254
|
-
const projLabel = stripTerminalEscapes(decodeURIComponent(proj).replace(
|
|
7624
|
+
const projLabel = stripTerminalEscapes(decodeURIComponent(proj).replace(os13.homedir(), "~")).slice(
|
|
7255
7625
|
0,
|
|
7256
7626
|
40
|
|
7257
7627
|
);
|
|
@@ -7298,12 +7668,12 @@ function emptyClaudeScan() {
|
|
|
7298
7668
|
};
|
|
7299
7669
|
}
|
|
7300
7670
|
async function scanClaudeHistoryAsync(startDate, onProgress, onLine) {
|
|
7301
|
-
const projectsDir =
|
|
7671
|
+
const projectsDir = path15.join(os13.homedir(), ".claude", "projects");
|
|
7302
7672
|
const result = emptyClaudeScan();
|
|
7303
|
-
if (!
|
|
7673
|
+
if (!fs11.existsSync(projectsDir)) return result;
|
|
7304
7674
|
let projDirs;
|
|
7305
7675
|
try {
|
|
7306
|
-
projDirs =
|
|
7676
|
+
projDirs = fs11.readdirSync(projectsDir);
|
|
7307
7677
|
} catch {
|
|
7308
7678
|
return result;
|
|
7309
7679
|
}
|
|
@@ -7327,7 +7697,7 @@ async function scanClaudeHistoryAsync(startDate, onProgress, onLine) {
|
|
|
7327
7697
|
}
|
|
7328
7698
|
function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
7329
7699
|
const canaryVals = safeCanaryScanValues();
|
|
7330
|
-
const tmpDir =
|
|
7700
|
+
const tmpDir = path15.join(os13.homedir(), ".gemini", "tmp");
|
|
7331
7701
|
const result = {
|
|
7332
7702
|
filesScanned: 0,
|
|
7333
7703
|
sessions: 0,
|
|
@@ -7344,33 +7714,33 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
|
7344
7714
|
perSession: []
|
|
7345
7715
|
};
|
|
7346
7716
|
const dedup = emptyScanDedup();
|
|
7347
|
-
if (!
|
|
7717
|
+
if (!fs11.existsSync(tmpDir)) return result;
|
|
7348
7718
|
let slugDirs;
|
|
7349
7719
|
try {
|
|
7350
|
-
slugDirs =
|
|
7720
|
+
slugDirs = fs11.readdirSync(tmpDir);
|
|
7351
7721
|
} catch {
|
|
7352
7722
|
return result;
|
|
7353
7723
|
}
|
|
7354
7724
|
const ruleSources = buildRuleSources();
|
|
7355
7725
|
for (const slug2 of slugDirs) {
|
|
7356
|
-
const slugPath =
|
|
7726
|
+
const slugPath = path15.join(tmpDir, slug2);
|
|
7357
7727
|
try {
|
|
7358
|
-
if (!
|
|
7728
|
+
if (!fs11.statSync(slugPath).isDirectory()) continue;
|
|
7359
7729
|
} catch {
|
|
7360
7730
|
continue;
|
|
7361
7731
|
}
|
|
7362
7732
|
let projLabel = stripTerminalEscapes(slug2).slice(0, 40);
|
|
7363
7733
|
try {
|
|
7364
7734
|
projLabel = stripTerminalEscapes(
|
|
7365
|
-
|
|
7366
|
-
).replace(
|
|
7735
|
+
fs11.readFileSync(path15.join(slugPath, ".project_root"), "utf-8").trim()
|
|
7736
|
+
).replace(os13.homedir(), "~").slice(0, 40);
|
|
7367
7737
|
} catch {
|
|
7368
7738
|
}
|
|
7369
|
-
const chatsDir =
|
|
7370
|
-
if (!
|
|
7739
|
+
const chatsDir = path15.join(slugPath, "chats");
|
|
7740
|
+
if (!fs11.existsSync(chatsDir)) continue;
|
|
7371
7741
|
let chatFiles;
|
|
7372
7742
|
try {
|
|
7373
|
-
chatFiles =
|
|
7743
|
+
chatFiles = fs11.readdirSync(chatsDir).filter((f) => f.endsWith(".json") || f.endsWith(".jsonl")).sort((a, b) => Number(b.endsWith(".jsonl")) - Number(a.endsWith(".jsonl")));
|
|
7374
7744
|
} catch {
|
|
7375
7745
|
continue;
|
|
7376
7746
|
}
|
|
@@ -7383,7 +7753,7 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
|
7383
7753
|
onProgress?.(result.filesScanned);
|
|
7384
7754
|
let raw;
|
|
7385
7755
|
try {
|
|
7386
|
-
raw =
|
|
7756
|
+
raw = fs11.readFileSync(path15.join(chatsDir, chatFile), "utf-8");
|
|
7387
7757
|
} catch {
|
|
7388
7758
|
continue;
|
|
7389
7759
|
}
|
|
@@ -7602,7 +7972,7 @@ function scanCodexHistory(startDate, onProgress, onLine) {
|
|
|
7602
7972
|
onProgress?.(result.filesScanned);
|
|
7603
7973
|
let lines;
|
|
7604
7974
|
try {
|
|
7605
|
-
lines =
|
|
7975
|
+
lines = fs11.readFileSync(filePath, "utf-8").split("\n");
|
|
7606
7976
|
} catch {
|
|
7607
7977
|
continue;
|
|
7608
7978
|
}
|
|
@@ -7625,7 +7995,7 @@ function scanCodexHistory(startDate, onProgress, onLine) {
|
|
|
7625
7995
|
sessionId = String(payload["id"] ?? filePath);
|
|
7626
7996
|
startTime = String(payload["timestamp"] ?? "");
|
|
7627
7997
|
const cwd = String(payload["cwd"] ?? "");
|
|
7628
|
-
projLabel = stripTerminalEscapes(cwd.replace(
|
|
7998
|
+
projLabel = stripTerminalEscapes(cwd.replace(os13.homedir(), "~")).slice(0, 40);
|
|
7629
7999
|
continue;
|
|
7630
8000
|
}
|
|
7631
8001
|
if (entry.type === "event_msg" && payload["type"] === "user_message") {
|
|
@@ -7870,23 +8240,23 @@ var init_scan = __esm({
|
|
|
7870
8240
|
});
|
|
7871
8241
|
|
|
7872
8242
|
// src/tui/dashboard/data.ts
|
|
7873
|
-
import
|
|
7874
|
-
import
|
|
7875
|
-
import
|
|
8243
|
+
import fs12 from "fs";
|
|
8244
|
+
import os14 from "os";
|
|
8245
|
+
import path16 from "path";
|
|
7876
8246
|
import http from "http";
|
|
7877
8247
|
function auditLogPath() {
|
|
7878
|
-
return
|
|
8248
|
+
return path16.join(os14.homedir(), ".node9", "audit.log");
|
|
7879
8249
|
}
|
|
7880
8250
|
function readAuditEntriesAsync(chunkSize = 1e3, customPath) {
|
|
7881
8251
|
return new Promise((resolve) => {
|
|
7882
8252
|
const p = customPath ?? auditLogPath();
|
|
7883
|
-
if (!
|
|
8253
|
+
if (!fs12.existsSync(p)) {
|
|
7884
8254
|
resolve([]);
|
|
7885
8255
|
return;
|
|
7886
8256
|
}
|
|
7887
8257
|
let raw;
|
|
7888
8258
|
try {
|
|
7889
|
-
raw =
|
|
8259
|
+
raw = fs12.readFileSync(p, "utf8");
|
|
7890
8260
|
} catch {
|
|
7891
8261
|
resolve([]);
|
|
7892
8262
|
return;
|
|
@@ -8018,13 +8388,13 @@ function loadBlast() {
|
|
|
8018
8388
|
}
|
|
8019
8389
|
}
|
|
8020
8390
|
function shortenPath(p) {
|
|
8021
|
-
const home =
|
|
8391
|
+
const home = os14.homedir();
|
|
8022
8392
|
return p.startsWith(home) ? p.replace(home, "~") : p;
|
|
8023
8393
|
}
|
|
8024
8394
|
async function loadReportAuditAsync(period) {
|
|
8025
|
-
const claudeProjectsDir =
|
|
8395
|
+
const claudeProjectsDir = path16.join(os14.homedir(), ".claude", "projects");
|
|
8026
8396
|
const codexSessionsDir2 = codexSessionsDir();
|
|
8027
|
-
const geminiTmpDir =
|
|
8397
|
+
const geminiTmpDir = path16.join(os14.homedir(), ".gemini", "tmp");
|
|
8028
8398
|
const { start, end } = getDateRange(period, /* @__PURE__ */ new Date());
|
|
8029
8399
|
const entries = await readAuditEntriesAsync();
|
|
8030
8400
|
void ensurePricingLoaded();
|
|
@@ -9130,8 +9500,8 @@ import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-run
|
|
|
9130
9500
|
function TopToolsProjects({ audit }) {
|
|
9131
9501
|
const data = audit?.data;
|
|
9132
9502
|
const tools = data ? [...data.toolMap.entries()].sort(([, a], [, b]) => b.calls - a.calls).slice(0, ROW_LIMIT) : [];
|
|
9133
|
-
const projects = data ? [...data.cost.byProject.entries()].map(([
|
|
9134
|
-
name: basenameOf(
|
|
9503
|
+
const projects = data ? [...data.cost.byProject.entries()].map(([path17, r]) => ({
|
|
9504
|
+
name: basenameOf(path17),
|
|
9135
9505
|
cost: r.cost,
|
|
9136
9506
|
tokens: r.inputTokens + r.outputTokens
|
|
9137
9507
|
})).sort((a, b) => b.cost - a.cost).slice(0, ROW_LIMIT) : [];
|
|
@@ -9568,8 +9938,8 @@ function pickTopLoopFile(loops) {
|
|
|
9568
9938
|
map.set(k, (map.get(k) ?? 0) + (l.count ?? 0));
|
|
9569
9939
|
}
|
|
9570
9940
|
if (map.size === 0) return void 0;
|
|
9571
|
-
const [
|
|
9572
|
-
return { path:
|
|
9941
|
+
const [path17, count] = [...map.entries()].sort((a, b) => b[1] - a[1])[0];
|
|
9942
|
+
return { path: path17, count };
|
|
9573
9943
|
}
|
|
9574
9944
|
var EMPTY_FILTERED_SCAN;
|
|
9575
9945
|
var init_derive = __esm({
|