project-athena 0.3.0 → 0.5.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.
Files changed (140) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/README.md +56 -4
  3. package/dist/{activity-P4UUFHWV.js → activity-XCHKBZTG.js} +6 -6
  4. package/dist/{agents-3HTSQGL2.js → agents-HUJ4JXIB.js} +6 -6
  5. package/dist/{ai-ASL4FFQ7.js → ai-LGTLQKL4.js} +3 -3
  6. package/dist/{analyze-F3YOILNL.js → analyze-AP4XJT7D.js} +10 -9
  7. package/dist/{chunk-ZB7LC6FP.js → chunk-3KFOI2X4.js} +2 -2
  8. package/dist/{chunk-PANVPNYF.js → chunk-44EGCEB2.js} +59 -251
  9. package/dist/{chunk-SJOOXO34.js → chunk-4FETKOY3.js} +9 -8
  10. package/dist/chunk-4IIBVTBS.js +76 -0
  11. package/dist/{chunk-LEMPCNAY.js → chunk-56EXAGTM.js} +3 -3
  12. package/dist/{chunk-32PRS2MG.js → chunk-6OSQMXUP.js} +5 -5
  13. package/dist/{chunk-OLPSEQS5.js → chunk-AKOI4VTD.js} +10 -1
  14. package/dist/{chunk-EF6GHOIN.js → chunk-C25FDXOO.js} +133 -23
  15. package/dist/{chunk-S7T6VX56.js → chunk-DH5AGULY.js} +8 -4
  16. package/dist/chunk-F65QINMR.js +702 -0
  17. package/dist/chunk-GU3ZX2J7.js +166 -0
  18. package/dist/chunk-GYQG4Q2N.js +108 -0
  19. package/dist/chunk-IDODPGTX.js +595 -0
  20. package/dist/chunk-KJYSNZXS.js +934 -0
  21. package/dist/{chunk-PR53DT7I.js → chunk-L2EHJCH4.js} +8 -8
  22. package/dist/{chunk-65TSRDQ3.js → chunk-P4TRQMES.js} +2 -2
  23. package/dist/{chunk-TGJUUB44.js → chunk-PZPMWCXS.js} +5 -5
  24. package/dist/chunk-QCC4VJUH.js +217 -0
  25. package/dist/{chunk-DOQMAZL5.js → chunk-R5AX7VST.js} +3 -3
  26. package/dist/{chunk-JY6BFIJJ.js → chunk-RQKGT2EK.js} +3 -3
  27. package/dist/{chunk-PDFQ5BFG.js → chunk-SVTEJYMS.js} +4 -155
  28. package/dist/{chunk-VSYBC3VY.js → chunk-UMWX4UTK.js} +2 -2
  29. package/dist/{chunk-RXNRMIZP.js → chunk-WUIC65QO.js} +2 -2
  30. package/dist/{chunk-JCGDR4UV.js → chunk-WWQ5NKEC.js} +3 -1
  31. package/dist/{chunk-XB66PZ6Z.js → chunk-X7EYOAEX.js} +2 -2
  32. package/dist/chunk-Y6SDMZET.js +278 -0
  33. package/dist/{chunk-2RS6TA3D.js → chunk-YRIS7ZS6.js} +28 -17
  34. package/dist/{clean-S7PKCOLM.js → clean-LQOG56AM.js} +4 -4
  35. package/dist/cli.js +2 -2
  36. package/dist/{context-AH5MFHRG.js → context-G5YHY3L2.js} +24 -10
  37. package/dist/{doctor-RSJMLE2P.js → doctor-L34QJ7WR.js} +17 -10
  38. package/dist/{event-TNT6CQZR.js → event-6YW6TGFI.js} +3 -3
  39. package/dist/{git-hook-YZNZGAAP.js → git-hook-Y7Y262F7.js} +2 -2
  40. package/dist/{init-2V34DVRQ.js → init-427QR5AS.js} +10 -9
  41. package/dist/{mcp-T2GRTEX5.js → mcp-DWNWRGJW.js} +196 -21
  42. package/dist/memory-XBZCYQLW.js +393 -0
  43. package/dist/{open-2QSZKKUG.js → open-LJMOB6U2.js} +262 -69
  44. package/dist/{program-V2USHGXN.js → program-DCP526LU.js} +63 -32
  45. package/dist/{review-R3R2J5BJ.js → review-VEJBPACX.js} +14 -11
  46. package/dist/{rules-4FXJUCXB.js → rules-M4DLLXBL.js} +4 -4
  47. package/dist/scan-JVKCVHPD.js +939 -0
  48. package/dist/{security-PI4LXCOB.js → security-JTJN3E7K.js} +15 -11
  49. package/dist/{staged-check-HHIBKKMR.js → staged-check-PQSLGQBW.js} +15 -13
  50. package/dist/{status-6M73WX5I.js → status-PO27BSOZ.js} +10 -4
  51. package/dist/{sync-3ZNJDSSU.js → sync-GVO2VHPI.js} +14 -12
  52. package/dist/{watch-USN5CQIV.js → watch-TEHFO47V.js} +14 -12
  53. package/dist/web/assets/{Editor-BI56-HX5.js → Editor-Bnilgch0.js} +1 -1
  54. package/dist/web/assets/{abnfDiagram-O67JEVCF-y_9px2ob.js → abnfDiagram-O67JEVCF-DW9LWr8f.js} +1 -1
  55. package/dist/web/assets/architecture-7GRP2DOG-BWDZV2yn.js +1 -0
  56. package/dist/web/assets/{architectureDiagram-NJMV4G6O-lB6fcVzU.js → architectureDiagram-NJMV4G6O-0eKeQRhj.js} +1 -1
  57. package/dist/web/assets/{blockDiagram-BEXU5L5S-DcM5pEjM.js → blockDiagram-BEXU5L5S-D5i3-ogI.js} +1 -1
  58. package/dist/web/assets/{c4Diagram-YGBWAQC7-CJgqxTY2.js → c4Diagram-YGBWAQC7-BAWQZrEu.js} +1 -1
  59. package/dist/web/assets/channel-Bkh05NKd.js +1 -0
  60. package/dist/web/assets/{chunk-3FUC2YCW-B8l_VuWR.js → chunk-3FUC2YCW-fKRGOZF3.js} +1 -1
  61. package/dist/web/assets/{chunk-5DYCD2WN-CZecgNhf.js → chunk-5DYCD2WN-C5jOlx42.js} +1 -1
  62. package/dist/web/assets/{chunk-742MDFTN-BeZ7P5jB.js → chunk-742MDFTN-urY4RP_A.js} +1 -1
  63. package/dist/web/assets/{chunk-7INBJB4K-IKy0pr5m.js → chunk-7INBJB4K-nk48sKqZ.js} +1 -1
  64. package/dist/web/assets/{chunk-7M6MHVWA-Dg_QqJEX.js → chunk-7M6MHVWA-BfBD9X0F.js} +1 -1
  65. package/dist/web/assets/{chunk-7PRAP22T-DqQNS8oq.js → chunk-7PRAP22T-jYf59QlI.js} +1 -1
  66. package/dist/web/assets/{chunk-GTNCS2PH-x4sM51nJ.js → chunk-GTNCS2PH-BOCxryKf.js} +1 -1
  67. package/dist/web/assets/{chunk-GWA4HPMP-CE5GcwVN.js → chunk-GWA4HPMP-CFpaW0ru.js} +1 -1
  68. package/dist/web/assets/{chunk-MBY4JIJT-DA_D1hjF.js → chunk-MBY4JIJT-BIB8HctN.js} +1 -1
  69. package/dist/web/assets/{chunk-NETBCI7D-CeJ3XBZ2.js → chunk-NETBCI7D-CpUUK6rh.js} +1 -1
  70. package/dist/web/assets/{chunk-O7XYJQB3-BH2gESvO.js → chunk-O7XYJQB3-BNx_V4Oi.js} +1 -1
  71. package/dist/web/assets/{chunk-UA2S7LBM-BWr0xCux.js → chunk-UA2S7LBM-D3tMiIOY.js} +1 -1
  72. package/dist/web/assets/{chunk-WEXAMYUT-B4QwUZPM.js → chunk-WEXAMYUT-CC-3C32l.js} +1 -1
  73. package/dist/web/assets/{chunk-Z7XXMR3K-BxcA-y5B.js → chunk-Z7XXMR3K-BzhxKQUc.js} +1 -1
  74. package/dist/web/assets/{chunk-ZIGJFQKS-BZYvP63G.js → chunk-ZIGJFQKS-D4tBHzxg.js} +1 -1
  75. package/dist/web/assets/{classDiagram-v2-NBCMYWYE-BS3P_NXb.js → classDiagram-v2-NBCMYWYE-BbtyanXR.js} +1 -1
  76. package/dist/web/assets/{cynefin-OW5HDTMX-BALD4Pve.js → cynefin-OW5HDTMX-BIArb8W1.js} +1 -1
  77. package/dist/web/assets/{cynefinDiagram-VND7K2PF-BM2y4W6d.js → cynefinDiagram-VND7K2PF-BrAcfkXL.js} +1 -1
  78. package/dist/web/assets/{dagre-6A5THRUB-PTfOrIdF.js → dagre-6A5THRUB-BcHXRl4s.js} +1 -1
  79. package/dist/web/assets/{diagram-22UHCM2B-CpXEHhUl.js → diagram-22UHCM2B-Dx9tSCdE.js} +1 -1
  80. package/dist/web/assets/{diagram-3UASUU5V-DPHfXzr6.js → diagram-3UASUU5V-CLLDP9yH.js} +1 -1
  81. package/dist/web/assets/{diagram-ATOU4E4O-DMBNXvAC.js → diagram-ATOU4E4O-B46H9iPt.js} +1 -1
  82. package/dist/web/assets/{diagram-CDSNMT55-CDLe1M7G.js → diagram-CDSNMT55-CYtvB6aG.js} +1 -1
  83. package/dist/web/assets/{diagram-MLGK6HIB-D6L8oE7j.js → diagram-MLGK6HIB-Dar7ktoX.js} +1 -1
  84. package/dist/web/assets/{diagram-MPIPVDR6-CP-S7rrQ.js → diagram-MPIPVDR6-C-DljnYN.js} +1 -1
  85. package/dist/web/assets/{ebnfDiagram-ZINNZB2B-CkUc63TK.js → ebnfDiagram-ZINNZB2B-Dv_3jWnh.js} +1 -1
  86. package/dist/web/assets/{elk-276RUBZZ-C1CAJLH_.js → elk-276RUBZZ-DnHciE9k.js} +1 -1
  87. package/dist/web/assets/{erDiagram-OPXOYQCR-DXBnaTHm.js → erDiagram-OPXOYQCR-Bn46KrHz.js} +1 -1
  88. package/dist/web/assets/eventmodeling-NTZA5JFV-DYwoptMd.js +1 -0
  89. package/dist/web/assets/flowDiagram-KWPJA3E3-y4rS0gIB.js +1 -0
  90. package/dist/web/assets/{ganttDiagram-FUAMR5RP-BVm8SiGg.js → ganttDiagram-FUAMR5RP-DOjjJtqz.js} +1 -1
  91. package/dist/web/assets/{gitGraph-4MIJSDKK-Dm7xBrW0.js → gitGraph-4MIJSDKK-BIoMXJRQ.js} +1 -1
  92. package/dist/web/assets/{gitGraphDiagram-X574FWY7-D_PIKKhx.js → gitGraphDiagram-X574FWY7-BsJ01R0v.js} +1 -1
  93. package/dist/web/assets/{index-CeOIBpu2.js → index-Cen0jOXj.js} +5 -4
  94. package/dist/web/assets/{index-DKE3ojTe.css → index-WwRY6n68.css} +1 -1
  95. package/dist/web/assets/{info-A6RAGUB7-CLGrx8pn.js → info-A6RAGUB7-BmlXzRWj.js} +1 -1
  96. package/dist/web/assets/{infoDiagram-VRGFBTTK-BlBjaKm7.js → infoDiagram-VRGFBTTK-CYPJPmBP.js} +1 -1
  97. package/dist/web/assets/{ishikawaDiagram-OU5B5YK6-Kxe7GGS3.js → ishikawaDiagram-OU5B5YK6-YN96geky.js} +1 -1
  98. package/dist/web/assets/{journeyDiagram-ZHPQQLJL-wCMoHK4M.js → journeyDiagram-ZHPQQLJL-20juojKY.js} +1 -1
  99. package/dist/web/assets/{kanban-definition-PNTS6WVX-BxdCz9Ko.js → kanban-definition-PNTS6WVX-BhCKIEbP.js} +1 -1
  100. package/dist/web/assets/{line-Zf11FnWF.js → line-BERsMFG6.js} +1 -1
  101. package/dist/web/assets/{mermaid-parser.core-BvFFklev.js → mermaid-parser.core-Bis_wTB-.js} +3 -3
  102. package/dist/web/assets/{mermaid.core-Cd_Ssti9.js → mermaid.core-DMsM3ZX7.js} +4 -4
  103. package/dist/web/assets/{mindmap-definition-NLK3R4M7-BGPQl-GY.js → mindmap-definition-NLK3R4M7-BkcGdOfv.js} +1 -1
  104. package/dist/web/assets/{packet-AYTQ26CC-6lI3cEX_.js → packet-AYTQ26CC-Bh30-JJX.js} +1 -1
  105. package/dist/web/assets/{pegDiagram-GJSIUBJH-BPIjlSAj.js → pegDiagram-GJSIUBJH-DxnIo9RF.js} +1 -1
  106. package/dist/web/assets/{pie-WAS4IAKB-D6Dzj3Ja.js → pie-WAS4IAKB-ChaTr-GD.js} +1 -1
  107. package/dist/web/assets/{pieDiagram-5QR66LMP-DtA-vZmv.js → pieDiagram-5QR66LMP-CzAp78FM.js} +1 -1
  108. package/dist/web/assets/{quadrantDiagram-O4NWA36T-EettyqQn.js → quadrantDiagram-O4NWA36T-BDSE-cfY.js} +1 -1
  109. package/dist/web/assets/{radar-RG4KPBEZ-xmRc3Dl8.js → radar-RG4KPBEZ-DgJ-o_dq.js} +1 -1
  110. package/dist/web/assets/{railroad-74A4TZTK-BLB7dhOn.js → railroad-74A4TZTK-C8MmcBKz.js} +1 -1
  111. package/dist/web/assets/railroad-abnf-HS5TGJTU-CTLXjcdP.js +1 -0
  112. package/dist/web/assets/railroad-ebnf-LZEXJU2U-ggWsTTL0.js +1 -0
  113. package/dist/web/assets/railroad-peg-WCYAUIDC-D1mCX705.js +1 -0
  114. package/dist/web/assets/{railroadDiagram-XR7U4H2S-PBw4We6b.js → railroadDiagram-XR7U4H2S-DfbcWRNh.js} +1 -1
  115. package/dist/web/assets/{requirementDiagram-PLB6GJNP-DjQpaUFA.js → requirementDiagram-PLB6GJNP-DM3nksxs.js} +1 -1
  116. package/dist/web/assets/{sankeyDiagram-IPEJSGJF-CBW-_AoF.js → sankeyDiagram-IPEJSGJF-DthUpiiC.js} +1 -1
  117. package/dist/web/assets/{sequenceDiagram-PO4LG4MO-DEoPYqY9.js → sequenceDiagram-PO4LG4MO-DLA3nuvA.js} +1 -1
  118. package/dist/web/assets/{stateDiagram-v2-GCMORJYK-UZq5rPud.js → stateDiagram-v2-GCMORJYK-B0X6ihBW.js} +1 -1
  119. package/dist/web/assets/{swimlanes-2SLR337P-CACfuXJR.js → swimlanes-2SLR337P-B4qWe6vE.js} +1 -1
  120. package/dist/web/assets/swimlanesDiagram-TC7HE7FX-ClIk3e7S.js +8 -0
  121. package/dist/web/assets/{timeline-definition-EJHVYXUP-CYJl7mP-.js → timeline-definition-EJHVYXUP-F-cDzYrj.js} +1 -1
  122. package/dist/web/assets/{treeView-Q6P3EWNA-DtZ5nkPQ.js → treeView-Q6P3EWNA-BaI30nLZ.js} +1 -1
  123. package/dist/web/assets/{treemap-WGGIJYW6-m49GDM-M.js → treemap-WGGIJYW6-D2jGGoMl.js} +1 -1
  124. package/dist/web/assets/{usecaseDiagram-POWQR4AR-C2Vg20UK.js → usecaseDiagram-POWQR4AR-B9qoAQCy.js} +1 -1
  125. package/dist/web/assets/{vennDiagram-UO4OBE2U-C07v5OGI.js → vennDiagram-UO4OBE2U-DGVNOaKO.js} +1 -1
  126. package/dist/web/assets/{wardley-WFR3VGLG-C9Z7EZtz.js → wardley-WFR3VGLG-DbKqhNAh.js} +1 -1
  127. package/dist/web/assets/{wardleyDiagram-VNRHLVJA-DdA8CcSC.js → wardleyDiagram-VNRHLVJA-BgZ1gprw.js} +1 -1
  128. package/dist/web/assets/{xychartDiagram-PMCCYNJV-C9EZw07b.js → xychartDiagram-PMCCYNJV-D73RYid1.js} +1 -1
  129. package/dist/web/index.html +2 -2
  130. package/package.json +3 -1
  131. package/dist/chunk-CXUTYCZX.js +0 -238
  132. package/dist/chunk-LMOFZNTQ.js +0 -379
  133. package/dist/web/assets/architecture-7GRP2DOG-DyxkwYiW.js +0 -1
  134. package/dist/web/assets/channel-BvDD7AMO.js +0 -1
  135. package/dist/web/assets/eventmodeling-NTZA5JFV-cLIzvgDp.js +0 -1
  136. package/dist/web/assets/flowDiagram-KWPJA3E3-D7Zq63VQ.js +0 -1
  137. package/dist/web/assets/railroad-abnf-HS5TGJTU-DBFym6g8.js +0 -1
  138. package/dist/web/assets/railroad-ebnf-LZEXJU2U-CkG28ig8.js +0 -1
  139. package/dist/web/assets/railroad-peg-WCYAUIDC-Df_Ifaad.js +0 -1
  140. package/dist/web/assets/swimlanesDiagram-TC7HE7FX-CDSrqdgx.js +0 -8
