kirograph 0.27.1 → 0.28.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 (222) hide show
  1. package/README.md +35 -2
  2. package/dist/bin/commands/annotations.js +56 -0
  3. package/dist/bin/commands/annotations.js.map +7 -0
  4. package/dist/bin/commands/ast-rewrite.js +55 -0
  5. package/dist/bin/commands/ast-rewrite.js.map +7 -0
  6. package/dist/bin/commands/bench.js +103 -0
  7. package/dist/bin/commands/bench.js.map +7 -0
  8. package/dist/bin/commands/branch.js +133 -0
  9. package/dist/bin/commands/branch.js.map +7 -0
  10. package/dist/bin/commands/callees.js +76 -0
  11. package/dist/bin/commands/callees.js.map +7 -0
  12. package/dist/bin/commands/callers.js +76 -0
  13. package/dist/bin/commands/callers.js.map +7 -0
  14. package/dist/bin/commands/changelog.js +55 -0
  15. package/dist/bin/commands/changelog.js.map +7 -0
  16. package/dist/bin/commands/circular-deps.js +68 -0
  17. package/dist/bin/commands/circular-deps.js.map +7 -0
  18. package/dist/bin/commands/commit-context.js +55 -0
  19. package/dist/bin/commands/commit-context.js.map +7 -0
  20. package/dist/bin/commands/complexity.js +60 -0
  21. package/dist/bin/commands/complexity.js.map +7 -0
  22. package/dist/bin/commands/cost.js +146 -0
  23. package/dist/bin/commands/cost.js.map +7 -0
  24. package/dist/bin/commands/dependency-depth.js +55 -0
  25. package/dist/bin/commands/dependency-depth.js.map +7 -0
  26. package/dist/bin/commands/diff-context.js +55 -0
  27. package/dist/bin/commands/diff-context.js.map +7 -0
  28. package/dist/bin/commands/distribution.js +56 -0
  29. package/dist/bin/commands/distribution.js.map +7 -0
  30. package/dist/bin/commands/doc-coverage.js +56 -0
  31. package/dist/bin/commands/doc-coverage.js.map +7 -0
  32. package/dist/bin/commands/doctor.js +202 -0
  33. package/dist/bin/commands/doctor.js.map +7 -0
  34. package/dist/bin/commands/dsm.js +56 -0
  35. package/dist/bin/commands/dsm.js.map +7 -0
  36. package/dist/bin/commands/gini.js +55 -0
  37. package/dist/bin/commands/gini.js.map +7 -0
  38. package/dist/bin/commands/god-class.js +56 -0
  39. package/dist/bin/commands/god-class.js.map +7 -0
  40. package/dist/bin/commands/health.js +55 -0
  41. package/dist/bin/commands/health.js.map +7 -0
  42. package/dist/bin/commands/help.js +224 -45
  43. package/dist/bin/commands/help.js.map +2 -2
  44. package/dist/bin/commands/impact.js +76 -0
  45. package/dist/bin/commands/impact.js.map +7 -0
  46. package/dist/bin/commands/inheritance-depth.js +56 -0
  47. package/dist/bin/commands/inheritance-depth.js.map +7 -0
  48. package/dist/bin/commands/insert-at.js +55 -0
  49. package/dist/bin/commands/insert-at.js.map +7 -0
  50. package/dist/bin/commands/largest.js +56 -0
  51. package/dist/bin/commands/largest.js.map +7 -0
  52. package/dist/bin/commands/manifest.js +158 -0
  53. package/dist/bin/commands/manifest.js.map +7 -0
  54. package/dist/bin/commands/module-api.js +57 -0
  55. package/dist/bin/commands/module-api.js.map +7 -0
  56. package/dist/bin/commands/monitor.js +91 -0
  57. package/dist/bin/commands/monitor.js.map +7 -0
  58. package/dist/bin/commands/multi-replace.js +66 -0
  59. package/dist/bin/commands/multi-replace.js.map +7 -0
  60. package/dist/bin/commands/pr-context.js +55 -0
  61. package/dist/bin/commands/pr-context.js.map +7 -0
  62. package/dist/bin/commands/query.js +19 -1
  63. package/dist/bin/commands/query.js.map +3 -3
  64. package/dist/bin/commands/rank.js +56 -0
  65. package/dist/bin/commands/rank.js.map +7 -0
  66. package/dist/bin/commands/read.js +1 -1
  67. package/dist/bin/commands/read.js.map +2 -2
  68. package/dist/bin/commands/recursion.js +56 -0
  69. package/dist/bin/commands/recursion.js.map +7 -0
  70. package/dist/bin/commands/rename-preview.js +56 -0
  71. package/dist/bin/commands/rename-preview.js.map +7 -0
  72. package/dist/bin/commands/session.js +60 -0
  73. package/dist/bin/commands/session.js.map +7 -0
  74. package/dist/bin/commands/simplify-scan.js +56 -0
  75. package/dist/bin/commands/simplify-scan.js.map +7 -0
  76. package/dist/bin/commands/str-replace.js +55 -0
  77. package/dist/bin/commands/str-replace.js.map +7 -0
  78. package/dist/bin/commands/test-coverage.js +59 -0
  79. package/dist/bin/commands/test-coverage.js.map +7 -0
  80. package/dist/bin/commands/test-map.js +56 -0
  81. package/dist/bin/commands/test-map.js.map +7 -0
  82. package/dist/bin/commands/test-risk.js +59 -0
  83. package/dist/bin/commands/test-risk.js.map +7 -0
  84. package/dist/bin/commands/type-hierarchy.js +76 -0
  85. package/dist/bin/commands/type-hierarchy.js.map +7 -0
  86. package/dist/bin/commands/unused-imports.js +55 -0
  87. package/dist/bin/commands/unused-imports.js.map +7 -0
  88. package/dist/bin/commands/upgrade.js +78 -0
  89. package/dist/bin/commands/upgrade.js.map +7 -0
  90. package/dist/bin/installer/common.js +19 -9
  91. package/dist/bin/installer/common.js.map +2 -2
  92. package/dist/bin/installer/config-prompt.js +147 -12
  93. package/dist/bin/installer/config-prompt.js.map +3 -3
  94. package/dist/bin/installer/index.js +62 -6
  95. package/dist/bin/installer/index.js.map +2 -2
  96. package/dist/bin/installer/instructions.js +14 -25
  97. package/dist/bin/installer/instructions.js.map +2 -2
  98. package/dist/bin/installer/mcp.js.map +2 -2
  99. package/dist/bin/installer/steering.js +569 -100
  100. package/dist/bin/installer/steering.js.map +2 -2
  101. package/dist/bin/installer/targets/aider.js +3 -3
  102. package/dist/bin/installer/targets/aider.js.map +2 -2
  103. package/dist/bin/installer/targets/amp.js +3 -3
  104. package/dist/bin/installer/targets/amp.js.map +2 -2
  105. package/dist/bin/installer/targets/antigravity.js +4 -4
  106. package/dist/bin/installer/targets/antigravity.js.map +2 -2
  107. package/dist/bin/installer/targets/augment.js +3 -3
  108. package/dist/bin/installer/targets/augment.js.map +2 -2
  109. package/dist/bin/installer/targets/claude.js +3 -3
  110. package/dist/bin/installer/targets/claude.js.map +2 -2
  111. package/dist/bin/installer/targets/cline.js +4 -4
  112. package/dist/bin/installer/targets/cline.js.map +2 -2
  113. package/dist/bin/installer/targets/codex.js +4 -4
  114. package/dist/bin/installer/targets/codex.js.map +2 -2
  115. package/dist/bin/installer/targets/continue.js +4 -4
  116. package/dist/bin/installer/targets/continue.js.map +2 -2
  117. package/dist/bin/installer/targets/copilot-cli.js +4 -4
  118. package/dist/bin/installer/targets/copilot-cli.js.map +2 -2
  119. package/dist/bin/installer/targets/copilot.js +4 -4
  120. package/dist/bin/installer/targets/copilot.js.map +2 -2
  121. package/dist/bin/installer/targets/cursor.js +5 -5
  122. package/dist/bin/installer/targets/cursor.js.map +2 -2
  123. package/dist/bin/installer/targets/devin.js +4 -4
  124. package/dist/bin/installer/targets/devin.js.map +2 -2
  125. package/dist/bin/installer/targets/gemini-cli.js +4 -4
  126. package/dist/bin/installer/targets/gemini-cli.js.map +2 -2
  127. package/dist/bin/installer/targets/generic.js +2 -2
  128. package/dist/bin/installer/targets/generic.js.map +2 -2
  129. package/dist/bin/installer/targets/goose.js +3 -4
  130. package/dist/bin/installer/targets/goose.js.map +3 -3
  131. package/dist/bin/installer/targets/index.js.map +1 -1
  132. package/dist/bin/installer/targets/junie.js +4 -4
  133. package/dist/bin/installer/targets/junie.js.map +2 -2
  134. package/dist/bin/installer/targets/kilo.js +4 -4
  135. package/dist/bin/installer/targets/kilo.js.map +2 -2
  136. package/dist/bin/installer/targets/kiro.js +27 -3
  137. package/dist/bin/installer/targets/kiro.js.map +2 -2
  138. package/dist/bin/installer/targets/opencode.js +3 -3
  139. package/dist/bin/installer/targets/opencode.js.map +2 -2
  140. package/dist/bin/installer/targets/openhands.js +3 -3
  141. package/dist/bin/installer/targets/openhands.js.map +2 -2
  142. package/dist/bin/installer/targets/qoder.js +2 -2
  143. package/dist/bin/installer/targets/qoder.js.map +2 -2
  144. package/dist/bin/installer/targets/qwen.js +2 -2
  145. package/dist/bin/installer/targets/qwen.js.map +2 -2
  146. package/dist/bin/installer/targets/replit.js +3 -3
  147. package/dist/bin/installer/targets/replit.js.map +2 -2
  148. package/dist/bin/installer/targets/roo.js +4 -4
  149. package/dist/bin/installer/targets/roo.js.map +2 -2
  150. package/dist/bin/installer/targets/tabnine.js +3 -3
  151. package/dist/bin/installer/targets/tabnine.js.map +2 -2
  152. package/dist/bin/installer/targets/trae.js +3 -3
  153. package/dist/bin/installer/targets/trae.js.map +2 -2
  154. package/dist/bin/installer/targets/warp.js +4 -4
  155. package/dist/bin/installer/targets/warp.js.map +2 -2
  156. package/dist/bin/installer/targets/windsurf.js +4 -4
  157. package/dist/bin/installer/targets/windsurf.js.map +2 -2
  158. package/dist/bin/kirograph.js +83 -1
  159. package/dist/bin/kirograph.js.map +3 -3
  160. package/dist/config.js +30 -8
  161. package/dist/config.js.map +2 -2
  162. package/dist/core/branch-manager.js +111 -0
  163. package/dist/core/branch-manager.js.map +7 -0
  164. package/dist/core/pipeline.js +3 -3
  165. package/dist/core/pipeline.js.map +2 -2
  166. package/dist/data/pixelrag-bridge.js +93 -0
  167. package/dist/data/pixelrag-bridge.js.map +7 -0
  168. package/dist/data/pixelrag-manager.js +416 -0
  169. package/dist/data/pixelrag-manager.js.map +7 -0
  170. package/dist/db/database.js +34 -7
  171. package/dist/db/database.js.map +2 -2
  172. package/dist/db/schema.sql +7 -1
  173. package/dist/extraction/complexity.js +116 -0
  174. package/dist/extraction/complexity.js.map +7 -0
  175. package/dist/extraction/extractor.js +52 -1
  176. package/dist/extraction/extractor.js.map +2 -2
  177. package/dist/graph/git-context.js +229 -0
  178. package/dist/graph/git-context.js.map +7 -0
  179. package/dist/index.js +4 -2
  180. package/dist/index.js.map +2 -2
  181. package/dist/mcp/cache.js +1 -1
  182. package/dist/mcp/cache.js.map +2 -2
  183. package/dist/mcp/handler.js +446 -0
  184. package/dist/mcp/handler.js.map +7 -0
  185. package/dist/mcp/handlers/architecture.js +297 -0
  186. package/dist/mcp/handlers/architecture.js.map +7 -0
  187. package/dist/mcp/handlers/branch.js +175 -0
  188. package/dist/mcp/handlers/branch.js.map +7 -0
  189. package/dist/mcp/handlers/code-health.js +563 -0
  190. package/dist/mcp/handlers/code-health.js.map +7 -0
  191. package/dist/mcp/handlers/complexity.js +267 -0
  192. package/dist/mcp/handlers/complexity.js.map +7 -0
  193. package/dist/mcp/handlers/core.js +611 -0
  194. package/dist/mcp/handlers/core.js.map +7 -0
  195. package/dist/mcp/handlers/data.js +337 -0
  196. package/dist/mcp/handlers/data.js.map +7 -0
  197. package/dist/mcp/handlers/docs.js +159 -0
  198. package/dist/mcp/handlers/docs.js.map +7 -0
  199. package/dist/mcp/handlers/edit-primitives.js +200 -0
  200. package/dist/mcp/handlers/edit-primitives.js.map +7 -0
  201. package/dist/mcp/handlers/git-context.js +286 -0
  202. package/dist/mcp/handlers/git-context.js.map +7 -0
  203. package/dist/mcp/handlers/memory.js +363 -0
  204. package/dist/mcp/handlers/memory.js.map +7 -0
  205. package/dist/mcp/handlers/patterns.js +310 -0
  206. package/dist/mcp/handlers/patterns.js.map +7 -0
  207. package/dist/mcp/handlers/security.js +699 -0
  208. package/dist/mcp/handlers/security.js.map +7 -0
  209. package/dist/mcp/handlers/utils.js +97 -0
  210. package/dist/mcp/handlers/utils.js.map +7 -0
  211. package/dist/mcp/handlers/watchmen.js +104 -0
  212. package/dist/mcp/handlers/watchmen.js.map +7 -0
  213. package/dist/mcp/handlers/wiki.js +242 -0
  214. package/dist/mcp/handlers/wiki.js.map +7 -0
  215. package/dist/mcp/server.js +72 -3
  216. package/dist/mcp/server.js.map +2 -2
  217. package/dist/mcp/tool-names.js +136 -10
  218. package/dist/mcp/tool-names.js.map +2 -2
  219. package/dist/mcp/tools.js +846 -3019
  220. package/dist/mcp/tools.js.map +3 -3
  221. package/dist/types.js.map +2 -2
  222. package/package.json +36 -10
