@ash-technologia/fathom 0.1.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 (236) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/LICENSE +21 -0
  3. package/README.md +725 -0
  4. package/dist/analyzers/architecture/graph.d.ts +29 -0
  5. package/dist/analyzers/architecture/graph.d.ts.map +1 -0
  6. package/dist/analyzers/architecture/graph.js +347 -0
  7. package/dist/analyzers/architecture/graph.js.map +1 -0
  8. package/dist/analyzers/architecture/imports.d.ts +24 -0
  9. package/dist/analyzers/architecture/imports.d.ts.map +1 -0
  10. package/dist/analyzers/architecture/imports.js +155 -0
  11. package/dist/analyzers/architecture/imports.js.map +1 -0
  12. package/dist/analyzers/architecture/index.d.ts +13 -0
  13. package/dist/analyzers/architecture/index.d.ts.map +1 -0
  14. package/dist/analyzers/architecture/index.js +217 -0
  15. package/dist/analyzers/architecture/index.js.map +1 -0
  16. package/dist/analyzers/architecture/types.d.ts +65 -0
  17. package/dist/analyzers/architecture/types.d.ts.map +1 -0
  18. package/dist/analyzers/architecture/types.js +2 -0
  19. package/dist/analyzers/architecture/types.js.map +1 -0
  20. package/dist/analyzers/cicd/index.d.ts +11 -0
  21. package/dist/analyzers/cicd/index.d.ts.map +1 -0
  22. package/dist/analyzers/cicd/index.js +157 -0
  23. package/dist/analyzers/cicd/index.js.map +1 -0
  24. package/dist/analyzers/dependencies/index.d.ts +11 -0
  25. package/dist/analyzers/dependencies/index.d.ts.map +1 -0
  26. package/dist/analyzers/dependencies/index.js +403 -0
  27. package/dist/analyzers/dependencies/index.js.map +1 -0
  28. package/dist/analyzers/dependencies/lockfiles.d.ts +19 -0
  29. package/dist/analyzers/dependencies/lockfiles.d.ts.map +1 -0
  30. package/dist/analyzers/dependencies/lockfiles.js +172 -0
  31. package/dist/analyzers/dependencies/lockfiles.js.map +1 -0
  32. package/dist/analyzers/dependencies/online.d.ts +19 -0
  33. package/dist/analyzers/dependencies/online.d.ts.map +1 -0
  34. package/dist/analyzers/dependencies/online.js +116 -0
  35. package/dist/analyzers/dependencies/online.js.map +1 -0
  36. package/dist/analyzers/dependencies/types.d.ts +49 -0
  37. package/dist/analyzers/dependencies/types.d.ts.map +1 -0
  38. package/dist/analyzers/dependencies/types.js +2 -0
  39. package/dist/analyzers/dependencies/types.js.map +1 -0
  40. package/dist/analyzers/documentation/index.d.ts +11 -0
  41. package/dist/analyzers/documentation/index.d.ts.map +1 -0
  42. package/dist/analyzers/documentation/index.js +166 -0
  43. package/dist/analyzers/documentation/index.js.map +1 -0
  44. package/dist/analyzers/git/index.d.ts +11 -0
  45. package/dist/analyzers/git/index.d.ts.map +1 -0
  46. package/dist/analyzers/git/index.js +180 -0
  47. package/dist/analyzers/git/index.js.map +1 -0
  48. package/dist/analyzers/project/index.d.ts +11 -0
  49. package/dist/analyzers/project/index.d.ts.map +1 -0
  50. package/dist/analyzers/project/index.js +95 -0
  51. package/dist/analyzers/project/index.js.map +1 -0
  52. package/dist/analyzers/quality/index.d.ts +11 -0
  53. package/dist/analyzers/quality/index.d.ts.map +1 -0
  54. package/dist/analyzers/quality/index.js +198 -0
  55. package/dist/analyzers/quality/index.js.map +1 -0
  56. package/dist/analyzers/security/index.d.ts +11 -0
  57. package/dist/analyzers/security/index.d.ts.map +1 -0
  58. package/dist/analyzers/security/index.js +236 -0
  59. package/dist/analyzers/security/index.js.map +1 -0
  60. package/dist/analyzers/testing/index.d.ts +11 -0
  61. package/dist/analyzers/testing/index.d.ts.map +1 -0
  62. package/dist/analyzers/testing/index.js +134 -0
  63. package/dist/analyzers/testing/index.js.map +1 -0
  64. package/dist/baseline/baseline.d.ts +20 -0
  65. package/dist/baseline/baseline.d.ts.map +1 -0
  66. package/dist/baseline/baseline.js +134 -0
  67. package/dist/baseline/baseline.js.map +1 -0
  68. package/dist/baseline/compare.d.ts +14 -0
  69. package/dist/baseline/compare.d.ts.map +1 -0
  70. package/dist/baseline/compare.js +113 -0
  71. package/dist/baseline/compare.js.map +1 -0
  72. package/dist/baseline/types.d.ts +25 -0
  73. package/dist/baseline/types.d.ts.map +1 -0
  74. package/dist/baseline/types.js +2 -0
  75. package/dist/baseline/types.js.map +1 -0
  76. package/dist/cli/commands.d.ts +39 -0
  77. package/dist/cli/commands.d.ts.map +1 -0
  78. package/dist/cli/commands.js +277 -0
  79. package/dist/cli/commands.js.map +1 -0
  80. package/dist/cli/index.d.ts +3 -0
  81. package/dist/cli/index.d.ts.map +1 -0
  82. package/dist/cli/index.js +29 -0
  83. package/dist/cli/index.js.map +1 -0
  84. package/dist/cli/options.d.ts +55 -0
  85. package/dist/cli/options.d.ts.map +1 -0
  86. package/dist/cli/options.js +258 -0
  87. package/dist/cli/options.js.map +1 -0
  88. package/dist/core/analyzer.d.ts +33 -0
  89. package/dist/core/analyzer.d.ts.map +1 -0
  90. package/dist/core/analyzer.js +19 -0
  91. package/dist/core/analyzer.js.map +1 -0
  92. package/dist/core/context.d.ts +90 -0
  93. package/dist/core/context.d.ts.map +1 -0
  94. package/dist/core/context.js +159 -0
  95. package/dist/core/context.js.map +1 -0
  96. package/dist/core/errors.d.ts +74 -0
  97. package/dist/core/errors.d.ts.map +1 -0
  98. package/dist/core/errors.js +109 -0
  99. package/dist/core/errors.js.map +1 -0
  100. package/dist/core/findings.d.ts +59 -0
  101. package/dist/core/findings.d.ts.map +1 -0
  102. package/dist/core/findings.js +34 -0
  103. package/dist/core/findings.js.map +1 -0
  104. package/dist/core/orchestrator.d.ts +44 -0
  105. package/dist/core/orchestrator.d.ts.map +1 -0
  106. package/dist/core/orchestrator.js +160 -0
  107. package/dist/core/orchestrator.js.map +1 -0
  108. package/dist/core/result.d.ts +169 -0
  109. package/dist/core/result.d.ts.map +1 -0
  110. package/dist/core/result.js +2 -0
  111. package/dist/core/result.js.map +1 -0
  112. package/dist/detectors/framework.d.ts +22 -0
  113. package/dist/detectors/framework.d.ts.map +1 -0
  114. package/dist/detectors/framework.js +147 -0
  115. package/dist/detectors/framework.js.map +1 -0
  116. package/dist/detectors/language.d.ts +13 -0
  117. package/dist/detectors/language.d.ts.map +1 -0
  118. package/dist/detectors/language.js +100 -0
  119. package/dist/detectors/language.js.map +1 -0
  120. package/dist/detectors/package-manager.d.ts +17 -0
  121. package/dist/detectors/package-manager.d.ts.map +1 -0
  122. package/dist/detectors/package-manager.js +67 -0
  123. package/dist/detectors/package-manager.js.map +1 -0
  124. package/dist/detectors/project-type.d.ts +20 -0
  125. package/dist/detectors/project-type.d.ts.map +1 -0
  126. package/dist/detectors/project-type.js +48 -0
  127. package/dist/detectors/project-type.js.map +1 -0
  128. package/dist/diff/analyzer.d.ts +16 -0
  129. package/dist/diff/analyzer.d.ts.map +1 -0
  130. package/dist/diff/analyzer.js +296 -0
  131. package/dist/diff/analyzer.js.map +1 -0
  132. package/dist/diff/git-diff.d.ts +44 -0
  133. package/dist/diff/git-diff.d.ts.map +1 -0
  134. package/dist/diff/git-diff.js +256 -0
  135. package/dist/diff/git-diff.js.map +1 -0
  136. package/dist/diff/types.d.ts +75 -0
  137. package/dist/diff/types.d.ts.map +1 -0
  138. package/dist/diff/types.js +2 -0
  139. package/dist/diff/types.js.map +1 -0
  140. package/dist/integrations/github/context.d.ts +22 -0
  141. package/dist/integrations/github/context.d.ts.map +1 -0
  142. package/dist/integrations/github/context.js +115 -0
  143. package/dist/integrations/github/context.js.map +1 -0
  144. package/dist/integrations/github/types.d.ts +30 -0
  145. package/dist/integrations/github/types.d.ts.map +1 -0
  146. package/dist/integrations/github/types.js +2 -0
  147. package/dist/integrations/github/types.js.map +1 -0
  148. package/dist/plugins/context.d.ts +13 -0
  149. package/dist/plugins/context.d.ts.map +1 -0
  150. package/dist/plugins/context.js +102 -0
  151. package/dist/plugins/context.js.map +1 -0
  152. package/dist/plugins/examples/react.d.ts +8 -0
  153. package/dist/plugins/examples/react.d.ts.map +1 -0
  154. package/dist/plugins/examples/react.js +129 -0
  155. package/dist/plugins/examples/react.js.map +1 -0
  156. package/dist/plugins/index.d.ts +5 -0
  157. package/dist/plugins/index.d.ts.map +1 -0
  158. package/dist/plugins/index.js +4 -0
  159. package/dist/plugins/index.js.map +1 -0
  160. package/dist/plugins/registry.d.ts +45 -0
  161. package/dist/plugins/registry.d.ts.map +1 -0
  162. package/dist/plugins/registry.js +236 -0
  163. package/dist/plugins/registry.js.map +1 -0
  164. package/dist/plugins/types.d.ts +148 -0
  165. package/dist/plugins/types.d.ts.map +1 -0
  166. package/dist/plugins/types.js +2 -0
  167. package/dist/plugins/types.js.map +1 -0
  168. package/dist/reporters/html.d.ts +11 -0
  169. package/dist/reporters/html.d.ts.map +1 -0
  170. package/dist/reporters/html.js +1352 -0
  171. package/dist/reporters/html.js.map +1 -0
  172. package/dist/reporters/json.d.ts +10 -0
  173. package/dist/reporters/json.d.ts.map +1 -0
  174. package/dist/reporters/json.js +11 -0
  175. package/dist/reporters/json.js.map +1 -0
  176. package/dist/reporters/markdown.d.ts +7 -0
  177. package/dist/reporters/markdown.d.ts.map +1 -0
  178. package/dist/reporters/markdown.js +74 -0
  179. package/dist/reporters/markdown.js.map +1 -0
  180. package/dist/reporters/sarif.d.ts +133 -0
  181. package/dist/reporters/sarif.d.ts.map +1 -0
  182. package/dist/reporters/sarif.js +268 -0
  183. package/dist/reporters/sarif.js.map +1 -0
  184. package/dist/reporters/terminal.d.ts +17 -0
  185. package/dist/reporters/terminal.d.ts.map +1 -0
  186. package/dist/reporters/terminal.js +423 -0
  187. package/dist/reporters/terminal.js.map +1 -0
  188. package/dist/rules/categories.d.ts +8 -0
  189. package/dist/rules/categories.d.ts.map +1 -0
  190. package/dist/rules/categories.js +23 -0
  191. package/dist/rules/categories.js.map +1 -0
  192. package/dist/rules/definitions.d.ts +25 -0
  193. package/dist/rules/definitions.d.ts.map +1 -0
  194. package/dist/rules/definitions.js +429 -0
  195. package/dist/rules/definitions.js.map +1 -0
  196. package/dist/rules/registry.d.ts +36 -0
  197. package/dist/rules/registry.d.ts.map +1 -0
  198. package/dist/rules/registry.js +64 -0
  199. package/dist/rules/registry.js.map +1 -0
  200. package/dist/rules/severity.d.ts +13 -0
  201. package/dist/rules/severity.d.ts.map +1 -0
  202. package/dist/rules/severity.js +15 -0
  203. package/dist/rules/severity.js.map +1 -0
  204. package/dist/scoring/confidence.d.ts +12 -0
  205. package/dist/scoring/confidence.d.ts.map +1 -0
  206. package/dist/scoring/confidence.js +22 -0
  207. package/dist/scoring/confidence.js.map +1 -0
  208. package/dist/scoring/score.d.ts +27 -0
  209. package/dist/scoring/score.d.ts.map +1 -0
  210. package/dist/scoring/score.js +81 -0
  211. package/dist/scoring/score.js.map +1 -0
  212. package/dist/scoring/weights.d.ts +11 -0
  213. package/dist/scoring/weights.d.ts.map +1 -0
  214. package/dist/scoring/weights.js +26 -0
  215. package/dist/scoring/weights.js.map +1 -0
  216. package/dist/utils/filesystem.d.ts +65 -0
  217. package/dist/utils/filesystem.d.ts.map +1 -0
  218. package/dist/utils/filesystem.js +299 -0
  219. package/dist/utils/filesystem.js.map +1 -0
  220. package/dist/utils/git.d.ts +47 -0
  221. package/dist/utils/git.d.ts.map +1 -0
  222. package/dist/utils/git.js +125 -0
  223. package/dist/utils/git.js.map +1 -0
  224. package/dist/utils/logger.d.ts +14 -0
  225. package/dist/utils/logger.d.ts.map +1 -0
  226. package/dist/utils/logger.js +25 -0
  227. package/dist/utils/logger.js.map +1 -0
  228. package/dist/utils/paths.d.ts +30 -0
  229. package/dist/utils/paths.d.ts.map +1 -0
  230. package/dist/utils/paths.js +48 -0
  231. package/dist/utils/paths.js.map +1 -0
  232. package/dist/utils/timing.d.ts +12 -0
  233. package/dist/utils/timing.d.ts.map +1 -0
  234. package/dist/utils/timing.js +19 -0
  235. package/dist/utils/timing.js.map +1 -0
  236. package/package.json +73 -0