@@ -0,0 +1,595 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ whichExecutable
4
+ } from "./chunk-UMWX4UTK.js";
5
+ import {
6
+ Coverage,
7
+ Finding,
8
+ SECRETS_ENGINE,
9
+ ScanResult,
10
+ dedupeFindings,
11
+ packageFingerprint
12
+ } from "./chunk-Y6SDMZET.js";
13
+ import {
14
+ loadScanFromDir
15
+ } from "./chunk-44EGCEB2.js";
16
+ import {
17
+ walkProject
18
+ } from "./chunk-ELKFRTZT.js";
19
+ import {
20
+ athenaDir
21
+ } from "./chunk-2DY4PUR4.js";
22
+ import {
23
+ readTextIfExists,
24
+ writeFileAtomic
25
+ } from "./chunk-IQQBAWSM.js";
26
+
27
+ // src/core/findings/store.ts
28
+ import path2 from "path";
29
+
30
+ // src/core/scanners/dependencies.ts
31
+ import { execFile } from "child_process";
32
+ import { promises as fs } from "fs";
33
+ import path from "path";
34
+ var AuditToolError = class extends Error {
35
+ };
36
+ var asRecord = (v) => v && typeof v === "object" && !Array.isArray(v) ? v : {};
37
+ var isObject = (v) => Boolean(v) && typeof v === "object" && !Array.isArray(v);
38
+ var strings = (v) => Array.isArray(v) ? v.filter((x) => typeof x === "string" && x.trim() !== "") : [];
39
+ var cwes = (v) => strings(v).filter((c) => /^CWE-\d+$/.test(c));
40
+ var uniq = (xs) => [...new Set(xs.filter((x) => Boolean(x)))];
41
+ var GHSA = /GHSA-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}/i;
42
+ var sev = (s) => {
43
+ const v = String(s ?? "").toLowerCase();
44
+ if (v === "critical") return "critical";
45
+ if (v === "high") return "high";
46
+ if (v === "moderate" || v === "medium") return "moderate";
47
+ if (v === "low" || v === "info") return "low";
48
+ return "unrated";
49
+ };
50
+ function parseJsonObject(stdout, tool) {
51
+ const text = stdout.trim();
52
+ if (!text) throw new AuditToolError(`${tool} produced no output`);
53
+ const json = JSON.parse(text);
54
+ if (!isObject(json)) throw new AuditToolError(`${tool} produced unexpected output`);
55
+ return json;
56
+ }
57
+ function throwIfToolError(json, tool) {
58
+ if (json.error === void 0) return;
59
+ const e = asRecord(json.error);
60
+ const parts = [e.code, e.summary ?? e.message].filter((x) => typeof x === "string" && x.trim());
61
+ const text = parts.length ? parts.join(": ") : typeof json.error === "string" ? json.error : "unknown error";
62
+ throw new AuditToolError(`${tool} reported an error: ${text.split("\n")[0].slice(0, 200)}`);
63
+ }
64
+ function parseAdvisories(advisories) {
65
+ const out = [];
66
+ for (const [key, raw] of Object.entries(advisories)) {
67
+ const a = asRecord(raw);
68
+ const versions = [...new Set((Array.isArray(a.findings) ? a.findings : []).map((f) => asRecord(f).version).filter((v) => typeof v === "string"))];
69
+ const patched = typeof a.patched_versions === "string" ? a.patched_versions.trim() : "";
70
+ const id = typeof a.github_advisory_id === "string" ? a.github_advisory_id : Array.isArray(a.cves) && typeof a.cves[0] === "string" ? a.cves[0] : String(a.id ?? key);
71
+ const url = typeof a.url === "string" ? a.url : void 0;
72
+ const title = String(a.title ?? `Vulnerable dependency: ${String(a.module_name ?? key)}`);
73
+ const range = typeof a.vulnerable_versions === "string" ? a.vulnerable_versions : void 0;
74
+ out.push({
75
+ package: String(a.module_name ?? "unknown"),
76
+ severity: sev(a.severity),
77
+ title,
78
+ id,
79
+ url,
80
+ vulnerableRange: range !== void 0 ? range + (versions.length ? ` (installed ${versions.join(", ")})` : "") : void 0,
81
+ fixAvailable: Boolean(patched) && patched !== "<0.0.0",
82
+ ...versions.length === 1 ? { version: versions[0] } : {},
83
+ advisories: [{ id, ids: uniq([id, typeof a.github_advisory_id === "string" ? a.github_advisory_id : void 0, ...strings(a.cves), a.id !== void 0 ? String(a.id) : void 0]), title, severity: sev(a.severity), url, cwe: cwes(a.cwe), range, ...patched && patched !== "<0.0.0" ? { fixedIn: patched } : {} }]
84
+ });
85
+ }
86
+ return out;
87
+ }
88
+ function parseNpmAudit(stdout) {
89
+ const json = parseJsonObject(stdout, "npm audit");
90
+ throwIfToolError(json, "npm audit");
91
+ if (!isObject(json.vulnerabilities)) {
92
+ if (isObject(json.advisories)) return parseAdvisories(json.advisories);
93
+ throw new AuditToolError('npm audit output has no "vulnerabilities" report');
94
+ }
95
+ const out = [];
96
+ for (const [name, raw] of Object.entries(json.vulnerabilities)) {
97
+ const v = asRecord(raw);
98
+ const via = Array.isArray(v.via) ? v.via : [];
99
+ const objects = via.filter(isObject);
100
+ const advisory = objects[0];
101
+ const viaNames = via.filter((x) => typeof x === "string");
102
+ const fix = asRecord(v.fixAvailable);
103
+ out.push({
104
+ package: name,
105
+ severity: sev(v.severity),
106
+ title: String(advisory?.title ?? (viaNames.length ? `Depends on vulnerable ${viaNames.join(", ")}` : `Vulnerable dependency: ${name}`)),
107
+ id: advisory?.source !== void 0 ? String(advisory.source) : void 0,
108
+ url: typeof advisory?.url === "string" ? advisory.url : void 0,
109
+ vulnerableRange: typeof v.range === "string" ? v.range : void 0,
110
+ fixAvailable: Boolean(v.fixAvailable),
111
+ via: viaNames,
112
+ advisories: objects.filter((a) => typeof a.name !== "string" || a.name === name).map((a) => {
113
+ const url = typeof a.url === "string" ? a.url : void 0;
114
+ const ghsa = url ? GHSA.exec(url)?.[0] : void 0;
115
+ return {
116
+ id: ghsa ?? (a.source !== void 0 ? String(a.source) : void 0),
117
+ ids: uniq([ghsa, a.source !== void 0 ? String(a.source) : void 0]),
118
+ title: String(a.title ?? `Vulnerable dependency: ${name}`),
119
+ severity: sev(a.severity ?? v.severity),
120
+ url,
121
+ cwe: cwes(a.cwe),
122
+ range: typeof a.range === "string" ? a.range : typeof v.range === "string" ? v.range : void 0,
123
+ ...fix.name === name && typeof fix.version === "string" ? { fixedIn: fix.version } : {}
124
+ };
125
+ })
126
+ });
127
+ }
128
+ return out;
129
+ }
130
+ function parsePnpmAudit(stdout) {
131
+ const json = parseJsonObject(stdout, "pnpm audit");
132
+ throwIfToolError(json, "pnpm audit");
133
+ if (!isObject(json.advisories)) throw new AuditToolError('pnpm audit output has no "advisories" report');
134
+ return parseAdvisories(json.advisories);
135
+ }
136
+ function parsePipAudit(stdout) {
137
+ const text = stdout.trim();
138
+ if (!text) throw new AuditToolError("pip-audit produced no output");
139
+ const json = JSON.parse(text);
140
+ const deps = isObject(json) && Array.isArray(json.dependencies) ? json.dependencies : Array.isArray(json) ? json : null;
141
+ if (!deps) throw new AuditToolError('pip-audit output has no "dependencies" list');
142
+ const out = [];
143
+ for (const raw of deps) {
144
+ const d = asRecord(raw);
145
+ for (const vRaw of Array.isArray(d.vulns) ? d.vulns : []) {
146
+ const v = asRecord(vRaw);
147
+ const aliases = strings(v.aliases);
148
+ const id = typeof v.id === "string" ? v.id : void 0;
149
+ const fixes = strings(v.fix_versions);
150
+ const title = String(v.description ?? id ?? "Known vulnerability").split("\n")[0].slice(0, 300);
151
+ const url = id ? `https://osv.dev/vulnerability/${id}` : void 0;
152
+ out.push({
153
+ package: String(d.name ?? "unknown"),
154
+ severity: "unrated",
155
+ // pip-audit does not report severity
156
+ title,
157
+ id: id ?? aliases[0],
158
+ url,
159
+ vulnerableRange: typeof d.version === "string" ? `installed ${d.version}` : void 0,
160
+ fixAvailable: fixes.length > 0,
161
+ ...typeof d.version === "string" ? { version: d.version } : {},
162
+ advisories: [{ id: id ?? aliases[0], ids: uniq([id, ...aliases]), title, severity: "unrated", url, cwe: [], ...fixes[0] ? { fixedIn: fixes[0] } : {} }]
163
+ });
164
+ }
165
+ }
166
+ return out;
167
+ }
168
+ function splitJsonStream(text) {
169
+ const out = [];
170
+ let depth = 0;
171
+ let inString = false;
172
+ let escaped = false;
173
+ let start = -1;
174
+ for (let i = 0; i < text.length; i++) {
175
+ const ch = text[i];
176
+ if (inString) {
177
+ if (escaped) escaped = false;
178
+ else if (ch === "\\") escaped = true;
179
+ else if (ch === '"') inString = false;
180
+ continue;
181
+ }
182
+ if (ch === '"') {
183
+ if (depth > 0) inString = true;
184
+ } else if (ch === "{") {
185
+ if (depth === 0) start = i;
186
+ depth++;
187
+ } else if (ch === "}" && depth > 0) {
188
+ depth--;
189
+ if (depth === 0) {
190
+ out.push(JSON.parse(text.slice(start, i + 1)));
191
+ start = -1;
192
+ }
193
+ }
194
+ }
195
+ if (depth !== 0) throw new AuditToolError("truncated JSON output");
196
+ return out;
197
+ }
198
+ var REACH_RANK = { required: 0, imported: 1, called: 2 };
199
+ function parseGovulncheck(stdout) {
200
+ const messages = splitJsonStream(stdout).map(asRecord);
201
+ if (!messages.some((m) => isObject(m.config))) throw new AuditToolError("govulncheck produced no JSON report");
202
+ const osvs = /* @__PURE__ */ new Map();
203
+ for (const m of messages) {
204
+ const osv = asRecord(m.osv);
205
+ if (typeof osv.id === "string") osvs.set(osv.id, osv);
206
+ }
207
+ const found = /* @__PURE__ */ new Map();
208
+ for (const m of messages) {
209
+ const f = asRecord(m.finding);
210
+ if (typeof f.osv !== "string") continue;
211
+ const trace = (Array.isArray(f.trace) ? f.trace : []).map(asRecord);
212
+ const reach = trace.some((t) => typeof t.function === "string") ? "called" : trace.some((t) => typeof t.package === "string") ? "imported" : "required";
213
+ const first2 = trace[0] ?? {};
214
+ const prev = found.get(f.osv);
215
+ if (prev && REACH_RANK[prev.reach] >= REACH_RANK[reach]) continue;
216
+ found.set(f.osv, {
217
+ module: typeof first2.module === "string" ? first2.module : prev?.module,
218
+ version: typeof first2.version === "string" ? first2.version : prev?.version,
219
+ fixed: typeof f.fixed_version === "string" ? f.fixed_version : prev?.fixed,
220
+ reach
221
+ });
222
+ }
223
+ const label = { called: "called by your code", imported: "package imported, vulnerable symbol not called", required: "module required, package not imported" };
224
+ return [...found].map(([id, f]) => {
225
+ const osv = osvs.get(id) ?? {};
226
+ const affected = Array.isArray(osv.affected) ? asRecord(osv.affected[0]) : {};
227
+ const title = `${String(osv.summary ?? id)} (${label[f.reach]})`;
228
+ const url = `https://pkg.go.dev/vuln/${id}`;
229
+ return {
230
+ package: f.module ?? String(asRecord(affected.package).name ?? "unknown"),
231
+ severity: "unrated",
232
+ // the Go vulnerability database does not assign severities
233
+ title,
234
+ id,
235
+ url,
236
+ vulnerableRange: f.version ? `installed ${f.version}` : void 0,
237
+ fixAvailable: Boolean(f.fixed),
238
+ ...f.version ? { version: f.version } : {},
239
+ advisories: [{ id, ids: uniq([id, ...strings(osv.aliases)]), title, severity: "unrated", url, cwe: [], ...f.fixed ? { fixedIn: f.fixed } : {} }]
240
+ };
241
+ });
242
+ }
243
+ function parseCargoAudit(stdout) {
244
+ const json = parseJsonObject(stdout, "cargo audit");
245
+ if (!isObject(json.vulnerabilities)) throw new AuditToolError('cargo audit output has no "vulnerabilities" report');
246
+ const list = Array.isArray(json.vulnerabilities.list) ? json.vulnerabilities.list : [];
247
+ return list.map((raw) => {
248
+ const v = asRecord(raw);
249
+ const advisory = asRecord(v.advisory);
250
+ const pkg = asRecord(v.package);
251
+ const id = typeof advisory.id === "string" ? advisory.id : void 0;
252
+ const title = String(advisory.title ?? "Known vulnerability");
253
+ const url = typeof advisory.url === "string" ? advisory.url : void 0;
254
+ const severity = sev(asRecord(advisory.cvss).severity ?? advisory.severity);
255
+ const patched = strings(asRecord(v.versions).patched);
256
+ return {
257
+ package: String(pkg.name ?? "unknown"),
258
+ severity,
259
+ title,
260
+ id,
261
+ url,
262
+ ...typeof pkg.version === "string" ? { version: pkg.version } : {},
263
+ advisories: [{ id, ids: uniq([id, ...strings(advisory.aliases)]), title, severity, url, cwe: [], ...patched[0] ? { fixedIn: patched.join(", ") } : {} }]
264
+ };
265
+ });
266
+ }
267
+ function parseComposerAudit(stdout) {
268
+ const json = parseJsonObject(stdout, "composer audit");
269
+ if (json.advisories === void 0) throw new AuditToolError('composer audit output has no "advisories" report');
270
+ const advisories = asRecord(json.advisories);
271
+ const out = [];
272
+ for (const [pkg, raw] of Object.entries(advisories)) {
273
+ for (const aRaw of Array.isArray(raw) ? raw : Object.values(asRecord(raw))) {
274
+ const a = asRecord(aRaw);
275
+ const id = typeof a.cve === "string" ? a.cve : typeof a.advisoryId === "string" ? a.advisoryId : void 0;
276
+ const title = String(a.title ?? "Known vulnerability");
277
+ const url = typeof a.link === "string" ? a.link : void 0;
278
+ const range = typeof a.affectedVersions === "string" ? a.affectedVersions : void 0;
279
+ out.push({
280
+ package: pkg,
281
+ severity: sev(a.severity),
282
+ title,
283
+ id,
284
+ url,
285
+ vulnerableRange: range,
286
+ advisories: [{ id, ids: uniq([id, typeof a.advisoryId === "string" ? a.advisoryId : void 0, typeof a.cve === "string" ? a.cve : void 0]), title, severity: sev(a.severity), url, cwe: [], range }]
287
+ });
288
+ }
289
+ }
290
+ return out;
291
+ }
292
+ var MAX_PIP_TARGETS = 10;
293
+ var REQUIREMENTS_FILE = /(^|\/)requirements[\w.-]*\.txt$/i;
294
+ var PYTHON_MANIFEST = /(^|\/)(pyproject\.toml|setup\.py|setup\.cfg|Pipfile)$/;
295
+ async function planPipAudit(ctx) {
296
+ const common = ["--format", "json", "--progress-spinner", "off"];
297
+ const reqs = ctx.files.filter((f) => REQUIREMENTS_FILE.test(f)).sort();
298
+ const reqDirs = new Set(reqs.map((f) => path.posix.dirname(f)));
299
+ const out = reqs.map((f) => ({ target: f, args: ["-r", f, ...common] }));
300
+ for (const f of ctx.files.filter((x) => x === "pyproject.toml" || x.endsWith("/pyproject.toml")).sort()) {
301
+ const dir = path.posix.dirname(f);
302
+ if (reqDirs.has(dir)) continue;
303
+ const text = await fs.readFile(path.join(ctx.root, f), "utf8").catch(() => "");
304
+ if (!/^\s*\[project\]\s*$/m.test(text)) continue;
305
+ out.push({ target: f, args: [...common, dir === "." ? "." : `./${dir}`] });
306
+ }
307
+ if (!out.length) return "no requirements file to audit (pip-audit needs requirements*.txt or a PEP 621 pyproject.toml; the active Python environment is never audited)";
308
+ return out.slice(0, MAX_PIP_TARGETS);
309
+ }
310
+ var single = (args) => async () => [{ args }];
311
+ var first = (...files) => (has) => files.find(has);
312
+ var RUNNERS = [
313
+ {
314
+ tool: "npm audit",
315
+ engine: "npm-audit",
316
+ ecosystem: "npm",
317
+ command: "npm",
318
+ manifest: first("package-lock.json", "npm-shrinkwrap.json"),
319
+ applies: (_m, has) => has("package-lock.json") || has("npm-shrinkwrap.json"),
320
+ plan: single(["audit", "--json", "--audit-level=low"]),
321
+ parse: parseNpmAudit
322
+ },
323
+ {
324
+ tool: "pnpm audit",
325
+ engine: "pnpm-audit",
326
+ ecosystem: "npm",
327
+ command: "pnpm",
328
+ manifest: first("pnpm-lock.yaml"),
329
+ applies: (_m, has) => has("pnpm-lock.yaml"),
330
+ plan: single(["audit", "--json"]),
331
+ parse: parsePnpmAudit
332
+ },
333
+ {
334
+ tool: "pip-audit",
335
+ engine: "pip-audit",
336
+ ecosystem: "python",
337
+ command: "pip-audit",
338
+ applies: (m, _has, ctx) => m.manifests.some((x) => x.ecosystem === "python") || ctx.files.some((f) => REQUIREMENTS_FILE.test(f)),
339
+ plan: planPipAudit,
340
+ parse: parsePipAudit
341
+ },
342
+ {
343
+ tool: "govulncheck",
344
+ engine: "govulncheck",
345
+ ecosystem: "go",
346
+ command: "govulncheck",
347
+ manifest: first("go.mod"),
348
+ applies: (_m, has) => has("go.mod"),
349
+ plan: single(["-format=json", "./..."]),
350
+ parse: parseGovulncheck
351
+ },
352
+ {
353
+ tool: "cargo audit",
354
+ engine: "cargo-audit",
355
+ ecosystem: "cargo",
356
+ command: "cargo",
357
+ manifest: first("Cargo.lock"),
358
+ applies: (_m, has) => has("Cargo.lock"),
359
+ plan: single(["audit", "--json"]),
360
+ parse: parseCargoAudit
361
+ },
362
+ {
363
+ tool: "composer audit",
364
+ engine: "composer-audit",
365
+ ecosystem: "composer",
366
+ command: "composer",
367
+ manifest: first("composer.lock"),
368
+ applies: (_m, has) => has("composer.lock"),
369
+ plan: single(["audit", "--format=json"]),
370
+ parse: parseComposerAudit
371
+ }
372
+ ];
373
+ var engineIdForTool = (tool) => RUNNERS.find((r) => r.tool === tool)?.engine ?? tool.trim().toLowerCase().replace(/\s+/g, "-");
374
+ function run(command, args, cwd, timeoutMs, signal) {
375
+ return new Promise((resolve) => {
376
+ execFile(command, args, { cwd, timeout: timeoutMs, maxBuffer: 32 * 1024 * 1024, windowsHide: true, signal, env: { ...process.env, NO_COLOR: "1", npm_config_color: "false" } }, (error, stdout, stderr) => {
377
+ const timedOut = Boolean(error && (error.code === "ETIMEDOUT" || error.killed && !signal?.aborted));
378
+ resolve({ stdout: String(stdout ?? ""), stderr: String(stderr ?? ""), timedOut, error: error ?? null });
379
+ });
380
+ });
381
+ }
382
+ async function runTool(runner, inv, root, timeoutMs, manifest, signal) {
383
+ const t = Date.now();
384
+ const base = { tool: runner.tool, engine: runner.engine, ecosystem: runner.ecosystem, ...inv.target ? { target: inv.target } : {}, ...manifest ? { manifest } : {}, network: true };
385
+ const r = await run(runner.command, inv.args, root, timeoutMs, signal);
386
+ signal?.throwIfAborted();
387
+ if (r.timedOut) return { ...base, status: "timeout", message: `Timed out after ${timeoutMs / 1e3}s`, durationMs: Date.now() - t, findings: [] };
388
+ try {
389
+ const findings = runner.parse(r.stdout, r.stderr);
390
+ return { ...base, status: "ok", durationMs: Date.now() - t, findings };
391
+ } catch (err) {
392
+ const reason = err instanceof AuditToolError ? err.message : `Could not parse output: ${r.stderr.trim().split("\n")[0] || r.error?.message || err.message || "unreadable output"}`;
393
+ return { ...base, status: "failed", message: reason.slice(0, 300), durationMs: Date.now() - t, findings: [] };
394
+ }
395
+ }
396
+ async function runAudits(ctx, opts = {}) {
397
+ const timeoutMs = opts.timeoutMs ?? 12e4;
398
+ const walk = opts.walk ?? (await walkProject(ctx.root, { config: ctx.config, signal: ctx.signal })).files;
399
+ const plan = { root: ctx.root, files: walk.map((f) => f.path) };
400
+ const model = opts.model ?? { manifests: plan.files.filter((f) => PYTHON_MANIFEST.test(f)).map((p) => ({ path: p, ecosystem: "python" })) };
401
+ const files = new Set(model.manifests.map((m) => m.path));
402
+ for (const pm of opts.model?.packageManagers ?? []) for (const e of pm.provenance.evidence) files.add(e.file);
403
+ for (const f of plan.files) files.add(f);
404
+ const has = (file) => files.has(file);
405
+ const runs = [];
406
+ for (const runner of RUNNERS) {
407
+ if (!runner.applies(model, has, plan)) continue;
408
+ ctx.signal?.throwIfAborted();
409
+ const t = Date.now();
410
+ const manifest = runner.manifest?.(has);
411
+ const base = { tool: runner.tool, engine: runner.engine, ecosystem: runner.ecosystem, ...manifest ? { manifest } : {}, findings: [] };
412
+ if (ctx.offline) {
413
+ runs.push({ ...base, status: "skipped", message: `offline: ${runner.tool} queries an online advisory database`, network: false, durationMs: 0 });
414
+ continue;
415
+ }
416
+ opts.onTool?.(runner.tool);
417
+ const bin = await whichExecutable(runner.command);
418
+ if (!bin) {
419
+ runs.push({ ...base, status: "unavailable", message: `${runner.command} is not installed or not on PATH`, network: false, durationMs: Date.now() - t });
420
+ continue;
421
+ }
422
+ const invocations = await runner.plan(plan);
423
+ if (typeof invocations === "string") {
424
+ runs.push({ ...base, status: "unavailable", message: invocations, network: false, durationMs: Date.now() - t });
425
+ continue;
426
+ }
427
+ for (const inv of invocations) {
428
+ ctx.signal?.throwIfAborted();
429
+ runs.push(await runTool(runner, inv, ctx.root, timeoutMs, inv.target ?? manifest, ctx.signal));
430
+ }
431
+ }
432
+ return runs;
433
+ }
434
+ var SEVERITY = { critical: "critical", high: "high", moderate: "medium", low: "low", unrated: "unrated" };
435
+ var mapLegacySeverity = (s) => SEVERITY[s];
436
+ function legacyVulnerability(v) {
437
+ return {
438
+ package: v.package,
439
+ severity: v.severity,
440
+ title: v.title,
441
+ ...v.id !== void 0 ? { id: v.id } : {},
442
+ ...v.url !== void 0 ? { url: v.url } : {},
443
+ ...v.vulnerableRange !== void 0 ? { vulnerableRange: v.vulnerableRange } : {},
444
+ ...v.fixAvailable !== void 0 ? { fixAvailable: v.fixAvailable } : {}
445
+ };
446
+ }
447
+ function legacyToolResult(r) {
448
+ return {
449
+ tool: r.tool,
450
+ ecosystem: r.ecosystem,
451
+ ...r.target ? { target: r.target } : {},
452
+ status: r.status === "skipped" ? "unavailable" : r.status,
453
+ ...r.message ? { message: r.message } : {},
454
+ durationMs: r.durationMs,
455
+ findings: r.findings.map(legacyVulnerability)
456
+ };
457
+ }
458
+ var DEPENDENCY_RULE = "dependency/vulnerable-package";
459
+ function auditFindings(r) {
460
+ const reported = new Set(r.findings.filter((v) => v.advisories === void 0 || v.advisories.length > 0).map((v) => v.package));
461
+ const manifest = r.target ?? r.manifest;
462
+ const out = /* @__PURE__ */ new Map();
463
+ for (const v of r.findings) {
464
+ let advisories = v.advisories;
465
+ if (advisories && !advisories.length) {
466
+ if ((v.via ?? []).some((name) => reported.has(name))) continue;
467
+ advisories = void 0;
468
+ }
469
+ for (const a of advisories ?? [{ id: v.id, ids: uniq([v.id]), title: v.title, severity: v.severity, url: v.url, cwe: [], range: v.vulnerableRange }]) {
470
+ const advisoryId = a.id ?? a.ids[0];
471
+ const fingerprint = packageFingerprint({ ruleId: DEPENDENCY_RULE, ecosystem: r.ecosystem, name: v.package, manifest, advisoryId: advisoryId ?? a.title });
472
+ if (out.has(fingerprint)) continue;
473
+ const fixedIn = a.fixedIn;
474
+ const at = v.version ? `${v.package}@${v.version}` : v.package;
475
+ out.set(fingerprint, {
476
+ fingerprint,
477
+ ruleId: DEPENDENCY_RULE,
478
+ category: "dependency",
479
+ severity: mapLegacySeverity(a.severity),
480
+ confidence: "high",
481
+ label: "FACT",
482
+ potential: false,
483
+ title: `${v.package}: ${a.title}`.slice(0, 300),
484
+ message: `${at} is affected by ${advisoryId ?? "a known vulnerability"}${a.title ? ` (${a.title})` : ""}, as reported by ${r.engine}.`.slice(0, 600),
485
+ cwe: a.cwe,
486
+ package: {
487
+ ecosystem: r.ecosystem,
488
+ name: v.package,
489
+ ...v.version ? { version: v.version } : {},
490
+ ...manifest ? { manifest } : {},
491
+ advisoryIds: a.ids,
492
+ ...a.range ? { vulnerableRange: a.range } : {},
493
+ ...fixedIn ? { fixedIn } : {},
494
+ ...v.fixAvailable !== void 0 || fixedIn ? { fixAvailable: Boolean(v.fixAvailable || fixedIn) } : {}
495
+ },
496
+ engine: { id: r.engine },
497
+ help: {
498
+ text: fixedIn ? `Upgrade ${v.package} to ${fixedIn} or later.` : v.fixAvailable ? `A fixed version of ${v.package} is available; upgrade it.` : `No fixed version was reported for ${v.package}. Check the advisory for mitigations.`,
499
+ ...a.url ? { url: a.url } : {}
500
+ }
501
+ });
502
+ }
503
+ }
504
+ return [...out.values()];
505
+ }
506
+ function auditCoverage(r) {
507
+ const target = r.target ?? r.manifest;
508
+ return {
509
+ engine: r.engine,
510
+ category: "dependency",
511
+ status: r.status,
512
+ ...r.message ? { reason: r.message } : {},
513
+ ...target ? { target } : {},
514
+ network: r.network,
515
+ durationMs: r.durationMs
516
+ };
517
+ }
518
+ async function auditDependencies(ctx, opts = {}) {
519
+ const runs = await runAudits(ctx, opts);
520
+ return { runs, findings: runs.flatMap((r) => r.status === "ok" ? auditFindings(r) : []), coverage: runs.map(auditCoverage) };
521
+ }
522
+ function dependencyScanner(opts = {}) {
523
+ return {
524
+ id: "athena-dependencies",
525
+ category: "dependency",
526
+ title: "Vulnerable dependencies",
527
+ async run(ctx) {
528
+ const { findings, coverage } = await auditDependencies(ctx, opts);
529
+ return { findings, coverage };
530
+ }
531
+ };
532
+ }
533
+
534
+ // src/core/findings/store.ts
535
+ var FINDINGS_FILE = "findings.json";
536
+ async function readFindingsFile(root) {
537
+ const raw = await readTextIfExists(path2.join(athenaDir(root), FINDINGS_FILE));
538
+ if (!raw) return null;
539
+ try {
540
+ const parsed = ScanResult.safeParse(JSON.parse(raw));
541
+ return parsed.success ? parsed.data : null;
542
+ } catch {
543
+ return null;
544
+ }
545
+ }
546
+ async function loadFindingsWithSource(root) {
547
+ const [current, legacy] = await Promise.all([readFindingsFile(root), loadScanFromDir(athenaDir(root))]);
548
+ if (current && (!legacy || Date.parse(legacy.scannedAt) <= Date.parse(current.scannedAt))) return { result: current, source: "findings" };
549
+ if (legacy) return { result: migrateLegacyScan(legacy), source: "migrated" };
550
+ return null;
551
+ }
552
+ async function loadFindings(root) {
553
+ return (await loadFindingsWithSource(root))?.result ?? null;
554
+ }
555
+ async function saveFindings(root, result) {
556
+ const valid = ScanResult.parse(result);
557
+ await writeFileAtomic(path2.join(athenaDir(root), FINDINGS_FILE), `${JSON.stringify(valid, null, 2)}
558
+ `);
559
+ }
560
+ function migrateLegacyScan(scan) {
561
+ const findings = [];
562
+ const coverage = [];
563
+ for (const t of scan.tools) {
564
+ const engine = engineIdForTool(t.tool);
565
+ const ran = t.status !== "unavailable";
566
+ if (t.status === "ok") {
567
+ for (const f of auditFindings({ engine, ecosystem: t.ecosystem, target: t.target, findings: t.findings })) findings.push(Finding.parse(f));
568
+ }
569
+ coverage.push(Coverage.parse(auditCoverage({ engine, status: t.status, message: t.message, target: t.target, network: ran, durationMs: t.durationMs })));
570
+ }
571
+ const s = scan.secrets;
572
+ const notes = [
573
+ s.count ? `migrated from security-scan.json (v1): ${s.count} potential secret(s) in ${s.files.length} file(s) (${s.files.slice(0, 10).join(", ")}${s.files.length > 10 ? ", \u2026" : ""}); v1 did not record lines, so they are not listed as findings \u2014 run the scan again for locations` : "migrated from security-scan.json (v1): no secrets matched",
574
+ ...s.skippedLongLines ? [`generic keyword patterns not applied to ${s.skippedLongLines} long line(s)`] : [],
575
+ ...s.truncated ? ["the scan stopped early after too many findings"] : []
576
+ ];
577
+ coverage.push(Coverage.parse({ engine: SECRETS_ENGINE, category: "secret", status: "ok", reason: notes.join("; "), network: false, durationMs: 0 }));
578
+ return ScanResult.parse({
579
+ schemaVersion: 1,
580
+ scannedAt: scan.scannedAt,
581
+ durationMs: scan.durationMs,
582
+ scope: { mode: "all" },
583
+ findings: dedupeFindings(findings),
584
+ coverage
585
+ });
586
+ }
587
+
588
+ export {
589
+ legacyToolResult,
590
+ auditDependencies,
591
+ dependencyScanner,
592
+ loadFindings,
593
+ saveFindings
594
+ };
595
+ //# sourceMappingURL=chunk-IDODPGTX.js.map