@@ -0,0 +1,699 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var security_exports = {};
20
+ __export(security_exports, {
21
+ handleSecurity: () => handleSecurity
22
+ });
23
+ module.exports = __toCommonJS(security_exports);
24
+ var import_utils = require("./utils");
25
+ async function handleSecurity(toolName, args, cg) {
26
+ switch (toolName) {
27
+ case "kirograph_security": {
28
+ const { loadConfig } = await Promise.resolve().then(() => require("../../config.js"));
29
+ const projectRoot = cg.getProjectRoot();
30
+ const config = await loadConfig(projectRoot);
31
+ if (!config.enableSecurity) return "Security analysis is not enabled. Set enableSecurity: true in .kirograph/config.json and run kirograph index.";
32
+ if (!config.enableArchitecture) return "Security requires architecture analysis. Set enableArchitecture: true in .kirograph/config.json and run kirograph index.";
33
+ const db = cg.getDatabase();
34
+ db.applySecuritySchema();
35
+ const rawDb = db.getRawDb();
36
+ const depCount = rawDb.get(
37
+ `SELECT COUNT(*) as count FROM sec_dependencies`
38
+ ) ?? { count: 0 };
39
+ const vulnCount = rawDb.get(
40
+ `SELECT COUNT(*) as count FROM sec_vulnerabilities`
41
+ ) ?? { count: 0 };
42
+ const verdictRows = rawDb.all(
43
+ `SELECT verdict, COUNT(*) as count FROM sec_reachability GROUP BY verdict`
44
+ );
45
+ const verdicts = {};
46
+ for (const row of verdictRows) {
47
+ verdicts[row.verdict] = row.count;
48
+ }
49
+ const staleCount = rawDb.get(
50
+ `SELECT COUNT(*) as count FROM sec_dependencies WHERE vuln_data_stale = 1`
51
+ ) ?? { count: 0 };
52
+ const { SuppressionManager: SecSuppressionManager } = await Promise.resolve().then(() => require("../../security/suppressions.js"));
53
+ const secSuppressions = new SecSuppressionManager(projectRoot);
54
+ const allCveIds = rawDb.all(`SELECT cve_id FROM sec_vulnerabilities`);
55
+ const suppressedCveCount = allCveIds.filter((r) => secSuppressions.isSuppressed(r.cve_id)).length;
56
+ const visibleVulnCount = vulnCount.count - suppressedCveCount;
57
+ const lines = [
58
+ "# Security Overview",
59
+ "",
60
+ `Dependencies: ${depCount.count}`,
61
+ `Vulnerabilities: ${visibleVulnCount}${suppressedCveCount > 0 ? ` (${suppressedCveCount} suppressed)` : ""}`
62
+ ];
63
+ if (visibleVulnCount > 0) {
64
+ const affected = verdicts["affected"] ?? 0;
65
+ const notAffected = verdicts["not_affected"] ?? 0;
66
+ const underInvestigation = verdicts["under_investigation"] ?? 0;
67
+ const pending = visibleVulnCount - affected - notAffected - underInvestigation;
68
+ lines.push("", "## Reachability Verdicts", "");
69
+ if (affected > 0) lines.push(`\u25CF Affected: ${affected}`);
70
+ if (notAffected > 0) lines.push(`\u25CF Not affected: ${notAffected}`);
71
+ if (underInvestigation > 0) lines.push(`\u25CF Under investigation: ${underInvestigation}`);
72
+ if (pending > 0) lines.push(`\u25CF Pending analysis: ${pending}`);
73
+ }
74
+ if (staleCount.count > 0) {
75
+ lines.push("", `\u26A0 ${staleCount.count} dependenc${staleCount.count === 1 ? "y has" : "ies have"} stale vulnerability data. Use kirograph_vulns with refresh=true to update.`);
76
+ }
77
+ const lastVulnCheck = rawDb.get(
78
+ `SELECT MIN(last_vuln_check) as oldest FROM sec_dependencies WHERE last_vuln_check IS NOT NULL`
79
+ )?.oldest;
80
+ if (lastVulnCheck != null) {
81
+ const ageMs = Date.now() - lastVulnCheck;
82
+ const ageDays = ageMs / (1e3 * 60 * 60 * 24);
83
+ const maxAge = config.securityEnrichMaxAgeDays ?? 7;
84
+ if (ageDays > maxAge) {
85
+ lines.push("", `\u26A0 Vulnerability data is ${Math.floor(ageDays)} days old (max: ${maxAge}). Run kirograph vulns --refresh to update.`);
86
+ }
87
+ }
88
+ try {
89
+ const rawDbSec = db.getRawDb();
90
+ const tableExistsSec = rawDbSec.get("SELECT name FROM sqlite_master WHERE type='table' AND name='pattern_matches'");
91
+ if (tableExistsSec) {
92
+ const patternCount = rawDbSec.get("SELECT COUNT(*) as cnt FROM pattern_matches")?.cnt ?? 0;
93
+ if (patternCount > 0) {
94
+ const critCount = rawDbSec.get("SELECT COUNT(*) as cnt FROM pattern_matches WHERE severity = 'critical'")?.cnt ?? 0;
95
+ lines.push(`SAST findings: ${patternCount} pattern matches (${critCount} critical)`);
96
+ }
97
+ }
98
+ } catch {
99
+ }
100
+ return lines.join("\n");
101
+ }
102
+ case "kirograph_vulns": {
103
+ const { loadConfig } = await Promise.resolve().then(() => require("../../config.js"));
104
+ const projectRoot = cg.getProjectRoot();
105
+ const config = await loadConfig(projectRoot);
106
+ if (!config.enableSecurity) return "Security analysis is not enabled. Set enableSecurity: true in .kirograph/config.json and run kirograph index.";
107
+ const db = cg.getDatabase();
108
+ db.applySecuritySchema();
109
+ const rawDb = db.getRawDb();
110
+ if (args.refresh === true) {
111
+ const { OsvAdapter } = await Promise.resolve().then(() => require("../../security/vuln/osv-adapter.js"));
112
+ const { VulnerabilityDatabaseClient } = await Promise.resolve().then(() => require("../../security/vuln/client.js"));
113
+ const adapters = config.securityDatabases.map((dbName) => {
114
+ if (dbName.toUpperCase() === "OSV") return new OsvAdapter();
115
+ return null;
116
+ }).filter(Boolean);
117
+ const client = new VulnerabilityDatabaseClient(adapters, db);
118
+ await client.enrichAll();
119
+ }
120
+ let query = `
121
+ SELECT
122
+ v.node_id, v.cve_id, v.severity_score, v.fixed_version, v.summary,
123
+ v.epss_score, v.epss_percentile, v.risk_score,
124
+ d.package_name, d.ecosystem, d.resolved_version, d.declared_constraint,
125
+ r.verdict
126
+ FROM sec_vulnerabilities v
127
+ LEFT JOIN edges e ON e.target = v.node_id AND e.kind = 'has_vulnerability'
128
+ LEFT JOIN sec_dependencies d ON d.node_id = e.source
129
+ LEFT JOIN sec_reachability r ON r.vulnerability_node_id = v.node_id
130
+ WHERE 1=1
131
+ `;
132
+ const params = [];
133
+ if (args.severity) {
134
+ const severityRanges = {
135
+ critical: [9, 10],
136
+ high: [7, 8.9],
137
+ medium: [4, 6.9],
138
+ low: [0.1, 3.9]
139
+ };
140
+ const range = severityRanges[args.severity.toLowerCase()];
141
+ if (!range) return `Invalid severity: ${args.severity}. Use: critical, high, medium, low`;
142
+ query += ` AND v.severity_score >= ? AND v.severity_score <= ?`;
143
+ params.push(range[0], range[1]);
144
+ }
145
+ if (args.verdict) {
146
+ const validVerdicts = ["affected", "not_affected", "under_investigation"];
147
+ if (!validVerdicts.includes(args.verdict)) {
148
+ return `Invalid verdict: ${args.verdict}. Use: affected, not_affected, under_investigation`;
149
+ }
150
+ query += ` AND r.verdict = ?`;
151
+ params.push(args.verdict);
152
+ }
153
+ const limit = (0, import_utils.clampLimit)(args.limit, 20);
154
+ query += ` ORDER BY v.risk_score DESC NULLS LAST LIMIT ?`;
155
+ params.push(limit);
156
+ const rows = rawDb.all(query, params);
157
+ const verdictRankMcp = (v) => v === "affected" ? 3 : v === "under_investigation" ? 2 : v === "not_affected" ? 1 : 0;
158
+ const dedupMapMcp = /* @__PURE__ */ new Map();
159
+ for (const row of rows) {
160
+ const key = `${row.cve_id}::${row.package_name ?? ""}::${row.ecosystem ?? ""}`;
161
+ const ex = dedupMapMcp.get(key);
162
+ if (!ex || verdictRankMcp(row.verdict) > verdictRankMcp(ex.verdict)) dedupMapMcp.set(key, row);
163
+ }
164
+ const dedupedRowsMcp = [...dedupMapMcp.values()];
165
+ const { SuppressionManager } = await Promise.resolve().then(() => require("../../security/suppressions.js"));
166
+ const suppressionMgr = new SuppressionManager(projectRoot);
167
+ const suppressedCount = dedupedRowsMcp.filter((row) => suppressionMgr.isSuppressed(row.cve_id)).length;
168
+ const filteredRows = dedupedRowsMcp.filter((row) => !suppressionMgr.isSuppressed(row.cve_id));
169
+ if (filteredRows.length === 0) {
170
+ const noMatch = "No vulnerabilities found" + (args.severity || args.verdict ? " matching filters." : ".");
171
+ return suppressedCount > 0 ? `${noMatch} (${suppressedCount} suppressed)` : noMatch;
172
+ }
173
+ const { formatFixSuggestion } = await Promise.resolve().then(() => require("../../security/export/fix-suggestions.js"));
174
+ const lines = [`Vulnerabilities (${filteredRows.length}${suppressedCount > 0 ? `, ${suppressedCount} suppressed` : ""}):
175
+ `];
176
+ for (const row of filteredRows) {
177
+ const score = row.severity_score;
178
+ let severityLabel;
179
+ if (score == null) severityLabel = "UNKNOWN";
180
+ else if (score >= 9) severityLabel = "CRITICAL";
181
+ else if (score >= 7) severityLabel = "HIGH";
182
+ else if (score >= 4) severityLabel = "MEDIUM";
183
+ else severityLabel = "LOW";
184
+ let verdictLabel;
185
+ if (!row.verdict) verdictLabel = "pending";
186
+ else if (row.verdict === "affected") verdictLabel = "affected";
187
+ else if (row.verdict === "not_affected") verdictLabel = "not affected";
188
+ else verdictLabel = "investigating";
189
+ const pkg = row.package_name ? `${row.package_name}@${row.resolved_version || row.declared_constraint || "?"}` : "unknown package";
190
+ const epssNote = row.epss_score != null ? ` [EPSS: ${row.epss_score.toFixed(2)}]` : "";
191
+ const riskNote = row.risk_score != null ? ` [Risk: ${row.risk_score.toFixed(1)}]` : "";
192
+ lines.push(`${severityLabel} ${row.cve_id} ${pkg} [${verdictLabel}]${epssNote}${riskNote}`);
193
+ if (row.summary && row.summary !== "Manually registered") {
194
+ const truncSummary = row.summary.length > 120 ? row.summary.slice(0, 120) + "\u2026" : row.summary;
195
+ lines.push(` ${truncSummary}`);
196
+ }
197
+ if (row.fixed_version && row.ecosystem && row.package_name) {
198
+ const fix = formatFixSuggestion(row.ecosystem, row.package_name, row.fixed_version);
199
+ if (fix) lines.push(` ${fix}`);
200
+ }
201
+ }
202
+ return (0, import_utils.truncate)(lines.join("\n"));
203
+ }
204
+ case "kirograph_vuln_add": {
205
+ const { loadConfig } = await Promise.resolve().then(() => require("../../config.js"));
206
+ const projectRoot = cg.getProjectRoot();
207
+ const config = await loadConfig(projectRoot);
208
+ if (!config.enableSecurity) return "Security analysis is not enabled. Set enableSecurity: true in .kirograph/config.json and run kirograph index.";
209
+ const cveId = args.cveId;
210
+ const pkgName = args.package;
211
+ if (!cveId) return "Error: cveId is required.";
212
+ if (!pkgName) return "Error: package is required.";
213
+ const db = cg.getDatabase();
214
+ db.applySecuritySchema();
215
+ const rawDb = db.getRawDb();
216
+ const depRow = rawDb.get(
217
+ `SELECT node_id, ecosystem FROM sec_dependencies WHERE package_name = ?`,
218
+ [pkgName]
219
+ );
220
+ if (!depRow) {
221
+ return `No dependency found matching "${pkgName}". Run kirograph index first to discover dependencies.`;
222
+ }
223
+ const vulnNodeId = `vuln:${cveId}`;
224
+ const now = Date.now();
225
+ const severity = args.severity;
226
+ const summary = args.summary ?? "Manually registered";
227
+ const fixedVersion = args.fixedVersion ?? null;
228
+ rawDb.run(
229
+ `INSERT OR REPLACE INTO nodes
230
+ (id, kind, name, qualified_name, file_path, language,
231
+ start_line, end_line, start_column, end_column,
232
+ is_exported, is_async, is_static, is_abstract, updated_at)
233
+ VALUES (?, 'vulnerability', ?, ?, '', 'unknown', 0, 0, 0, 0, 0, 0, 0, 0, ?)`,
234
+ [vulnNodeId, cveId, cveId, now]
235
+ );
236
+ rawDb.run(
237
+ `INSERT OR REPLACE INTO sec_vulnerabilities
238
+ (node_id, cve_id, severity_score, affected_ranges, fixed_version, summary, source_database)
239
+ VALUES (?, ?, ?, '[]', ?, ?, 'manual')`,
240
+ [vulnNodeId, cveId, severity ?? null, fixedVersion, summary]
241
+ );
242
+ rawDb.run(
243
+ `INSERT OR IGNORE INTO edges (source, target, kind, confidence, confidence_score)
244
+ VALUES (?, ?, 'has_vulnerability', 'extracted', 1.0)`,
245
+ [depRow.node_id, vulnNodeId]
246
+ );
247
+ return `Registered ${cveId} against ${pkgName}.`;
248
+ }
249
+ case "kirograph_vuln_suppress": {
250
+ const projectRoot = cg.getProjectRoot();
251
+ const cveId = args.cveId;
252
+ if (!cveId) return "Error: cveId is required.";
253
+ const { SuppressionManager } = await Promise.resolve().then(() => require("../../security/suppressions.js"));
254
+ const manager = new SuppressionManager(projectRoot);
255
+ manager.add(cveId, args.reason, args.expires);
256
+ const reasonNote = args.reason ? ` Reason: ${args.reason}.` : "";
257
+ const expiresNote = args.expires ? ` Expires: ${args.expires}.` : "";
258
+ return `${cveId} suppressed.${reasonNote}${expiresNote}`;
259
+ }
260
+ case "kirograph_sbom": {
261
+ const { loadConfig } = await Promise.resolve().then(() => require("../../config.js"));
262
+ const projectRoot = cg.getProjectRoot();
263
+ const config = await loadConfig(projectRoot);
264
+ if (!config.enableSecurity) return "Security analysis is not enabled. Set enableSecurity: true in .kirograph/config.json and run kirograph index.";
265
+ const db = cg.getDatabase();
266
+ db.applySecuritySchema();
267
+ const { SBOMExporter } = await Promise.resolve().then(() => require("../../security/export/sbom.js"));
268
+ const exporter = new SBOMExporter(db, projectRoot);
269
+ const json = exporter.exportJSON();
270
+ return (0, import_utils.truncate)(json);
271
+ }
272
+ case "kirograph_vex": {
273
+ const { loadConfig } = await Promise.resolve().then(() => require("../../config.js"));
274
+ const projectRoot = cg.getProjectRoot();
275
+ const config = await loadConfig(projectRoot);
276
+ if (!config.enableSecurity) return "Security analysis is not enabled. Set enableSecurity: true in .kirograph/config.json and run kirograph index.";
277
+ const db = cg.getDatabase();
278
+ db.applySecuritySchema();
279
+ const { VEXExporter } = await Promise.resolve().then(() => require("../../security/export/vex.js"));
280
+ const exporter = new VEXExporter(db, projectRoot);
281
+ const json = exporter.exportJSON();
282
+ return (0, import_utils.truncate)(json);
283
+ }
284
+ case "kirograph_reachability": {
285
+ const { loadConfig } = await Promise.resolve().then(() => require("../../config.js"));
286
+ const projectRoot = cg.getProjectRoot();
287
+ const config = await loadConfig(projectRoot);
288
+ if (!config.enableSecurity) return "Security analysis is not enabled. Set enableSecurity: true in .kirograph/config.json and run kirograph index.";
289
+ const target = args.target;
290
+ if (!target) return "Error: target is required (dependency name or CVE ID).";
291
+ const db = cg.getDatabase();
292
+ db.applySecuritySchema();
293
+ const rawDb = db.getRawDb();
294
+ let vulnerabilityNodeId = null;
295
+ let targetLabel = target;
296
+ const vulnRow = rawDb.get(
297
+ `SELECT node_id FROM sec_vulnerabilities WHERE cve_id = ?`,
298
+ [target]
299
+ );
300
+ if (vulnRow) {
301
+ vulnerabilityNodeId = vulnRow.node_id;
302
+ } else {
303
+ const depRow = rawDb.get(
304
+ `SELECT node_id, package_name FROM sec_dependencies WHERE package_name = ?`,
305
+ [target]
306
+ );
307
+ if (depRow) {
308
+ targetLabel = depRow.package_name;
309
+ const vulnEdge = rawDb.get(
310
+ `SELECT target FROM edges WHERE source = ? AND kind = 'has_vulnerability' LIMIT 1`,
311
+ [depRow.node_id]
312
+ );
313
+ if (vulnEdge) {
314
+ vulnerabilityNodeId = vulnEdge.target;
315
+ } else {
316
+ return `No vulnerabilities found for dependency "${target}". The dependency exists but has no known vulnerabilities.`;
317
+ }
318
+ } else {
319
+ return `Target "${target}" not found. Provide a valid CVE ID or dependency package name.`;
320
+ }
321
+ }
322
+ const { ReachabilityAnalyzer } = await Promise.resolve().then(() => require("../../security/reachability.js"));
323
+ const analyzer = new ReachabilityAnalyzer(db, config);
324
+ const result = await analyzer.analyze(vulnerabilityNodeId);
325
+ const lines = [
326
+ `# Reachability: ${targetLabel}`,
327
+ "",
328
+ `Verdict: ${result.verdict}`,
329
+ `Reaching entry points: ${result.reachingEntryPointCount}`
330
+ ];
331
+ if (result.paths.length > 0) {
332
+ lines.push("", "## Paths");
333
+ for (const p of result.paths.slice(0, 5)) {
334
+ lines.push(`- From ${p.entryPoint}: ${p.path.join(" \u2192 ")}`);
335
+ }
336
+ if (result.paths.length > 5) {
337
+ lines.push(` \u2026and ${result.paths.length - 5} more paths`);
338
+ }
339
+ }
340
+ if (result.unresolvedSymbols.length > 0) {
341
+ lines.push("", "## Unresolved Symbols");
342
+ for (const sym of result.unresolvedSymbols.slice(0, 10)) {
343
+ lines.push(`- ${sym}`);
344
+ }
345
+ if (result.unresolvedSymbols.length > 10) {
346
+ lines.push(` \u2026and ${result.unresolvedSymbols.length - 10} more`);
347
+ }
348
+ }
349
+ if (result.verdict === "affected") {
350
+ const impact = await analyzer.getImpactSummary(vulnerabilityNodeId);
351
+ if (impact) {
352
+ lines.push("", "## Impact Summary");
353
+ if (impact.affectedLayers.length > 0) {
354
+ lines.push(`Affected layers: ${impact.affectedLayers.join(", ")}`);
355
+ }
356
+ lines.push(`Affected entry points: ${impact.affectedEntryPoints.length}`);
357
+ lines.push(`Distinct paths: ${impact.distinctPathCount}`);
358
+ }
359
+ }
360
+ return (0, import_utils.truncate)(lines.join("\n"));
361
+ }
362
+ case "kirograph_staleness": {
363
+ const { loadConfig } = await Promise.resolve().then(() => require("../../config.js"));
364
+ const projectRoot = cg.getProjectRoot();
365
+ const config = await loadConfig(projectRoot);
366
+ if (!config.enableSecurity) return "Security analysis is not enabled. Set enableSecurity: true in .kirograph/config.json and run kirograph index.";
367
+ const db = cg.getDatabase();
368
+ db.applySecuritySchema();
369
+ const rawDb = db.getRawDb();
370
+ const threshold = typeof args.threshold === "number" ? args.threshold : 0.3;
371
+ if (args.refresh === true) {
372
+ const { StalenessChecker } = await Promise.resolve().then(() => require("../../security/staleness.js"));
373
+ const checker = new StalenessChecker(db);
374
+ await checker.checkAll();
375
+ }
376
+ const rows = rawDb.all(
377
+ `SELECT package_name, ecosystem, resolved_version, declared_constraint,
378
+ latest_version, latest_published, staleness_score
379
+ FROM sec_dependencies
380
+ WHERE staleness_score >= ?
381
+ ORDER BY staleness_score DESC`,
382
+ [threshold]
383
+ );
384
+ if (rows.length === 0) {
385
+ return `No dependencies found with staleness_score >= ${threshold}.` + (args.refresh ? "" : " Use refresh=true to fetch latest version data from registries.");
386
+ }
387
+ const lines = [`Stale Dependencies (threshold: ${threshold}):
388
+ `];
389
+ for (const row of rows) {
390
+ const resolved = row.resolved_version ?? row.declared_constraint ?? "?";
391
+ const latest = row.latest_version ?? "?";
392
+ const score = row.staleness_score ?? 0;
393
+ const months = row.latest_published ? Math.round((Date.now() - row.latest_published) / (1e3 * 60 * 60 * 24 * 30)) : null;
394
+ const bar = "\u2588".repeat(Math.round(score * 10)) + "\u2591".repeat(10 - Math.round(score * 10));
395
+ const monthsStr = months !== null ? `, ${months}mo since latest` : "";
396
+ lines.push(`${row.package_name} (${row.ecosystem}): ${resolved} \u2192 ${latest}${monthsStr}`);
397
+ lines.push(` ${bar} ${score.toFixed(2)}`);
398
+ }
399
+ const totalCount = rawDb.get(`SELECT COUNT(*) as count FROM sec_dependencies`) ?? { count: 0 };
400
+ lines.push("", `${rows.length} of ${totalCount.count} dependencies are stale (score >= ${threshold})`);
401
+ return (0, import_utils.truncate)(lines.join("\n"));
402
+ }
403
+ case "kirograph_licenses": {
404
+ const { loadConfig } = await Promise.resolve().then(() => require("../../config.js"));
405
+ const projectRoot = cg.getProjectRoot();
406
+ const config = await loadConfig(projectRoot);
407
+ if (!config.enableSecurity) return "Security analysis is not enabled. Set enableSecurity: true in .kirograph/config.json and run kirograph index.";
408
+ const db = cg.getDatabase();
409
+ db.applySecuritySchema();
410
+ const rawDb = db.getRawDb();
411
+ const deps = rawDb.all(
412
+ `SELECT package_name, ecosystem, license FROM sec_dependencies ORDER BY ecosystem, package_name`
413
+ );
414
+ const { checkLicensePolicy } = await Promise.resolve().then(() => require("../../security/license.js"));
415
+ const policy = config.securityLicensePolicy;
416
+ const violations = checkLicensePolicy(deps, policy);
417
+ if (args.policy === true) {
418
+ if (violations.length === 0) {
419
+ return "No license policy violations found.";
420
+ }
421
+ const lines2 = ["# License Policy Violations\n"];
422
+ for (const v of violations) {
423
+ lines2.push(`${v.severity.toUpperCase()} ${v.packageName} [${v.ecosystem}] ${v.license}`);
424
+ }
425
+ const denyCount2 = violations.filter((v) => v.severity === "deny").length;
426
+ const warnCount2 = violations.filter((v) => v.severity === "warn").length;
427
+ lines2.push("");
428
+ if (denyCount2 > 0) lines2.push(`${denyCount2} denied license${denyCount2 !== 1 ? "s" : ""}`);
429
+ if (warnCount2 > 0) lines2.push(`${warnCount2} license warning${warnCount2 !== 1 ? "s" : ""}`);
430
+ return (0, import_utils.truncate)(lines2.join("\n"));
431
+ }
432
+ if (deps.length === 0) {
433
+ return "No dependencies found. Run kirograph index first.";
434
+ }
435
+ const violationMap = /* @__PURE__ */ new Map();
436
+ for (const v of violations) {
437
+ violationMap.set(`${v.ecosystem}:${v.packageName}`, v.severity);
438
+ }
439
+ const lines = [`# License Report (${deps.length} dependencies)
440
+ `];
441
+ if (violations.length > 0) {
442
+ lines.push("## Policy Violations\n");
443
+ for (const v of violations) {
444
+ lines.push(`${v.severity.toUpperCase()} ${v.packageName} [${v.ecosystem}] ${v.license}`);
445
+ }
446
+ lines.push("");
447
+ }
448
+ lines.push("## All Dependencies\n");
449
+ lines.push("package | ecosystem | license | status");
450
+ lines.push("------- | --------- | ------- | ------");
451
+ for (const dep of deps) {
452
+ const key = `${dep.ecosystem}:${dep.package_name}`;
453
+ const violation = violationMap.get(key);
454
+ const license = dep.license ?? "(unknown)";
455
+ const status = violation ?? (dep.license ? "ok" : "unknown");
456
+ lines.push(`${dep.package_name} | ${dep.ecosystem} | ${license} | ${status}`);
457
+ }
458
+ const denyCount = violations.filter((v) => v.severity === "deny").length;
459
+ const warnCount = violations.filter((v) => v.severity === "warn").length;
460
+ const unknownCount = deps.filter((d) => !d.license).length;
461
+ lines.push("");
462
+ if (denyCount > 0) lines.push(`${denyCount} denied license${denyCount !== 1 ? "s" : ""}`);
463
+ if (warnCount > 0) lines.push(`${warnCount} license warning${warnCount !== 1 ? "s" : ""}`);
464
+ if (unknownCount > 0) lines.push(`${unknownCount} unknown license${unknownCount !== 1 ? "s" : ""}`);
465
+ if (denyCount === 0 && warnCount === 0) lines.push("No policy violations");
466
+ return (0, import_utils.truncate)(lines.join("\n"));
467
+ }
468
+ case "kirograph_attack_surface": {
469
+ const { loadConfig } = await Promise.resolve().then(() => require("../../config.js"));
470
+ const projectRoot = cg.getProjectRoot();
471
+ const config = await loadConfig(projectRoot);
472
+ if (!config.enableSecurity) return "Security analysis is not enabled. Set enableSecurity: true in .kirograph/config.json and run kirograph index.";
473
+ const db = cg.getDatabase();
474
+ db.applySecuritySchema();
475
+ const { AttackSurfaceAnalyzer } = await Promise.resolve().then(() => require("../../security/attack-surface.js"));
476
+ const analyzer = new AttackSurfaceAnalyzer(db);
477
+ const result = await analyzer.analyze();
478
+ if (result.totalRoutes === 0) {
479
+ return "No route nodes found in the graph. Ensure the project has been indexed with architecture analysis enabled.";
480
+ }
481
+ const limit = (0, import_utils.clampLimit)(args.limit, 20);
482
+ const routes = args.publicOnly === true ? result.criticalPaths.filter((r) => r.exposureLevel === "public") : result.criticalPaths;
483
+ const lines = [
484
+ "# Attack Surface",
485
+ "",
486
+ `Total routes: ${result.totalRoutes} Public: ${result.publicRoutes} Authenticated: ${result.authenticatedRoutes} Routes with vulns: ${result.routesWithVulns}`,
487
+ ""
488
+ ];
489
+ const displayed = routes.slice(0, limit);
490
+ if (displayed.length === 0) {
491
+ lines.push(args.publicOnly === true ? "No public routes with vulnerable dependencies found." : "No routes with vulnerable dependencies found.");
492
+ } else {
493
+ for (const entry of displayed) {
494
+ const authTag = entry.isAuthenticated ? "[auth]" : "[public]";
495
+ const riskTag = entry.riskScore > 0 ? ` risk=${entry.riskScore.toFixed(1)}` : "";
496
+ lines.push(`${authTag} ${entry.route} (${entry.exposureLevel})${riskTag} ${entry.filePath}`);
497
+ for (const dep of entry.vulnerableDeps.slice(0, 3)) {
498
+ lines.push(` \u2514 ${dep.cveId} via ${dep.packageName} (${dep.hopCount} hop${dep.hopCount !== 1 ? "s" : ""}${dep.verdict ? `, ${dep.verdict}` : ""})`);
499
+ }
500
+ if (entry.vulnerableDeps.length > 3) {
501
+ lines.push(` \u2514 \u2026and ${entry.vulnerableDeps.length - 3} more vulns`);
502
+ }
503
+ }
504
+ if (routes.length > limit) {
505
+ lines.push("", `\u2026and ${routes.length - limit} more routes (increase limit to see all)`);
506
+ }
507
+ }
508
+ return (0, import_utils.truncate)(lines.join("\n"));
509
+ }
510
+ case "kirograph_secrets": {
511
+ const { loadConfig } = await Promise.resolve().then(() => require("../../config.js"));
512
+ const projectRoot = cg.getProjectRoot();
513
+ const config = await loadConfig(projectRoot);
514
+ if (!config.enableSecurity) return "Security analysis is not enabled. Set enableSecurity: true in .kirograph/config.json and run kirograph index.";
515
+ const db = cg.getDatabase();
516
+ db.applySecuritySchema();
517
+ const { SecretsScanner } = await Promise.resolve().then(() => require("../../security/secrets.js"));
518
+ const scanner = new SecretsScanner(db, projectRoot);
519
+ const result = await scanner.scan({ includeTests: args.includeTests === true });
520
+ if (result.totalFindings === 0) {
521
+ return `No secrets found. Scanned ${result.filesScanned} file${result.filesScanned !== 1 ? "s" : ""}.`;
522
+ }
523
+ let findings = result.findings;
524
+ if (args.severity) {
525
+ findings = findings.filter((f) => f.severity === args.severity);
526
+ }
527
+ if (findings.length === 0) {
528
+ return `No secrets found matching severity "${args.severity}". Total findings (all severities): ${result.totalFindings}.`;
529
+ }
530
+ const lines = [
531
+ `# Secrets Scan (${result.filesScanned} files scanned)`,
532
+ "",
533
+ `Found: ${result.totalFindings} Critical: ${result.criticalCount} High: ${result.highCount}`,
534
+ ""
535
+ ];
536
+ for (const f of findings) {
537
+ lines.push(`${f.severity.toUpperCase()} ${f.type}`);
538
+ lines.push(` ${f.filePath}:${f.line}:${f.column} snippet: ${f.snippet}`);
539
+ if (f.nodeName) {
540
+ lines.push(` in function: ${f.nodeName}`);
541
+ }
542
+ if (f.entryPointCount > 0) {
543
+ lines.push(` reachable from ${f.entryPointCount} entry point${f.entryPointCount !== 1 ? "s" : ""}`);
544
+ }
545
+ }
546
+ return (0, import_utils.truncate)(lines.join("\n"));
547
+ }
548
+ case "kirograph_security_flows": {
549
+ const { loadConfig } = await Promise.resolve().then(() => require("../../config.js"));
550
+ const projectRoot = cg.getProjectRoot();
551
+ const config = await loadConfig(projectRoot);
552
+ if (!config.enableSecurity) return "Security analysis is not enabled. Set enableSecurity: true in .kirograph/config.json and run kirograph index.";
553
+ const db = cg.getDatabase();
554
+ db.applySecuritySchema();
555
+ const { DataFlowAnalyzer } = await Promise.resolve().then(() => require("../../security/data-flows.js"));
556
+ const analyzer = new DataFlowAnalyzer(db);
557
+ let findings = await analyzer.analyze();
558
+ const typeFilter = args.type ?? "all";
559
+ const TYPE_MAP = {
560
+ sql: "sql-injection",
561
+ eval: "dangerous-eval",
562
+ deserialize: "unsafe-deserialize",
563
+ path: "path-traversal",
564
+ crypto: "hardcoded-crypto"
565
+ };
566
+ if (typeFilter !== "all") {
567
+ const mapped = TYPE_MAP[typeFilter];
568
+ if (!mapped) return `Invalid type filter "${typeFilter}". Use: sql, eval, deserialize, path, crypto, all`;
569
+ findings = findings.filter((f) => f.type === mapped);
570
+ }
571
+ if (findings.length === 0) {
572
+ return typeFilter === "all" ? "No dangerous data flows detected." : `No "${typeFilter}" findings detected.`;
573
+ }
574
+ const lines = [
575
+ `# Security Flows (${findings.length} finding${findings.length !== 1 ? "s" : ""})`,
576
+ ""
577
+ ];
578
+ for (const f of findings) {
579
+ lines.push(`${f.severity.toUpperCase()} [${f.owaspCategory}] ${f.type}`);
580
+ lines.push(` ${f.filePath}:${f.line} symbol: ${f.symbol}`);
581
+ lines.push(` ${f.description}`);
582
+ lines.push(` Fix: ${f.recommendation}`);
583
+ lines.push("");
584
+ }
585
+ return (0, import_utils.truncate)(lines.join("\n"));
586
+ }
587
+ case "kirograph_supply_chain": {
588
+ const { loadConfig } = await Promise.resolve().then(() => require("../../config.js"));
589
+ const projectRoot = cg.getProjectRoot();
590
+ const config = await loadConfig(projectRoot);
591
+ if (!config.enableSecurity) return "Security analysis is not enabled. Set enableSecurity: true in .kirograph/config.json and run kirograph index.";
592
+ const db = cg.getDatabase();
593
+ db.applySecuritySchema();
594
+ const { SupplyChainChecker } = await Promise.resolve().then(() => require("../../security/supply-chain.js"));
595
+ const checker = new SupplyChainChecker(db);
596
+ const { results, errors } = await checker.checkAll();
597
+ const RISK_ORDER = { critical: 0, high: 1, medium: 2, low: 3, unknown: 4 };
598
+ const thresholdArg = args.threshold;
599
+ const thresholdOrder = thresholdArg ? RISK_ORDER[thresholdArg] ?? 4 : 2;
600
+ const filtered = results.filter((r) => (RISK_ORDER[r.riskLevel] ?? 4) <= thresholdOrder).sort((a, b) => (RISK_ORDER[a.riskLevel] ?? 4) - (RISK_ORDER[b.riskLevel] ?? 4));
601
+ if (filtered.length === 0) {
602
+ return `No supply chain risks found at threshold "${thresholdArg ?? "medium"}" or above. Checked ${results.length} dependencies.`;
603
+ }
604
+ const lines = [
605
+ `# Supply Chain Health (${filtered.length} risks, threshold: ${thresholdArg ?? "medium"})`,
606
+ ""
607
+ ];
608
+ for (const r of filtered) {
609
+ const scoreStr = r.scorecardScore !== null ? ` scorecard=${r.scorecardScore.toFixed(1)}/10` : "";
610
+ const maintainerStr = r.maintainerCount !== null ? ` maintainers=${r.maintainerCount}` : "";
611
+ lines.push(`${r.riskLevel.toUpperCase()} ${r.packageName} (${r.ecosystem})${scoreStr}${maintainerStr}`);
612
+ for (const reason of r.riskReasons) {
613
+ lines.push(` \u2022 ${reason}`);
614
+ }
615
+ }
616
+ if (errors.length > 0) {
617
+ lines.push("", `\u26A0 ${errors.length} package${errors.length !== 1 ? "s" : ""} could not be checked (network errors)`);
618
+ }
619
+ return (0, import_utils.truncate)(lines.join("\n"));
620
+ }
621
+ case "kirograph_dep_confusion": {
622
+ const { loadConfig } = await Promise.resolve().then(() => require("../../config.js"));
623
+ const projectRoot = cg.getProjectRoot();
624
+ const config = await loadConfig(projectRoot);
625
+ if (!config.enableSecurity) return "Security analysis is not enabled. Set enableSecurity: true in .kirograph/config.json and run kirograph index.";
626
+ const db = cg.getDatabase();
627
+ db.applySecuritySchema();
628
+ const { DepConfusionChecker } = await Promise.resolve().then(() => require("../../security/dep-confusion.js"));
629
+ const checker = new DepConfusionChecker(db);
630
+ const findings = await checker.check();
631
+ if (findings.length === 0) {
632
+ return "No dependency confusion vulnerabilities detected.";
633
+ }
634
+ const RISK_ORDER = { critical: 0, high: 1, medium: 2 };
635
+ const sorted = [...findings].sort((a, b) => (RISK_ORDER[a.riskLevel] ?? 3) - (RISK_ORDER[b.riskLevel] ?? 3));
636
+ const lines = [
637
+ `# Dependency Confusion (${findings.length} finding${findings.length !== 1 ? "s" : ""})`,
638
+ ""
639
+ ];
640
+ for (const f of sorted) {
641
+ lines.push(`${f.riskLevel.toUpperCase()} ${f.packageName} (${f.ecosystem})`);
642
+ lines.push(` ${f.explanation}`);
643
+ if (f.publicExists && f.publicVersion) {
644
+ lines.push(` Public version: ${f.publicVersion}${f.publicPublishedAt ? ` published: ${f.publicPublishedAt}` : ""}`);
645
+ }
646
+ }
647
+ return (0, import_utils.truncate)(lines.join("\n"));
648
+ }
649
+ case "kirograph_remediation": {
650
+ const { loadConfig } = await Promise.resolve().then(() => require("../../config.js"));
651
+ const projectRoot = cg.getProjectRoot();
652
+ const config = await loadConfig(projectRoot);
653
+ if (!config.enableSecurity) return "Security analysis is not enabled. Set enableSecurity: true in .kirograph/config.json and run kirograph index.";
654
+ const db = cg.getDatabase();
655
+ db.applySecuritySchema();
656
+ const { RemediationTracker } = await Promise.resolve().then(() => require("../../security/remediation.js"));
657
+ const tracker = new RemediationTracker(db);
658
+ let statuses = tracker.getStatus();
659
+ if (args.overdueOnly === true) {
660
+ statuses = statuses.filter((s) => s.isOverdue);
661
+ }
662
+ if (statuses.length === 0) {
663
+ return args.overdueOnly === true ? "No overdue vulnerabilities found." : "No open vulnerabilities with SLA tracking data found.";
664
+ }
665
+ const SLA_ORDER = { overdue: 0, warning: 1, no_fix: 2, ok: 3 };
666
+ statuses.sort((a, b) => {
667
+ const slaA = SLA_ORDER[a.slaStatus] ?? 3;
668
+ const slaB = SLA_ORDER[b.slaStatus] ?? 3;
669
+ if (slaA !== slaB) return slaA - slaB;
670
+ return (b.severity ?? 0) - (a.severity ?? 0);
671
+ });
672
+ const overdueCount = statuses.filter((s) => s.isOverdue).length;
673
+ const warningCount = statuses.filter((s) => s.slaStatus === "warning").length;
674
+ const lines = [
675
+ `# Remediation SLA (${statuses.length} open${overdueCount > 0 ? `, ${overdueCount} overdue` : ""}${warningCount > 0 ? `, ${warningCount} warning` : ""})`,
676
+ ""
677
+ ];
678
+ for (const s of statuses) {
679
+ const severityLabel = s.severity == null ? "UNKNOWN" : s.severity >= 9 ? "CRITICAL" : s.severity >= 7 ? "HIGH" : s.severity >= 4 ? "MEDIUM" : "LOW";
680
+ const slaTag = s.slaStatus === "overdue" ? "[OVERDUE]" : s.slaStatus === "warning" ? "[WARNING]" : s.slaStatus === "no_fix" ? "[NO_FIX]" : "[OK]";
681
+ lines.push(`${slaTag} ${severityLabel} ${s.cveId} ${s.packageName}`);
682
+ if (s.daysOpen !== null) lines.push(` Open for ${s.daysOpen} day${s.daysOpen !== 1 ? "s" : ""}`);
683
+ if (s.daysWithFixAvailable !== null) lines.push(` Fix available for ${s.daysWithFixAvailable} day${s.daysWithFixAvailable !== 1 ? "s" : ""}`);
684
+ if (s.slaDeadline !== null) {
685
+ const deadlineDate = new Date(s.slaDeadline).toISOString().slice(0, 10);
686
+ lines.push(` SLA deadline: ${deadlineDate}`);
687
+ }
688
+ }
689
+ return (0, import_utils.truncate)(lines.join("\n"));
690
+ }
691
+ default:
692
+ return `Unknown tool: ${toolName}`;
693
+ }
694
+ }
695
+ // Annotate the CommonJS export names for ESM import in node:
696
+ 0 && (module.exports = {
697
+ handleSecurity
698
+ });
699
+ //# sourceMappingURL=security.js.map