@node9/proxy 2.22.1 → 2.23.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 +2352 -1826
- package/dist/cli.mjs +2279 -1753
- package/dist/dashboard.mjs +468 -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,52 @@ 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-app-server.ts
|
|
7063
|
+
import { parse as parseToml3 } from "smol-toml";
|
|
7064
|
+
var init_codex_app_server = __esm({
|
|
7065
|
+
"src/codex-app-server.ts"() {
|
|
7066
|
+
"use strict";
|
|
7067
|
+
}
|
|
7068
|
+
});
|
|
7069
|
+
|
|
7070
|
+
// src/codex-trust.ts
|
|
7071
|
+
import { parse as parseToml4 } from "smol-toml";
|
|
7072
|
+
var init_codex_trust = __esm({
|
|
7073
|
+
"src/codex-trust.ts"() {
|
|
7074
|
+
"use strict";
|
|
7075
|
+
init_platform_shell();
|
|
7076
|
+
init_codex_app_server();
|
|
7077
|
+
}
|
|
7078
|
+
});
|
|
7079
|
+
|
|
6719
7080
|
// src/daemon/hook-baseline.ts
|
|
6720
|
-
import
|
|
7081
|
+
import path13 from "path";
|
|
6721
7082
|
import os11 from "os";
|
|
6722
7083
|
var BASELINE_FILE, NOTIFIED_FILE;
|
|
6723
7084
|
var init_hook_baseline = __esm({
|
|
6724
7085
|
"src/daemon/hook-baseline.ts"() {
|
|
6725
7086
|
"use strict";
|
|
6726
|
-
BASELINE_FILE =
|
|
6727
|
-
NOTIFIED_FILE =
|
|
7087
|
+
BASELINE_FILE = path13.join(os11.homedir(), ".node9", "hooks-baseline.json");
|
|
7088
|
+
NOTIFIED_FILE = path13.join(os11.homedir(), ".node9", "hook-heal-notified.json");
|
|
6728
7089
|
}
|
|
6729
7090
|
});
|
|
6730
7091
|
|
|
@@ -6750,19 +7111,38 @@ var init_atomic_write = __esm({
|
|
|
6750
7111
|
});
|
|
6751
7112
|
|
|
6752
7113
|
// src/setup.ts
|
|
7114
|
+
import path14 from "path";
|
|
7115
|
+
import os12 from "os";
|
|
6753
7116
|
import chalk3 from "chalk";
|
|
6754
7117
|
import { confirm as rawConfirm } from "@inquirer/prompts";
|
|
6755
|
-
import { parse as
|
|
6756
|
-
import * as
|
|
7118
|
+
import { parse as parseToml5, stringify as stringifyToml2 } from "smol-toml";
|
|
7119
|
+
import * as yaml2 from "yaml";
|
|
7120
|
+
function opencodeConfigDir(home = os12.homedir()) {
|
|
7121
|
+
const xdg = process.env.XDG_CONFIG_HOME;
|
|
7122
|
+
const base = xdg && path14.isAbsolute(xdg) ? xdg : path14.join(home, ".config");
|
|
7123
|
+
return path14.join(base, "opencode");
|
|
7124
|
+
}
|
|
7125
|
+
function hermesHomeDir(homeDir = os12.homedir()) {
|
|
7126
|
+
const env = process.env.HERMES_HOME?.trim();
|
|
7127
|
+
if (env && path14.isAbsolute(env)) return env;
|
|
7128
|
+
return path14.join(homeDir, ".hermes");
|
|
7129
|
+
}
|
|
7130
|
+
function hermesConfigPath(homeDir = os12.homedir()) {
|
|
7131
|
+
return path14.join(hermesHomeDir(homeDir), HERMES_CONFIG_FILENAME);
|
|
7132
|
+
}
|
|
7133
|
+
var HERMES_CONFIG_FILENAME;
|
|
6757
7134
|
var init_setup = __esm({
|
|
6758
7135
|
"src/setup.ts"() {
|
|
6759
7136
|
"use strict";
|
|
7137
|
+
init_mcp_wrap();
|
|
6760
7138
|
init_codex_trust();
|
|
7139
|
+
init_codex_app_server();
|
|
6761
7140
|
init_mcp_pin();
|
|
6762
7141
|
init_hook_baseline();
|
|
6763
7142
|
init_setup_opencode_shim();
|
|
6764
7143
|
init_setup_pi_shim();
|
|
6765
7144
|
init_atomic_write();
|
|
7145
|
+
HERMES_CONFIG_FILENAME = "config.yaml";
|
|
6766
7146
|
}
|
|
6767
7147
|
});
|
|
6768
7148
|
|
|
@@ -6793,9 +7173,9 @@ var init_scan_history = __esm({
|
|
|
6793
7173
|
|
|
6794
7174
|
// src/cli/commands/scan.ts
|
|
6795
7175
|
import chalk5 from "chalk";
|
|
6796
|
-
import
|
|
6797
|
-
import
|
|
6798
|
-
import
|
|
7176
|
+
import fs11 from "fs";
|
|
7177
|
+
import path15 from "path";
|
|
7178
|
+
import os13 from "os";
|
|
6799
7179
|
import stringWidth2 from "string-width";
|
|
6800
7180
|
function claudeModelPrice2(model) {
|
|
6801
7181
|
const t = pricingFor(model);
|
|
@@ -7003,7 +7383,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
|
|
|
7003
7383
|
const session = { sessionId, costUSD: 0, toolCalls: 0 };
|
|
7004
7384
|
let raw;
|
|
7005
7385
|
try {
|
|
7006
|
-
raw =
|
|
7386
|
+
raw = fs11.readFileSync(path15.join(projPath, file), "utf-8");
|
|
7007
7387
|
} catch {
|
|
7008
7388
|
return;
|
|
7009
7389
|
}
|
|
@@ -7066,7 +7446,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
|
|
|
7066
7446
|
if (block.type !== "tool_result") continue;
|
|
7067
7447
|
const filePath = block.tool_use_id ? toolUseFilePaths.get(block.tool_use_id) : void 0;
|
|
7068
7448
|
if (filePath) {
|
|
7069
|
-
const ext =
|
|
7449
|
+
const ext = path15.extname(filePath).toLowerCase();
|
|
7070
7450
|
if (CODE_EXTENSIONS.has(ext)) continue;
|
|
7071
7451
|
}
|
|
7072
7452
|
const resultText = typeof block.content === "string" ? block.content : Array.isArray(block.content) ? block.content.map((c) => c.text ?? "").join("\n") : null;
|
|
@@ -7136,7 +7516,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
|
|
|
7136
7516
|
const rawCmd = String(input.command ?? "").trimStart();
|
|
7137
7517
|
if (/^node9\s+(scan|explain|report|tail|dlp|status|sessions|audit)\b/.test(rawCmd)) continue;
|
|
7138
7518
|
const inputFilePath = typeof input.file_path === "string" ? input.file_path : "";
|
|
7139
|
-
const inputFileExt = inputFilePath ?
|
|
7519
|
+
const inputFileExt = inputFilePath ? path15.extname(inputFilePath).toLowerCase() : "";
|
|
7140
7520
|
const canaryInInput = recordCanaries(
|
|
7141
7521
|
input,
|
|
7142
7522
|
toolName,
|
|
@@ -7245,13 +7625,13 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
|
|
|
7245
7625
|
result.perSession.push(session);
|
|
7246
7626
|
}
|
|
7247
7627
|
async function processClaudeProjectAsync(proj, projectsDir, ruleSources, startDate, result, dedup, canaryVals, onProgress, onLine) {
|
|
7248
|
-
const projPath =
|
|
7628
|
+
const projPath = path15.join(projectsDir, proj);
|
|
7249
7629
|
try {
|
|
7250
|
-
if (!
|
|
7630
|
+
if (!fs11.statSync(projPath).isDirectory()) return;
|
|
7251
7631
|
} catch {
|
|
7252
7632
|
return;
|
|
7253
7633
|
}
|
|
7254
|
-
const projLabel = stripTerminalEscapes(decodeURIComponent(proj).replace(
|
|
7634
|
+
const projLabel = stripTerminalEscapes(decodeURIComponent(proj).replace(os13.homedir(), "~")).slice(
|
|
7255
7635
|
0,
|
|
7256
7636
|
40
|
|
7257
7637
|
);
|
|
@@ -7298,12 +7678,12 @@ function emptyClaudeScan() {
|
|
|
7298
7678
|
};
|
|
7299
7679
|
}
|
|
7300
7680
|
async function scanClaudeHistoryAsync(startDate, onProgress, onLine) {
|
|
7301
|
-
const projectsDir =
|
|
7681
|
+
const projectsDir = path15.join(os13.homedir(), ".claude", "projects");
|
|
7302
7682
|
const result = emptyClaudeScan();
|
|
7303
|
-
if (!
|
|
7683
|
+
if (!fs11.existsSync(projectsDir)) return result;
|
|
7304
7684
|
let projDirs;
|
|
7305
7685
|
try {
|
|
7306
|
-
projDirs =
|
|
7686
|
+
projDirs = fs11.readdirSync(projectsDir);
|
|
7307
7687
|
} catch {
|
|
7308
7688
|
return result;
|
|
7309
7689
|
}
|
|
@@ -7327,7 +7707,7 @@ async function scanClaudeHistoryAsync(startDate, onProgress, onLine) {
|
|
|
7327
7707
|
}
|
|
7328
7708
|
function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
7329
7709
|
const canaryVals = safeCanaryScanValues();
|
|
7330
|
-
const tmpDir =
|
|
7710
|
+
const tmpDir = path15.join(os13.homedir(), ".gemini", "tmp");
|
|
7331
7711
|
const result = {
|
|
7332
7712
|
filesScanned: 0,
|
|
7333
7713
|
sessions: 0,
|
|
@@ -7344,33 +7724,33 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
|
7344
7724
|
perSession: []
|
|
7345
7725
|
};
|
|
7346
7726
|
const dedup = emptyScanDedup();
|
|
7347
|
-
if (!
|
|
7727
|
+
if (!fs11.existsSync(tmpDir)) return result;
|
|
7348
7728
|
let slugDirs;
|
|
7349
7729
|
try {
|
|
7350
|
-
slugDirs =
|
|
7730
|
+
slugDirs = fs11.readdirSync(tmpDir);
|
|
7351
7731
|
} catch {
|
|
7352
7732
|
return result;
|
|
7353
7733
|
}
|
|
7354
7734
|
const ruleSources = buildRuleSources();
|
|
7355
7735
|
for (const slug2 of slugDirs) {
|
|
7356
|
-
const slugPath =
|
|
7736
|
+
const slugPath = path15.join(tmpDir, slug2);
|
|
7357
7737
|
try {
|
|
7358
|
-
if (!
|
|
7738
|
+
if (!fs11.statSync(slugPath).isDirectory()) continue;
|
|
7359
7739
|
} catch {
|
|
7360
7740
|
continue;
|
|
7361
7741
|
}
|
|
7362
7742
|
let projLabel = stripTerminalEscapes(slug2).slice(0, 40);
|
|
7363
7743
|
try {
|
|
7364
7744
|
projLabel = stripTerminalEscapes(
|
|
7365
|
-
|
|
7366
|
-
).replace(
|
|
7745
|
+
fs11.readFileSync(path15.join(slugPath, ".project_root"), "utf-8").trim()
|
|
7746
|
+
).replace(os13.homedir(), "~").slice(0, 40);
|
|
7367
7747
|
} catch {
|
|
7368
7748
|
}
|
|
7369
|
-
const chatsDir =
|
|
7370
|
-
if (!
|
|
7749
|
+
const chatsDir = path15.join(slugPath, "chats");
|
|
7750
|
+
if (!fs11.existsSync(chatsDir)) continue;
|
|
7371
7751
|
let chatFiles;
|
|
7372
7752
|
try {
|
|
7373
|
-
chatFiles =
|
|
7753
|
+
chatFiles = fs11.readdirSync(chatsDir).filter((f) => f.endsWith(".json") || f.endsWith(".jsonl")).sort((a, b) => Number(b.endsWith(".jsonl")) - Number(a.endsWith(".jsonl")));
|
|
7374
7754
|
} catch {
|
|
7375
7755
|
continue;
|
|
7376
7756
|
}
|
|
@@ -7383,7 +7763,7 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
|
7383
7763
|
onProgress?.(result.filesScanned);
|
|
7384
7764
|
let raw;
|
|
7385
7765
|
try {
|
|
7386
|
-
raw =
|
|
7766
|
+
raw = fs11.readFileSync(path15.join(chatsDir, chatFile), "utf-8");
|
|
7387
7767
|
} catch {
|
|
7388
7768
|
continue;
|
|
7389
7769
|
}
|
|
@@ -7602,7 +7982,7 @@ function scanCodexHistory(startDate, onProgress, onLine) {
|
|
|
7602
7982
|
onProgress?.(result.filesScanned);
|
|
7603
7983
|
let lines;
|
|
7604
7984
|
try {
|
|
7605
|
-
lines =
|
|
7985
|
+
lines = fs11.readFileSync(filePath, "utf-8").split("\n");
|
|
7606
7986
|
} catch {
|
|
7607
7987
|
continue;
|
|
7608
7988
|
}
|
|
@@ -7625,7 +8005,7 @@ function scanCodexHistory(startDate, onProgress, onLine) {
|
|
|
7625
8005
|
sessionId = String(payload["id"] ?? filePath);
|
|
7626
8006
|
startTime = String(payload["timestamp"] ?? "");
|
|
7627
8007
|
const cwd = String(payload["cwd"] ?? "");
|
|
7628
|
-
projLabel = stripTerminalEscapes(cwd.replace(
|
|
8008
|
+
projLabel = stripTerminalEscapes(cwd.replace(os13.homedir(), "~")).slice(0, 40);
|
|
7629
8009
|
continue;
|
|
7630
8010
|
}
|
|
7631
8011
|
if (entry.type === "event_msg" && payload["type"] === "user_message") {
|
|
@@ -7870,23 +8250,23 @@ var init_scan = __esm({
|
|
|
7870
8250
|
});
|
|
7871
8251
|
|
|
7872
8252
|
// src/tui/dashboard/data.ts
|
|
7873
|
-
import
|
|
7874
|
-
import
|
|
7875
|
-
import
|
|
8253
|
+
import fs12 from "fs";
|
|
8254
|
+
import os14 from "os";
|
|
8255
|
+
import path16 from "path";
|
|
7876
8256
|
import http from "http";
|
|
7877
8257
|
function auditLogPath() {
|
|
7878
|
-
return
|
|
8258
|
+
return path16.join(os14.homedir(), ".node9", "audit.log");
|
|
7879
8259
|
}
|
|
7880
8260
|
function readAuditEntriesAsync(chunkSize = 1e3, customPath) {
|
|
7881
8261
|
return new Promise((resolve) => {
|
|
7882
8262
|
const p = customPath ?? auditLogPath();
|
|
7883
|
-
if (!
|
|
8263
|
+
if (!fs12.existsSync(p)) {
|
|
7884
8264
|
resolve([]);
|
|
7885
8265
|
return;
|
|
7886
8266
|
}
|
|
7887
8267
|
let raw;
|
|
7888
8268
|
try {
|
|
7889
|
-
raw =
|
|
8269
|
+
raw = fs12.readFileSync(p, "utf8");
|
|
7890
8270
|
} catch {
|
|
7891
8271
|
resolve([]);
|
|
7892
8272
|
return;
|
|
@@ -8018,13 +8398,13 @@ function loadBlast() {
|
|
|
8018
8398
|
}
|
|
8019
8399
|
}
|
|
8020
8400
|
function shortenPath(p) {
|
|
8021
|
-
const home =
|
|
8401
|
+
const home = os14.homedir();
|
|
8022
8402
|
return p.startsWith(home) ? p.replace(home, "~") : p;
|
|
8023
8403
|
}
|
|
8024
8404
|
async function loadReportAuditAsync(period) {
|
|
8025
|
-
const claudeProjectsDir =
|
|
8405
|
+
const claudeProjectsDir = path16.join(os14.homedir(), ".claude", "projects");
|
|
8026
8406
|
const codexSessionsDir2 = codexSessionsDir();
|
|
8027
|
-
const geminiTmpDir =
|
|
8407
|
+
const geminiTmpDir = path16.join(os14.homedir(), ".gemini", "tmp");
|
|
8028
8408
|
const { start, end } = getDateRange(period, /* @__PURE__ */ new Date());
|
|
8029
8409
|
const entries = await readAuditEntriesAsync();
|
|
8030
8410
|
void ensurePricingLoaded();
|
|
@@ -9130,8 +9510,8 @@ import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-run
|
|
|
9130
9510
|
function TopToolsProjects({ audit }) {
|
|
9131
9511
|
const data = audit?.data;
|
|
9132
9512
|
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(
|
|
9513
|
+
const projects = data ? [...data.cost.byProject.entries()].map(([path17, r]) => ({
|
|
9514
|
+
name: basenameOf(path17),
|
|
9135
9515
|
cost: r.cost,
|
|
9136
9516
|
tokens: r.inputTokens + r.outputTokens
|
|
9137
9517
|
})).sort((a, b) => b.cost - a.cost).slice(0, ROW_LIMIT) : [];
|
|
@@ -9568,8 +9948,8 @@ function pickTopLoopFile(loops) {
|
|
|
9568
9948
|
map.set(k, (map.get(k) ?? 0) + (l.count ?? 0));
|
|
9569
9949
|
}
|
|
9570
9950
|
if (map.size === 0) return void 0;
|
|
9571
|
-
const [
|
|
9572
|
-
return { path:
|
|
9951
|
+
const [path17, count] = [...map.entries()].sort((a, b) => b[1] - a[1])[0];
|
|
9952
|
+
return { path: path17, count };
|
|
9573
9953
|
}
|
|
9574
9954
|
var EMPTY_FILTERED_SCAN;
|
|
9575
9955
|
var init_derive = __esm({
|