@node9/proxy 2.13.0 → 2.14.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/README.md +1 -1
- package/dist/cli.js +763 -521
- package/dist/cli.mjs +763 -521
- package/dist/dashboard.mjs +2806 -2331
- package/dist/index.js +375 -92
- package/dist/index.mjs +375 -92
- package/dist/scan-ink.mjs +86 -12
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -1157,9 +1157,7 @@ function unwrapCommandHead(words) {
|
|
|
1157
1157
|
while (i < words.length) {
|
|
1158
1158
|
const head = (words[i] ?? "").toLowerCase().split("/").pop() ?? "";
|
|
1159
1159
|
if (head === "find") {
|
|
1160
|
-
const x = words.findIndex(
|
|
1161
|
-
(w, k) => k > i && (w === "-exec" || w === "-execdir" || w === "-ok")
|
|
1162
|
-
);
|
|
1160
|
+
const x = words.findIndex((w, k) => k > i && w !== null && FIND_EXEC_FLAGS.has(w));
|
|
1163
1161
|
if (x < 0) break;
|
|
1164
1162
|
i = x + 1;
|
|
1165
1163
|
continue;
|
|
@@ -1181,7 +1179,11 @@ function unwrapCommandHead(words) {
|
|
|
1181
1179
|
if (t.startsWith("-")) {
|
|
1182
1180
|
i++;
|
|
1183
1181
|
const nxt = words[i];
|
|
1184
|
-
if (nxt != null && !nxt.startsWith("-") && !INLINE_INTERPRETER.test(nxt.split("/").pop() ?? "") && !COMMAND_WRAPPERS.has(nxt.toLowerCase()) && !RUNNER_WRAPPERS.has(nxt.toLowerCase())
|
|
1182
|
+
if (nxt != null && !nxt.startsWith("-") && !INLINE_INTERPRETER.test(nxt.split("/").pop() ?? "") && !COMMAND_WRAPPERS.has(nxt.toLowerCase()) && !RUNNER_WRAPPERS.has(nxt.toLowerCase()) && // A reader is a command, never a flag's operand: `env - cat X`,
|
|
1183
|
+
// `stdbuf -o0 cat X`, `ionice -c3 cat X`. Without this the head was
|
|
1184
|
+
// swallowed and the jail needed a looser fallback whose cost was a
|
|
1185
|
+
// false positive on `sudo echo cat X`.
|
|
1186
|
+
!FS_READ_TOOLS.has(nxt.split("/").pop()?.toLowerCase() ?? ""))
|
|
1185
1187
|
i++;
|
|
1186
1188
|
continue;
|
|
1187
1189
|
}
|
|
@@ -1300,36 +1302,32 @@ function isProtectedHomePath(rawPath) {
|
|
|
1300
1302
|
}
|
|
1301
1303
|
return true;
|
|
1302
1304
|
}
|
|
1303
|
-
function
|
|
1304
|
-
const
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
const
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
if (t === "Lit") s += (p.Value ?? "").replace(/\\(.)/g, "$1");
|
|
1312
|
-
else if (t === "SglQuoted") s += p.Value ?? "";
|
|
1313
|
-
else if (t === "DblQuoted") {
|
|
1314
|
-
const inner = p.Parts || [];
|
|
1315
|
-
if (!inner.every((ip) => syntax.NodeType(ip) === "Lit")) return null;
|
|
1316
|
-
s += inner.map((ip) => ip.Value ?? "").join("");
|
|
1317
|
-
} else {
|
|
1318
|
-
return null;
|
|
1319
|
-
}
|
|
1305
|
+
function positionedArgs(words, from = 1, to = words.length) {
|
|
1306
|
+
const out = [];
|
|
1307
|
+
let afterFlag = null;
|
|
1308
|
+
for (let i = from; i < to; i++) {
|
|
1309
|
+
const v = words[i];
|
|
1310
|
+
if (v === null) {
|
|
1311
|
+
afterFlag = null;
|
|
1312
|
+
continue;
|
|
1320
1313
|
}
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
const v = litFromWord(args[i]);
|
|
1328
|
-
if (v === null) continue;
|
|
1329
|
-
if (v.startsWith("-")) flags.push(v);
|
|
1330
|
-
else paths.push(v);
|
|
1314
|
+
if (v.startsWith("-")) {
|
|
1315
|
+
afterFlag = v;
|
|
1316
|
+
continue;
|
|
1317
|
+
}
|
|
1318
|
+
out.push({ value: v, index: out.length, argv: i, afterFlag });
|
|
1319
|
+
afterFlag = null;
|
|
1331
1320
|
}
|
|
1332
|
-
return
|
|
1321
|
+
return out;
|
|
1322
|
+
}
|
|
1323
|
+
function extractLiteralArgs(callExpr) {
|
|
1324
|
+
const rawArgs = callExpr.Args || [];
|
|
1325
|
+
if (rawArgs.length === 0) return { name: "", flags: [], paths: [], words: [], args: [] };
|
|
1326
|
+
const words = rawArgs.map((a) => resolveWordLiteral(a));
|
|
1327
|
+
const name = baseWord(words[0]);
|
|
1328
|
+
const flags = words.slice(1).filter((w) => w !== null && w.startsWith("-"));
|
|
1329
|
+
const args = positionedArgs(words);
|
|
1330
|
+
return { name, flags, paths: args.map((a) => a.value), words, args };
|
|
1333
1331
|
}
|
|
1334
1332
|
function resolveWordLiteral(w) {
|
|
1335
1333
|
const parts = w?.Parts || [];
|
|
@@ -1587,16 +1585,21 @@ function isRmCreatedInCommandCleanup(command) {
|
|
|
1587
1585
|
}
|
|
1588
1586
|
return sawRm && ok2;
|
|
1589
1587
|
}
|
|
1590
|
-
function analyzeFsOperationImpl(command) {
|
|
1588
|
+
function analyzeFsOperationImpl(command, depth = 0) {
|
|
1591
1589
|
const f = parseShared(command);
|
|
1592
1590
|
if (f === PARSE_FAIL) return null;
|
|
1593
1591
|
let result = null;
|
|
1594
1592
|
try {
|
|
1595
1593
|
syntax.Walk(f, (node) => {
|
|
1596
|
-
if (!node || result) return false;
|
|
1594
|
+
if (!node || result?.verdict === "block") return false;
|
|
1597
1595
|
const n = node;
|
|
1598
|
-
|
|
1599
|
-
|
|
1596
|
+
const nodeType = syntax.NodeType(n);
|
|
1597
|
+
if (nodeType === "Stmt") {
|
|
1598
|
+
result = stricter(result, jailedRedirectRead(n));
|
|
1599
|
+
return result?.verdict !== "block";
|
|
1600
|
+
}
|
|
1601
|
+
if (nodeType !== "CallExpr") return true;
|
|
1602
|
+
const { name, flags, paths, words } = extractLiteralArgs(n);
|
|
1600
1603
|
if (!name) return true;
|
|
1601
1604
|
if (name === "rm") {
|
|
1602
1605
|
const flagStr = flags.join("").toLowerCase();
|
|
@@ -1625,21 +1628,27 @@ function analyzeFsOperationImpl(command) {
|
|
|
1625
1628
|
}
|
|
1626
1629
|
}
|
|
1627
1630
|
}
|
|
1628
|
-
if (
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
reason: sp.reason,
|
|
1636
|
-
path: p
|
|
1637
|
-
};
|
|
1638
|
-
return false;
|
|
1639
|
-
}
|
|
1631
|
+
if (depth < 1) {
|
|
1632
|
+
const payload = literalShellPayload(words, name);
|
|
1633
|
+
if (payload !== null) {
|
|
1634
|
+
const inner = analyzeFsOperationImpl(payload, depth + 1);
|
|
1635
|
+
if (inner) {
|
|
1636
|
+
result = inner;
|
|
1637
|
+
return false;
|
|
1640
1638
|
}
|
|
1639
|
+
return true;
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
const readPaths = FS_READ_TOOLS.has(name) ? paths : wrappedReadPaths(words, name);
|
|
1643
|
+
if (readPaths) {
|
|
1644
|
+
for (const p of readPaths) {
|
|
1645
|
+
result = stricter(result, matchSensitivePath2(p));
|
|
1646
|
+
if (result?.verdict === "block") return false;
|
|
1641
1647
|
}
|
|
1642
1648
|
}
|
|
1649
|
+
for (const p of copySourcePaths(words)) {
|
|
1650
|
+
result = stricter(result, copyVerdictOf(matchSensitivePath2(p)));
|
|
1651
|
+
}
|
|
1643
1652
|
return true;
|
|
1644
1653
|
});
|
|
1645
1654
|
return result;
|
|
@@ -1647,6 +1656,183 @@ function analyzeFsOperationImpl(command) {
|
|
|
1647
1656
|
return null;
|
|
1648
1657
|
}
|
|
1649
1658
|
}
|
|
1659
|
+
function stricter(a, b) {
|
|
1660
|
+
if (!a) return b;
|
|
1661
|
+
if (!b) return a;
|
|
1662
|
+
return b.verdict === "block" && a.verdict !== "block" ? b : a;
|
|
1663
|
+
}
|
|
1664
|
+
function flagInfo(w) {
|
|
1665
|
+
if (w.startsWith("--")) {
|
|
1666
|
+
const eq = w.indexOf("=");
|
|
1667
|
+
return eq < 0 ? { letter: null, long: w, attached: null } : { letter: null, long: w.slice(0, eq), attached: w.slice(eq + 1) };
|
|
1668
|
+
}
|
|
1669
|
+
const m = /^-([a-zA-Z]+)(.*)$/.exec(w);
|
|
1670
|
+
if (!m) return { letter: null, long: null, attached: null };
|
|
1671
|
+
return { letter: m[1][m[1].length - 1], long: null, attached: m[2] === "" ? null : m[2] };
|
|
1672
|
+
}
|
|
1673
|
+
function flagIs(w, names) {
|
|
1674
|
+
if (w === null) return false;
|
|
1675
|
+
const f = flagInfo(w);
|
|
1676
|
+
return names.some((n) => n.startsWith("--") ? f.long === n : f.letter === n);
|
|
1677
|
+
}
|
|
1678
|
+
function operandOf(a, names) {
|
|
1679
|
+
if (!names || a.afterFlag === null) return false;
|
|
1680
|
+
return flagIs(a.afterFlag, names) && flagInfo(a.afterFlag).attached === null;
|
|
1681
|
+
}
|
|
1682
|
+
function resolveCopyShape(words, h) {
|
|
1683
|
+
const verb = baseWord(words[h]);
|
|
1684
|
+
if (!verb) return null;
|
|
1685
|
+
const direct = COPY_VERBS[verb];
|
|
1686
|
+
if (direct) return { shape: direct, last: h };
|
|
1687
|
+
const slots = positionedArgs(words, h + 1);
|
|
1688
|
+
for (let i = 0; i < slots.length; i++) {
|
|
1689
|
+
for (let n = 3; n >= 1; n--) {
|
|
1690
|
+
const part = slots.slice(i, i + n);
|
|
1691
|
+
if (part.length < n) continue;
|
|
1692
|
+
const key = [verb, ...part.map((a) => a.value.toLowerCase())].join(" ");
|
|
1693
|
+
const shape = COPY_VERBS[key];
|
|
1694
|
+
if (shape) return { shape, last: part[n - 1].argv };
|
|
1695
|
+
}
|
|
1696
|
+
if (slots[i].afterFlag === null) return null;
|
|
1697
|
+
}
|
|
1698
|
+
return null;
|
|
1699
|
+
}
|
|
1700
|
+
function findStartPoints(words, h) {
|
|
1701
|
+
const k = words.findIndex((w, i) => i > h && w !== null && FIND_EXEC_FLAGS.has(w));
|
|
1702
|
+
if (k < 0) return { k, starts: [] };
|
|
1703
|
+
const firstPredicate = words.findIndex(
|
|
1704
|
+
(w, i) => i > h && w !== null && w.startsWith("-") && !FIND_OPTIONS.has(w)
|
|
1705
|
+
);
|
|
1706
|
+
const end = firstPredicate > h ? firstPredicate : k;
|
|
1707
|
+
return { k, starts: positionalAfter(words, h + 1, end) };
|
|
1708
|
+
}
|
|
1709
|
+
function copySourcePaths(words) {
|
|
1710
|
+
const h = unwrapCommandHead(words);
|
|
1711
|
+
const fi = words.findIndex((w, i) => i <= h && baseWord(w) === "find");
|
|
1712
|
+
if (fi >= 0) {
|
|
1713
|
+
const { k, starts } = findStartPoints(words, fi);
|
|
1714
|
+
if (k < 0) return [];
|
|
1715
|
+
const action = unwrapCommandHead(words.slice(k + 1));
|
|
1716
|
+
return resolveCopyShape(words.slice(k + 1), action) ? starts : [];
|
|
1717
|
+
}
|
|
1718
|
+
if (!COPY_VERB_HEADS.has(baseWord(words[h]))) return [];
|
|
1719
|
+
const r = resolveCopyShape(words, h);
|
|
1720
|
+
if (!r) return [];
|
|
1721
|
+
const { shape, last } = r;
|
|
1722
|
+
const args = positionedArgs(words, last + 1);
|
|
1723
|
+
const tail = words.slice(last + 1);
|
|
1724
|
+
const skipped = (a) => operandOf(a, shape.skipFlags);
|
|
1725
|
+
const targetDir = shape.targetDirFlag === true && tail.some((w) => w !== null && w.startsWith("-") && flagIs(w, ["t", "--target-directory"]));
|
|
1726
|
+
const targetOperand = (a) => targetDir && operandOf(a, ["t", "--target-directory"]);
|
|
1727
|
+
const lastOperand = [...tail].reverse().find((w) => w === null || !w.startsWith("-"));
|
|
1728
|
+
const dynamicDest = lastOperand === null;
|
|
1729
|
+
const destIsLastOperand = (shape.source === "allButLast" || shape.source === "first") && !targetDir && !dynamicDest;
|
|
1730
|
+
if (destIsLastOperand && typeof lastOperand === "string" && matchSensitivePath2(lastOperand))
|
|
1731
|
+
return [];
|
|
1732
|
+
let src;
|
|
1733
|
+
switch (shape.source) {
|
|
1734
|
+
case "all":
|
|
1735
|
+
src = args;
|
|
1736
|
+
break;
|
|
1737
|
+
case "first":
|
|
1738
|
+
src = targetDir ? args : args.slice(0, 1);
|
|
1739
|
+
break;
|
|
1740
|
+
case "flagOperand": {
|
|
1741
|
+
const inline = tail.filter((w) => w !== null && w.startsWith("--")).map((w) => flagInfo(w)).filter((f) => f.attached !== null && (shape.sourceFlags ?? []).includes(f.long ?? "")).map((f) => f.attached);
|
|
1742
|
+
return [
|
|
1743
|
+
...args.filter(
|
|
1744
|
+
(a) => flagIs(a.afterFlag, shape.sourceFlags ?? []) && flagInfo(a.afterFlag).attached === null
|
|
1745
|
+
).map((a) => a.value),
|
|
1746
|
+
...inline
|
|
1747
|
+
];
|
|
1748
|
+
}
|
|
1749
|
+
case "archive":
|
|
1750
|
+
src = archiveInputs(shape.archive, args, tail);
|
|
1751
|
+
break;
|
|
1752
|
+
case "allButLast":
|
|
1753
|
+
src = targetDir || dynamicDest ? args : args.slice(0, -1);
|
|
1754
|
+
break;
|
|
1755
|
+
}
|
|
1756
|
+
return src.filter((a) => !skipped(a) && !targetOperand(a)).map((a) => a.value);
|
|
1757
|
+
}
|
|
1758
|
+
function archiveInputs(kind, args, tail) {
|
|
1759
|
+
const first = args[0];
|
|
1760
|
+
const bareKey = first && first.afterFlag === null && TAR_MODE_WORD.test(first.value);
|
|
1761
|
+
if (kind === "tar") {
|
|
1762
|
+
const flagsText = tail.filter((w) => w !== null && w.startsWith("-")).join(" ");
|
|
1763
|
+
const mode = (bareKey ? first.value : "") + flagsText;
|
|
1764
|
+
const extracting = /x|t/.test(bareKey ? first.value.replace(/f/g, "") : "") || /(^|\s)-[a-zA-Z]*[xt]|--extract|--list|--get/.test(flagsText);
|
|
1765
|
+
const writing = /[cruA]/.test(bareKey ? first.value : "") || /(^|\s)-[a-zA-Z]*[cruA]|--create|--append|--update|--concatenate/.test(flagsText);
|
|
1766
|
+
if (extracting && !writing) return [];
|
|
1767
|
+
void mode;
|
|
1768
|
+
let i = 0;
|
|
1769
|
+
if (bareKey) {
|
|
1770
|
+
i = 1;
|
|
1771
|
+
const next = args[1];
|
|
1772
|
+
if (first.value.includes("f") && next && next.afterFlag === null) i = 2;
|
|
1773
|
+
}
|
|
1774
|
+
return args.slice(i);
|
|
1775
|
+
}
|
|
1776
|
+
if (kind === "zip") return first && first.afterFlag === "-" ? args : args.slice(1);
|
|
1777
|
+
if (kind === "ar") return bareKey ? args.slice(2) : args.slice(1);
|
|
1778
|
+
return args.slice(2);
|
|
1779
|
+
}
|
|
1780
|
+
function copyVerdictOf(hit) {
|
|
1781
|
+
if (!hit) return null;
|
|
1782
|
+
const ruleName = COPY_RULE_OF[hit.ruleName];
|
|
1783
|
+
if (!ruleName) return null;
|
|
1784
|
+
return {
|
|
1785
|
+
ruleName,
|
|
1786
|
+
verdict: "review",
|
|
1787
|
+
reason: `Copying ${hit.path} moves a credential out of its jail (project-jail shield)`,
|
|
1788
|
+
path: hit.path
|
|
1789
|
+
};
|
|
1790
|
+
}
|
|
1791
|
+
function matchSensitivePath2(p) {
|
|
1792
|
+
for (const sp of SENSITIVE_PATH_RULES) {
|
|
1793
|
+
if (sp.match(p))
|
|
1794
|
+
return { ruleName: sp.rule, verdict: sp.verdict ?? "block", reason: sp.reason, path: p };
|
|
1795
|
+
}
|
|
1796
|
+
return null;
|
|
1797
|
+
}
|
|
1798
|
+
function baseWord(w) {
|
|
1799
|
+
return (w ?? "").split("/").pop()?.toLowerCase() ?? "";
|
|
1800
|
+
}
|
|
1801
|
+
function wrappedReadPaths(words, name) {
|
|
1802
|
+
if (name === "find") {
|
|
1803
|
+
const { k, starts } = findStartPoints(words, 0);
|
|
1804
|
+
return k > 0 && isReaderWord(words[k + 1] ?? null) ? starts : null;
|
|
1805
|
+
}
|
|
1806
|
+
if (!COMMAND_WRAPPERS.has(name) && !RUNNER_WRAPPERS.has(name)) return null;
|
|
1807
|
+
const h = unwrapCommandHead(words);
|
|
1808
|
+
return h > 0 && isReaderWord(words[h] ?? null) ? positionalAfter(words, h + 1) : null;
|
|
1809
|
+
}
|
|
1810
|
+
function literalShellPayload(words, name) {
|
|
1811
|
+
const h = COMMAND_WRAPPERS.has(name) || RUNNER_WRAPPERS.has(name) ? unwrapCommandHead(words) : 0;
|
|
1812
|
+
const head = (words[h] ?? "").split("/").pop()?.toLowerCase() ?? "";
|
|
1813
|
+
if (head === "eval") {
|
|
1814
|
+
const rest = words.slice(h + 1);
|
|
1815
|
+
if (rest.length === 0 || rest.some((w) => w === null)) return null;
|
|
1816
|
+
return rest.join(" ");
|
|
1817
|
+
}
|
|
1818
|
+
if (SHELL_INTERPRETERS.has(head)) {
|
|
1819
|
+
const c = words.findIndex((w, i) => i > h && w !== null && isInlineCodeFlag(head, w));
|
|
1820
|
+
if (c < 0) return null;
|
|
1821
|
+
return words[c + 1] ?? null;
|
|
1822
|
+
}
|
|
1823
|
+
return null;
|
|
1824
|
+
}
|
|
1825
|
+
function jailedRedirectRead(stmt) {
|
|
1826
|
+
const redirs = stmt.Redirs || [];
|
|
1827
|
+
for (const r of redirs) {
|
|
1828
|
+
if (!r || !REDIR_FILE_IN_OPS.has(r.Op)) continue;
|
|
1829
|
+
const p = resolveWordLiteral(r.Word);
|
|
1830
|
+
if (p === null || p === "") continue;
|
|
1831
|
+
const hit = matchSensitivePath2(p);
|
|
1832
|
+
if (hit) return hit;
|
|
1833
|
+
}
|
|
1834
|
+
return null;
|
|
1835
|
+
}
|
|
1650
1836
|
function analyzeShellCommand(command) {
|
|
1651
1837
|
const actions = [];
|
|
1652
1838
|
const paths = [];
|
|
@@ -1782,8 +1968,8 @@ function splitOnPipe(cmd) {
|
|
|
1782
1968
|
if (current.trim()) segments2.push(current.trim());
|
|
1783
1969
|
return segments2.filter(Boolean);
|
|
1784
1970
|
}
|
|
1785
|
-
function positionalTokens(
|
|
1786
|
-
return
|
|
1971
|
+
function positionalTokens(tokens) {
|
|
1972
|
+
return tokens.slice(1).filter((t) => !t.startsWith("-") && !t.startsWith("@") && t.length > 0);
|
|
1787
1973
|
}
|
|
1788
1974
|
function analyzePipeChain(command) {
|
|
1789
1975
|
const segments2 = splitOnPipe(command);
|
|
@@ -1806,8 +1992,10 @@ function analyzePipeChain(command) {
|
|
|
1806
1992
|
for (const segment of segments2) {
|
|
1807
1993
|
const tokens = segment.split(/\s+/).filter(Boolean);
|
|
1808
1994
|
if (tokens.length === 0) continue;
|
|
1809
|
-
const
|
|
1810
|
-
const
|
|
1995
|
+
const h = unwrapCommandHead(tokens);
|
|
1996
|
+
const head = h < tokens.length ? h : 0;
|
|
1997
|
+
const binary = tokens[head].toLowerCase();
|
|
1998
|
+
const args = positionalTokens(tokens.slice(head));
|
|
1811
1999
|
if (SOURCE_COMMANDS.has(binary)) {
|
|
1812
2000
|
sourceFiles.push(...args);
|
|
1813
2001
|
if (args.some(isSensitivePath)) hasSensitiveSource = true;
|
|
@@ -2285,6 +2473,7 @@ async function evaluatePolicy(config, toolName, args, context = {}, hooks = {})
|
|
|
2285
2473
|
const shellShaped = isBashTool(toolName) || inspectsShellCommand(toolName, config.policy.toolInspection);
|
|
2286
2474
|
const shellShapedCommand = shellShaped ? isBashTool(toolName) && args && typeof args === "object" ? typeof args.command === "string" ? args.command : null : extractShellCommand(toolName, args, config.policy.toolInspection) : null;
|
|
2287
2475
|
const bashCommand = agent !== "Terminal" ? shellShapedCommand : null;
|
|
2476
|
+
let pendingAstReview;
|
|
2288
2477
|
if (bashCommand !== null) {
|
|
2289
2478
|
const pipeVerdict = pipeChainVerdict(
|
|
2290
2479
|
bashCommand,
|
|
@@ -2296,7 +2485,7 @@ async function evaluatePolicy(config, toolName, args, context = {}, hooks = {})
|
|
|
2296
2485
|
if (fsVerdict) {
|
|
2297
2486
|
const isShieldRule = fsVerdict.ruleName.startsWith("shield:");
|
|
2298
2487
|
const labelPrefix = isShieldRule ? "project-jail (AST)" : "Node9 (AST)";
|
|
2299
|
-
|
|
2488
|
+
const astVerdict = {
|
|
2300
2489
|
decision: fsVerdict.verdict,
|
|
2301
2490
|
blockedByLabel: `${labelPrefix}: ${fsVerdict.ruleName}`,
|
|
2302
2491
|
reason: fsVerdict.reason,
|
|
@@ -2304,6 +2493,8 @@ async function evaluatePolicy(config, toolName, args, context = {}, hooks = {})
|
|
|
2304
2493
|
ruleName: fsVerdict.ruleName,
|
|
2305
2494
|
ruleDescription: fsVerdict.reason
|
|
2306
2495
|
};
|
|
2496
|
+
if (fsVerdict.verdict === "block") return astVerdict;
|
|
2497
|
+
pendingAstReview = astVerdict;
|
|
2307
2498
|
}
|
|
2308
2499
|
const sqlAction = resolveCheck(config.policy.commandChecks?.sqlDdl);
|
|
2309
2500
|
const sqlVerdict = sqlAction === "off" ? null : analyzeSqlDestructive(bashCommand);
|
|
@@ -2346,7 +2537,7 @@ async function evaluatePolicy(config, toolName, args, context = {}, hooks = {})
|
|
|
2346
2537
|
const matchedRule = resolvePinned(matches);
|
|
2347
2538
|
if (matchedRule) {
|
|
2348
2539
|
if (matchedRule.verdict === "allow")
|
|
2349
|
-
return { decision: "allow", ruleName: matchedRule.name ?? matchedRule.tool };
|
|
2540
|
+
return pendingAstReview ?? { decision: "allow", ruleName: matchedRule.name ?? matchedRule.tool };
|
|
2350
2541
|
return {
|
|
2351
2542
|
decision: matchedRule.verdict,
|
|
2352
2543
|
blockedByLabel: `Smart Rule: ${matchedRule.name ?? matchedRule.tool}`,
|
|
@@ -2373,6 +2564,7 @@ async function evaluatePolicy(config, toolName, args, context = {}, hooks = {})
|
|
|
2373
2564
|
allTokens = analyzed.allTokens;
|
|
2374
2565
|
pathTokens = analyzed.paths;
|
|
2375
2566
|
const candidates2 = [];
|
|
2567
|
+
if (pendingAstReview) candidates2.push(pendingAstReview);
|
|
2376
2568
|
const evalVerdict = detectDangerousShellExec(shellCommand);
|
|
2377
2569
|
if (evalVerdict === "block") {
|
|
2378
2570
|
return {
|
|
@@ -2669,6 +2861,12 @@ function classifyRuleSeverity(name, verdict) {
|
|
|
2669
2861
|
"read-ssh",
|
|
2670
2862
|
"read-gcp",
|
|
2671
2863
|
"read-cred",
|
|
2864
|
+
// Stage 4 (2026-09-11): a copy of a credential file scores like a READ of it --
|
|
2865
|
+
// read-ssh/aws/cred are critical, so copy-ssh/aws/cred are; read-env is high
|
|
2866
|
+
// (below), so copy-env joins the high list, not this one (/code-review).
|
|
2867
|
+
"copy-ssh",
|
|
2868
|
+
"copy-aws",
|
|
2869
|
+
"copy-cred",
|
|
2672
2870
|
"delete-repo",
|
|
2673
2871
|
"helm-uninstall",
|
|
2674
2872
|
"drop-table",
|
|
@@ -2681,6 +2879,7 @@ function classifyRuleSeverity(name, verdict) {
|
|
|
2681
2879
|
];
|
|
2682
2880
|
if (criticalPatterns.some((p) => n.includes(p))) return "critical";
|
|
2683
2881
|
const highPatterns = [
|
|
2882
|
+
"copy-env",
|
|
2684
2883
|
"force-push",
|
|
2685
2884
|
"force_push",
|
|
2686
2885
|
"git-destructive",
|
|
@@ -2698,6 +2897,11 @@ function narrativeRuleLabel(name) {
|
|
|
2698
2897
|
const map = {
|
|
2699
2898
|
"read-aws": "AWS credentials read",
|
|
2700
2899
|
"read-ssh": "SSH private key read",
|
|
2900
|
+
// Stage 4 copy twins, so `scan --narrative` prints a label, not a raw slug.
|
|
2901
|
+
"copy-ssh": "SSH private key copied out",
|
|
2902
|
+
"copy-aws": "AWS credentials copied out",
|
|
2903
|
+
"copy-env": ".env file copied out",
|
|
2904
|
+
"copy-cred": "credential file copied out",
|
|
2701
2905
|
"read-gcp": "GCP credentials read",
|
|
2702
2906
|
"read-cred": "credential file read",
|
|
2703
2907
|
"delete-repo": "GitHub repository deletion",
|
|
@@ -3287,7 +3491,7 @@ function* stringValues(obj, depth = 0) {
|
|
|
3287
3491
|
}
|
|
3288
3492
|
for (const v of Object.values(obj)) yield* stringValues(v, depth + 1);
|
|
3289
3493
|
}
|
|
3290
|
-
var IBAN_LENGTH, B58, B58_INDEX, XPRV_VERSIONS, MAX, UNTRUSTED_TOOLS, SIGNALS, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, MAX_DEPTH, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, DLP_SCAN_LIMITS, 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, SQL_DB_CLIS, SQL_DDL_RE, CHMOD_OPEN_PERM_TOKENS, COMMAND_WRAPPERS, INLINE_INTERPRETER, RUNNER_WRAPPERS, _redirStdinOps, _listOps, WRAPPER_TAKES_TARGET, INTERP_LEADING_TARGET, NET_BINARIES, VALUE_FLAGS, FS_OP_CACHE_MAX, fsOpCache, stripDotSlash, REDIR_TRUNCATE_OPS, REDIR_HEREDOC_OPS, DEFAULT_EGRESS_ALLOWLIST, SOURCE_COMMANDS, SINK_COMMANDS, OBFUSCATORS, SENSITIVE_PATTERNS, FLAGS_WITH_VALUES, SSRF_MAX_HOST, METADATA_ADDRESSES, STRICT_TIERS, METADATA_HOSTNAMES, v4Octets, TIER_REASON, DESTINATION_ARGS, VERDICT_RANK, SQL_DML_KEYWORDS, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, BUILTIN_SHIELDS, LOOP_MAX_RECORDS, FINDING_TO_SIGNAL, SCAN_SIGNAL_WEIGHTS, LOOP_THRESHOLD_FOR_WASTE, DESTRUCTIVE_OP_RE, SENSITIVE_PATH_RE, FILE_TOOLS, PII_EMAIL_RE, PII_SSN_RE, PII_PHONE_RE, PII_CC16_RE, PII_CC15_RE, PII_IBAN_RE, REALTIME_PII_PATTERNS, MAX_PII_SCAN_BYTES, 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, CANONICAL_EXTRACTOR_VERSION, DEDUPE_PREVIEW_LEN, TERMINAL_ESCAPE_RE, ENGINE_VERSION;
|
|
3494
|
+
var IBAN_LENGTH, B58, B58_INDEX, XPRV_VERSIONS, MAX, UNTRUSTED_TOOLS, SIGNALS, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, MAX_DEPTH, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, DLP_SCAN_LIMITS, 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, SCP_VALUE_FLAGS, RSYNC_SKIP, 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, SQL_DB_CLIS, SQL_DDL_RE, CHMOD_OPEN_PERM_TOKENS, COMMAND_WRAPPERS, INLINE_INTERPRETER, RUNNER_WRAPPERS, _redirStdinOps, _listOps, WRAPPER_TAKES_TARGET, FIND_EXEC_FLAGS, INTERP_LEADING_TARGET, NET_BINARIES, VALUE_FLAGS, FS_OP_CACHE_MAX, fsOpCache, stripDotSlash, REDIR_TRUNCATE_OPS, REDIR_FILE_IN_OPS, REDIR_HEREDOC_OPS, FIND_OPTIONS, COPY_RULE_OF, isReaderWord, positionalAfter, DEFAULT_EGRESS_ALLOWLIST, SOURCE_COMMANDS, SINK_COMMANDS, OBFUSCATORS, SENSITIVE_PATTERNS, FLAGS_WITH_VALUES, SSRF_MAX_HOST, METADATA_ADDRESSES, STRICT_TIERS, METADATA_HOSTNAMES, v4Octets, TIER_REASON, DESTINATION_ARGS, VERDICT_RANK, SQL_DML_KEYWORDS, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, BUILTIN_SHIELDS, LOOP_MAX_RECORDS, FINDING_TO_SIGNAL, SCAN_SIGNAL_WEIGHTS, LOOP_THRESHOLD_FOR_WASTE, DESTRUCTIVE_OP_RE, SENSITIVE_PATH_RE, FILE_TOOLS, PII_EMAIL_RE, PII_SSN_RE, PII_PHONE_RE, PII_CC16_RE, PII_CC15_RE, PII_IBAN_RE, REALTIME_PII_PATTERNS, MAX_PII_SCAN_BYTES, 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, CANONICAL_EXTRACTOR_VERSION, DEDUPE_PREVIEW_LEN, TERMINAL_ESCAPE_RE, ENGINE_VERSION;
|
|
3291
3495
|
var init_dist = __esm({
|
|
3292
3496
|
"packages/policy-engine/dist/index.mjs"() {
|
|
3293
3497
|
"use strict";
|
|
@@ -3905,13 +4109,32 @@ var init_dist = __esm({
|
|
|
3905
4109
|
})
|
|
3906
4110
|
);
|
|
3907
4111
|
SENSITIVE_PATH_PATTERNS = [
|
|
3908
|
-
/[/\\]\.ssh[/\\]/i,
|
|
3909
|
-
/[/\\]\.aws[/\\]/i,
|
|
4112
|
+
/[/\\]\.ssh([/\\]|$)/i,
|
|
4113
|
+
/[/\\]\.aws([/\\]|$)/i,
|
|
3910
4114
|
/[/\\]\.config[/\\]gcloud[/\\]/i,
|
|
3911
4115
|
/[/\\]\.azure[/\\]/i,
|
|
3912
4116
|
/[/\\]\.kube[/\\]config$/i,
|
|
3913
|
-
|
|
3914
|
-
// .
|
|
4117
|
+
// ⚠️ ONE SEMANTIC, FOUR COPIES. This is the AST tier's `.env` rule verbatim
|
|
4118
|
+
// (shell/index.ts SENSITIVE_PATH_RULES), whose reasoning is documented there:
|
|
4119
|
+
// structural suffix chain rather than a hand-written list, `example|sample|
|
|
4120
|
+
// template` exempt because a fixture stays a fixture whatever follows, and
|
|
4121
|
+
// `.test` anchored because `test` names an ENVIRONMENT -- `.env.test` is the
|
|
4122
|
+
// committed template, `.env.test.local` is gitignored and holds real values.
|
|
4123
|
+
//
|
|
4124
|
+
// It was previously `[/\\]\.env($|\.)` with NO exemptions, so `Read .env.example`
|
|
4125
|
+
// blocked while `cat .env.example` allowed: the same file, opposite verdicts,
|
|
4126
|
+
// decided only by which tool asked. See src/__tests__/jail-both-doors.test.ts,
|
|
4127
|
+
// which is the contract that now holds these copies in step, and stage 5 of
|
|
4128
|
+
// doc/credential-jail-architecture.md, which replaces them with one generated
|
|
4129
|
+
// source.
|
|
4130
|
+
// ⚠️ The `.local` branch comes FIRST and takes no exemption. A fixture stays a
|
|
4131
|
+
// fixture whatever follows it -- `.env.example.md` is documentation -- but
|
|
4132
|
+
// `.env.example.local` is gitignored by the `.env*.local` convention and holds
|
|
4133
|
+
// real values, exactly the reasoning that anchors `(?!\.test$)` rather than
|
|
4134
|
+
// using `\b`. Without this branch the fixture exemption also bought a two-step
|
|
4135
|
+
// bypass: `cp .env .env.sample`, then read the copy.
|
|
4136
|
+
/[/\\]\.env(?![\w-])(?:[\w.-]*\.local$|(?!\.(?:example|sample|template)\b)(?!\.test$)[\w.-]*$)/i,
|
|
4137
|
+
// .env + any suffix chain; fixtures exempt unless .local
|
|
3915
4138
|
/[/\\]\.git-credentials$/i,
|
|
3916
4139
|
/[/\\]\.npmrc$/i,
|
|
3917
4140
|
/[/\\]\.docker[/\\]config\.json$/i,
|
|
@@ -3993,6 +4216,10 @@ var init_dist = __esm({
|
|
|
3993
4216
|
"od",
|
|
3994
4217
|
"xxd",
|
|
3995
4218
|
"hexdump",
|
|
4219
|
+
// Emits the file's bytes, re-encoded, so it is a read by the set's own test
|
|
4220
|
+
// ("does it emit file contents"). Absent until 2026-09-10, which is why
|
|
4221
|
+
// `base64 ~/.ssh/id_rsa` printed a private key with no verdict.
|
|
4222
|
+
"base64",
|
|
3996
4223
|
"strings",
|
|
3997
4224
|
"sort",
|
|
3998
4225
|
"uniq",
|
|
@@ -4000,8 +4227,56 @@ var init_dist = __esm({
|
|
|
4000
4227
|
"nl",
|
|
4001
4228
|
"dd"
|
|
4002
4229
|
]);
|
|
4230
|
+
SCP_VALUE_FLAGS = ["i", "F", "o", "c", "S", "P", "J", "D", "W", "l"];
|
|
4231
|
+
RSYNC_SKIP = [
|
|
4232
|
+
"e",
|
|
4233
|
+
"--rsh",
|
|
4234
|
+
"--exclude",
|
|
4235
|
+
"--exclude-from",
|
|
4236
|
+
"--include",
|
|
4237
|
+
"--include-from",
|
|
4238
|
+
"--files-from",
|
|
4239
|
+
"f",
|
|
4240
|
+
"--filter"
|
|
4241
|
+
];
|
|
4242
|
+
COPY_VERBS = {
|
|
4243
|
+
cp: { source: "allButLast", targetDirFlag: true },
|
|
4244
|
+
mv: { source: "allButLast", targetDirFlag: true },
|
|
4245
|
+
install: { source: "allButLast", targetDirFlag: true },
|
|
4246
|
+
ln: { source: "first", targetDirFlag: true },
|
|
4247
|
+
scp: { source: "allButLast", skipFlags: SCP_VALUE_FLAGS },
|
|
4248
|
+
rsync: { source: "allButLast", skipFlags: RSYNC_SKIP },
|
|
4249
|
+
tar: {
|
|
4250
|
+
source: "archive",
|
|
4251
|
+
archive: "tar",
|
|
4252
|
+
skipFlags: ["f", "X", "T", "--file", "--exclude", "--exclude-from", "--files-from"]
|
|
4253
|
+
},
|
|
4254
|
+
zip: { source: "archive", archive: "zip", skipFlags: ["x", "i", "--exclude", "--include"] },
|
|
4255
|
+
ar: { source: "archive", archive: "ar" },
|
|
4256
|
+
"7z": { source: "archive", archive: "7z", skipFlags: ["x", "--exclude"] },
|
|
4257
|
+
gzip: { source: "all" },
|
|
4258
|
+
bzip2: { source: "all" },
|
|
4259
|
+
xz: { source: "all" },
|
|
4260
|
+
"docker cp": { source: "allButLast" },
|
|
4261
|
+
"kubectl cp": { source: "allButLast" },
|
|
4262
|
+
"gsutil cp": { source: "allButLast" },
|
|
4263
|
+
"gsutil rsync": { source: "allButLast" },
|
|
4264
|
+
"rclone copy": { source: "allButLast" },
|
|
4265
|
+
"rclone sync": { source: "allButLast" },
|
|
4266
|
+
"aws s3 cp": { source: "allButLast" },
|
|
4267
|
+
"aws s3 mv": { source: "allButLast" },
|
|
4268
|
+
"aws s3 sync": { source: "allButLast" },
|
|
4269
|
+
"gcloud storage cp": { source: "allButLast" },
|
|
4270
|
+
"az storage blob upload": { source: "flagOperand", sourceFlags: ["f", "--file"] }
|
|
4271
|
+
};
|
|
4272
|
+
TAR_MODE_WORD = /^[a-zA-Z]+$/;
|
|
4273
|
+
COPY_VERB_HEADS = new Set(Object.keys(COPY_VERBS).map((k) => k.split(" ")[0]));
|
|
4003
4274
|
FS_OP_PRESCREEN_RE = new RegExp(
|
|
4004
|
-
|
|
4275
|
+
// A quote is a separator too: `eval "cat X"` and `sh -c 'cat X'` put the
|
|
4276
|
+
// reader right after `"` / `'`, and without these two characters the
|
|
4277
|
+
// prescreen rejected every string-wrapped read before the parser ran.
|
|
4278
|
+
// Found 2026-09-11 by instrumenting the walk -- no CallExpr was ever visited.
|
|
4279
|
+
`(?:^|[\\s|;&("'\`\\n/])(?:rm|${[...FS_READ_TOOLS, ...COPY_VERB_HEADS].map((t) => t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})\\b|(?<!<)<(?!<)`
|
|
4005
4280
|
);
|
|
4006
4281
|
HOME_CACHE_ALLOWLIST = [
|
|
4007
4282
|
".cache",
|
|
@@ -4022,12 +4297,12 @@ var init_dist = __esm({
|
|
|
4022
4297
|
{
|
|
4023
4298
|
rule: "shield:project-jail:block-read-ssh",
|
|
4024
4299
|
reason: "Reading SSH private keys is blocked by project-jail shield",
|
|
4025
|
-
match: (p) => /(
|
|
4300
|
+
match: (p) => /([\\/]\.ssh[\\/]|^\.ssh[\\/]|^(?:[~/]|[A-Za-z]:).*[\\/]\.ssh$)/i.test(p)
|
|
4026
4301
|
},
|
|
4027
4302
|
{
|
|
4028
4303
|
rule: "shield:project-jail:block-read-aws",
|
|
4029
4304
|
reason: "Reading AWS credentials is blocked by project-jail shield",
|
|
4030
|
-
match: (p) => /(
|
|
4305
|
+
match: (p) => /([\\/]\.aws[\\/]|^\.aws[\\/]|^(?:[~/]|[A-Za-z]:).*[\\/]\.aws$)/i.test(p)
|
|
4031
4306
|
},
|
|
4032
4307
|
{
|
|
4033
4308
|
// Mirrors the JSON shield's `.env` pattern (project-jail.json's
|
|
@@ -4071,7 +4346,9 @@ var init_dist = __esm({
|
|
|
4071
4346
|
// symmetry — silently exempts every `.env.test.*` file.
|
|
4072
4347
|
//
|
|
4073
4348
|
// shields.test.ts:983-995 is the canonical contract; keep both in step.
|
|
4074
|
-
match: (p) => /(?:^|[\\/])\.env(?![\w-])(?!\.(?:example|sample|template)\b)(?!\.test$)[\w.-]
|
|
4349
|
+
match: (p) => /(?:^|[\\/])\.env(?![\w-])(?:[\w.-]*\.local$|(?!\.(?:example|sample|template)\b)(?!\.test$)[\w.-]*$)/i.test(
|
|
4350
|
+
p
|
|
4351
|
+
)
|
|
4075
4352
|
},
|
|
4076
4353
|
{
|
|
4077
4354
|
// verdict: 'review' (not 'block') is a deliberate design choice
|
|
@@ -4182,8 +4459,18 @@ var init_dist = __esm({
|
|
|
4182
4459
|
_redirStdinOps = null;
|
|
4183
4460
|
_listOps = null;
|
|
4184
4461
|
WRAPPER_TAKES_TARGET = /* @__PURE__ */ new Set(["chroot"]);
|
|
4462
|
+
FIND_EXEC_FLAGS = /* @__PURE__ */ new Set(["-exec", "-execdir", "-ok", "-okdir"]);
|
|
4185
4463
|
INTERP_LEADING_TARGET = /* @__PURE__ */ new Set(["su"]);
|
|
4186
|
-
NET_BINARIES = /* @__PURE__ */ new Set([
|
|
4464
|
+
NET_BINARIES = /* @__PURE__ */ new Set([
|
|
4465
|
+
"curl",
|
|
4466
|
+
"wget",
|
|
4467
|
+
"scp",
|
|
4468
|
+
"ssh",
|
|
4469
|
+
"nc",
|
|
4470
|
+
"ncat",
|
|
4471
|
+
"netcat",
|
|
4472
|
+
"rsync"
|
|
4473
|
+
]);
|
|
4187
4474
|
VALUE_FLAGS = {
|
|
4188
4475
|
curl: /* @__PURE__ */ new Set([
|
|
4189
4476
|
"-d",
|
|
@@ -4272,10 +4559,22 @@ var init_dist = __esm({
|
|
|
4272
4559
|
fsOpCache = /* @__PURE__ */ new Map();
|
|
4273
4560
|
stripDotSlash = (p) => p.replace(/^\.\//, "");
|
|
4274
4561
|
REDIR_TRUNCATE_OPS = /* @__PURE__ */ new Set([deriveRedirOp(">_f")]);
|
|
4562
|
+
REDIR_FILE_IN_OPS = new Set(
|
|
4563
|
+
[deriveRedirOp("cat < f"), deriveRedirOp("cat <> f")].filter((op) => op >= 0)
|
|
4564
|
+
);
|
|
4275
4565
|
REDIR_HEREDOC_OPS = /* @__PURE__ */ new Set([
|
|
4276
4566
|
deriveRedirOp("cat <<X\nX"),
|
|
4277
4567
|
deriveRedirOp("cat <<-X\nX")
|
|
4278
4568
|
]);
|
|
4569
|
+
FIND_OPTIONS = /* @__PURE__ */ new Set(["-H", "-L", "-P"]);
|
|
4570
|
+
COPY_RULE_OF = {
|
|
4571
|
+
"shield:project-jail:block-read-ssh": "shield:project-jail:review-copy-ssh",
|
|
4572
|
+
"shield:project-jail:block-read-aws": "shield:project-jail:review-copy-aws",
|
|
4573
|
+
"shield:project-jail:block-read-env": "shield:project-jail:review-copy-env",
|
|
4574
|
+
"shield:project-jail:review-read-credentials": "shield:project-jail:review-copy-credentials"
|
|
4575
|
+
};
|
|
4576
|
+
isReaderWord = (w) => w !== null && FS_READ_TOOLS.has(baseWord(w));
|
|
4577
|
+
positionalAfter = (words, from, to = words.length) => positionedArgs(words, from, to).map((a) => a.value);
|
|
4279
4578
|
DEFAULT_EGRESS_ALLOWLIST = [
|
|
4280
4579
|
// node9's own control plane (api, app, dev-api, staging and the apex).
|
|
4281
4580
|
// Without it, turning egress on asks the user to approve node9 itself.
|
|
@@ -4299,21 +4598,7 @@ var init_dist = __esm({
|
|
|
4299
4598
|
"deb.debian.org",
|
|
4300
4599
|
"*.ubuntu.com"
|
|
4301
4600
|
];
|
|
4302
|
-
SOURCE_COMMANDS = /* @__PURE__ */ new Set([
|
|
4303
|
-
"cat",
|
|
4304
|
-
"head",
|
|
4305
|
-
"tail",
|
|
4306
|
-
"grep",
|
|
4307
|
-
"awk",
|
|
4308
|
-
"sed",
|
|
4309
|
-
"cut",
|
|
4310
|
-
"sort",
|
|
4311
|
-
"tee",
|
|
4312
|
-
"less",
|
|
4313
|
-
"more",
|
|
4314
|
-
"strings",
|
|
4315
|
-
"xxd"
|
|
4316
|
-
]);
|
|
4601
|
+
SOURCE_COMMANDS = /* @__PURE__ */ new Set([...FS_READ_TOOLS, "tee"]);
|
|
4317
4602
|
SINK_COMMANDS = /* @__PURE__ */ new Set([
|
|
4318
4603
|
"curl",
|
|
4319
4604
|
"wget",
|
|
@@ -4344,16 +4629,25 @@ var init_dist = __esm({
|
|
|
4344
4629
|
"node"
|
|
4345
4630
|
]);
|
|
4346
4631
|
SENSITIVE_PATTERNS = [
|
|
4347
|
-
/
|
|
4348
|
-
|
|
4632
|
+
// Kept in step with the AST tier and dlp/ -- see jail-both-doors.test.ts.
|
|
4633
|
+
/(?:^|\/)\.env(?![\w-])(?:[\w.-]*\.local$|(?!\.(?:example|sample|template)\b)(?!\.test$)[\w.-]*$)/i,
|
|
4634
|
+
// .env chain; fixtures exempt unless .local
|
|
4349
4635
|
/id_rsa|id_ed25519|id_ecdsa|id_dsa/i,
|
|
4350
4636
|
// SSH private keys
|
|
4351
4637
|
/\.pem$|\.key$|\.p12$|\.pfx$/i,
|
|
4352
4638
|
// certificate files
|
|
4353
|
-
/
|
|
4354
|
-
//
|
|
4355
|
-
/
|
|
4356
|
-
//
|
|
4639
|
+
// The `$` half mirrors shell/index.ts's SENSITIVE_PATH_RULES: a file INSIDE
|
|
4640
|
+
// the directory counts wherever it appears, while the directory ITSELF counts
|
|
4641
|
+
// only when the path is ROOTED (`~/.ssh`, `/home/u/.ssh`) -- an unrooted
|
|
4642
|
+
// `config/.ssh` is more likely a search pattern than a read. These are
|
|
4643
|
+
// extracted TOKENS (see `args.some(isSensitivePath)` below), the same input
|
|
4644
|
+
// contract as the shell tier, so the same boundary is the right one.
|
|
4645
|
+
// Without it `grep -r x ~/.ssh | curl -d @-` scored one tier BELOW the
|
|
4646
|
+
// identical pipeline naming a file inside that directory.
|
|
4647
|
+
/(?:^|\/)\.ssh\/|^(?:[~/]|[A-Za-z]:).*\/\.ssh$/i,
|
|
4648
|
+
// ~/.ssh/ and ~/.ssh
|
|
4649
|
+
/(?:^|\/)\.aws\/credentials|^(?:[~/]|[A-Za-z]:).*\/\.aws$/i,
|
|
4650
|
+
// AWS creds + dir
|
|
4357
4651
|
/(?:^|\/)\.netrc$/i,
|
|
4358
4652
|
// netrc (stores HTTP credentials)
|
|
4359
4653
|
/(?:^|\/)(passwd|shadow|sudoers)$/i,
|
|
@@ -5021,7 +5315,7 @@ var init_dist = __esm({
|
|
|
5021
5315
|
{
|
|
5022
5316
|
field: "command",
|
|
5023
5317
|
op: "matches",
|
|
5024
|
-
value: "(cat|less|head|tail|bat|more|open|print|nano|vim|vi|emacs|code|type|grep|egrep|fgrep|rg|ag|ack|awk|gawk|sed|cut|tr|jq|yq|od|xxd|hexdump|strings|sort|uniq|tac|nl|dd)\\s+.*?\\.ssh[\\/\\\\]",
|
|
5318
|
+
value: "(cat|less|head|tail|bat|more|open|print|nano|vim|vi|emacs|code|type|grep|egrep|fgrep|rg|ag|ack|awk|gawk|sed|cut|tr|jq|yq|od|xxd|hexdump|strings|base64|sort|uniq|tac|nl|dd)\\s+.*?\\.ssh[\\/\\\\]",
|
|
5025
5319
|
flags: "i"
|
|
5026
5320
|
}
|
|
5027
5321
|
],
|
|
@@ -5035,7 +5329,7 @@ var init_dist = __esm({
|
|
|
5035
5329
|
{
|
|
5036
5330
|
field: "command",
|
|
5037
5331
|
op: "matches",
|
|
5038
|
-
value: "(cat|less|head|tail|bat|more|open|print|nano|vim|vi|emacs|code|type|grep|egrep|fgrep|rg|ag|ack|awk|gawk|sed|cut|tr|jq|yq|od|xxd|hexdump|strings|sort|uniq|tac|nl|dd)\\s+.*?\\.aws[\\/\\\\]",
|
|
5332
|
+
value: "(cat|less|head|tail|bat|more|open|print|nano|vim|vi|emacs|code|type|grep|egrep|fgrep|rg|ag|ack|awk|gawk|sed|cut|tr|jq|yq|od|xxd|hexdump|strings|base64|sort|uniq|tac|nl|dd)\\s+.*?\\.aws[\\/\\\\]",
|
|
5039
5333
|
flags: "i"
|
|
5040
5334
|
}
|
|
5041
5335
|
],
|
|
@@ -5049,7 +5343,7 @@ var init_dist = __esm({
|
|
|
5049
5343
|
{
|
|
5050
5344
|
field: "command",
|
|
5051
5345
|
op: "matches",
|
|
5052
|
-
value: "(cat|less|head|tail|bat|more|open|print|nano|vim|vi|emacs|code|type|grep|egrep|fgrep|rg|ag|ack|awk|gawk|sed|cut|tr|jq|yq|od|xxd|hexdump|strings|sort|uniq|tac|nl|dd)\\s+.*?\\.env(\\.(local|production|staging|development|production\\.local|staging\\.local|development\\.local))?(?=\\s|$|[;&|>)<])",
|
|
5346
|
+
value: "(cat|less|head|tail|bat|more|open|print|nano|vim|vi|emacs|code|type|grep|egrep|fgrep|rg|ag|ack|awk|gawk|sed|cut|tr|jq|yq|od|xxd|hexdump|strings|base64|sort|uniq|tac|nl|dd)\\s+.*?\\.env(\\.(local|production|staging|development|production\\.local|staging\\.local|development\\.local))?(?=\\s|$|[;&|>)<])",
|
|
5053
5347
|
flags: "i"
|
|
5054
5348
|
}
|
|
5055
5349
|
],
|
|
@@ -5063,7 +5357,7 @@ var init_dist = __esm({
|
|
|
5063
5357
|
{
|
|
5064
5358
|
field: "command",
|
|
5065
5359
|
op: "matches",
|
|
5066
|
-
value: "(cat|less|head|tail|bat|more|open|print|nano|vim|vi|emacs|code|type|grep|egrep|fgrep|rg|ag|ack|awk|gawk|sed|cut|tr|jq|yq|od|xxd|hexdump|strings|sort|uniq|tac|nl|dd)\\s+.*(credentials\\.json|\\.netrc|\\.npmrc|\\.docker[\\/\\\\]config\\.json|gcloud[\\/\\\\]credentials)",
|
|
5360
|
+
value: "(cat|less|head|tail|bat|more|open|print|nano|vim|vi|emacs|code|type|grep|egrep|fgrep|rg|ag|ack|awk|gawk|sed|cut|tr|jq|yq|od|xxd|hexdump|strings|base64|sort|uniq|tac|nl|dd)\\s+.*(credentials\\.json|\\.netrc|\\.npmrc|\\.docker[\\/\\\\]config\\.json|gcloud[\\/\\\\]credentials)",
|
|
5067
5361
|
flags: "i"
|
|
5068
5362
|
}
|
|
5069
5363
|
],
|
|
@@ -5077,7 +5371,7 @@ var init_dist = __esm({
|
|
|
5077
5371
|
{
|
|
5078
5372
|
field: "file_path",
|
|
5079
5373
|
op: "matches",
|
|
5080
|
-
value: "(
|
|
5374
|
+
value: "([\\/\\\\]\\.ssh([\\/\\\\]|$)|^\\.ssh[\\/\\\\])",
|
|
5081
5375
|
flags: "i"
|
|
5082
5376
|
}
|
|
5083
5377
|
],
|
|
@@ -5091,7 +5385,7 @@ var init_dist = __esm({
|
|
|
5091
5385
|
{
|
|
5092
5386
|
field: "file_path",
|
|
5093
5387
|
op: "matches",
|
|
5094
|
-
value: "(
|
|
5388
|
+
value: "([\\/\\\\]\\.aws([\\/\\\\]|$)|^\\.aws[\\/\\\\])",
|
|
5095
5389
|
flags: "i"
|
|
5096
5390
|
}
|
|
5097
5391
|
],
|
|
@@ -5105,7 +5399,7 @@ var init_dist = __esm({
|
|
|
5105
5399
|
{
|
|
5106
5400
|
field: "file_path",
|
|
5107
5401
|
op: "matches",
|
|
5108
|
-
value: "(^|[\\/\\\\])\\.env(
|
|
5402
|
+
value: "(^|[\\/\\\\])\\.env(?![\\w-])(?:[\\w.-]*\\.local$|(?!\\.(example|sample|template)\\b)(?!\\.test$)[\\w.-]*$)",
|
|
5109
5403
|
flags: "i"
|
|
5110
5404
|
}
|
|
5111
5405
|
],
|
|
@@ -5248,7 +5542,7 @@ var init_dist = __esm({
|
|
|
5248
5542
|
};
|
|
5249
5543
|
LOOP_THRESHOLD_FOR_WASTE = 3;
|
|
5250
5544
|
DESTRUCTIVE_OP_RE = /\brm\s+-[rRf]+\b|\bDROP\s+(TABLE|DATABASE|COLLECTION|SCHEMA)\b|\bTRUNCATE\s+TABLE\b|\bgit\s+push\s+(--force|-f)\b|\bFLUSHALL\b|\bFLUSHDB\b|\bkubectl\s+delete\b|\bhelm\s+uninstall\b/i;
|
|
5251
|
-
SENSITIVE_PATH_RE =
|
|
5545
|
+
SENSITIVE_PATH_RE = /[\\/]\.aws(?:[\\/]|$)|^\.aws[\\/]|[\\/]\.ssh(?:[\\/]|$)|^\.ssh[\\/]|(?:^|[\\/])\.env(?![\w-])(?:[\w.-]*\.local$|(?!\.(?:example|sample|template)\b)(?!\.test$)[\w.-]*$)|\.config\/gcloud\/credentials\.db\b|\.docker\/config\.json\b|\.netrc\b|\.npmrc\b|\.node9\/credentials\.json\b/i;
|
|
5252
5546
|
FILE_TOOLS = /* @__PURE__ */ new Set([
|
|
5253
5547
|
"read",
|
|
5254
5548
|
"read_file",
|
|
@@ -5325,7 +5619,7 @@ var init_dist = __esm({
|
|
|
5325
5619
|
{ view: "separators-stripped", decoder: "separators", stripped: true }
|
|
5326
5620
|
];
|
|
5327
5621
|
LONG_OUTPUT_THRESHOLD_BYTES = 100 * 1024;
|
|
5328
|
-
CANONICAL_EXTRACTOR_VERSION = "canonical-
|
|
5622
|
+
CANONICAL_EXTRACTOR_VERSION = "canonical-v14";
|
|
5329
5623
|
DEDUPE_PREVIEW_LEN = 120;
|
|
5330
5624
|
TERMINAL_ESCAPE_RE = // eslint-disable-next-line no-control-regex
|
|
5331
5625
|
/\x1b\[[0-9;?]*[A-Za-z]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b[@-_]|[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g;
|
|
@@ -8643,7 +8937,7 @@ function isNetworkTool(toolName, args) {
|
|
|
8643
8937
|
if (t === "bash" || t === "shell" || t === "run_shell_command" || t === "terminal.execute") {
|
|
8644
8938
|
const a = args;
|
|
8645
8939
|
const cmd = typeof a?.command === "string" ? a.command : typeof a?.cmd === "string" ? a.cmd : "";
|
|
8646
|
-
return
|
|
8940
|
+
return NETWORK_COMMAND_RE.test(cmd);
|
|
8647
8941
|
}
|
|
8648
8942
|
return false;
|
|
8649
8943
|
}
|
|
@@ -9518,7 +9812,7 @@ function canaryRecordById(id) {
|
|
|
9518
9812
|
return null;
|
|
9519
9813
|
}
|
|
9520
9814
|
}
|
|
9521
|
-
var WRITE_TOOLS;
|
|
9815
|
+
var WRITE_TOOLS, NETWORK_COMMAND_RE;
|
|
9522
9816
|
var init_orchestrator = __esm({
|
|
9523
9817
|
"src/auth/orchestrator.ts"() {
|
|
9524
9818
|
"use strict";
|
|
@@ -9548,6 +9842,7 @@ var init_orchestrator = __esm({
|
|
|
9548
9842
|
"notebook_edit",
|
|
9549
9843
|
"notebookedit"
|
|
9550
9844
|
]);
|
|
9845
|
+
NETWORK_COMMAND_RE = new RegExp(`(?<![.\\w-])(${[...NET_BINARIES].join("|")})\\b`);
|
|
9551
9846
|
}
|
|
9552
9847
|
});
|
|
9553
9848
|
|
|
@@ -12613,9 +12908,10 @@ async function ensurePricingLoaded() {
|
|
|
12613
12908
|
memCacheAt = Date.now();
|
|
12614
12909
|
lookupCache.clear();
|
|
12615
12910
|
}
|
|
12616
|
-
function pricingFor(model) {
|
|
12911
|
+
function pricingFor(model, options = {}) {
|
|
12617
12912
|
const norm = normalizeModel(model);
|
|
12618
|
-
const
|
|
12913
|
+
const lookupKey = options.exact ? `exact:${norm}` : norm;
|
|
12914
|
+
const cached = lookupCache.get(lookupKey);
|
|
12619
12915
|
if (cached !== void 0) return cached;
|
|
12620
12916
|
if (memCache === null && !diskChecked) {
|
|
12621
12917
|
diskChecked = true;
|
|
@@ -12635,6 +12931,7 @@ function pricingFor(model) {
|
|
|
12635
12931
|
resolved = exact;
|
|
12636
12932
|
break;
|
|
12637
12933
|
}
|
|
12934
|
+
if (options.exact) continue;
|
|
12638
12935
|
let best = null;
|
|
12639
12936
|
for (const key of Object.keys(source)) {
|
|
12640
12937
|
if (norm.startsWith(key.toLowerCase()) && (best === null || key.length > best.length)) {
|
|
@@ -12646,7 +12943,7 @@ function pricingFor(model) {
|
|
|
12646
12943
|
break;
|
|
12647
12944
|
}
|
|
12648
12945
|
}
|
|
12649
|
-
lookupCache.set(
|
|
12946
|
+
lookupCache.set(lookupKey, resolved);
|
|
12650
12947
|
return resolved;
|
|
12651
12948
|
}
|
|
12652
12949
|
var LITELLM_URL, BUNDLED_PRICING, CACHE_FILE, TTL_MS, memCache, memCacheAt, diskChecked, lookupCache;
|
|
@@ -12680,6 +12977,18 @@ var init_litellm = __esm({
|
|
|
12680
12977
|
"gpt-5": [125e-8, 1e-5, 0, 125e-9],
|
|
12681
12978
|
"gpt-5-codex": [125e-8, 1e-5, 0, 125e-9],
|
|
12682
12979
|
"gpt-5-mini": [25e-8, 2e-6, 0, 25e-9],
|
|
12980
|
+
// Codex offline rates checked against official OpenAI model pages, 2026-09-11.
|
|
12981
|
+
"gpt-5.1-codex": [125e-8, 1e-5, 0, 125e-9],
|
|
12982
|
+
"gpt-5.1-codex-max": [125e-8, 1e-5, 0, 125e-9],
|
|
12983
|
+
"gpt-5.1-codex-mini": [25e-8, 2e-6, 0, 25e-9],
|
|
12984
|
+
"gpt-5.2-codex": [175e-8, 14e-6, 0, 175e-9],
|
|
12985
|
+
"gpt-5.3-codex": [175e-8, 14e-6, 0, 175e-9],
|
|
12986
|
+
"gpt-5.4": [25e-7, 15e-6, 0, 25e-8],
|
|
12987
|
+
"gpt-5.4-mini": [75e-8, 45e-7, 0, 75e-9],
|
|
12988
|
+
"gpt-5.5": [5e-6, 3e-5, 0, 5e-7],
|
|
12989
|
+
"gpt-5.6-sol": [4e-6, 2e-5, 5e-6, 4e-7],
|
|
12990
|
+
"gpt-5.6-terra": [2e-6, 12e-6, 25e-7, 2e-7],
|
|
12991
|
+
"gpt-6-astra": [1e-5, 5e-5, 125e-7, 1e-6],
|
|
12683
12992
|
o3: [2e-6, 8e-6, 0, 5e-7],
|
|
12684
12993
|
"o4-mini": [11e-7, 44e-7, 0, 275e-9],
|
|
12685
12994
|
// Google. Values copied from the live LiteLLM table (verified 2026-06-14)
|
|
@@ -12851,103 +13160,259 @@ import fs21 from "fs";
|
|
|
12851
13160
|
import os20 from "os";
|
|
12852
13161
|
import path23 from "path";
|
|
12853
13162
|
function codexSessionsDir() {
|
|
12854
|
-
return path23.join(os20.homedir(), ".codex", "sessions");
|
|
13163
|
+
return path23.join(process.env.CODEX_HOME?.trim() || path23.join(os20.homedir(), ".codex"), "sessions");
|
|
12855
13164
|
}
|
|
12856
13165
|
function codexPriceFor(model) {
|
|
12857
|
-
return pricingFor(model) ?? CODEX_FALLBACK;
|
|
13166
|
+
return pricingFor(codexModel(model), { exact: true }) ?? CODEX_FALLBACK;
|
|
12858
13167
|
}
|
|
12859
|
-
function
|
|
12860
|
-
|
|
12861
|
-
const [pin, pout, , pcr] = codexPriceFor(model || "gpt-5");
|
|
12862
|
-
return nonCached * pin + tokens.cached * pcr + tokens.output * pout;
|
|
13168
|
+
function codexModel(model) {
|
|
13169
|
+
return normalizeModel(model.replace(/^openai\//i, "").replace(/-\d{4}-\d{2}-\d{2}$/, ""));
|
|
12863
13170
|
}
|
|
12864
|
-
function
|
|
12865
|
-
|
|
12866
|
-
|
|
12867
|
-
|
|
12868
|
-
|
|
12869
|
-
|
|
12870
|
-
|
|
12871
|
-
|
|
12872
|
-
|
|
12873
|
-
|
|
12874
|
-
|
|
12875
|
-
|
|
12876
|
-
|
|
12877
|
-
|
|
13171
|
+
function addTokens(previous, delta) {
|
|
13172
|
+
return {
|
|
13173
|
+
input: (previous?.input ?? 0) + delta.input,
|
|
13174
|
+
cached: (previous?.cached ?? 0) + delta.cached,
|
|
13175
|
+
output: (previous?.output ?? 0) + delta.output,
|
|
13176
|
+
cacheWrite: (previous?.cacheWrite ?? 0) + delta.cacheWrite
|
|
13177
|
+
};
|
|
13178
|
+
}
|
|
13179
|
+
function codexSessionCost(model, tokens, request2) {
|
|
13180
|
+
const input = tokenNumber(tokens.input);
|
|
13181
|
+
const cached = Math.min(input, tokenNumber(tokens.cached));
|
|
13182
|
+
const written = Math.min(input - cached, tokenNumber(tokens.cacheWrite));
|
|
13183
|
+
const [pin, pout, pcw, pcr] = codexPriceFor(model || "gpt-5");
|
|
13184
|
+
const longContext = request2 && request2.inputTokens > 272e3 && ["gpt-5.4", "gpt-5.5", "gpt-5.6-sol", "gpt-5.6-terra", "gpt-6-astra"].includes(
|
|
13185
|
+
codexModel(model)
|
|
13186
|
+
);
|
|
13187
|
+
const inputMultiplier = longContext ? 2 : 1;
|
|
13188
|
+
const outputMultiplier = longContext ? 1.5 : 1;
|
|
13189
|
+
const tierMultiplier = request2?.serviceTier === "flex" ? 0.5 : codexModel(model) === "gpt-6-astra" && (request2?.serviceTier === "fast" || request2?.serviceTier === "priority") ? 2 : 1;
|
|
13190
|
+
return (((input - cached - written) * pin + cached * pcr + written * (pcw || pin)) * inputMultiplier + tokenNumber(tokens.output) * pout * outputMultiplier) * tierMultiplier;
|
|
13191
|
+
}
|
|
13192
|
+
function statAndFirstLine(file) {
|
|
13193
|
+
const CAP = 4 * 1024 * 1024;
|
|
13194
|
+
const CHUNK = 64 * 1024;
|
|
13195
|
+
const fd = fs21.openSync(file, "r");
|
|
13196
|
+
try {
|
|
13197
|
+
const stat = fs21.fstatSync(fd);
|
|
13198
|
+
const limit = Math.min(stat.size, CAP);
|
|
13199
|
+
const parts = [];
|
|
13200
|
+
for (let pos = 0; pos < limit; pos += CHUNK) {
|
|
13201
|
+
const buf = Buffer.alloc(Math.min(CHUNK, limit - pos));
|
|
13202
|
+
const read2 = fs21.readSync(fd, buf, 0, buf.length, pos);
|
|
13203
|
+
if (read2 <= 0) break;
|
|
13204
|
+
const slice = buf.subarray(0, read2);
|
|
13205
|
+
const nl = slice.indexOf(10);
|
|
13206
|
+
parts.push(nl >= 0 ? slice.subarray(0, nl) : slice);
|
|
13207
|
+
if (nl >= 0) break;
|
|
13208
|
+
}
|
|
13209
|
+
return { stat, first: Buffer.concat(parts).toString("utf8") };
|
|
13210
|
+
} finally {
|
|
13211
|
+
fs21.closeSync(fd);
|
|
13212
|
+
}
|
|
13213
|
+
}
|
|
13214
|
+
function listCodexSessionFiles(base = codexSessionsDir()) {
|
|
13215
|
+
const files = [];
|
|
13216
|
+
const walk = (dir) => {
|
|
13217
|
+
try {
|
|
13218
|
+
for (const entry of fs21.readdirSync(dir, { withFileTypes: true })) {
|
|
13219
|
+
const file = path23.join(dir, entry.name);
|
|
13220
|
+
if (entry.isDirectory()) walk(file);
|
|
13221
|
+
else if (entry.isFile() && entry.name.endsWith(".jsonl")) files.push(file);
|
|
13222
|
+
}
|
|
13223
|
+
} catch {
|
|
13224
|
+
}
|
|
13225
|
+
};
|
|
13226
|
+
walk(base);
|
|
13227
|
+
if (path23.basename(base) === "sessions") walk(path23.join(path23.dirname(base), "archived_sessions"));
|
|
13228
|
+
const sessions = /* @__PURE__ */ new Map();
|
|
13229
|
+
for (const file of files.sort()) {
|
|
13230
|
+
try {
|
|
13231
|
+
const { stat, first: head } = statAndFirstLine(file);
|
|
13232
|
+
let id = "";
|
|
13233
|
+
try {
|
|
13234
|
+
const first = JSON.parse(head);
|
|
13235
|
+
if (first?.type === "session_meta" && typeof first.payload?.id === "string")
|
|
13236
|
+
id = first.payload.id;
|
|
13237
|
+
} catch {
|
|
12878
13238
|
}
|
|
13239
|
+
const key = id ? `session:${id}` : `file:${file}`;
|
|
13240
|
+
const prior = sessions.get(key);
|
|
13241
|
+
if (!prior || stat.mtimeMs > prior.mtime || stat.mtimeMs === prior.mtime && stat.size > prior.size) {
|
|
13242
|
+
sessions.set(key, { file, mtime: stat.mtimeMs, size: stat.size });
|
|
13243
|
+
}
|
|
13244
|
+
} catch {
|
|
12879
13245
|
}
|
|
12880
13246
|
}
|
|
12881
|
-
return
|
|
13247
|
+
return [...sessions.values()].map((s) => s.file);
|
|
12882
13248
|
}
|
|
12883
|
-
function
|
|
12884
|
-
|
|
12885
|
-
return fs21.readdirSync(dir);
|
|
12886
|
-
} catch {
|
|
12887
|
-
return [];
|
|
12888
|
-
}
|
|
13249
|
+
function record(value) {
|
|
13250
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
12889
13251
|
}
|
|
12890
|
-
function
|
|
12891
|
-
|
|
12892
|
-
|
|
12893
|
-
|
|
12894
|
-
|
|
13252
|
+
function tokenNumber(value) {
|
|
13253
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : 0;
|
|
13254
|
+
}
|
|
13255
|
+
function usage(value, fallback) {
|
|
13256
|
+
const u = record(value);
|
|
13257
|
+
if (!["input_tokens", "output_tokens"].some((k) => typeof u[k] === "number")) return null;
|
|
13258
|
+
for (const key of [
|
|
13259
|
+
"input_tokens",
|
|
13260
|
+
"cached_input_tokens",
|
|
13261
|
+
"cache_read_input_tokens",
|
|
13262
|
+
"output_tokens",
|
|
13263
|
+
"cache_write_input_tokens"
|
|
13264
|
+
]) {
|
|
13265
|
+
if (u[key] !== void 0 && (typeof u[key] !== "number" || !Number.isFinite(u[key]) || u[key] < 0))
|
|
13266
|
+
return null;
|
|
12895
13267
|
}
|
|
13268
|
+
return {
|
|
13269
|
+
input: tokenNumber(u.input_tokens ?? fallback?.input),
|
|
13270
|
+
cached: tokenNumber(u.cached_input_tokens ?? u.cache_read_input_tokens ?? fallback?.cached),
|
|
13271
|
+
output: tokenNumber(u.output_tokens ?? fallback?.output),
|
|
13272
|
+
cacheWrite: tokenNumber(u.cache_write_input_tokens ?? fallback?.cacheWrite)
|
|
13273
|
+
};
|
|
12896
13274
|
}
|
|
12897
|
-
function
|
|
12898
|
-
|
|
12899
|
-
|
|
12900
|
-
|
|
12901
|
-
|
|
12902
|
-
|
|
12903
|
-
|
|
12904
|
-
|
|
12905
|
-
|
|
13275
|
+
function timestamp(value) {
|
|
13276
|
+
return typeof value === "string" && Number.isFinite(Date.parse(value)) ? new Date(value).toISOString() : "";
|
|
13277
|
+
}
|
|
13278
|
+
function parseCodexUsage(lines) {
|
|
13279
|
+
const result = {
|
|
13280
|
+
events: [],
|
|
13281
|
+
sessionStart: "",
|
|
13282
|
+
runId: "",
|
|
13283
|
+
workingDir: "",
|
|
13284
|
+
legacyModels: []
|
|
13285
|
+
};
|
|
13286
|
+
let model = "gpt-5";
|
|
13287
|
+
let serviceTier;
|
|
13288
|
+
let previous = null;
|
|
13289
|
+
const legacyModels = /* @__PURE__ */ new Set();
|
|
13290
|
+
const seenStandalone = /* @__PURE__ */ new Set();
|
|
12906
13291
|
for (const raw of lines) {
|
|
12907
|
-
if (!raw.trim()) continue;
|
|
12908
13292
|
let entry;
|
|
12909
13293
|
try {
|
|
12910
|
-
entry = JSON.parse(raw);
|
|
13294
|
+
entry = record(JSON.parse(raw));
|
|
12911
13295
|
} catch {
|
|
12912
13296
|
continue;
|
|
12913
13297
|
}
|
|
12914
|
-
const p = entry.payload
|
|
13298
|
+
const p = record(entry.payload);
|
|
12915
13299
|
if (entry.type === "session_meta") {
|
|
12916
|
-
|
|
12917
|
-
if (!runId && typeof p
|
|
12918
|
-
if (!
|
|
13300
|
+
result.sessionStart ||= timestamp(p.timestamp ?? entry.timestamp);
|
|
13301
|
+
if (!result.runId && typeof p.id === "string") result.runId = p.id;
|
|
13302
|
+
if (!result.workingDir && typeof p.cwd === "string") result.workingDir = p.cwd;
|
|
12919
13303
|
continue;
|
|
12920
13304
|
}
|
|
12921
13305
|
if (entry.type === "turn_context") {
|
|
12922
|
-
if (typeof p
|
|
12923
|
-
|
|
13306
|
+
if (typeof p.model === "string" && p.model) {
|
|
13307
|
+
model = p.model;
|
|
13308
|
+
legacyModels.add(normalizeModel(model));
|
|
13309
|
+
}
|
|
13310
|
+
if (!result.workingDir && typeof p.cwd === "string") result.workingDir = p.cwd;
|
|
13311
|
+
serviceTier = typeof p.service_tier === "string" ? p.service_tier : void 0;
|
|
12924
13312
|
continue;
|
|
12925
13313
|
}
|
|
12926
|
-
if (entry.type
|
|
12927
|
-
|
|
12928
|
-
|
|
12929
|
-
|
|
12930
|
-
|
|
12931
|
-
|
|
12932
|
-
|
|
13314
|
+
if (entry.type !== "event_msg" || p.type !== "token_count") continue;
|
|
13315
|
+
const info = record(p.info);
|
|
13316
|
+
const total = usage(info.total_token_usage, previous);
|
|
13317
|
+
const last = usage(info.last_token_usage);
|
|
13318
|
+
if (!total && !last) continue;
|
|
13319
|
+
const eventModel = [info.model, info.model_name, p.model].find(
|
|
13320
|
+
(v) => typeof v === "string" && v
|
|
13321
|
+
);
|
|
13322
|
+
if (typeof eventModel === "string") model = eventModel;
|
|
13323
|
+
let delta;
|
|
13324
|
+
if (total) {
|
|
13325
|
+
if (previous && Object.keys(total).every(
|
|
13326
|
+
(k) => total[k] === previous[k]
|
|
13327
|
+
))
|
|
13328
|
+
continue;
|
|
13329
|
+
const reset = previous && (total.input < previous.input || total.output < previous.output);
|
|
13330
|
+
delta = reset ? last ?? total : {
|
|
13331
|
+
input: Math.max(0, total.input - (previous?.input ?? 0)),
|
|
13332
|
+
cached: Math.max(0, total.cached - (previous?.cached ?? 0)),
|
|
13333
|
+
output: Math.max(0, total.output - (previous?.output ?? 0)),
|
|
13334
|
+
cacheWrite: Math.max(0, total.cacheWrite - (previous?.cacheWrite ?? 0))
|
|
13335
|
+
};
|
|
13336
|
+
previous = total;
|
|
13337
|
+
} else {
|
|
13338
|
+
delta = last;
|
|
13339
|
+
const key = JSON.stringify([entry.timestamp, model, delta]);
|
|
13340
|
+
if (entry.timestamp && seenStandalone.has(key)) continue;
|
|
13341
|
+
if (entry.timestamp) seenStandalone.add(key);
|
|
13342
|
+
previous = addTokens(previous, delta);
|
|
13343
|
+
}
|
|
13344
|
+
if (delta.input === 0 && delta.output === 0) continue;
|
|
13345
|
+
const ts = timestamp(entry.timestamp) || result.sessionStart;
|
|
13346
|
+
if (!ts) continue;
|
|
13347
|
+
const cached = Math.min(delta.input, delta.cached);
|
|
13348
|
+
const written = Math.min(delta.input - cached, delta.cacheWrite);
|
|
13349
|
+
result.events.push({
|
|
13350
|
+
timestamp: ts,
|
|
13351
|
+
date: ts.slice(0, 10),
|
|
13352
|
+
model: normalizeModel(model),
|
|
13353
|
+
workingDir: result.workingDir,
|
|
13354
|
+
runId: result.runId,
|
|
13355
|
+
costUSD: codexSessionCost(model, delta, {
|
|
13356
|
+
inputTokens: last?.input ?? delta.input,
|
|
13357
|
+
serviceTier: typeof info.service_tier === "string" ? info.service_tier : serviceTier
|
|
13358
|
+
}),
|
|
13359
|
+
inputTokens: delta.input - cached - written,
|
|
13360
|
+
outputTokens: delta.output,
|
|
13361
|
+
cacheReadTokens: cached,
|
|
13362
|
+
cacheWriteTokens: written
|
|
13363
|
+
});
|
|
13364
|
+
}
|
|
13365
|
+
result.legacyModels = [...legacyModels.size ? legacyModels : ["gpt-5"]];
|
|
13366
|
+
return result;
|
|
13367
|
+
}
|
|
13368
|
+
function codexUsageInWindow(usage2, start, end) {
|
|
13369
|
+
return usage2.events.filter(
|
|
13370
|
+
(e) => (!start || Date.parse(e.timestamp) >= start.getTime()) && (!end || Date.parse(e.timestamp) <= end.getTime())
|
|
13371
|
+
);
|
|
13372
|
+
}
|
|
13373
|
+
function parseCodexSession(lines) {
|
|
13374
|
+
const parsed = parseCodexUsage(lines);
|
|
13375
|
+
if (!parsed.events.length) return [];
|
|
13376
|
+
const rows = /* @__PURE__ */ new Map();
|
|
13377
|
+
if (parsed.sessionStart) {
|
|
13378
|
+
for (const model of parsed.legacyModels) {
|
|
13379
|
+
rows.set(`${parsed.sessionStart.slice(0, 10)}::${model}`, {
|
|
13380
|
+
date: parsed.sessionStart.slice(0, 10),
|
|
13381
|
+
model,
|
|
13382
|
+
workingDir: parsed.workingDir,
|
|
13383
|
+
runId: parsed.runId,
|
|
13384
|
+
costUSD: 0,
|
|
13385
|
+
inputTokens: 0,
|
|
13386
|
+
outputTokens: 0,
|
|
13387
|
+
cacheReadTokens: 0,
|
|
13388
|
+
cacheWriteTokens: 0
|
|
13389
|
+
});
|
|
13390
|
+
}
|
|
13391
|
+
}
|
|
13392
|
+
for (const event of parsed.events) {
|
|
13393
|
+
const e = {
|
|
13394
|
+
date: event.date,
|
|
13395
|
+
model: event.model,
|
|
13396
|
+
workingDir: event.workingDir,
|
|
13397
|
+
runId: event.runId,
|
|
13398
|
+
costUSD: event.costUSD,
|
|
13399
|
+
inputTokens: event.inputTokens,
|
|
13400
|
+
outputTokens: event.outputTokens,
|
|
13401
|
+
cacheReadTokens: event.cacheReadTokens,
|
|
13402
|
+
cacheWriteTokens: event.cacheWriteTokens
|
|
13403
|
+
};
|
|
13404
|
+
const key = `${e.date}::${e.model}`;
|
|
13405
|
+
const prev = rows.get(key);
|
|
13406
|
+
if (!prev) rows.set(key, { ...e });
|
|
13407
|
+
else {
|
|
13408
|
+
prev.costUSD += e.costUSD;
|
|
13409
|
+
prev.inputTokens += e.inputTokens;
|
|
13410
|
+
prev.outputTokens += e.outputTokens;
|
|
13411
|
+
prev.cacheReadTokens += e.cacheReadTokens;
|
|
13412
|
+
prev.cacheWriteTokens += e.cacheWriteTokens;
|
|
12933
13413
|
}
|
|
12934
13414
|
}
|
|
12935
|
-
|
|
12936
|
-
const nonCached = Math.max(0, input - cached);
|
|
12937
|
-
if (nonCached === 0 && output === 0 && cached === 0) return null;
|
|
12938
|
-
const norm = normalizeModel(model || "gpt-5");
|
|
12939
|
-
const costUSD = codexSessionCost(model, { input, cached, output });
|
|
12940
|
-
return {
|
|
12941
|
-
date: sessionStart.slice(0, 10),
|
|
12942
|
-
model: norm,
|
|
12943
|
-
workingDir: cwd,
|
|
12944
|
-
runId,
|
|
12945
|
-
costUSD,
|
|
12946
|
-
inputTokens: nonCached,
|
|
12947
|
-
outputTokens: output,
|
|
12948
|
-
cacheReadTokens: cached,
|
|
12949
|
-
cacheWriteTokens: 0
|
|
12950
|
-
};
|
|
13415
|
+
return [...rows.values()];
|
|
12951
13416
|
}
|
|
12952
13417
|
var CODEX_FALLBACK, codexSource;
|
|
12953
13418
|
var init_cost_codex = __esm({
|
|
@@ -12957,43 +13422,17 @@ var init_cost_codex = __esm({
|
|
|
12957
13422
|
CODEX_FALLBACK = [5e-6, 15e-6, 0, 25e-7];
|
|
12958
13423
|
codexSource = {
|
|
12959
13424
|
id: "codex",
|
|
12960
|
-
available()
|
|
12961
|
-
try {
|
|
12962
|
-
return fs21.existsSync(codexSessionsDir());
|
|
12963
|
-
} catch {
|
|
12964
|
-
return false;
|
|
12965
|
-
}
|
|
12966
|
-
},
|
|
13425
|
+
available: () => fs21.existsSync(codexSessionsDir()) || fs21.existsSync(path23.join(path23.dirname(codexSessionsDir()), "archived_sessions")),
|
|
12967
13426
|
collect(sinceMs) {
|
|
12968
|
-
const
|
|
12969
|
-
const
|
|
12970
|
-
for (const file of listCodexSessionFiles(base)) {
|
|
13427
|
+
const entries = [];
|
|
13428
|
+
for (const file of listCodexSessionFiles()) {
|
|
12971
13429
|
try {
|
|
12972
13430
|
if (sinceMs !== void 0 && fs21.statSync(file).mtimeMs < sinceMs) continue;
|
|
13431
|
+
entries.push(...parseCodexSession(fs21.readFileSync(file, "utf8").split("\n")));
|
|
12973
13432
|
} catch {
|
|
12974
|
-
continue;
|
|
12975
|
-
}
|
|
12976
|
-
let content;
|
|
12977
|
-
try {
|
|
12978
|
-
content = fs21.readFileSync(file, "utf8");
|
|
12979
|
-
} catch {
|
|
12980
|
-
continue;
|
|
12981
|
-
}
|
|
12982
|
-
const e = parseCodexSession(content.split("\n"));
|
|
12983
|
-
if (!e) continue;
|
|
12984
|
-
const key = `${e.date}::${e.model}::${e.workingDir ?? ""}::${e.runId ?? ""}`;
|
|
12985
|
-
const prev = combined.get(key);
|
|
12986
|
-
if (prev) {
|
|
12987
|
-
prev.costUSD += e.costUSD;
|
|
12988
|
-
prev.inputTokens += e.inputTokens;
|
|
12989
|
-
prev.outputTokens += e.outputTokens;
|
|
12990
|
-
prev.cacheReadTokens += e.cacheReadTokens;
|
|
12991
|
-
prev.cacheWriteTokens += e.cacheWriteTokens;
|
|
12992
|
-
} else {
|
|
12993
|
-
combined.set(key, { ...e });
|
|
12994
13433
|
}
|
|
12995
13434
|
}
|
|
12996
|
-
return
|
|
13435
|
+
return entries;
|
|
12997
13436
|
}
|
|
12998
13437
|
};
|
|
12999
13438
|
}
|
|
@@ -13006,7 +13445,7 @@ import path24 from "path";
|
|
|
13006
13445
|
function copilotSessionsDir() {
|
|
13007
13446
|
return path24.join(os21.homedir(), ".copilot", "session-state");
|
|
13008
13447
|
}
|
|
13009
|
-
function
|
|
13448
|
+
function safeReaddir2(dir) {
|
|
13010
13449
|
try {
|
|
13011
13450
|
return fs22.readdirSync(dir);
|
|
13012
13451
|
} catch {
|
|
@@ -13092,7 +13531,7 @@ var init_cost_copilot = __esm({
|
|
|
13092
13531
|
collect(sinceMs) {
|
|
13093
13532
|
const base = copilotSessionsDir();
|
|
13094
13533
|
const combined = /* @__PURE__ */ new Map();
|
|
13095
|
-
for (const sid of
|
|
13534
|
+
for (const sid of safeReaddir2(base)) {
|
|
13096
13535
|
const file = path24.join(base, sid, "events.jsonl");
|
|
13097
13536
|
try {
|
|
13098
13537
|
if (sinceMs !== void 0 && fs22.statSync(file).mtimeMs < sinceMs) continue;
|
|
@@ -13707,8 +14146,8 @@ function originForRule(ruleName, sections, enabled) {
|
|
|
13707
14146
|
}
|
|
13708
14147
|
return "";
|
|
13709
14148
|
}
|
|
13710
|
-
function relativeDate(
|
|
13711
|
-
const t = new Date(
|
|
14149
|
+
function relativeDate(timestamp2, now = /* @__PURE__ */ new Date()) {
|
|
14150
|
+
const t = new Date(timestamp2).getTime();
|
|
13712
14151
|
if (Number.isNaN(t)) return "?";
|
|
13713
14152
|
const days = Math.floor((now.getTime() - t) / 864e5);
|
|
13714
14153
|
if (days < 1) return "today";
|
|
@@ -13819,7 +14258,7 @@ function readPreviousScan(opts = {}) {
|
|
|
13819
14258
|
return null;
|
|
13820
14259
|
}
|
|
13821
14260
|
}
|
|
13822
|
-
function appendScanHistory(
|
|
14261
|
+
function appendScanHistory(record2, opts = {}) {
|
|
13823
14262
|
const filePath = opts.path ?? defaultHistoryPath();
|
|
13824
14263
|
const cap = opts.cap ?? SCAN_HISTORY_CAP;
|
|
13825
14264
|
try {
|
|
@@ -13834,7 +14273,7 @@ function appendScanHistory(record, opts = {}) {
|
|
|
13834
14273
|
} catch {
|
|
13835
14274
|
}
|
|
13836
14275
|
}
|
|
13837
|
-
history.push(
|
|
14276
|
+
history.push(record2);
|
|
13838
14277
|
if (history.length > cap) {
|
|
13839
14278
|
history = history.slice(history.length - cap);
|
|
13840
14279
|
}
|
|
@@ -13895,17 +14334,17 @@ function parseJSONLFile(filePath, fallbackWorkingDir) {
|
|
|
13895
14334
|
if (row["type"] !== "assistant") continue;
|
|
13896
14335
|
const msg = row["message"];
|
|
13897
14336
|
if (!msg?.["usage"] || typeof msg["model"] !== "string") continue;
|
|
13898
|
-
const
|
|
14337
|
+
const usage2 = msg["usage"];
|
|
13899
14338
|
const model = msg["model"];
|
|
13900
|
-
const
|
|
13901
|
-
if (typeof
|
|
13902
|
-
const date =
|
|
14339
|
+
const timestamp2 = row["timestamp"];
|
|
14340
|
+
if (typeof timestamp2 !== "string" || timestamp2.length < 10) continue;
|
|
14341
|
+
const date = timestamp2.slice(0, 10);
|
|
13903
14342
|
const p = pricingFor(model);
|
|
13904
14343
|
if (!p) continue;
|
|
13905
|
-
const inp = Number(
|
|
13906
|
-
const out = Number(
|
|
13907
|
-
const cw = Number(
|
|
13908
|
-
const cr = Number(
|
|
14344
|
+
const inp = Number(usage2["input_tokens"] ?? 0);
|
|
14345
|
+
const out = Number(usage2["output_tokens"] ?? 0);
|
|
14346
|
+
const cw = Number(usage2["cache_creation_input_tokens"] ?? 0);
|
|
14347
|
+
const cr = Number(usage2["cache_read_input_tokens"] ?? 0);
|
|
13909
14348
|
const cost = inp * p[0] + out * p[1] + cw * p[2] + cr * p[3];
|
|
13910
14349
|
const rowCwd = typeof row["cwd"] === "string" ? row["cwd"] : null;
|
|
13911
14350
|
const workingDir = rowCwd && rowCwd.startsWith("/") ? rowCwd : fallbackWorkingDir;
|
|
@@ -14935,7 +15374,7 @@ function safeCanaryScanValues() {
|
|
|
14935
15374
|
return [];
|
|
14936
15375
|
}
|
|
14937
15376
|
}
|
|
14938
|
-
function recordCanaries(scanned, toolName,
|
|
15377
|
+
function recordCanaries(scanned, toolName, timestamp2, projLabel, sessionId, agent, result, dedup, values) {
|
|
14939
15378
|
if (values.length === 0) return [];
|
|
14940
15379
|
let pool = [...values];
|
|
14941
15380
|
const matched = [];
|
|
@@ -14949,8 +15388,8 @@ function recordCanaries(scanned, toolName, timestamp, projLabel, sessionId, agen
|
|
|
14949
15388
|
const existing = dedup.canaryIndex.get(key);
|
|
14950
15389
|
if (existing) {
|
|
14951
15390
|
existing.count++;
|
|
14952
|
-
if (
|
|
14953
|
-
existing.timestamp =
|
|
15391
|
+
if (timestamp2 && (!existing.timestamp || timestamp2 < existing.timestamp)) {
|
|
15392
|
+
existing.timestamp = timestamp2;
|
|
14954
15393
|
}
|
|
14955
15394
|
continue;
|
|
14956
15395
|
}
|
|
@@ -14963,7 +15402,7 @@ function recordCanaries(scanned, toolName, timestamp, projLabel, sessionId, agen
|
|
|
14963
15402
|
view: hit.view,
|
|
14964
15403
|
retired: hit.retired,
|
|
14965
15404
|
toolName,
|
|
14966
|
-
timestamp,
|
|
15405
|
+
timestamp: timestamp2,
|
|
14967
15406
|
project: projLabel,
|
|
14968
15407
|
sessionId,
|
|
14969
15408
|
agent,
|
|
@@ -14995,7 +15434,7 @@ function scrubDecoys(subject, values) {
|
|
|
14995
15434
|
};
|
|
14996
15435
|
return walk(subject, 0);
|
|
14997
15436
|
}
|
|
14998
|
-
function pushFsOpAstFinding(command, toolName, input,
|
|
15437
|
+
function pushFsOpAstFinding(command, toolName, input, timestamp2, projLabel, sessionId, agent, result, dedup) {
|
|
14999
15438
|
const fsVerdict = analyzeFsOperation(command);
|
|
15000
15439
|
if (!fsVerdict) return false;
|
|
15001
15440
|
const synthRule = {
|
|
@@ -15025,7 +15464,7 @@ function pushFsOpAstFinding(command, toolName, input, timestamp, projLabel, sess
|
|
|
15025
15464
|
source: synthSource,
|
|
15026
15465
|
toolName,
|
|
15027
15466
|
input,
|
|
15028
|
-
timestamp,
|
|
15467
|
+
timestamp: timestamp2,
|
|
15029
15468
|
project: projLabel,
|
|
15030
15469
|
sessionId,
|
|
15031
15470
|
agent
|
|
@@ -15033,9 +15472,9 @@ function pushFsOpAstFinding(command, toolName, input, timestamp, projLabel, sess
|
|
|
15033
15472
|
}
|
|
15034
15473
|
return true;
|
|
15035
15474
|
}
|
|
15036
|
-
function isStaleFinding(
|
|
15037
|
-
if (!
|
|
15038
|
-
const t = Date.parse(
|
|
15475
|
+
function isStaleFinding(timestamp2, now = Date.now()) {
|
|
15476
|
+
if (!timestamp2) return false;
|
|
15477
|
+
const t = Date.parse(timestamp2);
|
|
15039
15478
|
if (Number.isNaN(t)) return false;
|
|
15040
15479
|
const ageDays = (now - t) / 864e5;
|
|
15041
15480
|
return ageDays > STALE_AGE_DAYS;
|
|
@@ -15183,37 +15622,7 @@ function countScanFiles() {
|
|
|
15183
15622
|
} catch {
|
|
15184
15623
|
}
|
|
15185
15624
|
}
|
|
15186
|
-
|
|
15187
|
-
if (fs29.existsSync(codexDir)) {
|
|
15188
|
-
try {
|
|
15189
|
-
for (const year of fs29.readdirSync(codexDir)) {
|
|
15190
|
-
const yp = path31.join(codexDir, year);
|
|
15191
|
-
try {
|
|
15192
|
-
if (!fs29.statSync(yp).isDirectory()) continue;
|
|
15193
|
-
for (const month of fs29.readdirSync(yp)) {
|
|
15194
|
-
const mp = path31.join(yp, month);
|
|
15195
|
-
try {
|
|
15196
|
-
if (!fs29.statSync(mp).isDirectory()) continue;
|
|
15197
|
-
for (const day of fs29.readdirSync(mp)) {
|
|
15198
|
-
const dp = path31.join(mp, day);
|
|
15199
|
-
try {
|
|
15200
|
-
if (!fs29.statSync(dp).isDirectory()) continue;
|
|
15201
|
-
total += listSessionFiles(dp).length;
|
|
15202
|
-
} catch {
|
|
15203
|
-
continue;
|
|
15204
|
-
}
|
|
15205
|
-
}
|
|
15206
|
-
} catch {
|
|
15207
|
-
continue;
|
|
15208
|
-
}
|
|
15209
|
-
}
|
|
15210
|
-
} catch {
|
|
15211
|
-
continue;
|
|
15212
|
-
}
|
|
15213
|
-
}
|
|
15214
|
-
} catch {
|
|
15215
|
-
}
|
|
15216
|
-
}
|
|
15625
|
+
total += listCodexSessionFiles().length;
|
|
15217
15626
|
return total;
|
|
15218
15627
|
}
|
|
15219
15628
|
function renderProgressBar(done, total, lines) {
|
|
@@ -15336,12 +15745,12 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
|
|
|
15336
15745
|
}
|
|
15337
15746
|
continue;
|
|
15338
15747
|
}
|
|
15339
|
-
const
|
|
15748
|
+
const usage2 = entry.message?.usage;
|
|
15340
15749
|
const model = entry.message?.model;
|
|
15341
|
-
if (
|
|
15750
|
+
if (usage2 && model) {
|
|
15342
15751
|
const p = claudeModelPrice(model);
|
|
15343
15752
|
if (p) {
|
|
15344
|
-
const rowCost = (
|
|
15753
|
+
const rowCost = (usage2.input_tokens ?? 0) * p.i + (usage2.output_tokens ?? 0) * p.o + (usage2.cache_creation_input_tokens ?? 0) * p.cw + (usage2.cache_read_input_tokens ?? 0) * p.cr;
|
|
15345
15754
|
result.totalCostUSD += rowCost;
|
|
15346
15755
|
session.costUSD += rowCost;
|
|
15347
15756
|
}
|
|
@@ -15875,15 +16284,15 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
|
|
|
15875
16284
|
} catch {
|
|
15876
16285
|
continue;
|
|
15877
16286
|
}
|
|
15878
|
-
const
|
|
15879
|
-
if (startDate &&
|
|
16287
|
+
const timestamp2 = step.created_at ?? "";
|
|
16288
|
+
if (startDate && timestamp2 && new Date(timestamp2) < startDate) continue;
|
|
15880
16289
|
if (step.type === "USER_INPUT") {
|
|
15881
16290
|
const text = typeof step.content === "string" ? step.content : "";
|
|
15882
16291
|
if (text) {
|
|
15883
16292
|
const decoysHere5 = recordCanaries(
|
|
15884
16293
|
{ text },
|
|
15885
16294
|
"user-prompt",
|
|
15886
|
-
|
|
16295
|
+
timestamp2,
|
|
15887
16296
|
projLabel,
|
|
15888
16297
|
sessionId,
|
|
15889
16298
|
"antigravity",
|
|
@@ -15900,7 +16309,7 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
|
|
|
15900
16309
|
patternName: dlpMatch.patternName,
|
|
15901
16310
|
redactedSample: dlpMatch.redactedSample,
|
|
15902
16311
|
toolName: "user-prompt",
|
|
15903
|
-
timestamp,
|
|
16312
|
+
timestamp: timestamp2,
|
|
15904
16313
|
project: projLabel,
|
|
15905
16314
|
sessionId,
|
|
15906
16315
|
agent: "antigravity"
|
|
@@ -15911,16 +16320,16 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
|
|
|
15911
16320
|
continue;
|
|
15912
16321
|
}
|
|
15913
16322
|
if (!Array.isArray(step.tool_calls) || step.tool_calls.length === 0) continue;
|
|
15914
|
-
if (
|
|
15915
|
-
if (!result.firstDate ||
|
|
15916
|
-
if (!result.lastDate ||
|
|
16323
|
+
if (timestamp2) {
|
|
16324
|
+
if (!result.firstDate || timestamp2 < result.firstDate) result.firstDate = timestamp2;
|
|
16325
|
+
if (!result.lastDate || timestamp2 > result.lastDate) result.lastDate = timestamp2;
|
|
15917
16326
|
}
|
|
15918
16327
|
for (const tc of step.tool_calls) {
|
|
15919
16328
|
result.totalToolCalls++;
|
|
15920
16329
|
const toolName = tc.name ?? "";
|
|
15921
16330
|
const toolNameLower = toolName.toLowerCase();
|
|
15922
16331
|
const input = canonicalToolInput(toolName, tc.args ?? {});
|
|
15923
|
-
sessionCalls.push({ toolName, input, timestamp });
|
|
16332
|
+
sessionCalls.push({ toolName, input, timestamp: timestamp2 });
|
|
15924
16333
|
const isShellTool = toolNameLower === "run_command";
|
|
15925
16334
|
if (isShellTool) {
|
|
15926
16335
|
result.bashCalls++;
|
|
@@ -15935,7 +16344,7 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
|
|
|
15935
16344
|
const decoysHere6 = recordCanaries(
|
|
15936
16345
|
input,
|
|
15937
16346
|
toolName,
|
|
15938
|
-
|
|
16347
|
+
timestamp2,
|
|
15939
16348
|
projLabel,
|
|
15940
16349
|
sessionId,
|
|
15941
16350
|
"antigravity",
|
|
@@ -15952,7 +16361,7 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
|
|
|
15952
16361
|
patternName: dlpMatch.patternName,
|
|
15953
16362
|
redactedSample: dlpMatch.redactedSample,
|
|
15954
16363
|
toolName,
|
|
15955
|
-
timestamp,
|
|
16364
|
+
timestamp: timestamp2,
|
|
15956
16365
|
project: projLabel,
|
|
15957
16366
|
sessionId,
|
|
15958
16367
|
agent: "antigravity"
|
|
@@ -15965,7 +16374,7 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
|
|
|
15965
16374
|
String(input.command ?? ""),
|
|
15966
16375
|
toolName,
|
|
15967
16376
|
input,
|
|
15968
|
-
|
|
16377
|
+
timestamp2,
|
|
15969
16378
|
projLabel,
|
|
15970
16379
|
sessionId,
|
|
15971
16380
|
"antigravity",
|
|
@@ -15989,7 +16398,7 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
|
|
|
15989
16398
|
source,
|
|
15990
16399
|
toolName,
|
|
15991
16400
|
input,
|
|
15992
|
-
timestamp,
|
|
16401
|
+
timestamp: timestamp2,
|
|
15993
16402
|
project: projLabel,
|
|
15994
16403
|
sessionId,
|
|
15995
16404
|
agent: "antigravity"
|
|
@@ -16021,7 +16430,7 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
|
|
|
16021
16430
|
},
|
|
16022
16431
|
toolName,
|
|
16023
16432
|
input,
|
|
16024
|
-
timestamp,
|
|
16433
|
+
timestamp: timestamp2,
|
|
16025
16434
|
project: projLabel,
|
|
16026
16435
|
sessionId,
|
|
16027
16436
|
agent: "antigravity"
|
|
@@ -16091,7 +16500,7 @@ function scanCopilotHistory(startDate, onProgress, onLine) {
|
|
|
16091
16500
|
} catch {
|
|
16092
16501
|
continue;
|
|
16093
16502
|
}
|
|
16094
|
-
const
|
|
16503
|
+
const timestamp2 = ev.timestamp ?? "";
|
|
16095
16504
|
if (ev.type === "session.start") {
|
|
16096
16505
|
const cwd = ev.data?.context?.cwd;
|
|
16097
16506
|
if (typeof cwd === "string" && cwd) {
|
|
@@ -16099,14 +16508,14 @@ function scanCopilotHistory(startDate, onProgress, onLine) {
|
|
|
16099
16508
|
}
|
|
16100
16509
|
continue;
|
|
16101
16510
|
}
|
|
16102
|
-
if (startDate &&
|
|
16511
|
+
if (startDate && timestamp2 && new Date(timestamp2) < startDate) continue;
|
|
16103
16512
|
if (ev.type === "user.message") {
|
|
16104
16513
|
const text = ev.data?.content ?? ev.data?.text ?? "";
|
|
16105
16514
|
if (typeof text === "string" && text) {
|
|
16106
16515
|
const decoysHere7 = recordCanaries(
|
|
16107
16516
|
{ text },
|
|
16108
16517
|
"user-prompt",
|
|
16109
|
-
|
|
16518
|
+
timestamp2,
|
|
16110
16519
|
projLabel,
|
|
16111
16520
|
sessionId,
|
|
16112
16521
|
"copilot",
|
|
@@ -16123,7 +16532,7 @@ function scanCopilotHistory(startDate, onProgress, onLine) {
|
|
|
16123
16532
|
patternName: dlpMatch2.patternName,
|
|
16124
16533
|
redactedSample: dlpMatch2.redactedSample,
|
|
16125
16534
|
toolName: "user-prompt",
|
|
16126
|
-
timestamp,
|
|
16535
|
+
timestamp: timestamp2,
|
|
16127
16536
|
project: projLabel,
|
|
16128
16537
|
sessionId,
|
|
16129
16538
|
agent: "copilot"
|
|
@@ -16138,19 +16547,19 @@ function scanCopilotHistory(startDate, onProgress, onLine) {
|
|
|
16138
16547
|
const toolNameLower = toolName.toLowerCase();
|
|
16139
16548
|
const input = ev.data?.arguments ?? {};
|
|
16140
16549
|
result.totalToolCalls++;
|
|
16141
|
-
sessionCalls.push({ toolName, input, timestamp });
|
|
16550
|
+
sessionCalls.push({ toolName, input, timestamp: timestamp2 });
|
|
16142
16551
|
const isShellTool = isShellShapedTool(toolNameLower, toolInspectionMap);
|
|
16143
16552
|
if (isShellTool) result.bashCalls++;
|
|
16144
|
-
if (
|
|
16145
|
-
if (!result.firstDate ||
|
|
16146
|
-
if (!result.lastDate ||
|
|
16553
|
+
if (timestamp2) {
|
|
16554
|
+
if (!result.firstDate || timestamp2 < result.firstDate) result.firstDate = timestamp2;
|
|
16555
|
+
if (!result.lastDate || timestamp2 > result.lastDate) result.lastDate = timestamp2;
|
|
16147
16556
|
}
|
|
16148
16557
|
const rawCmd = String(input.command ?? "").trimStart();
|
|
16149
16558
|
if (/^node9\s+(scan|explain|report|tail|dlp|status|sessions|audit)\b/.test(rawCmd)) continue;
|
|
16150
16559
|
const decoysHere8 = recordCanaries(
|
|
16151
16560
|
input,
|
|
16152
16561
|
toolName,
|
|
16153
|
-
|
|
16562
|
+
timestamp2,
|
|
16154
16563
|
projLabel,
|
|
16155
16564
|
sessionId,
|
|
16156
16565
|
"copilot",
|
|
@@ -16167,7 +16576,7 @@ function scanCopilotHistory(startDate, onProgress, onLine) {
|
|
|
16167
16576
|
patternName: dlpMatch.patternName,
|
|
16168
16577
|
redactedSample: dlpMatch.redactedSample,
|
|
16169
16578
|
toolName,
|
|
16170
|
-
timestamp,
|
|
16579
|
+
timestamp: timestamp2,
|
|
16171
16580
|
project: projLabel,
|
|
16172
16581
|
sessionId,
|
|
16173
16582
|
agent: "copilot"
|
|
@@ -16180,7 +16589,7 @@ function scanCopilotHistory(startDate, onProgress, onLine) {
|
|
|
16180
16589
|
String(input.command ?? ""),
|
|
16181
16590
|
toolName,
|
|
16182
16591
|
input,
|
|
16183
|
-
|
|
16592
|
+
timestamp2,
|
|
16184
16593
|
projLabel,
|
|
16185
16594
|
sessionId,
|
|
16186
16595
|
"copilot",
|
|
@@ -16203,7 +16612,7 @@ function scanCopilotHistory(startDate, onProgress, onLine) {
|
|
|
16203
16612
|
source,
|
|
16204
16613
|
toolName,
|
|
16205
16614
|
input,
|
|
16206
|
-
timestamp,
|
|
16615
|
+
timestamp: timestamp2,
|
|
16207
16616
|
project: projLabel,
|
|
16208
16617
|
sessionId,
|
|
16209
16618
|
agent: "copilot"
|
|
@@ -16235,7 +16644,7 @@ function scanCopilotHistory(startDate, onProgress, onLine) {
|
|
|
16235
16644
|
},
|
|
16236
16645
|
toolName,
|
|
16237
16646
|
input,
|
|
16238
|
-
timestamp,
|
|
16647
|
+
timestamp: timestamp2,
|
|
16239
16648
|
project: projLabel,
|
|
16240
16649
|
sessionId,
|
|
16241
16650
|
agent: "copilot"
|
|
@@ -16250,7 +16659,6 @@ function scanCopilotHistory(startDate, onProgress, onLine) {
|
|
|
16250
16659
|
}
|
|
16251
16660
|
function scanCodexHistory(startDate, onProgress, onLine) {
|
|
16252
16661
|
const canaryVals = safeCanaryScanValues();
|
|
16253
|
-
const sessionsBase = path31.join(os27.homedir(), ".codex", "sessions");
|
|
16254
16662
|
const result = {
|
|
16255
16663
|
filesScanned: 0,
|
|
16256
16664
|
sessions: 0,
|
|
@@ -16267,39 +16675,7 @@ function scanCodexHistory(startDate, onProgress, onLine) {
|
|
|
16267
16675
|
perSession: []
|
|
16268
16676
|
};
|
|
16269
16677
|
const dedup = emptyScanDedup();
|
|
16270
|
-
|
|
16271
|
-
const jsonlFiles = [];
|
|
16272
|
-
try {
|
|
16273
|
-
for (const year of fs29.readdirSync(sessionsBase)) {
|
|
16274
|
-
const yearPath = path31.join(sessionsBase, year);
|
|
16275
|
-
try {
|
|
16276
|
-
if (!fs29.statSync(yearPath).isDirectory()) continue;
|
|
16277
|
-
} catch {
|
|
16278
|
-
continue;
|
|
16279
|
-
}
|
|
16280
|
-
for (const month of fs29.readdirSync(yearPath)) {
|
|
16281
|
-
const monthPath = path31.join(yearPath, month);
|
|
16282
|
-
try {
|
|
16283
|
-
if (!fs29.statSync(monthPath).isDirectory()) continue;
|
|
16284
|
-
} catch {
|
|
16285
|
-
continue;
|
|
16286
|
-
}
|
|
16287
|
-
for (const day of fs29.readdirSync(monthPath)) {
|
|
16288
|
-
const dayPath = path31.join(monthPath, day);
|
|
16289
|
-
try {
|
|
16290
|
-
if (!fs29.statSync(dayPath).isDirectory()) continue;
|
|
16291
|
-
} catch {
|
|
16292
|
-
continue;
|
|
16293
|
-
}
|
|
16294
|
-
for (const file of fs29.readdirSync(dayPath)) {
|
|
16295
|
-
if (file.endsWith(".jsonl")) jsonlFiles.push(path31.join(dayPath, file));
|
|
16296
|
-
}
|
|
16297
|
-
}
|
|
16298
|
-
}
|
|
16299
|
-
}
|
|
16300
|
-
} catch {
|
|
16301
|
-
return result;
|
|
16302
|
-
}
|
|
16678
|
+
const jsonlFiles = listCodexSessionFiles();
|
|
16303
16679
|
const ruleSources = buildRuleSources();
|
|
16304
16680
|
for (const filePath of jsonlFiles) {
|
|
16305
16681
|
result.filesScanned++;
|
|
@@ -16315,10 +16691,6 @@ function scanCodexHistory(startDate, onProgress, onLine) {
|
|
|
16315
16691
|
let projLabel = "";
|
|
16316
16692
|
result.sessions++;
|
|
16317
16693
|
const sessionCalls = [];
|
|
16318
|
-
let lastTotalInput = 0;
|
|
16319
|
-
let lastTotalCached = 0;
|
|
16320
|
-
let lastTotalOutput = 0;
|
|
16321
|
-
let model = "";
|
|
16322
16694
|
for (const line of lines) {
|
|
16323
16695
|
if (!line.trim()) continue;
|
|
16324
16696
|
onLine?.();
|
|
@@ -16336,18 +16708,6 @@ function scanCodexHistory(startDate, onProgress, onLine) {
|
|
|
16336
16708
|
projLabel = stripTerminalEscapes(cwd.replace(os27.homedir(), "~")).slice(0, 40);
|
|
16337
16709
|
continue;
|
|
16338
16710
|
}
|
|
16339
|
-
if (entry.type === "turn_context" && typeof payload["model"] === "string") {
|
|
16340
|
-
model = payload["model"];
|
|
16341
|
-
continue;
|
|
16342
|
-
}
|
|
16343
|
-
if (entry.type === "event_msg" && payload["type"] === "token_count") {
|
|
16344
|
-
const info = payload["info"];
|
|
16345
|
-
const usage = info?.["total_token_usage"] ?? {};
|
|
16346
|
-
lastTotalInput = usage["input_tokens"] ?? lastTotalInput;
|
|
16347
|
-
lastTotalCached = usage["cached_input_tokens"] ?? lastTotalCached;
|
|
16348
|
-
lastTotalOutput = usage["output_tokens"] ?? lastTotalOutput;
|
|
16349
|
-
continue;
|
|
16350
|
-
}
|
|
16351
16711
|
if (entry.type === "event_msg" && payload["type"] === "user_message") {
|
|
16352
16712
|
const text = String(payload["message"] ?? "");
|
|
16353
16713
|
if (text) {
|
|
@@ -16504,13 +16864,8 @@ function scanCodexHistory(startDate, onProgress, onLine) {
|
|
|
16504
16864
|
}
|
|
16505
16865
|
}
|
|
16506
16866
|
}
|
|
16507
|
-
const
|
|
16508
|
-
|
|
16509
|
-
result.totalCostUSD += codexSessionCost(model, {
|
|
16510
|
-
input: lastTotalInput,
|
|
16511
|
-
cached: lastTotalCached,
|
|
16512
|
-
output: lastTotalOutput
|
|
16513
|
-
});
|
|
16867
|
+
for (const event of codexUsageInWindow(parseCodexUsage(lines), startDate)) {
|
|
16868
|
+
result.totalCostUSD += event.costUSD;
|
|
16514
16869
|
}
|
|
16515
16870
|
result.loopFindings.push(...detectLoops(sessionCalls, projLabel, sessionId, "codex"));
|
|
16516
16871
|
}
|
|
@@ -17942,13 +18297,13 @@ var init_taint_store = __esm({
|
|
|
17942
18297
|
*/
|
|
17943
18298
|
check(filePath) {
|
|
17944
18299
|
const resolved = this._resolve(filePath);
|
|
17945
|
-
const
|
|
17946
|
-
if (!
|
|
17947
|
-
if (Date.now() >
|
|
18300
|
+
const record2 = this.records.get(resolved);
|
|
18301
|
+
if (!record2) return null;
|
|
18302
|
+
if (Date.now() > record2.expiresAt) {
|
|
17948
18303
|
this.records.delete(resolved);
|
|
17949
18304
|
return null;
|
|
17950
18305
|
}
|
|
17951
|
-
return
|
|
18306
|
+
return record2;
|
|
17952
18307
|
}
|
|
17953
18308
|
/**
|
|
17954
18309
|
* Propagate taint from sourcePath to destPath (e.g. cp, mv).
|
|
@@ -17969,8 +18324,8 @@ var init_taint_store = __esm({
|
|
|
17969
18324
|
/** Remove all expired records. Called periodically by the daemon. */
|
|
17970
18325
|
prune() {
|
|
17971
18326
|
const now = Date.now();
|
|
17972
|
-
for (const [key,
|
|
17973
|
-
if (now >
|
|
18327
|
+
for (const [key, record2] of this.records) {
|
|
18328
|
+
if (now > record2.expiresAt) this.records.delete(key);
|
|
17974
18329
|
}
|
|
17975
18330
|
}
|
|
17976
18331
|
/** Return all non-expired taint records (for audit/debug). */
|
|
@@ -18009,13 +18364,13 @@ var init_taint_store = __esm({
|
|
|
18009
18364
|
* Expired records are pruned on access. */
|
|
18010
18365
|
check(sessionId) {
|
|
18011
18366
|
if (!sessionId) return null;
|
|
18012
|
-
const
|
|
18013
|
-
if (!
|
|
18014
|
-
if (Date.now() >
|
|
18367
|
+
const record2 = this.records.get(sessionId);
|
|
18368
|
+
if (!record2) return null;
|
|
18369
|
+
if (Date.now() > record2.expiresAt) {
|
|
18015
18370
|
this.records.delete(sessionId);
|
|
18016
18371
|
return null;
|
|
18017
18372
|
}
|
|
18018
|
-
return
|
|
18373
|
+
return record2;
|
|
18019
18374
|
}
|
|
18020
18375
|
/** Clear a session's taint (e.g. the user resolved it). Returns true if a
|
|
18021
18376
|
* record was actually removed (false if the session wasn't tainted). */
|
|
@@ -18030,8 +18385,8 @@ var init_taint_store = __esm({
|
|
|
18030
18385
|
/** Remove all expired records. Called periodically by the daemon. */
|
|
18031
18386
|
prune() {
|
|
18032
18387
|
const now = Date.now();
|
|
18033
|
-
for (const [key,
|
|
18034
|
-
if (now >
|
|
18388
|
+
for (const [key, record2] of this.records) {
|
|
18389
|
+
if (now > record2.expiresAt) this.records.delete(key);
|
|
18035
18390
|
}
|
|
18036
18391
|
}
|
|
18037
18392
|
/** Remove all records. Used by tests to reset state between runs. */
|
|
@@ -22765,10 +23120,10 @@ data: ${JSON.stringify(item.data)}
|
|
|
22765
23120
|
return res.end(JSON.stringify({ error: "all paths must be strings" }));
|
|
22766
23121
|
}
|
|
22767
23122
|
for (const p of body.paths) {
|
|
22768
|
-
const
|
|
22769
|
-
if (
|
|
23123
|
+
const record2 = taintStore.check(p);
|
|
23124
|
+
if (record2) {
|
|
22770
23125
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
22771
|
-
return res.end(JSON.stringify({ tainted: true, record }));
|
|
23126
|
+
return res.end(JSON.stringify({ tainted: true, record: record2 }));
|
|
22772
23127
|
}
|
|
22773
23128
|
}
|
|
22774
23129
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
@@ -22817,9 +23172,9 @@ data: ${JSON.stringify(item.data)}
|
|
|
22817
23172
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
22818
23173
|
return res.end(JSON.stringify({ error: "sessionId must be a string" }));
|
|
22819
23174
|
}
|
|
22820
|
-
const
|
|
23175
|
+
const record2 = sessionTaintStore.check(body.sessionId);
|
|
22821
23176
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
22822
|
-
return res.end(JSON.stringify(
|
|
23177
|
+
return res.end(JSON.stringify(record2 ? { tainted: true, record: record2 } : { tainted: false }));
|
|
22823
23178
|
} catch {
|
|
22824
23179
|
res.writeHead(400).end();
|
|
22825
23180
|
return;
|
|
@@ -28765,8 +29120,8 @@ var require_util2 = __commonJS({
|
|
|
28765
29120
|
request2.headersList.append("origin", serializedOrigin, true);
|
|
28766
29121
|
}
|
|
28767
29122
|
}
|
|
28768
|
-
function coarsenTime(
|
|
28769
|
-
return
|
|
29123
|
+
function coarsenTime(timestamp2, crossOriginIsolatedCapability) {
|
|
29124
|
+
return timestamp2;
|
|
28770
29125
|
}
|
|
28771
29126
|
function clampAndCoarsenConnectionTimingInfo(connectionTimingInfo, defaultStartTime, crossOriginIsolatedCapability) {
|
|
28772
29127
|
if (!connectionTimingInfo?.startTime || connectionTimingInfo.startTime < defaultStartTime) {
|
|
@@ -38847,20 +39202,20 @@ var require_dns = __commonJS({
|
|
|
38847
39202
|
return ip;
|
|
38848
39203
|
}
|
|
38849
39204
|
setRecords(origin, addresses) {
|
|
38850
|
-
const
|
|
39205
|
+
const timestamp2 = Date.now();
|
|
38851
39206
|
const records = { records: { 4: null, 6: null } };
|
|
38852
39207
|
let minTTL = this.#maxTTL;
|
|
38853
|
-
for (const
|
|
38854
|
-
|
|
38855
|
-
if (typeof
|
|
38856
|
-
|
|
38857
|
-
minTTL = Math.min(minTTL,
|
|
39208
|
+
for (const record2 of addresses) {
|
|
39209
|
+
record2.timestamp = timestamp2;
|
|
39210
|
+
if (typeof record2.ttl === "number") {
|
|
39211
|
+
record2.ttl = Math.min(record2.ttl, this.#maxTTL);
|
|
39212
|
+
minTTL = Math.min(minTTL, record2.ttl);
|
|
38858
39213
|
} else {
|
|
38859
|
-
|
|
39214
|
+
record2.ttl = this.#maxTTL;
|
|
38860
39215
|
}
|
|
38861
|
-
const familyRecords = records.records[
|
|
38862
|
-
familyRecords.ips.push(
|
|
38863
|
-
records.records[
|
|
39216
|
+
const familyRecords = records.records[record2.family] ?? { ips: [] };
|
|
39217
|
+
familyRecords.ips.push(record2);
|
|
39218
|
+
records.records[record2.family] = familyRecords;
|
|
38864
39219
|
}
|
|
38865
39220
|
this.storage.set(origin.hostname, records, { ttl: minTTL });
|
|
38866
39221
|
}
|
|
@@ -53524,15 +53879,15 @@ import chalk16 from "chalk";
|
|
|
53524
53879
|
import fs59 from "fs";
|
|
53525
53880
|
import path57 from "path";
|
|
53526
53881
|
import os53 from "os";
|
|
53527
|
-
function formatRelativeTime(
|
|
53528
|
-
const diff = Date.now() - new Date(
|
|
53882
|
+
function formatRelativeTime(timestamp2) {
|
|
53883
|
+
const diff = Date.now() - new Date(timestamp2).getTime();
|
|
53529
53884
|
const sec = Math.floor(diff / 1e3);
|
|
53530
53885
|
if (sec < 60) return `${sec}s ago`;
|
|
53531
53886
|
const min = Math.floor(sec / 60);
|
|
53532
53887
|
if (min < 60) return `${min}m ago`;
|
|
53533
53888
|
const hrs = Math.floor(min / 60);
|
|
53534
53889
|
if (hrs < 24) return `${hrs}h ago`;
|
|
53535
|
-
return new Date(
|
|
53890
|
+
return new Date(timestamp2).toLocaleDateString();
|
|
53536
53891
|
}
|
|
53537
53892
|
function registerAuditCommand(program2) {
|
|
53538
53893
|
program2.command("audit").description("View local execution audit log").option("--tail <n>", "Number of entries to show", "20").option("--tool <pattern>", "Filter by tool name (substring match)").option("--deny", "Show only denied actions").option("--json", "Output raw JSON").action((options) => {
|
|
@@ -53778,15 +54133,15 @@ function processClaudeCostProject(proj, projectsDir, start, end, acc) {
|
|
|
53778
54133
|
if (!entry.timestamp) continue;
|
|
53779
54134
|
const ts = new Date(entry.timestamp);
|
|
53780
54135
|
if (ts < start || ts > end) continue;
|
|
53781
|
-
const
|
|
54136
|
+
const usage2 = entry.message?.usage;
|
|
53782
54137
|
const model = entry.message?.model;
|
|
53783
|
-
if (!
|
|
54138
|
+
if (!usage2 || !model) continue;
|
|
53784
54139
|
const p = claudeModelPrice2(model);
|
|
53785
54140
|
if (!p) continue;
|
|
53786
|
-
const inp =
|
|
53787
|
-
const out =
|
|
53788
|
-
const cw =
|
|
53789
|
-
const cr =
|
|
54141
|
+
const inp = usage2.input_tokens ?? 0;
|
|
54142
|
+
const out = usage2.output_tokens ?? 0;
|
|
54143
|
+
const cw = usage2.cache_creation_input_tokens ?? 0;
|
|
54144
|
+
const cr = usage2.cache_read_input_tokens ?? 0;
|
|
53790
54145
|
const cost = inp * p.i + out * p.o + cw * p.cw + cr * p.cr;
|
|
53791
54146
|
acc.total += cost;
|
|
53792
54147
|
acc.inputTokens += inp;
|
|
@@ -53834,90 +54189,21 @@ function processCodexCostFile(filePath, start, end, acc) {
|
|
|
53834
54189
|
} catch {
|
|
53835
54190
|
return;
|
|
53836
54191
|
}
|
|
53837
|
-
|
|
53838
|
-
|
|
53839
|
-
|
|
53840
|
-
|
|
53841
|
-
|
|
53842
|
-
|
|
54192
|
+
const parsed = parseCodexUsage(lines);
|
|
54193
|
+
for (const event of codexUsageInWindow(parsed, start, end)) {
|
|
54194
|
+
acc.total += event.costUSD;
|
|
54195
|
+
acc.byDay.set(event.date, (acc.byDay.get(event.date) ?? 0) + event.costUSD);
|
|
54196
|
+
acc.byModel.set(event.model, (acc.byModel.get(event.model) ?? 0) + event.costUSD);
|
|
54197
|
+
}
|
|
53843
54198
|
for (const line of lines) {
|
|
53844
|
-
if (!line.trim()) continue;
|
|
53845
|
-
let entry;
|
|
53846
54199
|
try {
|
|
53847
|
-
entry = JSON.parse(line);
|
|
54200
|
+
const entry = JSON.parse(line);
|
|
54201
|
+
if (entry?.type !== "response_item" || entry.payload?.type !== "function_call") continue;
|
|
54202
|
+
const ts = new Date(entry.timestamp ?? parsed.sessionStart);
|
|
54203
|
+
if (ts >= start && ts <= end) acc.toolCalls++;
|
|
53848
54204
|
} catch {
|
|
53849
|
-
continue;
|
|
53850
|
-
}
|
|
53851
|
-
const p = entry.payload ?? {};
|
|
53852
|
-
if (entry.type === "session_meta") {
|
|
53853
|
-
sessionStart = String(p["timestamp"] ?? "");
|
|
53854
|
-
continue;
|
|
53855
|
-
}
|
|
53856
|
-
if (entry.type === "turn_context" && typeof p["model"] === "string") {
|
|
53857
|
-
model = p["model"];
|
|
53858
|
-
continue;
|
|
53859
|
-
}
|
|
53860
|
-
if (entry.type === "event_msg" && p["type"] === "token_count") {
|
|
53861
|
-
const info = p["info"] ?? {};
|
|
53862
|
-
const usage = info["total_token_usage"] ?? {};
|
|
53863
|
-
lastTotalInput = usage["input_tokens"] ?? lastTotalInput;
|
|
53864
|
-
lastTotalCached = usage["cached_input_tokens"] ?? lastTotalCached;
|
|
53865
|
-
lastTotalOutput = usage["output_tokens"] ?? lastTotalOutput;
|
|
53866
|
-
}
|
|
53867
|
-
if (entry.type === "response_item" && p["type"] === "function_call") {
|
|
53868
|
-
sessionToolCalls++;
|
|
53869
|
-
}
|
|
53870
|
-
}
|
|
53871
|
-
if (!sessionStart) return;
|
|
53872
|
-
const ts = new Date(sessionStart);
|
|
53873
|
-
if (ts < start || ts > end) return;
|
|
53874
|
-
const cost = codexSessionCost(model, {
|
|
53875
|
-
input: lastTotalInput,
|
|
53876
|
-
cached: lastTotalCached,
|
|
53877
|
-
output: lastTotalOutput
|
|
53878
|
-
});
|
|
53879
|
-
acc.total += cost;
|
|
53880
|
-
acc.toolCalls += sessionToolCalls;
|
|
53881
|
-
const dateKey = sessionStart.slice(0, 10);
|
|
53882
|
-
acc.byDay.set(dateKey, (acc.byDay.get(dateKey) ?? 0) + cost);
|
|
53883
|
-
const normModel = normalizeModel(model || "gpt-5");
|
|
53884
|
-
acc.byModel.set(normModel, (acc.byModel.get(normModel) ?? 0) + cost);
|
|
53885
|
-
}
|
|
53886
|
-
function listCodexSessionFiles2(sessionsBase) {
|
|
53887
|
-
const jsonlFiles = [];
|
|
53888
|
-
if (!fs60.existsSync(sessionsBase)) return jsonlFiles;
|
|
53889
|
-
try {
|
|
53890
|
-
for (const year of fs60.readdirSync(sessionsBase)) {
|
|
53891
|
-
const yearPath = path58.join(sessionsBase, year);
|
|
53892
|
-
try {
|
|
53893
|
-
if (!fs60.statSync(yearPath).isDirectory()) continue;
|
|
53894
|
-
} catch {
|
|
53895
|
-
continue;
|
|
53896
|
-
}
|
|
53897
|
-
for (const month of fs60.readdirSync(yearPath)) {
|
|
53898
|
-
const monthPath = path58.join(yearPath, month);
|
|
53899
|
-
try {
|
|
53900
|
-
if (!fs60.statSync(monthPath).isDirectory()) continue;
|
|
53901
|
-
} catch {
|
|
53902
|
-
continue;
|
|
53903
|
-
}
|
|
53904
|
-
for (const day of fs60.readdirSync(monthPath)) {
|
|
53905
|
-
const dayPath = path58.join(monthPath, day);
|
|
53906
|
-
try {
|
|
53907
|
-
if (!fs60.statSync(dayPath).isDirectory()) continue;
|
|
53908
|
-
} catch {
|
|
53909
|
-
continue;
|
|
53910
|
-
}
|
|
53911
|
-
for (const file of fs60.readdirSync(dayPath)) {
|
|
53912
|
-
if (file.endsWith(".jsonl")) jsonlFiles.push(path58.join(dayPath, file));
|
|
53913
|
-
}
|
|
53914
|
-
}
|
|
53915
|
-
}
|
|
53916
54205
|
}
|
|
53917
|
-
} catch {
|
|
53918
|
-
return [];
|
|
53919
54206
|
}
|
|
53920
|
-
return jsonlFiles;
|
|
53921
54207
|
}
|
|
53922
54208
|
function mergeByModel(...maps) {
|
|
53923
54209
|
const out = /* @__PURE__ */ new Map();
|
|
@@ -53933,7 +54219,7 @@ function loadCodexCost(start, end, sessionsBase) {
|
|
|
53933
54219
|
byDay: /* @__PURE__ */ new Map(),
|
|
53934
54220
|
byModel: /* @__PURE__ */ new Map()
|
|
53935
54221
|
};
|
|
53936
|
-
const files =
|
|
54222
|
+
const files = listCodexSessionFiles(sessionsBase);
|
|
53937
54223
|
for (const filePath of files) {
|
|
53938
54224
|
processCodexCostFile(filePath, start, end, acc);
|
|
53939
54225
|
}
|
|
@@ -54073,7 +54359,7 @@ function aggregateReportFromAudit(period, opts = {}) {
|
|
|
54073
54359
|
const now = opts.now ?? /* @__PURE__ */ new Date();
|
|
54074
54360
|
const auditLogPath = opts.auditLogPath ?? path58.join(os54.homedir(), ".node9", "audit.log");
|
|
54075
54361
|
const claudeProjectsDir = opts.claudeProjectsDir ?? path58.join(os54.homedir(), ".claude", "projects");
|
|
54076
|
-
const codexSessionsDir2 = opts.codexSessionsDir ??
|
|
54362
|
+
const codexSessionsDir2 = opts.codexSessionsDir ?? codexSessionsDir();
|
|
54077
54363
|
const geminiTmpDir2 = opts.geminiTmpDir ?? path58.join(os54.homedir(), ".gemini", "tmp");
|
|
54078
54364
|
const hasAuditFile = fs60.existsSync(auditLogPath);
|
|
54079
54365
|
const allEntries = opts.preloadedAuditEntries ?? parseAuditLog(auditLogPath);
|
|
@@ -59398,7 +59684,11 @@ var BUILTIN_JAIL = [
|
|
|
59398
59684
|
"~/.ssh \u2014 SSH private keys",
|
|
59399
59685
|
"~/.aws \u2014 AWS credentials",
|
|
59400
59686
|
".env files",
|
|
59401
|
-
"credential files \u2014 credentials.json, .netrc, .npmrc, .docker, .kube, gcloud"
|
|
59687
|
+
"credential files \u2014 credentials.json, .netrc, .npmrc, .docker, .kube, gcloud",
|
|
59688
|
+
// Stage 4 (2026-09-11): reads are blocked; a COPY out of the jail (cp, tar,
|
|
59689
|
+
// scp, rsync, aws s3 cp, ...) is reviewed, because a backup and a theft are
|
|
59690
|
+
// the same command shape.
|
|
59691
|
+
"copies out of the jail (cp, tar, scp, rsync, cloud upload) \u2014 review"
|
|
59402
59692
|
];
|
|
59403
59693
|
function registerJailCommand(program2) {
|
|
59404
59694
|
const jail = program2.command("jail").description("Grow the credential jail \u2014 block/review reads of your sensitive paths");
|
|
@@ -60272,12 +60562,12 @@ function parseSessionLines(lines) {
|
|
|
60272
60562
|
continue;
|
|
60273
60563
|
}
|
|
60274
60564
|
if (entry.type !== "assistant") continue;
|
|
60275
|
-
const
|
|
60565
|
+
const usage2 = entry.message?.usage;
|
|
60276
60566
|
const model = entry.message?.model;
|
|
60277
|
-
if (
|
|
60567
|
+
if (usage2 && model) {
|
|
60278
60568
|
const p = modelPrice(model);
|
|
60279
60569
|
if (p) {
|
|
60280
|
-
costUSD += (
|
|
60570
|
+
costUSD += (usage2.input_tokens ?? 0) * p.i + (usage2.output_tokens ?? 0) * p.o + (usage2.cache_creation_input_tokens ?? 0) * p.cw + (usage2.cache_read_input_tokens ?? 0) * p.cr;
|
|
60281
60571
|
}
|
|
60282
60572
|
}
|
|
60283
60573
|
const content = entry.message?.content;
|
|
@@ -60457,46 +60747,13 @@ function buildGeminiSessions(days, allAuditEntries) {
|
|
|
60457
60747
|
return summaries;
|
|
60458
60748
|
}
|
|
60459
60749
|
function buildCodexSessions(days, allAuditEntries) {
|
|
60460
|
-
const sessionsBase = path69.join(os61.homedir(), ".codex", "sessions");
|
|
60461
|
-
if (!fs74.existsSync(sessionsBase)) return [];
|
|
60462
60750
|
const cutoff = days !== null ? (() => {
|
|
60463
60751
|
const d = /* @__PURE__ */ new Date();
|
|
60464
60752
|
d.setDate(d.getDate() - days);
|
|
60465
60753
|
d.setHours(0, 0, 0, 0);
|
|
60466
60754
|
return d;
|
|
60467
60755
|
})() : null;
|
|
60468
|
-
const jsonlFiles =
|
|
60469
|
-
try {
|
|
60470
|
-
for (const year of fs74.readdirSync(sessionsBase)) {
|
|
60471
|
-
const yearPath = path69.join(sessionsBase, year);
|
|
60472
|
-
try {
|
|
60473
|
-
if (!fs74.statSync(yearPath).isDirectory()) continue;
|
|
60474
|
-
} catch {
|
|
60475
|
-
continue;
|
|
60476
|
-
}
|
|
60477
|
-
for (const month of fs74.readdirSync(yearPath)) {
|
|
60478
|
-
const monthPath = path69.join(yearPath, month);
|
|
60479
|
-
try {
|
|
60480
|
-
if (!fs74.statSync(monthPath).isDirectory()) continue;
|
|
60481
|
-
} catch {
|
|
60482
|
-
continue;
|
|
60483
|
-
}
|
|
60484
|
-
for (const day of fs74.readdirSync(monthPath)) {
|
|
60485
|
-
const dayPath = path69.join(monthPath, day);
|
|
60486
|
-
try {
|
|
60487
|
-
if (!fs74.statSync(dayPath).isDirectory()) continue;
|
|
60488
|
-
} catch {
|
|
60489
|
-
continue;
|
|
60490
|
-
}
|
|
60491
|
-
for (const file of fs74.readdirSync(dayPath)) {
|
|
60492
|
-
if (file.endsWith(".jsonl")) jsonlFiles.push(path69.join(dayPath, file));
|
|
60493
|
-
}
|
|
60494
|
-
}
|
|
60495
|
-
}
|
|
60496
|
-
}
|
|
60497
|
-
} catch {
|
|
60498
|
-
return [];
|
|
60499
|
-
}
|
|
60756
|
+
const jsonlFiles = listCodexSessionFiles();
|
|
60500
60757
|
const summaries = [];
|
|
60501
60758
|
for (const filePath of jsonlFiles) {
|
|
60502
60759
|
let lines;
|
|
@@ -60511,10 +60768,6 @@ function buildCodexSessions(days, allAuditEntries) {
|
|
|
60511
60768
|
let firstPrompt = "";
|
|
60512
60769
|
const toolCalls = [];
|
|
60513
60770
|
let lastToolTs = "";
|
|
60514
|
-
let lastTotalInput = 0;
|
|
60515
|
-
let lastTotalCached = 0;
|
|
60516
|
-
let lastTotalOutput = 0;
|
|
60517
|
-
let model = "";
|
|
60518
60771
|
for (const line of lines) {
|
|
60519
60772
|
if (!line.trim()) continue;
|
|
60520
60773
|
let entry;
|
|
@@ -60530,22 +60783,10 @@ function buildCodexSessions(days, allAuditEntries) {
|
|
|
60530
60783
|
cwd = String(p["cwd"] ?? "");
|
|
60531
60784
|
continue;
|
|
60532
60785
|
}
|
|
60533
|
-
if (entry.type === "turn_context" && typeof p["model"] === "string") {
|
|
60534
|
-
model = p["model"];
|
|
60535
|
-
continue;
|
|
60536
|
-
}
|
|
60537
60786
|
if (entry.type === "event_msg" && p["type"] === "user_message" && !firstPrompt) {
|
|
60538
60787
|
firstPrompt = String(p["message"] ?? "");
|
|
60539
60788
|
continue;
|
|
60540
60789
|
}
|
|
60541
|
-
if (entry.type === "event_msg" && p["type"] === "token_count") {
|
|
60542
|
-
const info = p["info"] ?? {};
|
|
60543
|
-
const usage = info["total_token_usage"] ?? {};
|
|
60544
|
-
lastTotalInput = usage["input_tokens"] ?? lastTotalInput;
|
|
60545
|
-
lastTotalCached = usage["cached_input_tokens"] ?? lastTotalCached;
|
|
60546
|
-
lastTotalOutput = usage["output_tokens"] ?? lastTotalOutput;
|
|
60547
|
-
continue;
|
|
60548
|
-
}
|
|
60549
60790
|
if (entry.type === "response_item" && p["type"] === "function_call") {
|
|
60550
60791
|
const tool = String(p["name"] ?? "");
|
|
60551
60792
|
let input = {};
|
|
@@ -60559,12 +60800,13 @@ function buildCodexSessions(days, allAuditEntries) {
|
|
|
60559
60800
|
}
|
|
60560
60801
|
}
|
|
60561
60802
|
if (!sessionId || !startTime) continue;
|
|
60562
|
-
|
|
60563
|
-
const
|
|
60564
|
-
|
|
60565
|
-
|
|
60566
|
-
|
|
60567
|
-
|
|
60803
|
+
const parsedUsage = parseCodexUsage(lines);
|
|
60804
|
+
const usageEvents = codexUsageInWindow(parsedUsage, cutoff);
|
|
60805
|
+
if (cutoff && new Date(startTime) < cutoff && usageEvents.length === 0 && !toolCalls.some((call) => new Date(call.timestamp) >= cutoff))
|
|
60806
|
+
continue;
|
|
60807
|
+
const costUSD = usageEvents.reduce((sum, event) => sum + event.costUSD, 0);
|
|
60808
|
+
const lastUsageTs = parsedUsage.events.at(-1)?.timestamp ?? "";
|
|
60809
|
+
if (lastUsageTs > lastToolTs) lastToolTs = lastUsageTs;
|
|
60568
60810
|
const windowEnd = new Date(
|
|
60569
60811
|
Math.max(new Date(startTime).getTime(), lastToolTs ? new Date(lastToolTs).getTime() : 0) + 5 * 60 * 1e3
|
|
60570
60812
|
).toISOString();
|