@vyuhlabs/dxkit 1.5.1 → 1.6.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 (257) hide show
  1. package/CHANGELOG.md +264 -0
  2. package/README.md +265 -352
  3. package/THIRD_PARTY_NOTICES.md +40 -0
  4. package/dist/analyzers/developer/detailed.d.ts +26 -0
  5. package/dist/analyzers/developer/detailed.d.ts.map +1 -0
  6. package/dist/analyzers/developer/detailed.js +193 -0
  7. package/dist/analyzers/developer/detailed.js.map +1 -0
  8. package/dist/analyzers/developer/gather.d.ts +11 -0
  9. package/dist/analyzers/developer/gather.d.ts.map +1 -0
  10. package/dist/analyzers/developer/gather.js +167 -0
  11. package/dist/analyzers/developer/gather.js.map +1 -0
  12. package/dist/analyzers/developer/index.d.ts +8 -0
  13. package/dist/analyzers/developer/index.d.ts.map +1 -0
  14. package/dist/analyzers/developer/index.js +168 -0
  15. package/dist/analyzers/developer/index.js.map +1 -0
  16. package/dist/analyzers/developer/types.d.ts +49 -0
  17. package/dist/analyzers/developer/types.d.ts.map +1 -0
  18. package/dist/analyzers/developer/types.js +6 -0
  19. package/dist/analyzers/developer/types.js.map +1 -0
  20. package/dist/analyzers/docs/shallow.d.ts +9 -0
  21. package/dist/analyzers/docs/shallow.d.ts.map +1 -0
  22. package/dist/analyzers/docs/shallow.js +8 -0
  23. package/dist/analyzers/docs/shallow.js.map +1 -0
  24. package/dist/analyzers/dx/shallow.d.ts +9 -0
  25. package/dist/analyzers/dx/shallow.d.ts.map +1 -0
  26. package/dist/analyzers/dx/shallow.js +8 -0
  27. package/dist/analyzers/dx/shallow.js.map +1 -0
  28. package/dist/analyzers/evidence.d.ts +36 -0
  29. package/dist/analyzers/evidence.d.ts.map +1 -0
  30. package/dist/analyzers/evidence.js +3 -0
  31. package/dist/analyzers/evidence.js.map +1 -0
  32. package/dist/analyzers/health/actions.d.ts +10 -0
  33. package/dist/analyzers/health/actions.d.ts.map +1 -0
  34. package/dist/analyzers/health/actions.js +284 -0
  35. package/dist/analyzers/health/actions.js.map +1 -0
  36. package/dist/analyzers/health/detailed.d.ts +26 -0
  37. package/dist/analyzers/health/detailed.d.ts.map +1 -0
  38. package/dist/analyzers/health/detailed.js +147 -0
  39. package/dist/analyzers/health/detailed.js.map +1 -0
  40. package/dist/analyzers/health.d.ts +22 -0
  41. package/dist/analyzers/health.d.ts.map +1 -0
  42. package/dist/analyzers/health.js +270 -0
  43. package/dist/analyzers/health.js.map +1 -0
  44. package/dist/analyzers/index.d.ts +3 -0
  45. package/dist/analyzers/index.d.ts.map +1 -0
  46. package/dist/analyzers/index.js +6 -0
  47. package/dist/analyzers/index.js.map +1 -0
  48. package/dist/analyzers/maintainability/shallow.d.ts +9 -0
  49. package/dist/analyzers/maintainability/shallow.d.ts.map +1 -0
  50. package/dist/analyzers/maintainability/shallow.js +8 -0
  51. package/dist/analyzers/maintainability/shallow.js.map +1 -0
  52. package/dist/analyzers/quality/actions.d.ts +5 -0
  53. package/dist/analyzers/quality/actions.d.ts.map +1 -0
  54. package/dist/analyzers/quality/actions.js +158 -0
  55. package/dist/analyzers/quality/actions.js.map +1 -0
  56. package/dist/analyzers/quality/detailed.d.ts +17 -0
  57. package/dist/analyzers/quality/detailed.d.ts.map +1 -0
  58. package/dist/analyzers/quality/detailed.js +122 -0
  59. package/dist/analyzers/quality/detailed.js.map +1 -0
  60. package/dist/analyzers/quality/gather.d.ts +38 -0
  61. package/dist/analyzers/quality/gather.d.ts.map +1 -0
  62. package/dist/analyzers/quality/gather.js +279 -0
  63. package/dist/analyzers/quality/gather.js.map +1 -0
  64. package/dist/analyzers/quality/index.d.ts +12 -0
  65. package/dist/analyzers/quality/index.d.ts.map +1 -0
  66. package/dist/analyzers/quality/index.js +281 -0
  67. package/dist/analyzers/quality/index.js.map +1 -0
  68. package/dist/analyzers/quality/shallow.d.ts +9 -0
  69. package/dist/analyzers/quality/shallow.d.ts.map +1 -0
  70. package/dist/analyzers/quality/shallow.js +8 -0
  71. package/dist/analyzers/quality/shallow.js.map +1 -0
  72. package/dist/analyzers/quality/types.d.ts +66 -0
  73. package/dist/analyzers/quality/types.d.ts.map +1 -0
  74. package/dist/analyzers/quality/types.js +3 -0
  75. package/dist/analyzers/quality/types.js.map +1 -0
  76. package/dist/analyzers/remediation.d.ts +42 -0
  77. package/dist/analyzers/remediation.d.ts.map +1 -0
  78. package/dist/analyzers/remediation.js +28 -0
  79. package/dist/analyzers/remediation.js.map +1 -0
  80. package/dist/analyzers/scoring.d.ts +32 -0
  81. package/dist/analyzers/scoring.d.ts.map +1 -0
  82. package/dist/analyzers/scoring.js +410 -0
  83. package/dist/analyzers/scoring.js.map +1 -0
  84. package/dist/analyzers/security/actions.d.ts +7 -0
  85. package/dist/analyzers/security/actions.d.ts.map +1 -0
  86. package/dist/analyzers/security/actions.js +104 -0
  87. package/dist/analyzers/security/actions.js.map +1 -0
  88. package/dist/analyzers/security/detailed.d.ts +14 -0
  89. package/dist/analyzers/security/detailed.d.ts.map +1 -0
  90. package/dist/analyzers/security/detailed.js +124 -0
  91. package/dist/analyzers/security/detailed.js.map +1 -0
  92. package/dist/analyzers/security/gather.d.ts +12 -0
  93. package/dist/analyzers/security/gather.d.ts.map +1 -0
  94. package/dist/analyzers/security/gather.js +195 -0
  95. package/dist/analyzers/security/gather.js.map +1 -0
  96. package/dist/analyzers/security/index.d.ts +8 -0
  97. package/dist/analyzers/security/index.d.ts.map +1 -0
  98. package/dist/analyzers/security/index.js +173 -0
  99. package/dist/analyzers/security/index.js.map +1 -0
  100. package/dist/analyzers/security/scoring.d.ts +29 -0
  101. package/dist/analyzers/security/scoring.d.ts.map +1 -0
  102. package/dist/analyzers/security/scoring.js +40 -0
  103. package/dist/analyzers/security/scoring.js.map +1 -0
  104. package/dist/analyzers/security/shallow.d.ts +10 -0
  105. package/dist/analyzers/security/shallow.d.ts.map +1 -0
  106. package/dist/analyzers/security/shallow.js +8 -0
  107. package/dist/analyzers/security/shallow.js.map +1 -0
  108. package/dist/analyzers/security/types.d.ts +43 -0
  109. package/dist/analyzers/security/types.d.ts.map +1 -0
  110. package/dist/analyzers/security/types.js +6 -0
  111. package/dist/analyzers/security/types.js.map +1 -0
  112. package/dist/analyzers/tests/actions.d.ts +6 -0
  113. package/dist/analyzers/tests/actions.d.ts.map +1 -0
  114. package/dist/analyzers/tests/actions.js +80 -0
  115. package/dist/analyzers/tests/actions.js.map +1 -0
  116. package/dist/analyzers/tests/detailed.d.ts +14 -0
  117. package/dist/analyzers/tests/detailed.d.ts.map +1 -0
  118. package/dist/analyzers/tests/detailed.js +121 -0
  119. package/dist/analyzers/tests/detailed.js.map +1 -0
  120. package/dist/analyzers/tests/gather.d.ts +5 -0
  121. package/dist/analyzers/tests/gather.d.ts.map +1 -0
  122. package/dist/analyzers/tests/gather.js +270 -0
  123. package/dist/analyzers/tests/gather.js.map +1 -0
  124. package/dist/analyzers/tests/import-graph.d.ts +48 -0
  125. package/dist/analyzers/tests/import-graph.d.ts.map +1 -0
  126. package/dist/analyzers/tests/import-graph.js +231 -0
  127. package/dist/analyzers/tests/import-graph.js.map +1 -0
  128. package/dist/analyzers/tests/index.d.ts +8 -0
  129. package/dist/analyzers/tests/index.d.ts.map +1 -0
  130. package/dist/analyzers/tests/index.js +247 -0
  131. package/dist/analyzers/tests/index.js.map +1 -0
  132. package/dist/analyzers/tests/scoring.d.ts +27 -0
  133. package/dist/analyzers/tests/scoring.d.ts.map +1 -0
  134. package/dist/analyzers/tests/scoring.js +38 -0
  135. package/dist/analyzers/tests/scoring.js.map +1 -0
  136. package/dist/analyzers/tests/shallow.d.ts +9 -0
  137. package/dist/analyzers/tests/shallow.d.ts.map +1 -0
  138. package/dist/analyzers/tests/shallow.js +8 -0
  139. package/dist/analyzers/tests/shallow.js.map +1 -0
  140. package/dist/analyzers/tests/types.d.ts +49 -0
  141. package/dist/analyzers/tests/types.d.ts.map +1 -0
  142. package/dist/analyzers/tests/types.js +6 -0
  143. package/dist/analyzers/tests/types.js.map +1 -0
  144. package/dist/analyzers/tools/cloc.d.ts +8 -0
  145. package/dist/analyzers/tools/cloc.d.ts.map +1 -0
  146. package/dist/analyzers/tools/cloc.js +49 -0
  147. package/dist/analyzers/tools/cloc.js.map +1 -0
  148. package/dist/analyzers/tools/coverage.d.ts +59 -0
  149. package/dist/analyzers/tools/coverage.d.ts.map +1 -0
  150. package/dist/analyzers/tools/coverage.js +280 -0
  151. package/dist/analyzers/tools/coverage.js.map +1 -0
  152. package/dist/analyzers/tools/cvss-v4-lookup.d.ts +10 -0
  153. package/dist/analyzers/tools/cvss-v4-lookup.d.ts.map +1 -0
  154. package/dist/analyzers/tools/cvss-v4-lookup.js +284 -0
  155. package/dist/analyzers/tools/cvss-v4-lookup.js.map +1 -0
  156. package/dist/analyzers/tools/cvss-v4.d.ts +24 -0
  157. package/dist/analyzers/tools/cvss-v4.d.ts.map +1 -0
  158. package/dist/analyzers/tools/cvss-v4.js +362 -0
  159. package/dist/analyzers/tools/cvss-v4.js.map +1 -0
  160. package/dist/analyzers/tools/default-exclusions.gitignore +56 -0
  161. package/dist/analyzers/tools/exclusions.d.ts +70 -0
  162. package/dist/analyzers/tools/exclusions.d.ts.map +1 -0
  163. package/dist/analyzers/tools/exclusions.js +250 -0
  164. package/dist/analyzers/tools/exclusions.js.map +1 -0
  165. package/dist/analyzers/tools/generic.d.ts +4 -0
  166. package/dist/analyzers/tools/generic.d.ts.map +1 -0
  167. package/dist/analyzers/tools/generic.js +198 -0
  168. package/dist/analyzers/tools/generic.js.map +1 -0
  169. package/dist/analyzers/tools/gitleaks.d.ts +8 -0
  170. package/dist/analyzers/tools/gitleaks.d.ts.map +1 -0
  171. package/dist/analyzers/tools/gitleaks.js +58 -0
  172. package/dist/analyzers/tools/gitleaks.js.map +1 -0
  173. package/dist/analyzers/tools/graphify.d.ts +4 -0
  174. package/dist/analyzers/tools/graphify.d.ts.map +1 -0
  175. package/dist/analyzers/tools/graphify.js +222 -0
  176. package/dist/analyzers/tools/graphify.js.map +1 -0
  177. package/dist/analyzers/tools/osv.d.ts +51 -0
  178. package/dist/analyzers/tools/osv.d.ts.map +1 -0
  179. package/dist/analyzers/tools/osv.js +188 -0
  180. package/dist/analyzers/tools/osv.js.map +1 -0
  181. package/dist/analyzers/tools/parallel.d.ts +8 -0
  182. package/dist/analyzers/tools/parallel.d.ts.map +1 -0
  183. package/dist/analyzers/tools/parallel.js +195 -0
  184. package/dist/analyzers/tools/parallel.js.map +1 -0
  185. package/dist/analyzers/tools/runner.d.ts +13 -0
  186. package/dist/analyzers/tools/runner.d.ts.map +1 -0
  187. package/dist/analyzers/tools/runner.js +109 -0
  188. package/dist/analyzers/tools/runner.js.map +1 -0
  189. package/dist/analyzers/tools/suppressions.d.ts +55 -0
  190. package/dist/analyzers/tools/suppressions.d.ts.map +1 -0
  191. package/dist/analyzers/tools/suppressions.js +203 -0
  192. package/dist/analyzers/tools/suppressions.js.map +1 -0
  193. package/dist/analyzers/tools/timing.d.ts +9 -0
  194. package/dist/analyzers/tools/timing.d.ts.map +1 -0
  195. package/dist/analyzers/tools/timing.js +29 -0
  196. package/dist/analyzers/tools/timing.js.map +1 -0
  197. package/dist/analyzers/tools/tool-registry.d.ts +86 -0
  198. package/dist/analyzers/tools/tool-registry.d.ts.map +1 -0
  199. package/dist/analyzers/tools/tool-registry.js +705 -0
  200. package/dist/analyzers/tools/tool-registry.js.map +1 -0
  201. package/dist/analyzers/types.d.ts +125 -0
  202. package/dist/analyzers/types.d.ts.map +1 -0
  203. package/dist/analyzers/types.js +11 -0
  204. package/dist/analyzers/types.js.map +1 -0
  205. package/dist/cli.d.ts.map +1 -1
  206. package/dist/cli.js +405 -0
  207. package/dist/cli.js.map +1 -1
  208. package/dist/detect.d.ts.map +1 -1
  209. package/dist/detect.js +24 -15
  210. package/dist/detect.js.map +1 -1
  211. package/dist/languages/csharp.d.ts +5 -0
  212. package/dist/languages/csharp.d.ts.map +1 -0
  213. package/dist/languages/csharp.js +265 -0
  214. package/dist/languages/csharp.js.map +1 -0
  215. package/dist/languages/go.d.ts +11 -0
  216. package/dist/languages/go.d.ts.map +1 -0
  217. package/dist/languages/go.js +321 -0
  218. package/dist/languages/go.js.map +1 -0
  219. package/dist/languages/index.d.ts +6 -0
  220. package/dist/languages/index.d.ts.map +1 -0
  221. package/dist/languages/index.js +18 -0
  222. package/dist/languages/index.js.map +1 -0
  223. package/dist/languages/python.d.ts +3 -0
  224. package/dist/languages/python.d.ts.map +1 -0
  225. package/dist/languages/python.js +284 -0
  226. package/dist/languages/python.js.map +1 -0
  227. package/dist/languages/rust.d.ts +17 -0
  228. package/dist/languages/rust.d.ts.map +1 -0
  229. package/dist/languages/rust.js +333 -0
  230. package/dist/languages/rust.js.map +1 -0
  231. package/dist/languages/types.d.ts +38 -0
  232. package/dist/languages/types.d.ts.map +1 -0
  233. package/dist/languages/types.js +3 -0
  234. package/dist/languages/types.js.map +1 -0
  235. package/dist/languages/typescript.d.ts +15 -0
  236. package/dist/languages/typescript.d.ts.map +1 -0
  237. package/dist/languages/typescript.js +353 -0
  238. package/dist/languages/typescript.js.map +1 -0
  239. package/dist/logger.d.ts +1 -0
  240. package/dist/logger.d.ts.map +1 -1
  241. package/dist/logger.js +25 -12
  242. package/dist/logger.js.map +1 -1
  243. package/dist/project-yaml.d.ts.map +1 -1
  244. package/dist/project-yaml.js +1 -0
  245. package/dist/project-yaml.js.map +1 -1
  246. package/dist/tools-cli.d.ts +2 -0
  247. package/dist/tools-cli.d.ts.map +1 -0
  248. package/dist/tools-cli.js +231 -0
  249. package/dist/tools-cli.js.map +1 -0
  250. package/dist/types.d.ts +10 -0
  251. package/dist/types.d.ts.map +1 -1
  252. package/package.json +6 -2
  253. package/templates/.claude/commands/dev-report.md +34 -4
  254. package/templates/.claude/commands/health.md +45 -2
  255. package/templates/.claude/commands/quality.md.template +38 -15
  256. package/templates/.claude/commands/test-gaps.md +36 -2
  257. package/templates/.claude/commands/vulnerabilities.md +36 -2
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Gitleaks integration -- secret scanning with 800+ patterns.
3
+ * Layer 2 (optional): requires `gitleaks` binary.
4
+ */
5
+ import { HealthMetrics } from '../types';
6
+ /** Gather secret scanning metrics via gitleaks. */
7
+ export declare function gatherGitleaksMetrics(cwd: string): Partial<HealthMetrics>;
8
+ //# sourceMappingURL=gitleaks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gitleaks.d.ts","sourceRoot":"","sources":["../../../src/analyzers/tools/gitleaks.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAczC,mDAAmD;AACnD,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CA4DzE"}
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.gatherGitleaksMetrics = gatherGitleaksMetrics;
4
+ const runner_1 = require("./runner");
5
+ const tool_registry_1 = require("./tool-registry");
6
+ const exclusions_1 = require("./exclusions");
7
+ const suppressions_1 = require("./suppressions");
8
+ /** Gather secret scanning metrics via gitleaks. */
9
+ function gatherGitleaksMetrics(cwd) {
10
+ const gitleaksCmd = findGitleaks(cwd);
11
+ if (!gitleaksCmd) {
12
+ return { toolsUnavailable: ['gitleaks'] };
13
+ }
14
+ // Run gitleaks with JSON report (--no-git scans files, not git history)
15
+ const reportPath = `/tmp/dxkit-gitleaks-${Date.now()}.json`;
16
+ (0, runner_1.run)(`${gitleaksCmd} detect --source '${cwd}' --report-format json --report-path '${reportPath}' --no-git --exit-code 0 2>/dev/null`, cwd, 120000);
17
+ // Read report file
18
+ const reportRaw = (0, runner_1.run)(`cat '${reportPath}' 2>/dev/null`, cwd);
19
+ // Clean up
20
+ (0, runner_1.run)(`rm -f '${reportPath}'`, cwd);
21
+ if (!reportRaw) {
22
+ return { toolsUnavailable: ['gitleaks (no output)'] };
23
+ }
24
+ try {
25
+ const findings = JSON.parse(reportRaw);
26
+ if (!Array.isArray(findings)) {
27
+ return { toolsUsed: ['gitleaks'] };
28
+ }
29
+ const secretDetails = findings.map((f) => ({
30
+ file: f.File.replace(cwd + '/', '').replace(cwd, ''),
31
+ line: f.StartLine,
32
+ rule: f.RuleID,
33
+ severity: f.RuleID.includes('private-key') ? 'critical' : 'high',
34
+ }));
35
+ // Post-filter using project exclusions. Gitleaks --no-git scans everything
36
+ // on disk (ignores .gitignore), so we re-apply the resolved exclusion set
37
+ // via the centralized isExcludedPath() predicate.
38
+ const filtered = secretDetails.filter((d) => !(0, exclusions_1.isExcludedPath)(cwd, d.file));
39
+ // Apply user-defined suppressions from `.dxkit-suppressions.json` so
40
+ // known-false positives (test fixtures, approved exceptions) don't count.
41
+ const suppressions = (0, suppressions_1.loadSuppressions)(cwd);
42
+ const { kept, suppressed } = (0, suppressions_1.applySuppressions)(filtered, suppressions.gitleaks, (d) => d.rule, (d) => d.file);
43
+ return {
44
+ secretFindings: kept.length,
45
+ secretDetails: kept,
46
+ secretSuppressed: suppressed.length,
47
+ toolsUsed: ['gitleaks'],
48
+ };
49
+ }
50
+ catch {
51
+ return { toolsUnavailable: ['gitleaks (parse error)'] };
52
+ }
53
+ }
54
+ function findGitleaks(cwd) {
55
+ const status = (0, tool_registry_1.findTool)(tool_registry_1.TOOL_DEFS.gitleaks, cwd);
56
+ return status.available ? status.path : null;
57
+ }
58
+ //# sourceMappingURL=gitleaks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gitleaks.js","sourceRoot":"","sources":["../../../src/analyzers/tools/gitleaks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { HealthMetrics } from '../types';
2
+ /** Gather AST-derived metrics via graphify. */
3
+ export declare function gatherGraphifyMetrics(cwd: string): Partial<HealthMetrics>;
4
+ //# sourceMappingURL=graphify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graphify.d.ts","sourceRoot":"","sources":["../../../src/analyzers/tools/graphify.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAyIzC,+CAA+C;AAC/C,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAqDzE"}
@@ -0,0 +1,222 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.gatherGraphifyMetrics = gatherGraphifyMetrics;
37
+ /**
38
+ * Graphify integration -- deterministic AST extraction via tree-sitter.
39
+ * Layer 2 (optional): requires `pip install graphifyy`.
40
+ *
41
+ * Runs graphify's Python API via subprocess, parses structured JSON output.
42
+ * All metrics are derived from EXTRACTED confidence edges only (no LLM).
43
+ */
44
+ const fs = __importStar(require("fs"));
45
+ const runner_1 = require("./runner");
46
+ const tool_registry_1 = require("./tool-registry");
47
+ const exclusions_1 = require("./exclusions");
48
+ /** Build the graphify Python script with cwd-specific exclusions baked in. */
49
+ function buildGraphifyScript(cwd) {
50
+ return `# Exclusion set derived from src/analyzers/tools/exclusions.ts
51
+ import json, sys, os, tempfile
52
+ from pathlib import Path
53
+ from collections import Counter
54
+
55
+ # Redirect graphify cache to /tmp so we don't pollute the target repo
56
+ _cache_dir = Path(tempfile.mkdtemp(prefix='dxkit-graphify-'))
57
+
58
+ try:
59
+ from graphify.extract import extract, collect_files
60
+ from graphify.build import build
61
+ from graphify.cluster import cluster, score_all
62
+ from graphify.analyze import god_nodes
63
+ except ImportError:
64
+ print(json.dumps({"error": "graphify not installed"}))
65
+ sys.exit(0)
66
+
67
+ target = Path(sys.argv[1])
68
+
69
+ # collect_files doesn't exclude node_modules etc, so filter manually
70
+ EXCLUDE_DIRS = ${(0, exclusions_1.getPythonExcludeSet)(cwd)}
71
+ all_files = collect_files(target)
72
+ files = [f for f in all_files if not any(ex in f.parts for ex in EXCLUDE_DIRS)]
73
+ if not files:
74
+ print(json.dumps({"error": "no files found"}))
75
+ sys.exit(0)
76
+
77
+ # Monkey-patch cache to use /tmp instead of target repo
78
+ import graphify.cache as _gc
79
+ _gc.cache_dir = lambda root=None: _cache_dir / "cache"
80
+ (_cache_dir / "cache").mkdir(parents=True, exist_ok=True)
81
+
82
+ # Suppress progress output by redirecting stdout during extraction
83
+ import io
84
+ _real_stdout = sys.stdout
85
+ sys.stdout = io.StringIO()
86
+ result = extract(files)
87
+ sys.stdout = _real_stdout
88
+ G = build([result], directed=True)
89
+ communities = cluster(G)
90
+
91
+ # Functions vs modules
92
+ nodes = list(G.nodes(data=True))
93
+ functions = [(n, d) for n, d in nodes if "()" in d.get("label", "")]
94
+ modules = [(n, d) for n, d in nodes if "()" not in d.get("label", "")]
95
+
96
+ # Functions per file
97
+ file_funcs = Counter()
98
+ for n, d in functions:
99
+ sf = d.get("source_file", "")
100
+ file_funcs[sf] += 1
101
+
102
+ max_file = file_funcs.most_common(1)[0] if file_funcs else ("", 0)
103
+
104
+ # God nodes (degree > 15)
105
+ gods = god_nodes(G, top_n=50)
106
+ god_count = sum(1 for g in gods if g["edges"] > 15)
107
+
108
+ # Cohesion
109
+ scores = score_all(G, communities) if communities else {}
110
+ avg_cohesion = sum(scores.values()) / len(scores) if scores else 0.0
111
+
112
+ # Orphan modules (no inbound imports)
113
+ import_targets = set()
114
+ for u, v, data in G.edges(data=True):
115
+ if data.get("relation") == "imports_from":
116
+ import_targets.add(v)
117
+ module_ids = set(n for n, d in modules)
118
+ orphans = module_ids - import_targets
119
+
120
+ # Dead imports (imported but never called)
121
+ call_targets = set()
122
+ for u, v, data in G.edges(data=True):
123
+ if data.get("relation") == "calls":
124
+ call_targets.add(v)
125
+ dead = import_targets - call_targets - module_ids
126
+
127
+ # Commented code ratio: source files with 0 function/class AST nodes
128
+ source_files_set = set()
129
+ files_with_nodes = set()
130
+ for n, d in nodes:
131
+ sf = d.get("source_file", "")
132
+ if sf:
133
+ source_files_set.add(sf)
134
+ if "()" in d.get("label", "") or any(
135
+ data.get("relation") == "method"
136
+ for _, _, data in G.edges(n, data=True)
137
+ ):
138
+ files_with_nodes.add(sf)
139
+
140
+ total_src = len(source_files_set)
141
+ empty_files = total_src - len(files_with_nodes)
142
+ commented_ratio = empty_files / total_src if total_src > 0 else 0.0
143
+
144
+ # Clean up temp cache
145
+ import shutil
146
+ shutil.rmtree(str(_cache_dir), ignore_errors=True)
147
+
148
+ print(json.dumps({
149
+ "functionCount": len(functions),
150
+ "classCount": len([n for n, d in modules if any(
151
+ data.get("relation") == "method" for _, _, data in G.edges(n, data=True)
152
+ )]),
153
+ "maxFunctionsInFile": max_file[1] if max_file else 0,
154
+ "maxFunctionsFilePath": str(max_file[0]) if max_file else "",
155
+ "godNodeCount": god_count,
156
+ "communityCount": len(communities),
157
+ "avgCohesion": round(avg_cohesion, 3),
158
+ "orphanModuleCount": len(orphans),
159
+ "deadImportCount": len(dead),
160
+ "commentedCodeRatio": round(commented_ratio, 3),
161
+ "sourceFilesInGraph": total_src,
162
+ }))
163
+ `;
164
+ }
165
+ /** Gather AST-derived metrics via graphify. */
166
+ function gatherGraphifyMetrics(cwd) {
167
+ // findPython already verifies graphify is importable
168
+ const pythonCmd = findPython(cwd);
169
+ if (!pythonCmd) {
170
+ return { toolsUnavailable: ['graphify (not installed)'] };
171
+ }
172
+ // Write script to temp file to avoid shell escaping issues
173
+ const scriptPath = `/tmp/dxkit-graphify-${Date.now()}.py`;
174
+ fs.writeFileSync(scriptPath, buildGraphifyScript(cwd));
175
+ // Redirect stderr to suppress progress output, run from /tmp to avoid writing to target
176
+ const output = (0, runner_1.run)(`cd /tmp && ${pythonCmd} '${scriptPath}' '${cwd}' 2>/dev/null`, cwd, 120000);
177
+ try {
178
+ fs.unlinkSync(scriptPath);
179
+ }
180
+ catch {
181
+ /* ignore */
182
+ }
183
+ if (!output) {
184
+ return { toolsUnavailable: ['graphify (failed to run)'] };
185
+ }
186
+ // Graphify prints progress to stdout before the JSON — extract only the JSON line
187
+ const jsonLine = output
188
+ .split('\n')
189
+ .filter((l) => l.startsWith('{'))
190
+ .pop();
191
+ if (!jsonLine) {
192
+ return { toolsUnavailable: ['graphify (no JSON output)'] };
193
+ }
194
+ try {
195
+ const data = JSON.parse(jsonLine);
196
+ if (data.error) {
197
+ return { toolsUnavailable: [`graphify (${data.error})`] };
198
+ }
199
+ return {
200
+ functionCount: data.functionCount,
201
+ classCount: data.classCount,
202
+ maxFunctionsInFile: data.maxFunctionsInFile,
203
+ maxFunctionsFilePath: data.maxFunctionsFilePath,
204
+ godNodeCount: data.godNodeCount,
205
+ communityCount: data.communityCount,
206
+ avgCohesion: data.avgCohesion,
207
+ orphanModuleCount: data.orphanModuleCount,
208
+ deadImportCount: data.deadImportCount,
209
+ commentedCodeRatio: data.commentedCodeRatio,
210
+ toolsUsed: ['graphify'],
211
+ };
212
+ }
213
+ catch {
214
+ return { toolsUnavailable: ['graphify (parse error)'] };
215
+ }
216
+ }
217
+ /** Find a working python3 that has graphify installed. Delegates to tool-registry. */
218
+ function findPython(cwd) {
219
+ const status = (0, tool_registry_1.findTool)(tool_registry_1.TOOL_DEFS.graphify, cwd);
220
+ return status.available ? status.path : null;
221
+ }
222
+ //# sourceMappingURL=graphify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graphify.js","sourceRoot":"","sources":["../../../src/analyzers/tools/graphify.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * OSV.dev severity enrichment.
3
+ *
4
+ * Several dependency scanners (pip-audit, govulncheck) report vulnerabilities
5
+ * without per-finding severity tiers. This module looks up the vulnerability
6
+ * IDs against https://api.osv.dev/v1/vulns/{id} and classifies them into
7
+ * critical/high/medium/low buckets.
8
+ *
9
+ * Offline safety: if the API is unreachable or an ID is unknown, the caller
10
+ * falls back to a default bucket (pip-audit → medium, govulncheck → high).
11
+ * The analyzer must never fail because OSV was slow.
12
+ */
13
+ export type Severity = 'critical' | 'high' | 'medium' | 'low' | 'unknown';
14
+ export interface OsvVuln {
15
+ id?: string;
16
+ severity?: Array<{
17
+ type: string;
18
+ score: string;
19
+ }>;
20
+ database_specific?: {
21
+ severity?: string;
22
+ };
23
+ affected?: Array<{
24
+ severity?: Array<{
25
+ type: string;
26
+ score: string;
27
+ }>;
28
+ }>;
29
+ }
30
+ /** NVD CVSS 3.x base-score bands. */
31
+ export declare function scoreToTier(score: number): Severity;
32
+ /**
33
+ * Minimal CVSS v3.0/3.1 base-score calculator.
34
+ * Spec: https://www.first.org/cvss/v3.1/specification-document#7-1-Base-Metrics-Equations
35
+ * Returns null if the vector is malformed or missing required metrics.
36
+ */
37
+ export declare function parseCvssV3BaseScore(vector: string): number | null;
38
+ /** Classify a single OSV record. Prefers CVSS vector, then database_specific string. */
39
+ export declare function classifyOsvSeverity(vuln: OsvVuln): Severity;
40
+ /** Signature of the fetcher — swapped in tests to avoid real network. */
41
+ export type OsvFetcher = (id: string) => Promise<OsvVuln | null>;
42
+ /**
43
+ * Look up severities for a batch of vuln IDs.
44
+ * Runs requests in parallel, session-caches results, returns a map keyed by ID.
45
+ * IDs that fail (network error, 404, unparseable) map to 'unknown' — caller
46
+ * uses a per-scanner default bucket for those.
47
+ */
48
+ export declare function enrichSeverities(ids: string[], fetcher?: OsvFetcher): Promise<Map<string, Severity>>;
49
+ /** Test-only — reset the process cache between tests. */
50
+ export declare function __clearOsvCache(): void;
51
+ //# sourceMappingURL=osv.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"osv.d.ts","sourceRoot":"","sources":["../../../src/analyzers/tools/osv.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,MAAM,MAAM,QAAQ,GAAG,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,GAAG,SAAS,CAAC;AAE1E,MAAM,WAAW,OAAO;IACtB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClD,iBAAiB,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1C,QAAQ,CAAC,EAAE,KAAK,CAAC;QACf,QAAQ,CAAC,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;KACnD,CAAC,CAAC;CACJ;AAKD,qCAAqC;AACrC,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,CAMnD;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CA2ClE;AAED,wFAAwF;AACxF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,OAAO,GAAG,QAAQ,CAiC3D;AAED,yEAAyE;AACzE,MAAM,MAAM,UAAU,GAAG,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;AAwBjE;;;;;GAKG;AACH,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,MAAM,EAAE,EACb,OAAO,GAAE,UAA4B,GACpC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CA2BhC;AAED,yDAAyD;AACzD,wBAAgB,eAAe,IAAI,IAAI,CAEtC"}
@@ -0,0 +1,188 @@
1
+ "use strict";
2
+ /**
3
+ * OSV.dev severity enrichment.
4
+ *
5
+ * Several dependency scanners (pip-audit, govulncheck) report vulnerabilities
6
+ * without per-finding severity tiers. This module looks up the vulnerability
7
+ * IDs against https://api.osv.dev/v1/vulns/{id} and classifies them into
8
+ * critical/high/medium/low buckets.
9
+ *
10
+ * Offline safety: if the API is unreachable or an ID is unknown, the caller
11
+ * falls back to a default bucket (pip-audit → medium, govulncheck → high).
12
+ * The analyzer must never fail because OSV was slow.
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.scoreToTier = scoreToTier;
16
+ exports.parseCvssV3BaseScore = parseCvssV3BaseScore;
17
+ exports.classifyOsvSeverity = classifyOsvSeverity;
18
+ exports.enrichSeverities = enrichSeverities;
19
+ exports.__clearOsvCache = __clearOsvCache;
20
+ const cvss_v4_1 = require("./cvss-v4");
21
+ /** Process-scoped cache so repeated lookups in a session don't re-query. */
22
+ const cache = new Map();
23
+ /** NVD CVSS 3.x base-score bands. */
24
+ function scoreToTier(score) {
25
+ if (score >= 9.0)
26
+ return 'critical';
27
+ if (score >= 7.0)
28
+ return 'high';
29
+ if (score >= 4.0)
30
+ return 'medium';
31
+ if (score > 0.0)
32
+ return 'low';
33
+ return 'unknown';
34
+ }
35
+ /**
36
+ * Minimal CVSS v3.0/3.1 base-score calculator.
37
+ * Spec: https://www.first.org/cvss/v3.1/specification-document#7-1-Base-Metrics-Equations
38
+ * Returns null if the vector is malformed or missing required metrics.
39
+ */
40
+ function parseCvssV3BaseScore(vector) {
41
+ if (!vector.startsWith('CVSS:3.'))
42
+ return null;
43
+ const parts = new Map();
44
+ for (const kv of vector.split('/').slice(1)) {
45
+ const [k, v] = kv.split(':');
46
+ if (k && v)
47
+ parts.set(k, v);
48
+ }
49
+ // Required base metrics
50
+ const AV = parts.get('AV');
51
+ const AC = parts.get('AC');
52
+ const PR = parts.get('PR');
53
+ const UI = parts.get('UI');
54
+ const S = parts.get('S');
55
+ const C = parts.get('C');
56
+ const I = parts.get('I');
57
+ const A = parts.get('A');
58
+ if (!AV || !AC || !PR || !UI || !S || !C || !I || !A)
59
+ return null;
60
+ const avWeights = { N: 0.85, A: 0.62, L: 0.55, P: 0.2 };
61
+ const acWeights = { L: 0.77, H: 0.44 };
62
+ const prUnchanged = { N: 0.85, L: 0.62, H: 0.27 };
63
+ const prChanged = { N: 0.85, L: 0.68, H: 0.5 };
64
+ const uiWeights = { N: 0.85, R: 0.62 };
65
+ const ciaWeights = { H: 0.56, L: 0.22, N: 0 };
66
+ const av = avWeights[AV];
67
+ const ac = acWeights[AC];
68
+ const pr = S === 'C' ? prChanged[PR] : prUnchanged[PR];
69
+ const ui = uiWeights[UI];
70
+ const conf = ciaWeights[C];
71
+ const integ = ciaWeights[I];
72
+ const avail = ciaWeights[A];
73
+ if ([av, ac, pr, ui, conf, integ, avail].some((x) => x === undefined))
74
+ return null;
75
+ const iss = 1 - (1 - conf) * (1 - integ) * (1 - avail);
76
+ const impact = S === 'C' ? 7.52 * (iss - 0.029) - 3.25 * Math.pow(iss - 0.02, 15) : 6.42 * iss;
77
+ if (impact <= 0)
78
+ return 0;
79
+ const exploitability = 8.22 * av * ac * pr * ui;
80
+ const raw = S === 'C' ? 1.08 * (impact + exploitability) : impact + exploitability;
81
+ const base = Math.min(raw, 10);
82
+ // CVSS "round up to one decimal" (ceil to nearest 0.1)
83
+ return Math.ceil(base * 10) / 10;
84
+ }
85
+ /** Classify a single OSV record. Prefers CVSS vector, then database_specific string. */
86
+ function classifyOsvSeverity(vuln) {
87
+ // Collect CVSS_V4 + CVSS_V3 entries (top level and inside affected[].severity[]).
88
+ // V4 is preferred when available since modern CVEs (2025+) increasingly use V4 only.
89
+ const v4 = [];
90
+ const v3 = [];
91
+ const collect = (entries) => {
92
+ for (const s of entries ?? []) {
93
+ if (!s.score)
94
+ continue;
95
+ if (s.type === 'CVSS_V4')
96
+ v4.push(s.score);
97
+ else if (s.type === 'CVSS_V3')
98
+ v3.push(s.score);
99
+ }
100
+ };
101
+ collect(vuln.severity);
102
+ for (const a of vuln.affected ?? [])
103
+ collect(a.severity);
104
+ let maxScore = -1;
105
+ for (const vec of v4) {
106
+ const score = (0, cvss_v4_1.parseCvssV4BaseScore)(vec);
107
+ if (score !== null && score > maxScore)
108
+ maxScore = score;
109
+ }
110
+ for (const vec of v3) {
111
+ const score = parseCvssV3BaseScore(vec);
112
+ if (score !== null && score > maxScore)
113
+ maxScore = score;
114
+ }
115
+ if (maxScore >= 0)
116
+ return scoreToTier(maxScore);
117
+ // Fallback: database_specific.severity string (common on GHSA records)
118
+ const ds = vuln.database_specific?.severity?.toUpperCase();
119
+ if (ds === 'CRITICAL')
120
+ return 'critical';
121
+ if (ds === 'HIGH')
122
+ return 'high';
123
+ if (ds === 'MEDIUM' || ds === 'MODERATE')
124
+ return 'medium';
125
+ if (ds === 'LOW')
126
+ return 'low';
127
+ return 'unknown';
128
+ }
129
+ /**
130
+ * Per-request timeout. Must be generous enough that concurrent analyzer tools
131
+ * (gitleaks, semgrep, cloc) don't starve the fetches — 5s proved too aggressive
132
+ * under full-analyzer load. Unreachable hosts still fail fast via AbortSignal.
133
+ */
134
+ const OSV_REQUEST_TIMEOUT_MS = 10000;
135
+ const DEFAULT_FETCHER = async (id) => {
136
+ try {
137
+ const res = await fetch(`https://api.osv.dev/v1/vulns/${encodeURIComponent(id)}`, {
138
+ signal: AbortSignal.timeout(OSV_REQUEST_TIMEOUT_MS),
139
+ });
140
+ if (!res.ok)
141
+ return null;
142
+ return (await res.json());
143
+ }
144
+ catch (err) {
145
+ if (process.env.DXKIT_DEBUG_OSV) {
146
+ process.stderr.write(`[dxkit-osv] ${id}: ${err.message}\n`);
147
+ }
148
+ return null;
149
+ }
150
+ };
151
+ /**
152
+ * Look up severities for a batch of vuln IDs.
153
+ * Runs requests in parallel, session-caches results, returns a map keyed by ID.
154
+ * IDs that fail (network error, 404, unparseable) map to 'unknown' — caller
155
+ * uses a per-scanner default bucket for those.
156
+ */
157
+ async function enrichSeverities(ids, fetcher = DEFAULT_FETCHER) {
158
+ const result = new Map();
159
+ const toFetch = [];
160
+ for (const id of ids) {
161
+ if (cache.has(id)) {
162
+ result.set(id, cache.get(id));
163
+ }
164
+ else if (!toFetch.includes(id)) {
165
+ toFetch.push(id);
166
+ }
167
+ }
168
+ if (toFetch.length === 0)
169
+ return result;
170
+ const settled = await Promise.allSettled(toFetch.map(async (id) => {
171
+ const vuln = await fetcher(id);
172
+ const sev = vuln ? classifyOsvSeverity(vuln) : 'unknown';
173
+ return [id, sev];
174
+ }));
175
+ for (const p of settled) {
176
+ if (p.status === 'fulfilled') {
177
+ const [id, sev] = p.value;
178
+ cache.set(id, sev);
179
+ result.set(id, sev);
180
+ }
181
+ }
182
+ return result;
183
+ }
184
+ /** Test-only — reset the process cache between tests. */
185
+ function __clearOsvCache() {
186
+ cache.clear();
187
+ }
188
+ //# sourceMappingURL=osv.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"osv.js","sourceRoot":"","sources":["../../../src/analyzers/tools/osv.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import { HealthMetrics } from '../types';
2
+ /**
3
+ * Run cloc + gitleaks + graphify gather functions in parallel child processes.
4
+ * Each child calls the real gather function from the existing tool module.
5
+ * Falls back to sequential on low memory (<1GB free).
6
+ */
7
+ export declare function gatherLayer2Parallel(cwd: string, verbose?: boolean): Partial<HealthMetrics>;
8
+ //# sourceMappingURL=parallel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parallel.d.ts","sourceRoot":"","sources":["../../../src/analyzers/tools/parallel.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AASzC;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,UAAQ,GAAG,OAAO,CAAC,aAAa,CAAC,CAoFzF"}