@@ -0,0 +1,95 @@
1
+ import { createFindingId } from '../../core/findings.js';
2
+ import { createTimer } from '../../utils/timing.js';
3
+ export class ProjectAnalyzer {
4
+ id = 'project';
5
+ name = 'Project';
6
+ category = 'project';
7
+ description = 'Detects ecosystems, frameworks, package managers, and repository size.';
8
+ async analyze(context) {
9
+ const elapsed = createTimer();
10
+ const findings = [];
11
+ const warnings = [];
12
+ const sourceFiles = context.files.filter((f) => f.isSource);
13
+ const testFiles = context.files.filter((f) => f.isTest);
14
+ const totalSizeMb = +(context.metadata.totalSizeBytes / (1024 * 1024)).toFixed(2);
15
+ const metrics = {
16
+ totalFiles: context.files.length,
17
+ sourceFiles: sourceFiles.length,
18
+ testFiles: testFiles.length,
19
+ totalSizeMb,
20
+ languages: context.languages.map((l) => l.name),
21
+ frameworks: context.frameworks.map((f) => f.name),
22
+ packageManagers: context.packageManagers.map((pm) => pm.name),
23
+ projectType: context.projectType.type,
24
+ };
25
+ // PROJ-001: Ecosystem (informational)
26
+ if (context.languages.length === 0) {
27
+ findings.push({
28
+ id: createFindingId('PROJ-001', undefined, undefined),
29
+ ruleId: 'PROJ-001',
30
+ category: 'project',
31
+ severity: 'info',
32
+ title: 'No recognized ecosystem detected',
33
+ description: 'Fathom could not detect a primary programming ecosystem.',
34
+ recommendation: 'Verify that source files are present and not excluded.',
35
+ confidence: 0.8,
36
+ autoFixable: false,
37
+ });
38
+ }
39
+ // PROJ-002: Frameworks detected
40
+ if (context.frameworks.length > 0) {
41
+ const fwNames = context.frameworks.map((f) => f.name).join(', ');
42
+ findings.push({
43
+ id: createFindingId('PROJ-002', undefined, undefined),
44
+ ruleId: 'PROJ-002',
45
+ category: 'project',
46
+ severity: 'info',
47
+ title: `Frameworks detected: ${fwNames}`,
48
+ description: `Detected frameworks/libraries: ${fwNames}.`,
49
+ recommendation: 'Keep framework dependencies up to date with security releases.',
50
+ confidence: 0.95,
51
+ autoFixable: false,
52
+ });
53
+ }
54
+ // PROJ-003: Package managers detected
55
+ if (context.packageManagers.length > 0) {
56
+ const pmNames = context.packageManagers.map((p) => p.name).join(', ');
57
+ findings.push({
58
+ id: createFindingId('PROJ-003', undefined, undefined),
59
+ ruleId: 'PROJ-003',
60
+ category: 'project',
61
+ severity: 'info',
62
+ title: `Package manager detected: ${pmNames}`,
63
+ description: `Detected package manager(s): ${pmNames}.`,
64
+ recommendation: 'Ensure consistent package manager usage across your team.',
65
+ confidence: 0.95,
66
+ autoFixable: false,
67
+ });
68
+ }
69
+ // PROJ-004: Large repository notice
70
+ if (context.files.length > 10000) {
71
+ findings.push({
72
+ id: createFindingId('PROJ-004', undefined, undefined),
73
+ ruleId: 'PROJ-004',
74
+ category: 'project',
75
+ severity: 'info',
76
+ title: 'Large repository',
77
+ description: `This repository contains ${context.files.length.toLocaleString()} files (${totalSizeMb} MB). Analysis may be slower.`,
78
+ recommendation: 'Consider reviewing .fathomignore to exclude generated directories.',
79
+ confidence: 0.99,
80
+ autoFixable: false,
81
+ });
82
+ }
83
+ return {
84
+ analyzerId: this.id,
85
+ analyzerName: this.name,
86
+ category: this.category,
87
+ status: 'success',
88
+ findings,
89
+ metrics,
90
+ durationMs: elapsed(),
91
+ warnings,
92
+ };
93
+ }
94
+ }
95
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/analyzers/project/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { Analyzer } from '../../core/analyzer.js';
2
+ import type { RepositoryContext } from '../../core/context.js';
3
+ import type { AnalyzerResult } from '../../core/result.js';
4
+ export declare class QualityAnalyzer implements Analyzer {
5
+ readonly id = "quality";
6
+ readonly name = "Code Quality";
7
+ readonly category: "quality";
8
+ readonly description = "Scans source files for maintainability signals: TODOs, debug statements, large files, and empty catch blocks.";
9
+ analyze(context: RepositoryContext): Promise<AnalyzerResult>;
10
+ }
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/analyzers/quality/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAW,MAAM,sBAAsB,CAAC;AAuCpE,qBAAa,eAAgB,YAAW,QAAQ;IAC9C,QAAQ,CAAC,EAAE,aAAa;IACxB,QAAQ,CAAC,IAAI,kBAAkB;IAC/B,QAAQ,CAAC,QAAQ,EAAG,SAAS,CAAU;IACvC,QAAQ,CAAC,WAAW,mHAC8F;IAE5G,OAAO,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,cAAc,CAAC;CAmLnE"}
@@ -0,0 +1,198 @@
1
+ import { createFindingId } from '../../core/findings.js';
2
+ import { createTimer } from '../../utils/timing.js';
3
+ import { readFileSafe } from '../../utils/filesystem.js';
4
+ import { computeDepth } from '../../utils/paths.js';
5
+ const COMMENT_TODO_PATTERN = /(?:\/\/|#)\s*TODO\b|^\s*(?:\/\*|\*)\s*TODO\b/i;
6
+ const COMMENT_FIXME_PATTERN = /(?:\/\/|#)\s*FIXME\b|^\s*(?:\/\*|\*)\s*FIXME\b/i;
7
+ /** Debug statement patterns for JS/TS */
8
+ const DEBUG_PATTERNS_JS = [
9
+ /\bconsole\.(log|debug|info|warn|error|trace|dir)\s*\(/,
10
+ /\bdebugger\b/,
11
+ /\bdd\s*\(/, // Laravel dd()
12
+ /\bvar_dump\s*\(/, // PHP
13
+ ];
14
+ /** Debug statement patterns for Python only */
15
+ const DEBUG_PATTERNS_PYTHON = [
16
+ /\bpdb\.set_trace\(\)/,
17
+ /\bbreakpoint\(\)/,
18
+ /\bprint\s*\(/, // print() is a debug signal in Python source (not test/script) files
19
+ ];
20
+ /** Empty catch block patterns */
21
+ const EMPTY_CATCH_PATTERNS = [
22
+ // JS/TS: catch (e) {} or catch {}
23
+ /catch\s*\([^)]*\)\s*\{\s*\}/,
24
+ /catch\s*\{\s*\}/,
25
+ // Python: except: pass or except Exception: pass
26
+ /except\s*[^:]*:\s*\n\s*pass\s*\n/,
27
+ // Java: catch (Exception e) {}
28
+ /catch\s*\([^)]+\)\s*\{\s*\}/,
29
+ ];
30
+ const LARGE_FILE_LINE_THRESHOLD = 500;
31
+ const DEEP_NESTING_THRESHOLD = 6;
32
+ export class QualityAnalyzer {
33
+ id = 'quality';
34
+ name = 'Code Quality';
35
+ category = 'quality';
36
+ description = 'Scans source files for maintainability signals: TODOs, debug statements, large files, and empty catch blocks.';
37
+ async analyze(context) {
38
+ const elapsed = createTimer();
39
+ const findings = [];
40
+ const warnings = [];
41
+ let totalTodos = 0;
42
+ let totalFixmes = 0;
43
+ let totalDebugStatements = 0;
44
+ let emptyCatchCount = 0;
45
+ const largeFiles = [];
46
+ const sourceFiles = context.files.filter((f) => f.isSource && !f.isTest);
47
+ for (const file of sourceFiles) {
48
+ if (file.sizeBytes > 2 * 1024 * 1024) {
49
+ warnings.push(`Skipped quality scan for oversized file: ${file.relativePath}`);
50
+ continue;
51
+ }
52
+ const content = await readFileSafe(file.absolutePath);
53
+ if (!content)
54
+ continue;
55
+ const lines = content.split('\n');
56
+ // QUAL-003: Debug statements — language-aware
57
+ const isPython = file.extension === '.py' || file.extension === '.pyw';
58
+ const debugPatterns = isPython ? DEBUG_PATTERNS_PYTHON : DEBUG_PATTERNS_JS;
59
+ for (const line of lines) {
60
+ // QUAL-001 & QUAL-002: Comment-scoped TODOs and FIXMEs
61
+ if (COMMENT_TODO_PATTERN.test(line)) {
62
+ totalTodos++;
63
+ }
64
+ if (COMMENT_FIXME_PATTERN.test(line)) {
65
+ totalFixmes++;
66
+ }
67
+ // Skip pure comment lines for debug statement scan
68
+ if (/^\s*(\/\/|#|\/\*)/.test(line))
69
+ continue;
70
+ for (const pattern of debugPatterns) {
71
+ if (pattern.test(line)) {
72
+ totalDebugStatements++;
73
+ break;
74
+ }
75
+ }
76
+ }
77
+ // QUAL-004: Empty catch blocks
78
+ const strippedContent = content
79
+ .replace(/\/\*[\s\S]*?\*\//g, '')
80
+ .replace(/\/\/.*/g, '')
81
+ .replace(/#.*/g, '');
82
+ for (const pattern of EMPTY_CATCH_PATTERNS) {
83
+ if (pattern.test(strippedContent)) {
84
+ emptyCatchCount++;
85
+ findings.push({
86
+ id: createFindingId('QUAL-004', file.relativePath),
87
+ ruleId: 'QUAL-004',
88
+ category: 'quality',
89
+ severity: 'medium',
90
+ title: `Empty catch block in ${file.relativePath}`,
91
+ description: 'An empty catch block silently swallows errors, making debugging difficult.',
92
+ recommendation: 'Add error handling or at minimum log the error inside the catch block.',
93
+ confidence: 0.75,
94
+ location: { file: file.relativePath },
95
+ autoFixable: false,
96
+ });
97
+ break; // One finding per file
98
+ }
99
+ }
100
+ // QUAL-005: Large files
101
+ if (lines.length > LARGE_FILE_LINE_THRESHOLD) {
102
+ largeFiles.push(file.relativePath);
103
+ findings.push({
104
+ id: createFindingId('QUAL-005', file.relativePath),
105
+ ruleId: 'QUAL-005',
106
+ category: 'quality',
107
+ severity: 'low',
108
+ title: `Large source file: ${file.relativePath}`,
109
+ description: `This file has ${lines.length} lines. Large files can be harder to maintain and test.`,
110
+ recommendation: 'Consider splitting this into smaller, focused modules.',
111
+ confidence: 0.85,
112
+ location: { file: file.relativePath },
113
+ evidence: `${lines.length} lines`,
114
+ autoFixable: false,
115
+ });
116
+ }
117
+ }
118
+ // QUAL-001/002: Aggregate findings
119
+ if (totalTodos > 0) {
120
+ findings.push({
121
+ id: createFindingId('QUAL-001'),
122
+ ruleId: 'QUAL-001',
123
+ category: 'quality',
124
+ severity: 'low',
125
+ title: `${totalTodos} TODO comment${totalTodos > 1 ? 's' : ''} found`,
126
+ description: 'TODO comments indicate unfinished work.',
127
+ recommendation: 'Review and resolve TODO comments, or convert them to tracked issues.',
128
+ confidence: 0.99,
129
+ evidence: `${totalTodos} occurrences`,
130
+ autoFixable: false,
131
+ });
132
+ }
133
+ if (totalFixmes > 0) {
134
+ findings.push({
135
+ id: createFindingId('QUAL-002'),
136
+ ruleId: 'QUAL-002',
137
+ category: 'quality',
138
+ severity: 'low',
139
+ title: `${totalFixmes} FIXME comment${totalFixmes > 1 ? 's' : ''} found`,
140
+ description: 'FIXME comments indicate known broken or problematic code.',
141
+ recommendation: 'Prioritize resolving FIXME comments before shipping.',
142
+ confidence: 0.99,
143
+ evidence: `${totalFixmes} occurrences`,
144
+ autoFixable: false,
145
+ });
146
+ }
147
+ if (totalDebugStatements > 3) {
148
+ findings.push({
149
+ id: createFindingId('QUAL-003'),
150
+ ruleId: 'QUAL-003',
151
+ category: 'quality',
152
+ severity: 'low',
153
+ title: `${totalDebugStatements} debug statements found`,
154
+ description: 'Debug statements left in production code add noise and can expose sensitive information.',
155
+ recommendation: 'Remove or replace debug statements with proper logging.',
156
+ confidence: 0.75,
157
+ evidence: `${totalDebugStatements} occurrences`,
158
+ autoFixable: false,
159
+ });
160
+ }
161
+ // QUAL-006: Deep directory nesting
162
+ const maxDepth = context.files.reduce((max, f) => Math.max(max, computeDepth(f.relativePath)), 0);
163
+ if (maxDepth >= DEEP_NESTING_THRESHOLD) {
164
+ findings.push({
165
+ id: createFindingId('QUAL-006'),
166
+ ruleId: 'QUAL-006',
167
+ category: 'quality',
168
+ severity: 'low',
169
+ title: `Deep directory nesting detected (depth: ${maxDepth})`,
170
+ description: 'Very deep directory structures can make navigation and imports harder.',
171
+ recommendation: 'Consider flattening the directory structure where practical.',
172
+ confidence: 0.7,
173
+ evidence: `Maximum depth: ${maxDepth}`,
174
+ autoFixable: false,
175
+ });
176
+ }
177
+ const metrics = {
178
+ todoCount: totalTodos,
179
+ fixmeCount: totalFixmes,
180
+ debugStatements: totalDebugStatements,
181
+ emptyCatchBlocks: emptyCatchCount,
182
+ largeFilesCount: largeFiles.length,
183
+ maxDirectoryDepth: maxDepth,
184
+ sourceFilesScanned: sourceFiles.length,
185
+ };
186
+ return {
187
+ analyzerId: this.id,
188
+ analyzerName: this.name,
189
+ category: this.category,
190
+ status: 'success',
191
+ findings,
192
+ metrics,
193
+ durationMs: elapsed(),
194
+ warnings,
195
+ };
196
+ }
197
+ }
198
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/analyzers/quality/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { Analyzer } from '../../core/analyzer.js';
2
+ import type { RepositoryContext } from '../../core/context.js';
3
+ import type { AnalyzerResult } from '../../core/result.js';
4
+ export declare class SecurityAnalyzer implements Analyzer {
5
+ readonly id = "security";
6
+ readonly name = "Security";
7
+ readonly category: "security";
8
+ readonly description = "Checks for common security hygiene issues including exposed secrets and poor gitignore configuration.";
9
+ analyze(context: RepositoryContext): Promise<AnalyzerResult>;
10
+ }
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/analyzers/security/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAW,MAAM,sBAAsB,CAAC;AAyFpE,qBAAa,gBAAiB,YAAW,QAAQ;IAC/C,QAAQ,CAAC,EAAE,cAAc;IACzB,QAAQ,CAAC,IAAI,cAAc;IAC3B,QAAQ,CAAC,QAAQ,EAAG,UAAU,CAAU;IACxC,QAAQ,CAAC,WAAW,2GACsF;IAEpG,OAAO,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,cAAc,CAAC;CAuKnE"}
@@ -0,0 +1,236 @@
1
+ import path from 'node:path';
2
+ import { createFindingId } from '../../core/findings.js';
3
+ import { createTimer } from '../../utils/timing.js';
4
+ import { readFileSafe } from '../../utils/filesystem.js';
5
+ import { isPathMentionedInGitignore } from '../../utils/git.js';
6
+ /**
7
+ * Secret patterns — conservative regexes that minimize false positives.
8
+ * NEVER log the matched value.
9
+ */
10
+ const SECRET_PATTERNS = [
11
+ // AWS
12
+ { name: 'AWS Access Key ID', pattern: /AKIA[0-9A-Z]{16}/, confidence: 0.97 },
13
+ {
14
+ name: 'AWS Secret Access Key',
15
+ pattern: /(?:aws[_\-.]?secret[_\-.]?(?:access[_\-.]?)?key|AWS_SECRET_ACCESS_KEY)\s*[:=]\s*["']?[A-Za-z0-9/+]{40}["']?/i,
16
+ confidence: 0.85,
17
+ },
18
+ // GitHub tokens
19
+ { name: 'GitHub Personal Access Token', pattern: /ghp_[0-9a-zA-Z]{36}/, confidence: 0.99 },
20
+ { name: 'GitHub OAuth Token', pattern: /gho_[0-9a-zA-Z]{36}/, confidence: 0.99 },
21
+ { name: 'GitHub App Token', pattern: /ghs_[0-9a-zA-Z]{36}/, confidence: 0.99 },
22
+ // Generic API keys
23
+ {
24
+ name: 'Generic API Key',
25
+ pattern: /(?:api[_\-.]?key|apikey)\s*[:=]\s*["']?[A-Za-z0-9_-]{20,}["']?/i,
26
+ confidence: 0.7,
27
+ },
28
+ // Private keys
29
+ {
30
+ name: 'Private Key Block',
31
+ pattern: /-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/,
32
+ confidence: 0.99,
33
+ },
34
+ // Database URLs with credentials
35
+ {
36
+ name: 'Database Connection String',
37
+ pattern: /(?:postgres|postgresql|mysql|mongodb|redis):\/\/[^:]+:[^@]+@/,
38
+ confidence: 0.92,
39
+ },
40
+ // Stripe
41
+ { name: 'Stripe Secret Key', pattern: /sk_(?:live|test)_[0-9a-zA-Z]{24,}/, confidence: 0.99 },
42
+ // Twilio
43
+ { name: 'Twilio Account SID', pattern: /AC[0-9a-f]{32}/, confidence: 0.8 },
44
+ // Google API Key
45
+ { name: 'Google API Key', pattern: /AIza[0-9A-Za-z\-_]{35}/, confidence: 0.95 },
46
+ // Slack token
47
+ { name: 'Slack Token', pattern: /xox[boaprs]-[0-9a-zA-Z-]{10,}/, confidence: 0.95 },
48
+ // Generic "password = value" in config
49
+ {
50
+ name: 'Hardcoded Password',
51
+ pattern: /(?:password|passwd|pwd)\s*[:=]\s*["'][^"']{8,}["']/i,
52
+ confidence: 0.6,
53
+ },
54
+ ];
55
+ /**
56
+ * Files that are often credential containers and should not be tracked.
57
+ */
58
+ const SENSITIVE_FILE_PATTERNS = [
59
+ /^\.env$/,
60
+ /^\.env\..+$/,
61
+ /^\.env\.local$/,
62
+ /^secrets?\.(json|yaml|yml|toml|ini)$/i,
63
+ /^credentials?\.(json|yaml|yml|toml|ini)$/i,
64
+ /^config\/secrets\//,
65
+ ];
66
+ /**
67
+ * Private key file extensions / names.
68
+ */
69
+ const PRIVATE_KEY_PATTERNS = [
70
+ /\.pem$/,
71
+ /\.key$/,
72
+ /^id_rsa$/,
73
+ /^id_ecdsa$/,
74
+ /^id_ed25519$/,
75
+ /^id_dsa$/,
76
+ /\.p12$/,
77
+ /\.pfx$/,
78
+ ];
79
+ /**
80
+ * URL credential pattern.
81
+ */
82
+ const URL_CREDENTIAL_PATTERN = /https?:\/\/[^:@\s]+:[^@\s]+@[^\s]+/g;
83
+ export class SecurityAnalyzer {
84
+ id = 'security';
85
+ name = 'Security';
86
+ category = 'security';
87
+ description = 'Checks for common security hygiene issues including exposed secrets and poor gitignore configuration.';
88
+ async analyze(context) {
89
+ const elapsed = createTimer();
90
+ const findings = [];
91
+ const warnings = [];
92
+ let secretPatternCount = 0;
93
+ // SEC-001: .env not ignored
94
+ const envFiles = context.files.filter((f) => /^\.env($|\..+)/.test(path.basename(f.relativePath)));
95
+ for (const envFile of envFiles) {
96
+ const basename = path.basename(envFile.relativePath);
97
+ // Skip example/sample env files
98
+ if (/\.(example|sample|template)$/.test(basename))
99
+ continue;
100
+ const isIgnored = context.git.gitignoreContent
101
+ ? isPathMentionedInGitignore(context.git.gitignoreContent, basename) ||
102
+ isPathMentionedInGitignore(context.git.gitignoreContent, '.env')
103
+ : false;
104
+ if (!isIgnored) {
105
+ findings.push({
106
+ id: createFindingId('SEC-001', envFile.relativePath),
107
+ ruleId: 'SEC-001',
108
+ category: 'security',
109
+ severity: 'high',
110
+ title: `Environment file may not be gitignored: ${envFile.relativePath}`,
111
+ description: 'Environment files often contain secrets and should not be committed to Git.',
112
+ recommendation: `Add "${basename}" to .gitignore to prevent accidental commits.`,
113
+ confidence: 0.95,
114
+ location: { file: envFile.relativePath },
115
+ autoFixable: false,
116
+ references: ['https://12factor.net/config'],
117
+ });
118
+ }
119
+ }
120
+ // SEC-003: Private key files
121
+ for (const file of context.files) {
122
+ const basename = path.basename(file.relativePath);
123
+ const isPrivateKey = PRIVATE_KEY_PATTERNS.some((p) => p.test(basename));
124
+ if (isPrivateKey && !file.relativePath.includes('node_modules')) {
125
+ findings.push({
126
+ id: createFindingId('SEC-003', file.relativePath),
127
+ ruleId: 'SEC-003',
128
+ category: 'security',
129
+ severity: 'critical',
130
+ title: `Potential private key file: ${file.relativePath}`,
131
+ description: 'This file may contain a private key and should not be committed to the repository.',
132
+ recommendation: 'Add this file to .gitignore and rotate any associated credentials.',
133
+ confidence: 0.88,
134
+ location: { file: file.relativePath },
135
+ autoFixable: false,
136
+ });
137
+ }
138
+ }
139
+ // SEC-004: Tracked credential/config files
140
+ for (const file of context.files) {
141
+ const basename = path.basename(file.relativePath).toLowerCase();
142
+ const isSensitive = SENSITIVE_FILE_PATTERNS.some((p) => p.test(basename));
143
+ if (isSensitive) {
144
+ // SEC-001 already handles .env; avoid duplicate
145
+ if (basename.startsWith('.env'))
146
+ continue;
147
+ findings.push({
148
+ id: createFindingId('SEC-004', file.relativePath),
149
+ ruleId: 'SEC-004',
150
+ category: 'security',
151
+ severity: 'high',
152
+ title: `Potentially sensitive config file tracked: ${file.relativePath}`,
153
+ description: 'This file may contain credentials or secrets.',
154
+ recommendation: 'Move secrets to environment variables and add this file to .gitignore.',
155
+ confidence: 0.75,
156
+ location: { file: file.relativePath },
157
+ autoFixable: false,
158
+ });
159
+ }
160
+ }
161
+ // SEC-002 and SEC-005: Scan source/config files for secrets
162
+ const filesToScan = context.files.filter((f) => !f.isBinary &&
163
+ (f.isSource || f.isConfig || f.extension === '.yaml' || f.extension === '.yml'));
164
+ for (const file of filesToScan) {
165
+ if (file.sizeBytes > 512 * 1024) {
166
+ warnings.push(`Skipped large file for secret scan: ${file.relativePath}`);
167
+ continue;
168
+ }
169
+ const content = await readFileSafe(file.absolutePath);
170
+ if (!content)
171
+ continue;
172
+ const lines = content.split('\n');
173
+ // SEC-002: Secret patterns
174
+ for (const { name: patternName, pattern, confidence } of SECRET_PATTERNS) {
175
+ let lineIndex = 0;
176
+ for (const line of lines) {
177
+ lineIndex++;
178
+ // Skip comment lines
179
+ if (/^\s*(#|\/\/|\/\*)/.test(line))
180
+ continue;
181
+ if (pattern.test(line)) {
182
+ secretPatternCount++;
183
+ findings.push({
184
+ id: createFindingId('SEC-002', file.relativePath, lineIndex),
185
+ ruleId: 'SEC-002',
186
+ category: 'security',
187
+ severity: 'high',
188
+ title: `Potential credential detected: ${patternName}`,
189
+ description: `A value resembling a ${patternName} was found in a source file.`,
190
+ recommendation: 'Move credentials to environment variables and rotate the compromised value.',
191
+ confidence,
192
+ location: { file: file.relativePath, line: lineIndex },
193
+ // Deliberately no evidence value — never print the secret
194
+ autoFixable: false,
195
+ references: ['https://12factor.net/config'],
196
+ });
197
+ }
198
+ }
199
+ }
200
+ // SEC-005: URL credentials
201
+ const urlMatches = [...content.matchAll(URL_CREDENTIAL_PATTERN)];
202
+ for (const match of urlMatches) {
203
+ // Find the line number
204
+ const lineIndex = content.slice(0, match.index).split('\n').length;
205
+ findings.push({
206
+ id: createFindingId('SEC-005', file.relativePath, lineIndex),
207
+ ruleId: 'SEC-005',
208
+ category: 'security',
209
+ severity: 'high',
210
+ title: 'Credentials embedded in URL',
211
+ description: 'A URL containing embedded credentials was detected.',
212
+ recommendation: 'Use environment variables for usernames and passwords in connection strings.',
213
+ confidence: 0.88,
214
+ location: { file: file.relativePath, line: lineIndex },
215
+ autoFixable: false,
216
+ });
217
+ }
218
+ }
219
+ const metrics = {
220
+ envFilesFound: envFiles.length,
221
+ secretPatternsFound: secretPatternCount,
222
+ filesScanned: filesToScan.length,
223
+ };
224
+ return {
225
+ analyzerId: this.id,
226
+ analyzerName: this.name,
227
+ category: this.category,
228
+ status: 'success',
229
+ findings,
230
+ metrics,
231
+ durationMs: elapsed(),
232
+ warnings,
233
+ };
234
+ }
235
+ }
236
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/analyzers/security/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { Analyzer } from '../../core/analyzer.js';
2
+ import type { RepositoryContext } from '../../core/context.js';
3
+ import type { AnalyzerResult } from '../../core/result.js';
4
+ export declare class TestingAnalyzer implements Analyzer {
5
+ readonly id = "testing";
6
+ readonly name = "Testing";
7
+ readonly category: "testing";
8
+ readonly description = "Checks for test infrastructure: directories, test files, and test scripts.";
9
+ analyze(context: RepositoryContext): Promise<AnalyzerResult>;
10
+ }
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/analyzers/testing/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAW,MAAM,sBAAsB,CAAC;AAgCpE,qBAAa,eAAgB,YAAW,QAAQ;IAC9C,QAAQ,CAAC,EAAE,aAAa;IACxB,QAAQ,CAAC,IAAI,aAAa;IAC1B,QAAQ,CAAC,QAAQ,EAAG,SAAS,CAAU;IACvC,QAAQ,CAAC,WAAW,gFAC2D;IAEzE,OAAO,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,cAAc,CAAC;CA8GnE"}