@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,102 @@
1
+ import path from 'node:path';
2
+ import fs from 'node:fs/promises';
3
+ import { createFindingId } from '../core/findings.js';
4
+ const DEFAULT_MAX_BYTES = 1024 * 1024; // 1 MB
5
+ /**
6
+ * Creates an isolated, sandboxed PluginContext.
7
+ *
8
+ * Enforces:
9
+ * - Read-only access to repository metadata
10
+ * - Traversal-safe file reading (cannot read outside repository root)
11
+ * - Size-bounded reads to avoid out-of-memory errors
12
+ * - Rule validation on finding creation
13
+ */
14
+ export function createPluginContext(repoContext, rules) {
15
+ const root = path.resolve(repoContext.root);
16
+ const ruleMap = new Map();
17
+ for (const rule of rules) {
18
+ ruleMap.set(rule.id, rule);
19
+ }
20
+ const filePathSet = new Set(repoContext.files.map((f) => f.relativePath.replace(/\\/g, '/').toLowerCase()));
21
+ return {
22
+ repositoryRoot: root,
23
+ files: repoContext.files,
24
+ languages: repoContext.languages,
25
+ frameworks: repoContext.frameworks,
26
+ packageManagers: repoContext.packageManagers,
27
+ projectType: repoContext.projectType,
28
+ git: repoContext.git,
29
+ online: repoContext.online ?? false,
30
+ hasFile(relativePath) {
31
+ const normalized = relativePath.replace(/\\/g, '/').replace(/^\/+/, '').toLowerCase();
32
+ return filePathSet.has(normalized);
33
+ },
34
+ async getFileContent(relativePath, maxBytes = DEFAULT_MAX_BYTES) {
35
+ // Path traversal security check
36
+ const normalized = path.normalize(relativePath);
37
+ const absolutePath = path.resolve(root, normalized);
38
+ const rel = path.relative(root, absolutePath);
39
+ if (rel.startsWith('..') || path.isAbsolute(rel)) {
40
+ // Path attempts to escape repository root
41
+ return null;
42
+ }
43
+ try {
44
+ const stat = await fs.stat(absolutePath);
45
+ if (!stat.isFile()) {
46
+ return null;
47
+ }
48
+ // Bounded read if file exceeds maxBytes
49
+ if (stat.size > maxBytes) {
50
+ const handle = await fs.open(absolutePath, 'r');
51
+ try {
52
+ const buffer = Buffer.alloc(maxBytes);
53
+ const { bytesRead } = await handle.read(buffer, 0, maxBytes, 0);
54
+ return buffer.toString('utf8', 0, bytesRead);
55
+ }
56
+ finally {
57
+ await handle.close();
58
+ }
59
+ }
60
+ return await fs.readFile(absolutePath, 'utf8');
61
+ }
62
+ catch {
63
+ return null;
64
+ }
65
+ },
66
+ createFinding(ruleId, details) {
67
+ const rule = ruleMap.get(ruleId);
68
+ const category = rule?.category ?? 'quality';
69
+ const severity = details.severity ?? rule?.severity ?? 'low';
70
+ const title = details.title ?? rule?.title ?? rule?.name ?? `Finding from ${ruleId}`;
71
+ const description = details.description ?? rule?.description ?? '';
72
+ const recommendation = details.recommendation ?? rule?.recommendation ?? '';
73
+ const confidence = details.confidence ?? rule?.confidence ?? 0.85;
74
+ const autoFixable = details.autoFixable ?? rule?.autoFixable ?? false;
75
+ const normFile = details.filePath?.replace(/\\/g, '/');
76
+ const location = normFile
77
+ ? details.lineNumber !== undefined
78
+ ? { file: normFile, line: details.lineNumber }
79
+ : { file: normFile }
80
+ : undefined;
81
+ const finding = {
82
+ id: createFindingId(ruleId, normFile, details.lineNumber),
83
+ ruleId,
84
+ category,
85
+ severity,
86
+ title,
87
+ description,
88
+ recommendation,
89
+ confidence,
90
+ autoFixable,
91
+ };
92
+ if (location !== undefined) {
93
+ finding.location = location;
94
+ }
95
+ if (rule?.references && rule.references.length > 0) {
96
+ finding.references = [...rule.references];
97
+ }
98
+ return finding;
99
+ },
100
+ };
101
+ }
102
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/plugins/context.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { FathomPlugin, PluginAnalyzer, PluginRule } from '../types.js';
2
+ export declare const REACT_RULES: readonly PluginRule[];
3
+ export declare const reactAnalyzer: PluginAnalyzer;
4
+ /**
5
+ * Example Fathom plugin: @fathom/plugin-react
6
+ */
7
+ export declare const reactPlugin: FathomPlugin;
8
+ //# sourceMappingURL=react.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../../../src/plugins/examples/react.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAiB,UAAU,EAAE,MAAM,aAAa,CAAC;AAG3F,eAAO,MAAM,WAAW,EAAE,SAAS,UAAU,EA6C5C,CAAC;AAMF,eAAO,MAAM,aAAa,EAAE,cAwF3B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,WAAW,EAAE,YAUzB,CAAC"}
@@ -0,0 +1,129 @@
1
+ export const REACT_RULES = [
2
+ {
3
+ id: 'REACT-001',
4
+ name: 'dangerouslySetInnerHTML usage detected',
5
+ title: 'dangerouslySetInnerHTML usage detected',
6
+ category: 'security',
7
+ severity: 'medium',
8
+ description: 'Bypassing React DOM sanitization with dangerouslySetInnerHTML can expose the application to Cross-Site Scripting (XSS).',
9
+ recommendation: 'Use safe DOM text rendering or sanitize HTML content with DOMPurify before injecting.',
10
+ confidence: 0.95,
11
+ autoFixable: false,
12
+ enabledByDefault: true,
13
+ references: ['https://react.dev/reference/react-dom/components/common#dangerously-setting-the-inner-html'],
14
+ },
15
+ {
16
+ id: 'REACT-002',
17
+ name: 'Array index used as key or missing key in list render',
18
+ title: 'Array index used as key or missing key in list render',
19
+ category: 'quality',
20
+ severity: 'low',
21
+ description: 'Using array indices as keys in iterated elements leads to state preservation bugs and suboptimal reconciliation.',
22
+ recommendation: 'Use unique, stable IDs from data items as the key attribute (e.g. key={item.id}).',
23
+ confidence: 0.85,
24
+ autoFixable: false,
25
+ enabledByDefault: true,
26
+ references: ['https://react.dev/learn/rendering-lists#why-does-react-need-keys'],
27
+ },
28
+ {
29
+ id: 'REACT-003',
30
+ name: 'Direct React state mutation detected',
31
+ title: 'Direct React state mutation detected',
32
+ category: 'quality',
33
+ severity: 'medium',
34
+ description: 'Mutating state directly (e.g. this.state.x = value) bypasses React state scheduling and prevents proper component re-renders.',
35
+ recommendation: 'Always use setState() in class components or the updater function returned by useState/useReducer.',
36
+ confidence: 0.9,
37
+ autoFixable: false,
38
+ enabledByDefault: true,
39
+ },
40
+ ];
41
+ const DANGEROUS_HTML_REGEX = /dangerouslySetInnerHTML\s*=/;
42
+ const INDEX_AS_KEY_REGEX = /key\s*=\s*\{(?:\s*(?:index|idx|i)\s*)\}/i;
43
+ const DIRECT_STATE_MUTATION_REGEX = /this\.state\.[a-zA-Z0-9_$]+\s*=(?!=)/;
44
+ export const reactAnalyzer = {
45
+ id: 'react',
46
+ name: 'React Quality & Security',
47
+ category: 'quality',
48
+ description: 'Detects React security risks, array index keys, and direct state mutations in JSX/TSX',
49
+ async analyze(context) {
50
+ const isReactRepo = context.frameworks.some((f) => f.name === 'react' || f.name === 'nextjs') ||
51
+ context.files.some((f) => f.relativePath.endsWith('.jsx') || f.relativePath.endsWith('.tsx'));
52
+ if (!isReactRepo) {
53
+ return {
54
+ findings: [],
55
+ metrics: { reactFilesScanned: 0, reactIssuesFound: 0 },
56
+ };
57
+ }
58
+ const targetFiles = context.files.filter((f) => {
59
+ const p = f.relativePath.toLowerCase();
60
+ const isJsx = p.endsWith('.jsx') || p.endsWith('.tsx');
61
+ const isJs = (p.endsWith('.js') || p.endsWith('.ts')) &&
62
+ !p.endsWith('.d.ts') &&
63
+ !p.includes('.test.') &&
64
+ !p.includes('.spec.');
65
+ return isJsx || isJs;
66
+ });
67
+ const findings = [];
68
+ let filesScanned = 0;
69
+ for (const file of targetFiles) {
70
+ const content = await context.getFileContent(file.relativePath);
71
+ if (!content)
72
+ continue;
73
+ filesScanned++;
74
+ const lines = content.split(/\r?\n/);
75
+ for (let i = 0; i < lines.length; i++) {
76
+ const line = lines[i];
77
+ if (!line)
78
+ continue;
79
+ const lineNum = i + 1;
80
+ // Check REACT-001: dangerouslySetInnerHTML
81
+ if (DANGEROUS_HTML_REGEX.test(line)) {
82
+ findings.push(context.createFinding('REACT-001', {
83
+ filePath: file.relativePath,
84
+ lineNumber: lineNum,
85
+ description: `dangerouslySetInnerHTML detected in ${file.relativePath}:${lineNum}. Ensure input is sanitized with DOMPurify.`,
86
+ }));
87
+ }
88
+ // Check REACT-002: Array index as key
89
+ if (INDEX_AS_KEY_REGEX.test(line)) {
90
+ findings.push(context.createFinding('REACT-002', {
91
+ filePath: file.relativePath,
92
+ lineNumber: lineNum,
93
+ description: `Array index used as key attribute in ${file.relativePath}:${lineNum}. Prefer stable entity IDs.`,
94
+ }));
95
+ }
96
+ // Check REACT-003: Direct state mutation
97
+ if (DIRECT_STATE_MUTATION_REGEX.test(line)) {
98
+ findings.push(context.createFinding('REACT-003', {
99
+ filePath: file.relativePath,
100
+ lineNumber: lineNum,
101
+ description: `Direct assignment to this.state detected in ${file.relativePath}:${lineNum}. Use setState() instead.`,
102
+ }));
103
+ }
104
+ }
105
+ }
106
+ return {
107
+ findings,
108
+ metrics: {
109
+ reactFilesScanned: filesScanned,
110
+ reactIssuesFound: findings.length,
111
+ },
112
+ };
113
+ },
114
+ };
115
+ /**
116
+ * Example Fathom plugin: @fathom/plugin-react
117
+ */
118
+ export const reactPlugin = {
119
+ manifest: {
120
+ name: '@fathom/plugin-react',
121
+ version: '0.1.0',
122
+ description: 'React best practices, security, and anti-pattern analyzer for Fathom',
123
+ author: 'Fathom Core Team',
124
+ rules: REACT_RULES,
125
+ },
126
+ rules: REACT_RULES,
127
+ analyzers: [reactAnalyzer],
128
+ };
129
+ //# sourceMappingURL=react.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react.js","sourceRoot":"","sources":["../../../src/plugins/examples/react.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export type { PluginManifest, PluginRule, PluginContext, PluginAnalyzer, PluginAnalyzerResult, PluginSetupContext, FathomPlugin, } from './types.js';
2
+ export { createPluginContext } from './context.js';
3
+ export { PluginRegistry, defaultPluginRegistry } from './registry.js';
4
+ export { reactPlugin, REACT_RULES, reactAnalyzer } from './examples/react.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/plugins/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,cAAc,EACd,UAAU,EACV,aAAa,EACb,cAAc,EACd,oBAAoB,EACpB,kBAAkB,EAClB,YAAY,GACb,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACtE,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { createPluginContext } from './context.js';
2
+ export { PluginRegistry, defaultPluginRegistry } from './registry.js';
3
+ export { reactPlugin, REACT_RULES, reactAnalyzer } from './examples/react.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/plugins/index.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACtE,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1,45 @@
1
+ import type { Analyzer } from '../core/analyzer.js';
2
+ import type { FathomPlugin } from './types.js';
3
+ /**
4
+ * Registry managing Fathom plugins, their analyzers, and custom rules.
5
+ */
6
+ export declare class PluginRegistry {
7
+ private readonly plugins;
8
+ /**
9
+ * Validate a plugin definition schema.
10
+ * Throws FathomPluginError if the plugin is malformed.
11
+ */
12
+ validatePlugin(plugin: unknown): FathomPlugin;
13
+ /**
14
+ * Register a new plugin with the registry.
15
+ */
16
+ register(plugin: FathomPlugin): Promise<this>;
17
+ /**
18
+ * Unregister a plugin by name.
19
+ */
20
+ unregister(name: string): boolean;
21
+ /**
22
+ * Get a registered plugin by name.
23
+ */
24
+ get(name: string): FathomPlugin | undefined;
25
+ /**
26
+ * Get all registered plugins.
27
+ */
28
+ getAll(): FathomPlugin[];
29
+ /**
30
+ * Check if a plugin is registered.
31
+ */
32
+ has(name: string): boolean;
33
+ /**
34
+ * Clear all registered plugins.
35
+ */
36
+ clear(): void;
37
+ /**
38
+ * Adapt all plugin analyzers to the core Fathom Analyzer interface.
39
+ * Wraps analyzer execution in safe boundaries.
40
+ */
41
+ getAnalyzers(): Analyzer[];
42
+ }
43
+ /** Shared singleton plugin registry */
44
+ export declare const defaultPluginRegistry: PluginRegistry;
45
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/plugins/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAUpD,OAAO,KAAK,EACV,YAAY,EAIb,MAAM,YAAY,CAAC;AAIpB;;GAEG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmC;IAE3D;;;OAGG;IACH,cAAc,CAAC,MAAM,EAAE,OAAO,GAAG,YAAY;IAsH7C;;OAEG;IACG,QAAQ,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IA4DnD;;OAEG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAIjC;;OAEG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IAI3C;;OAEG;IACH,MAAM,IAAI,YAAY,EAAE;IAIxB;;OAEG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAI1B;;OAEG;IACH,KAAK,IAAI,IAAI;IAIb;;;OAGG;IACH,YAAY,IAAI,QAAQ,EAAE;CAsD3B;AAED,uCAAuC;AACvC,eAAO,MAAM,qBAAqB,gBAAuB,CAAC"}
@@ -0,0 +1,236 @@
1
+ import { CATEGORIES } from '../rules/categories.js';
2
+ import { SEVERITY_ORDER } from '../rules/severity.js';
3
+ import { ruleRegistry } from '../rules/registry.js';
4
+ import { FathomPluginError } from '../core/errors.js';
5
+ import { createTimer } from '../utils/timing.js';
6
+ import { logger } from '../utils/logger.js';
7
+ import { createPluginContext } from './context.js';
8
+ const VALID_PLUGIN_NAME_REGEX = /^(@[a-zA-Z0-9_.-]+\/)?[a-zA-Z0-9_.-]+$/;
9
+ /**
10
+ * Registry managing Fathom plugins, their analyzers, and custom rules.
11
+ */
12
+ export class PluginRegistry {
13
+ plugins = new Map();
14
+ /**
15
+ * Validate a plugin definition schema.
16
+ * Throws FathomPluginError if the plugin is malformed.
17
+ */
18
+ validatePlugin(plugin) {
19
+ if (!plugin || typeof plugin !== 'object') {
20
+ throw new FathomPluginError('Plugin definition must be a valid object');
21
+ }
22
+ const p = plugin;
23
+ if (!p['manifest'] || typeof p['manifest'] !== 'object') {
24
+ throw new FathomPluginError('Plugin must define a valid "manifest" object');
25
+ }
26
+ const manifest = p['manifest'];
27
+ const name = manifest['name'];
28
+ if (typeof name !== 'string' || !name.trim() || !VALID_PLUGIN_NAME_REGEX.test(name.trim())) {
29
+ throw new FathomPluginError(`Invalid plugin name "${String(name)}". Plugin names must be valid package names (e.g. "@fathom/plugin-react" or "custom-plugin").`);
30
+ }
31
+ const version = manifest['version'];
32
+ if (typeof version !== 'string' || !version.trim()) {
33
+ throw new FathomPluginError(`Plugin "${name}" must declare a valid non-empty "version" string.`, name);
34
+ }
35
+ const description = manifest['description'];
36
+ if (typeof description !== 'string') {
37
+ throw new FathomPluginError(`Plugin "${name}" must declare a "description" string in its manifest.`, name);
38
+ }
39
+ // Validate rules if present in manifest or plugin root
40
+ const rules = (manifest['rules'] ?? p['rules'] ?? []);
41
+ if (!Array.isArray(rules)) {
42
+ throw new FathomPluginError(`Plugin "${name}" rules must be an array.`, name);
43
+ }
44
+ const allowedCategories = CATEGORIES;
45
+ const allowedSeverities = Object.keys(SEVERITY_ORDER);
46
+ for (const r of rules) {
47
+ if (!r || typeof r !== 'object') {
48
+ throw new FathomPluginError(`Plugin "${name}" contains an invalid rule entry.`, name);
49
+ }
50
+ const rule = r;
51
+ const ruleId = rule['id'];
52
+ if (typeof ruleId !== 'string' || !ruleId.trim()) {
53
+ throw new FathomPluginError(`Plugin "${name}" has a rule with missing or empty id.`, name);
54
+ }
55
+ const title = rule['title'];
56
+ if (typeof title !== 'string' || !title.trim()) {
57
+ throw new FathomPluginError(`Plugin "${name}" rule "${ruleId}" has missing or empty title.`, name);
58
+ }
59
+ const category = rule['category'];
60
+ if (typeof category !== 'string' || !allowedCategories.includes(category)) {
61
+ throw new FathomPluginError(`Plugin "${name}" rule "${ruleId}" has invalid category "${String(category)}".`, name);
62
+ }
63
+ const severity = rule['severity'];
64
+ if (typeof severity !== 'string' || !allowedSeverities.includes(severity)) {
65
+ throw new FathomPluginError(`Plugin "${name}" rule "${ruleId}" has invalid severity "${String(severity)}".`, name);
66
+ }
67
+ }
68
+ // Validate analyzers if present
69
+ const analyzers = (p['analyzers'] ?? []);
70
+ if (!Array.isArray(analyzers)) {
71
+ throw new FathomPluginError(`Plugin "${name}" analyzers must be an array.`, name);
72
+ }
73
+ for (const a of analyzers) {
74
+ if (!a || typeof a !== 'object') {
75
+ throw new FathomPluginError(`Plugin "${name}" contains an invalid analyzer entry.`, name);
76
+ }
77
+ const analyzer = a;
78
+ const analyzerId = analyzer['id'];
79
+ if (typeof analyzerId !== 'string' || !analyzerId.trim()) {
80
+ throw new FathomPluginError(`Plugin "${name}" has an analyzer with missing or empty id.`, name);
81
+ }
82
+ const analyzerName = analyzer['name'];
83
+ if (typeof analyzerName !== 'string' || !analyzerName.trim()) {
84
+ throw new FathomPluginError(`Plugin "${name}" analyzer "${analyzerId}" has missing name.`, name);
85
+ }
86
+ const category = analyzer['category'];
87
+ if (typeof category !== 'string' || !allowedCategories.includes(category)) {
88
+ throw new FathomPluginError(`Plugin "${name}" analyzer "${analyzerId}" has invalid category "${String(category)}".`, name);
89
+ }
90
+ if (typeof analyzer['analyze'] !== 'function') {
91
+ throw new FathomPluginError(`Plugin "${name}" analyzer "${analyzerId}" must provide an "analyze" function.`, name);
92
+ }
93
+ }
94
+ return plugin;
95
+ }
96
+ /**
97
+ * Register a new plugin with the registry.
98
+ */
99
+ async register(plugin) {
100
+ const validated = this.validatePlugin(plugin);
101
+ const name = validated.manifest.name;
102
+ if (this.plugins.has(name)) {
103
+ throw new FathomPluginError(`Plugin "${name}" is already registered.`, name);
104
+ }
105
+ const programmaticRules = [];
106
+ const programmaticAnalyzers = [];
107
+ // Run setup hook if provided
108
+ if (typeof validated.setup === 'function') {
109
+ const setupContext = {
110
+ registerRule(rule) {
111
+ programmaticRules.push(rule);
112
+ },
113
+ registerAnalyzer(analyzer) {
114
+ programmaticAnalyzers.push(analyzer);
115
+ },
116
+ };
117
+ await validated.setup(setupContext);
118
+ }
119
+ // Collect all rules
120
+ const allRules = [
121
+ ...validated.manifest.rules,
122
+ ...(validated.rules ?? []),
123
+ ...programmaticRules,
124
+ ];
125
+ // Deduplicate rules by ID
126
+ const uniqueRules = new Map();
127
+ for (const r of allRules) {
128
+ uniqueRules.set(r.id, r);
129
+ // Register in global Fathom rule registry for config override compatibility
130
+ ruleRegistry.registerRule(r);
131
+ }
132
+ // Collect all analyzers
133
+ const allAnalyzers = [
134
+ ...(validated.analyzers ?? []),
135
+ ...programmaticAnalyzers,
136
+ ];
137
+ const finalizedPlugin = {
138
+ manifest: {
139
+ ...validated.manifest,
140
+ rules: [...uniqueRules.values()],
141
+ },
142
+ rules: [...uniqueRules.values()],
143
+ analyzers: allAnalyzers,
144
+ };
145
+ this.plugins.set(name, finalizedPlugin);
146
+ logger.debug(`Registered plugin: ${name} (v${validated.manifest.version})`);
147
+ return this;
148
+ }
149
+ /**
150
+ * Unregister a plugin by name.
151
+ */
152
+ unregister(name) {
153
+ return this.plugins.delete(name);
154
+ }
155
+ /**
156
+ * Get a registered plugin by name.
157
+ */
158
+ get(name) {
159
+ return this.plugins.get(name);
160
+ }
161
+ /**
162
+ * Get all registered plugins.
163
+ */
164
+ getAll() {
165
+ return [...this.plugins.values()];
166
+ }
167
+ /**
168
+ * Check if a plugin is registered.
169
+ */
170
+ has(name) {
171
+ return this.plugins.has(name);
172
+ }
173
+ /**
174
+ * Clear all registered plugins.
175
+ */
176
+ clear() {
177
+ this.plugins.clear();
178
+ }
179
+ /**
180
+ * Adapt all plugin analyzers to the core Fathom Analyzer interface.
181
+ * Wraps analyzer execution in safe boundaries.
182
+ */
183
+ getAnalyzers() {
184
+ const coreAnalyzers = [];
185
+ for (const plugin of this.plugins.values()) {
186
+ const pluginRules = plugin.rules ?? plugin.manifest.rules ?? [];
187
+ const analyzers = plugin.analyzers ?? [];
188
+ for (const analyzer of analyzers) {
189
+ const fullId = `plugin:${plugin.manifest.name}:${analyzer.id}`;
190
+ const displayName = `${analyzer.name} [${plugin.manifest.name}]`;
191
+ coreAnalyzers.push({
192
+ id: fullId,
193
+ name: displayName,
194
+ category: analyzer.category,
195
+ description: analyzer.description,
196
+ async analyze(context) {
197
+ const elapsed = createTimer();
198
+ try {
199
+ const pluginContext = createPluginContext(context, pluginRules);
200
+ const result = await analyzer.analyze(pluginContext);
201
+ return {
202
+ analyzerId: fullId,
203
+ analyzerName: displayName,
204
+ category: analyzer.category,
205
+ status: 'success',
206
+ findings: result.findings,
207
+ metrics: result.metrics ?? {},
208
+ warnings: result.warnings ?? [],
209
+ durationMs: elapsed(),
210
+ };
211
+ }
212
+ catch (err) {
213
+ const message = err instanceof Error ? err.message : String(err);
214
+ logger.warn(`Plugin analyzer "${displayName}" failed: ${message}`);
215
+ return {
216
+ analyzerId: fullId,
217
+ analyzerName: displayName,
218
+ category: analyzer.category,
219
+ status: 'failed',
220
+ findings: [],
221
+ metrics: {},
222
+ warnings: [],
223
+ durationMs: elapsed(),
224
+ error: message,
225
+ };
226
+ }
227
+ },
228
+ });
229
+ }
230
+ }
231
+ return coreAnalyzers;
232
+ }
233
+ }
234
+ /** Shared singleton plugin registry */
235
+ export const defaultPluginRegistry = new PluginRegistry();
236
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/plugins/registry.ts"],"names":[],"mappings":"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"}