code-auditor-mcp 3.4.17 → 3.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +94 -0
- package/README.md +5 -5
- package/dist/analyzers/applicability.d.ts +13 -0
- package/dist/analyzers/applicability.d.ts.map +1 -1
- package/dist/analyzers/applicability.js +37 -0
- package/dist/analyzers/applicability.js.map +1 -1
- package/dist/analyzers/cross-language/APIContractAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/cross-language/APIContractAnalyzer.js +23 -2
- package/dist/analyzers/cross-language/APIContractAnalyzer.js.map +1 -1
- package/dist/analyzers/cross-language/DependencyGraphBuilder.d.ts +52 -9
- package/dist/analyzers/cross-language/DependencyGraphBuilder.d.ts.map +1 -1
- package/dist/analyzers/cross-language/DependencyGraphBuilder.js +228 -55
- package/dist/analyzers/cross-language/DependencyGraphBuilder.js.map +1 -1
- package/dist/analyzers/cross-language/SchemaValidator.d.ts +19 -12
- package/dist/analyzers/cross-language/SchemaValidator.d.ts.map +1 -1
- package/dist/analyzers/cross-language/SchemaValidator.js +78 -111
- package/dist/analyzers/cross-language/SchemaValidator.js.map +1 -1
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.d.ts +0 -6
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js +85 -73
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js.map +1 -1
- package/dist/analyzers/documentationAnalyzer.js +5 -5
- package/dist/analyzers/documentationAnalyzer.js.map +1 -1
- package/dist/analyzers/invariantsAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/invariantsAnalyzer.js +1 -0
- package/dist/analyzers/invariantsAnalyzer.js.map +1 -1
- package/dist/analyzers/provenance.d.ts +7 -9
- package/dist/analyzers/provenance.d.ts.map +1 -1
- package/dist/analyzers/provenance.js +88 -124
- package/dist/analyzers/provenance.js.map +1 -1
- package/dist/analyzers/reactAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/reactAnalyzer.js +120 -21
- package/dist/analyzers/reactAnalyzer.js.map +1 -1
- package/dist/analyzers/ruleRegistry.d.ts +21 -0
- package/dist/analyzers/ruleRegistry.d.ts.map +1 -1
- package/dist/analyzers/ruleRegistry.js +61 -12
- package/dist/analyzers/ruleRegistry.js.map +1 -1
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.js +60 -24
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalDRYAnalyzer.js +5 -2
- package/dist/analyzers/universal/UniversalDRYAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js +173 -56
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalDocumentationAnalyzer.d.ts +2 -1
- package/dist/analyzers/universal/UniversalDocumentationAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalDocumentationAnalyzer.js +23 -12
- package/dist/analyzers/universal/UniversalDocumentationAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts +11 -1
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js +60 -22
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts +2 -3
- package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalStylesAnalyzer.js +258 -87
- package/dist/analyzers/universal/UniversalStylesAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/schema/codeAnalysis.d.ts.map +1 -1
- package/dist/analyzers/universal/schema/codeAnalysis.js +7 -1
- package/dist/analyzers/universal/schema/codeAnalysis.js.map +1 -1
- package/dist/analyzers/universal/schema/jsonSchema.js +7 -11
- package/dist/analyzers/universal/schema/jsonSchema.js.map +1 -1
- package/dist/applyDataDirEnv.js +1 -1
- package/dist/applyDataDirEnv.js.map +1 -1
- package/dist/auditRouter.d.ts +41 -0
- package/dist/auditRouter.d.ts.map +1 -0
- package/dist/auditRouter.js +185 -0
- package/dist/auditRouter.js.map +1 -0
- package/dist/auditRunner.d.ts.map +1 -1
- package/dist/auditRunner.js +97 -58
- package/dist/auditRunner.js.map +1 -1
- package/dist/cli.js +247 -23
- package/dist/cli.js.map +1 -1
- package/dist/codeIndexDB.d.ts.map +1 -1
- package/dist/codeIndexDB.js +36 -2
- package/dist/codeIndexDB.js.map +1 -1
- package/dist/componentScanner.d.ts.map +1 -1
- package/dist/componentScanner.js +7 -2
- package/dist/componentScanner.js.map +1 -1
- package/dist/config/configLoader.d.ts +9 -0
- package/dist/config/configLoader.d.ts.map +1 -1
- package/dist/config/configLoader.js +30 -2
- package/dist/config/configLoader.js.map +1 -1
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +10 -3
- package/dist/config/defaults.js.map +1 -1
- package/dist/dataPaths.d.ts +8 -1
- package/dist/dataPaths.d.ts.map +1 -1
- package/dist/dataPaths.js +57 -5
- package/dist/dataPaths.js.map +1 -1
- package/dist/functionScanner.d.ts.map +1 -1
- package/dist/functionScanner.js +1 -17
- package/dist/functionScanner.js.map +1 -1
- package/dist/grammars/tree-sitter-scss.wasm +0 -0
- package/dist/invariants/ruleEngine.d.ts +8 -0
- package/dist/invariants/ruleEngine.d.ts.map +1 -1
- package/dist/invariants/ruleEngine.js +35 -16
- package/dist/invariants/ruleEngine.js.map +1 -1
- package/dist/languages/LanguageOrchestrator.d.ts +10 -11
- package/dist/languages/LanguageOrchestrator.d.ts.map +1 -1
- package/dist/languages/LanguageOrchestrator.js +135 -101
- package/dist/languages/LanguageOrchestrator.js.map +1 -1
- package/dist/languages/RuntimeManager.d.ts +28 -1
- package/dist/languages/RuntimeManager.d.ts.map +1 -1
- package/dist/languages/RuntimeManager.js +160 -49
- package/dist/languages/RuntimeManager.js.map +1 -1
- package/dist/languages/go/GoAdapter.d.ts +2 -0
- package/dist/languages/go/GoAdapter.d.ts.map +1 -1
- package/dist/languages/go/GoAdapter.js +15 -22
- package/dist/languages/go/GoAdapter.js.map +1 -1
- package/dist/languages/go/analyzer +0 -0
- package/dist/languages/go/analyzer-src/analyzer.go +510 -0
- package/dist/languages/go/analyzer-src/go.mod +3 -0
- package/dist/languages/go/analyzer-src/indexer.go +268 -0
- package/dist/languages/go/analyzer-src/parser.go +461 -0
- package/dist/languages/go/analyzer-src/solid.go +395 -0
- package/dist/languages/go/analyzer-src/testconventions.go +25 -0
- package/dist/languages/go/analyzer-src/types.go +148 -0
- package/dist/languages/go/go.mod +7 -0
- package/dist/languages/go/main.go +203 -0
- package/dist/languages/testConventions.d.ts +35 -0
- package/dist/languages/testConventions.d.ts.map +1 -0
- package/dist/languages/testConventions.js +51 -0
- package/dist/languages/testConventions.js.map +1 -0
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.d.ts +3 -0
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.d.ts.map +1 -1
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js +25 -18
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js.map +1 -1
- package/dist/mcp-tools/whitelistTools.d.ts +1 -1
- package/dist/mcp-tools/workflowGuide.js +3 -3
- package/dist/mcp-tools/workflowGuide.js.map +1 -1
- package/dist/mcp-tools-shared.d.ts +0 -3
- package/dist/mcp-tools-shared.d.ts.map +1 -1
- package/dist/mcp-tools-shared.js +24 -128
- package/dist/mcp-tools-shared.js.map +1 -1
- package/dist/mcp.js +18 -12
- package/dist/mcp.js.map +1 -1
- package/dist/mcpAuditJobs.d.ts.map +1 -1
- package/dist/mcpAuditJobs.js +13 -0
- package/dist/mcpAuditJobs.js.map +1 -1
- package/dist/mcpAutoIndex.js +1 -1
- package/dist/mcpAutoIndex.js.map +1 -1
- package/dist/nextFile.d.ts +21 -0
- package/dist/nextFile.d.ts.map +1 -0
- package/dist/nextFile.js +51 -0
- package/dist/nextFile.js.map +1 -0
- package/dist/nextFileIncremental.d.ts +141 -0
- package/dist/nextFileIncremental.d.ts.map +1 -0
- package/dist/nextFileIncremental.js +314 -0
- package/dist/nextFileIncremental.js.map +1 -0
- package/dist/pipeline.d.ts.map +1 -1
- package/dist/pipeline.js +22 -0
- package/dist/pipeline.js.map +1 -1
- package/dist/pipelineAdapters.d.ts +17 -0
- package/dist/pipelineAdapters.d.ts.map +1 -1
- package/dist/pipelineAdapters.js +800 -44
- package/dist/pipelineAdapters.js.map +1 -1
- package/dist/reporting/jsonReportGenerator.d.ts.map +1 -1
- package/dist/reporting/jsonReportGenerator.js +11 -30
- package/dist/reporting/jsonReportGenerator.js.map +1 -1
- package/dist/styles/styleIndexer.d.ts +7 -0
- package/dist/styles/styleIndexer.d.ts.map +1 -1
- package/dist/styles/styleIndexer.js +44 -61
- package/dist/styles/styleIndexer.js.map +1 -1
- package/dist/types/crossLanguage.d.ts +1 -0
- package/dist/types/crossLanguage.d.ts.map +1 -1
- package/dist/types.d.ts +27 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/fileDiscovery.d.ts +11 -2
- package/dist/utils/fileDiscovery.d.ts.map +1 -1
- package/dist/utils/fileDiscovery.js +23 -5
- package/dist/utils/fileDiscovery.js.map +1 -1
- package/dist/utils/reactDetection.d.ts.map +1 -1
- package/dist/utils/reactDetection.js +59 -44
- package/dist/utils/reactDetection.js.map +1 -1
- package/package.json +31 -27
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/README.md +6 -6
- package/plugin/hooks/hooks.json +4 -0
- package/plugin/scripts/hook-audit.sh +0 -0
- package/plugin/scripts/hook-self-audit.sh +107 -0
- package/plugin/skills/code-auditor/SKILL-RULE-KINDS.md +55 -7
- package/plugin/skills/code-auditor/SKILL.md +22 -23
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audit router — the single entry point for choosing between the two analysis
|
|
3
|
+
* paths: the tree-sitter TypeScript pipeline (`createAuditRunner`) and the Go
|
|
4
|
+
* subprocess orchestrator (`LanguageOrchestrator`).
|
|
5
|
+
*
|
|
6
|
+
* The CLI and the MCP `audit.run` tool both need the same decision — "are there
|
|
7
|
+
* Go files? route to the Go subprocess; otherwise use the TS pipeline" — and
|
|
8
|
+
* for months they did not share it: `handleAudit` checked for `.go` and routed
|
|
9
|
+
* to the orchestrator, while the CLI called `createAuditRunner` directly and
|
|
10
|
+
* fed Go ASTs to TypeScript-tuned analyzers (the 762-finding gin result). Two
|
|
11
|
+
* copies, one silently wrong — the same shape as the duplicate
|
|
12
|
+
* `getLanguageFromPath`, one layer up.
|
|
13
|
+
*
|
|
14
|
+
* This module is that decision, in exactly one place. Both surfaces call
|
|
15
|
+
* `runAuditDispatch`; a future refactor that unwires one of them can no longer
|
|
16
|
+
* silently break the other.
|
|
17
|
+
*/
|
|
18
|
+
import * as path from 'path';
|
|
19
|
+
import { promises as fs } from 'fs';
|
|
20
|
+
import { createAuditRunner } from './auditRunner.js';
|
|
21
|
+
import { RuntimeManager } from './languages/RuntimeManager.js';
|
|
22
|
+
import { LanguageOrchestrator } from './languages/LanguageOrchestrator.js';
|
|
23
|
+
import { CodeIndexDB } from './codeIndexDB.js';
|
|
24
|
+
import { makeVisitorStatus } from './pipeline.js';
|
|
25
|
+
import { DEFAULT_EXCLUDED_ANY_DEPTH_DIRS } from './utils/fileDiscovery.js';
|
|
26
|
+
/**
|
|
27
|
+
* True when `target` (a file or directory) is, or contains, a file with the
|
|
28
|
+
* given extension. Walked recursively for directories.
|
|
29
|
+
*
|
|
30
|
+
* The walk skips the same any-depth excluded dirs discovery skips
|
|
31
|
+
* (`node_modules`, `.git`, `dist`, …). Without that, a single `.go` file
|
|
32
|
+
* buried in a dependency's `node_modules` (e.g. `flatted`'s golang port) would
|
|
33
|
+
* route an otherwise pure-TS project through the Go subprocess — which drops
|
|
34
|
+
* every non-Go analyzer and collapses the audit to a fraction of its findings.
|
|
35
|
+
*/
|
|
36
|
+
export async function hasFilesWithExtension(target, ext) {
|
|
37
|
+
try {
|
|
38
|
+
const stats = await fs.stat(target);
|
|
39
|
+
if (stats.isFile()) {
|
|
40
|
+
return path.extname(target).toLowerCase() === ext;
|
|
41
|
+
}
|
|
42
|
+
const entries = await fs.readdir(target, { withFileTypes: true });
|
|
43
|
+
for (const entry of entries) {
|
|
44
|
+
if (entry.isFile()) {
|
|
45
|
+
if (path.extname(entry.name).toLowerCase() === ext)
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
else if (entry.isDirectory()) {
|
|
49
|
+
if (DEFAULT_EXCLUDED_ANY_DEPTH_DIRS.has(entry.name))
|
|
50
|
+
continue;
|
|
51
|
+
if (await hasFilesWithExtension(path.join(target, entry.name), ext))
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Convert a polyglot analysis result to the legacy `AuditResult` shape the
|
|
63
|
+
* reporting and CLI surfaces consume.
|
|
64
|
+
*/
|
|
65
|
+
export function convertPolyglotToAuditResult(polyglotResult, auditPath) {
|
|
66
|
+
const violations = polyglotResult.violations || [];
|
|
67
|
+
const criticalIssues = violations.filter((v) => v.severity === 'critical').length;
|
|
68
|
+
const warnings = violations.filter((v) => v.severity === 'warning').length;
|
|
69
|
+
const suggestions = violations.filter((v) => v.severity === 'suggestion').length;
|
|
70
|
+
const totalFiles = polyglotResult.metrics?.totalFiles || 0;
|
|
71
|
+
const executionTime = polyglotResult.metrics?.executionTime || 0;
|
|
72
|
+
// Bucket violations by their category for the summary. The Go subprocess
|
|
73
|
+
// labels findings with `category` (open-closed, import-style, …); the
|
|
74
|
+
// TypeScript pipeline uses `type` for the same purpose. Fall back to the
|
|
75
|
+
// analyzer label so a categoryless finding still lands in a real bucket
|
|
76
|
+
// rather than vanishing into an empty `violationsByCategory`.
|
|
77
|
+
const violationsByCategory = {};
|
|
78
|
+
for (const v of violations) {
|
|
79
|
+
const category = v.category || v.type || v.analyzer || 'unknown';
|
|
80
|
+
violationsByCategory[category] = (violationsByCategory[category] || 0) + 1;
|
|
81
|
+
}
|
|
82
|
+
const topIssues = Object.entries(violationsByCategory)
|
|
83
|
+
.sort(([, a], [, b]) => b - a)
|
|
84
|
+
.slice(0, 5)
|
|
85
|
+
.map(([type, count]) => ({ type, count }));
|
|
86
|
+
// Build analyzerResults dynamically from the actual analyzer labels on the
|
|
87
|
+
// violations. The Go subprocess labels findings with the analyzer that
|
|
88
|
+
// produced them (`solid`, and potentially `imports`/`errors`/`goroutines`/
|
|
89
|
+
// `channels`) — never `go`. A hardcoded `go` bucket filtering
|
|
90
|
+
// `v.analyzer === 'go'` was therefore structurally always zero.
|
|
91
|
+
const analyzerResults = {};
|
|
92
|
+
for (const v of violations) {
|
|
93
|
+
const key = v.analyzer || 'unknown';
|
|
94
|
+
if (!analyzerResults[key]) {
|
|
95
|
+
analyzerResults[key] = {
|
|
96
|
+
violations: [],
|
|
97
|
+
status: makeVisitorStatus(totalFiles),
|
|
98
|
+
executionTime,
|
|
99
|
+
analyzerName: key,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
analyzerResults[key].violations.push(v);
|
|
103
|
+
}
|
|
104
|
+
// Surface languages discovered on disk but skipped (no runtime), plus any
|
|
105
|
+
// subprocess errors, as diagnostics — so "Go files present, Go toolchain
|
|
106
|
+
// not found" reaches the report instead of collapsing into a silent zero.
|
|
107
|
+
const diagnostics = [];
|
|
108
|
+
for (const na of polyglotResult.notApplicable || []) {
|
|
109
|
+
diagnostics.push({ analyzerName: na.language, kind: 'notApplicable', message: na.reason });
|
|
110
|
+
}
|
|
111
|
+
for (const err of polyglotResult.errors || []) {
|
|
112
|
+
diagnostics.push({
|
|
113
|
+
analyzerName: err.language || 'polyglot',
|
|
114
|
+
kind: err.type || 'error',
|
|
115
|
+
message: err.message,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
timestamp: new Date(),
|
|
120
|
+
summary: {
|
|
121
|
+
totalViolations: violations.length,
|
|
122
|
+
criticalIssues,
|
|
123
|
+
warnings,
|
|
124
|
+
suggestions,
|
|
125
|
+
totalFiles,
|
|
126
|
+
violationsByCategory,
|
|
127
|
+
topIssues,
|
|
128
|
+
},
|
|
129
|
+
analyzerResults,
|
|
130
|
+
recommendations: [],
|
|
131
|
+
metadata: {
|
|
132
|
+
auditDuration: executionTime,
|
|
133
|
+
filesAnalyzed: totalFiles,
|
|
134
|
+
analyzersRun: Object.keys(analyzerResults),
|
|
135
|
+
fileToFunctionsMap: polyglotResult.indexEntries
|
|
136
|
+
? createFileToFunctionsMap(polyglotResult.indexEntries)
|
|
137
|
+
: {},
|
|
138
|
+
...(diagnostics.length > 0 && { diagnostics }),
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
function createFileToFunctionsMap(indexEntries) {
|
|
143
|
+
const fileMap = {};
|
|
144
|
+
for (const entry of indexEntries) {
|
|
145
|
+
if (!fileMap[entry.file])
|
|
146
|
+
fileMap[entry.file] = [];
|
|
147
|
+
fileMap[entry.file].push(entry);
|
|
148
|
+
}
|
|
149
|
+
return fileMap;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Run an audit, routing to the Go subprocess when the target contains Go files
|
|
153
|
+
* and to the TypeScript tree-sitter pipeline otherwise. The single place both
|
|
154
|
+
* the CLI and MCP surfaces make that decision.
|
|
155
|
+
*/
|
|
156
|
+
export async function runAuditDispatch(options) {
|
|
157
|
+
// Resolve to an absolute path up front. The Go subprocess is spawned with a
|
|
158
|
+
// cwd of its own (dist/languages/go), so relative file paths produced by
|
|
159
|
+
// discovery (e.g. `bench/real/gin/auth.go`) would not resolve from its cwd —
|
|
160
|
+
// the subprocess errors "no such file or directory" and the whole audit
|
|
161
|
+
// collapses to zero findings. Absolute paths make discovery absolute, which
|
|
162
|
+
// works for both the in-process TS half and the Go subprocess.
|
|
163
|
+
const projectRoot = path.resolve(options.projectRoot || process.cwd());
|
|
164
|
+
const hasGo = await hasFilesWithExtension(projectRoot, '.go');
|
|
165
|
+
if (!hasGo) {
|
|
166
|
+
const runner = createAuditRunner(options);
|
|
167
|
+
return runner.run();
|
|
168
|
+
}
|
|
169
|
+
// Go present → the polyglot orchestrator, which runs the Go subprocess for
|
|
170
|
+
// `.go` files and the node runtime for any TypeScript/JavaScript alongside.
|
|
171
|
+
const runtimeManager = new RuntimeManager();
|
|
172
|
+
await runtimeManager.initialize();
|
|
173
|
+
const codeIndex = CodeIndexDB.getInstance();
|
|
174
|
+
await codeIndex.initialize();
|
|
175
|
+
const orchestrator = new LanguageOrchestrator(runtimeManager, codeIndex);
|
|
176
|
+
const polyglotResult = await orchestrator.analyzePolyglotProject(projectRoot, {
|
|
177
|
+
analyzers: options.enabledAnalyzers,
|
|
178
|
+
minSeverity: options.minSeverity,
|
|
179
|
+
updateIndex: options.indexFunctions,
|
|
180
|
+
enableCrossLanguageAnalysis: true,
|
|
181
|
+
buildCrossReferences: true,
|
|
182
|
+
});
|
|
183
|
+
return convertPolyglotToAuditResult(polyglotResult, projectRoot);
|
|
184
|
+
}
|
|
185
|
+
//# sourceMappingURL=auditRouter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auditRouter.js","sourceRoot":"","sources":["../src/auditRouter.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auditRunner.d.ts","sourceRoot":"","sources":["../src/auditRunner.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAQH,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,cAAc,EAWf,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"auditRunner.d.ts","sourceRoot":"","sources":["../src/auditRunner.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAQH,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,cAAc,EAWf,MAAM,YAAY,CAAC;AAmEpB;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,kBAAuB;oCAQnB,MAAM,KAAG,OAAO,CAAC,kBAAkB,CAAC;uBAQjD,kBAAkB,KAAG,OAAO,CAAC,WAAW,CAAC;6BAijC1B,WAAW,UAAU,MAAM,KAAG,OAAO,CAAC,MAAM,CAAC;EAS7F;AAkRD,MAAM,WAAW,iBAAiB;IAChC,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,WAAW,GAAG,YAAY,CAAC;IACjC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CACrC,gBAAgB,EAAE,MAAM,EAAE,EAC1B,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,EAC/C,UAAU,CAAC,EAAE,MAAM,GAClB,iBAAiB,EAAE,CA0CrB;AAED;;GAEG;AACH,wBAAsB,QAAQ,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,WAAW,CAAC,CAGjF;AAED,YAAY,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/auditRunner.js
CHANGED
|
@@ -11,9 +11,10 @@ import { randomUUID } from 'node:crypto';
|
|
|
11
11
|
import { AuditAbortedError, AuditHandoffError, } from './types.js';
|
|
12
12
|
import { discoverFiles, discoverFilesDetailed } from './utils/fileDiscovery.js';
|
|
13
13
|
import { FileAccounting } from './services/fileAccounting.js';
|
|
14
|
-
import { loadConfig } from './config/configLoader.js';
|
|
14
|
+
import { loadConfig, findConfigFileUp } from './config/configLoader.js';
|
|
15
15
|
import { mergePathProfiles } from './config/defaults.js';
|
|
16
16
|
import { checkThresholdRationales } from './config/thresholdRationales.js';
|
|
17
|
+
import { ALL_ANALYZERS } from './analyzers/ruleRegistry.js';
|
|
17
18
|
import { applyPresets } from './presets/presets.js';
|
|
18
19
|
import { generateReport } from './reporting/reportGenerator.js';
|
|
19
20
|
import { extractFunctionsFromFile } from './functionScanner.js';
|
|
@@ -29,7 +30,7 @@ import { CodeIndexDB } from './codeIndexDB.js';
|
|
|
29
30
|
import { writeAuditToLedger, detectRunInput } from './ledger.js';
|
|
30
31
|
// Pipeline imports (Spec 25 — pipeline replaces hand-rolled analyzer loop)
|
|
31
32
|
import { runPipeline, writeIndexFactsToDb, makeVisitorStatus, getFilesProcessed, isVisitorStatus, buildCoverageReport } from './pipeline.js';
|
|
32
|
-
import { createSolidVisitor, createDryVisitor, createDataAccessVisitor, createDocumentationVisitor, createFunctionIndexVisitor, createStylesCssVisitor, createReactVisitor, createStylesReducer, createConventionsReducer, createCrossDomainReducer, createInvariantsReducer, createSchemaSqlVisitor, createSchemaCodeVisitor, createSchemaPrismaVisitor, createSchemaJsonVisitor, createSchemaReducer, } from './pipelineAdapters.js';
|
|
33
|
+
import { createSolidVisitor, createDryVisitor, createDataAccessVisitor, createDocumentationVisitor, createFunctionIndexVisitor, createStylesCssVisitor, createStylesSourceVisitor, createReactVisitor, createStylesReducer, createConventionsReducer, createCrossDomainReducer, createInvariantsReducer, createSchemaSqlVisitor, createSchemaCodeVisitor, createSchemaPrismaVisitor, createSchemaJsonVisitor, createSchemaReducer, createCrossLanguageEntityVisitor, createSchemaValidatorReducer, createAPIContractReducer, createDependencyGraphReducer, } from './pipelineAdapters.js';
|
|
33
34
|
// Package version — read once at module load
|
|
34
35
|
const __auditRunnerDirname = path.dirname(fileURLToPath(import.meta.url));
|
|
35
36
|
const _pkg = JSON.parse(readFileSync(path.join(__auditRunnerDirname, '..', 'package.json'), 'utf-8'));
|
|
@@ -44,18 +45,7 @@ const SCHEMA_SUB_VISITORS = ['schema-sql', 'schema-code', 'schema-prisma', 'sche
|
|
|
44
45
|
* Create an audit runner with the given options
|
|
45
46
|
*/
|
|
46
47
|
export function createAuditRunner(options = {}) {
|
|
47
|
-
const analyzerRegistry = {
|
|
48
|
-
solid: { name: 'solid' },
|
|
49
|
-
dry: { name: 'dry' },
|
|
50
|
-
'data-access': { name: 'data-access' },
|
|
51
|
-
react: { name: 'react' },
|
|
52
|
-
documentation: { name: 'documentation' },
|
|
53
|
-
invariants: { name: 'invariants' },
|
|
54
|
-
schema: { name: 'schema' },
|
|
55
|
-
styles: { name: 'styles' },
|
|
56
|
-
conventions: { name: 'conventions' },
|
|
57
|
-
'cross-domain': { name: 'cross-domain' },
|
|
58
|
-
};
|
|
48
|
+
const analyzerRegistry = Object.fromEntries(ALL_ANALYZERS.map((name) => [name, { name }]));
|
|
59
49
|
/**
|
|
60
50
|
* Load configuration from file
|
|
61
51
|
*/
|
|
@@ -67,19 +57,16 @@ export function createAuditRunner(options = {}) {
|
|
|
67
57
|
* Run the audit
|
|
68
58
|
*/
|
|
69
59
|
async function run(runOptions) {
|
|
70
|
-
// Auto-load .codeauditor.json
|
|
71
|
-
//
|
|
72
|
-
//
|
|
60
|
+
// Auto-load .codeauditor.json so the programmatic API respects the same
|
|
61
|
+
// config the CLI surface reads. Walk UP from the audit path so a scoped
|
|
62
|
+
// audit (`--path src`) still finds the project-root config rather than
|
|
63
|
+
// silently using defaults. RunOptions (caller) override file config, and
|
|
64
|
+
// createAuditRunner options override both.
|
|
73
65
|
const rootForConfig = runOptions?.projectRoot || options.projectRoot || process.cwd();
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
fileConfig = await loadConfig({ configPath });
|
|
79
|
-
}
|
|
80
|
-
catch {
|
|
81
|
-
// No config file — proceed with defaults
|
|
82
|
-
}
|
|
66
|
+
const configPath = await findConfigFileUp(rootForConfig);
|
|
67
|
+
const fileConfig = configPath
|
|
68
|
+
? await loadConfig({ configPath })
|
|
69
|
+
: {};
|
|
83
70
|
const mergedOptions = { ...fileConfig, ...options, ...runOptions };
|
|
84
71
|
// Spec 36 R5 — a threshold change needs a written rationale. Check the
|
|
85
72
|
// user-facing analyzerConfigs layer (project config + inline options)
|
|
@@ -316,12 +303,17 @@ export function createAuditRunner(options = {}) {
|
|
|
316
303
|
// `.css`/`.scss`), so `consumedFiles` is the "any layer read it" evidence
|
|
317
304
|
// for files dropped at stage 2 — threaded into the pipeline below.
|
|
318
305
|
let styleConsumedFiles = [];
|
|
306
|
+
// In-scope files that contributed style data during the sync. Undefined when
|
|
307
|
+
// the sync did not run or failed — the styles reducer must not short-circuit
|
|
308
|
+
// on that (it falls back to full analysis), so we only assign on success.
|
|
309
|
+
let styleContributingFiles;
|
|
319
310
|
if (enabledAnalyzers.includes('styles')) {
|
|
320
311
|
try {
|
|
321
312
|
const styleDb = CodeIndexDB.getInstance(undefined, root);
|
|
322
313
|
await styleDb.initialize();
|
|
323
314
|
const styleSyncResult = await syncStyleIndex(styleDb.rawDb, files, root, { scoped: isScoped });
|
|
324
315
|
styleConsumedFiles = styleSyncResult.consumedFiles;
|
|
316
|
+
styleContributingFiles = styleSyncResult.contributingFiles;
|
|
325
317
|
logMcpInfo('style-index', 'style index sync complete', {
|
|
326
318
|
changed: styleSyncResult.changed,
|
|
327
319
|
skipped: styleSyncResult.skipped,
|
|
@@ -394,6 +386,7 @@ export function createAuditRunner(options = {}) {
|
|
|
394
386
|
exec: (sql) => auditIndex.rawDb.exec(sql),
|
|
395
387
|
getMeta: (key) => auditIndex.getMeta(key),
|
|
396
388
|
getUntestedTopDecile: (td) => auditIndex.getUntestedTopDecile(td),
|
|
389
|
+
rawDb: auditIndex.rawDb,
|
|
397
390
|
};
|
|
398
391
|
}
|
|
399
392
|
// ── 1. Build pipeline adapters ───────────────────────────────────────
|
|
@@ -409,6 +402,10 @@ export function createAuditRunner(options = {}) {
|
|
|
409
402
|
// styles-css visitor — AST-extracts .css files into style_* tables (Spec 26 Phase 2)
|
|
410
403
|
if (enabledAnalyzers.includes('styles'))
|
|
411
404
|
pipelineVisitors.push(createStylesCssVisitor());
|
|
405
|
+
// styles-source visitor — AST-extracts TS/JS CSS-in-JS into style_* tables,
|
|
406
|
+
// reusing the stage-1 parse (eliminates the style-index re-parse).
|
|
407
|
+
if (enabledAnalyzers.includes('styles'))
|
|
408
|
+
pipelineVisitors.push(createStylesSourceVisitor());
|
|
412
409
|
if (enabledAnalyzers.includes('solid'))
|
|
413
410
|
pipelineVisitors.push(createSolidVisitor());
|
|
414
411
|
if (enabledAnalyzers.includes('dry')) {
|
|
@@ -438,6 +435,22 @@ export function createAuditRunner(options = {}) {
|
|
|
438
435
|
pipelineVisitors.push(createSchemaJsonVisitor());
|
|
439
436
|
pipelineReducers.push(createSchemaReducer());
|
|
440
437
|
}
|
|
438
|
+
// Cross-language analyzers (SchemaValidator, APIContractAnalyzer,
|
|
439
|
+
// DependencyGraphBuilder) — three Stage-4 reducers fed by one shared
|
|
440
|
+
// entity-extraction visitor. The visitor is cheap (per-file) and runs
|
|
441
|
+
// whenever ANY cross-language analyzer is enabled; the corpus-wide reducers
|
|
442
|
+
// short-circuit on scoped/diff runs.
|
|
443
|
+
const crossLanguageEnabled = ['schema-validator', 'api-contract', 'dependency-graph']
|
|
444
|
+
.some((a) => enabledAnalyzers.includes(a));
|
|
445
|
+
if (crossLanguageEnabled) {
|
|
446
|
+
pipelineVisitors.push(createCrossLanguageEntityVisitor());
|
|
447
|
+
}
|
|
448
|
+
if (enabledAnalyzers.includes('schema-validator'))
|
|
449
|
+
pipelineDerivedReducers.push(createSchemaValidatorReducer());
|
|
450
|
+
if (enabledAnalyzers.includes('api-contract'))
|
|
451
|
+
pipelineDerivedReducers.push(createAPIContractReducer());
|
|
452
|
+
if (enabledAnalyzers.includes('dependency-graph'))
|
|
453
|
+
pipelineDerivedReducers.push(createDependencyGraphReducer());
|
|
441
454
|
// ── 2. Safeguard warnings ────────────────────────────────────────────
|
|
442
455
|
if (auditIndex) {
|
|
443
456
|
if (enabledAnalyzers.includes('cross-domain')) {
|
|
@@ -530,6 +543,7 @@ export function createAuditRunner(options = {}) {
|
|
|
530
543
|
isScoped,
|
|
531
544
|
fileAccounting,
|
|
532
545
|
consumedFilePaths: styleConsumedFiles,
|
|
546
|
+
styleContributingFiles,
|
|
533
547
|
onStage2Complete: async (ctx) => {
|
|
534
548
|
// Post-stage-2 setup: rebuild function_calls from the functions table
|
|
535
549
|
// (populated by the function-index visitor), then mine conventions.
|
|
@@ -581,7 +595,10 @@ export function createAuditRunner(options = {}) {
|
|
|
581
595
|
try {
|
|
582
596
|
const reactResult = analyzerResults['react'];
|
|
583
597
|
if (reactResult) {
|
|
584
|
-
|
|
598
|
+
// Pass the react namespace (not the whole namespaced config) so
|
|
599
|
+
// finalize reads `requireErrorBoundaries`/`rawElementCheck` at the
|
|
600
|
+
// same level the visitor's `context.config` does.
|
|
601
|
+
const extraViolations = await reactBundle.finalizeCrossComponent(pipelineAnalyzerConfig['react'] ?? {});
|
|
585
602
|
reactResult.violations.push(...extraViolations);
|
|
586
603
|
}
|
|
587
604
|
}
|
|
@@ -597,7 +614,7 @@ export function createAuditRunner(options = {}) {
|
|
|
597
614
|
// zero-violation rules promote from `unassessed` to `clean`/`notApplicable`.
|
|
598
615
|
pipelineCoverage = buildCoverageReport(analyzerResults, pipelineConfig, pipelineResult.metadata?.inputPresence, new Map((pipelineResult.metadata?.ruleApplicability ?? []).map((a) => [
|
|
599
616
|
a.ruleId,
|
|
600
|
-
{ applicable: a.applicable, reason: a.reason },
|
|
617
|
+
{ applicable: a.applicable, reason: a.reason, kind: a.kind },
|
|
601
618
|
])));
|
|
602
619
|
// Spec 29: extract table catalog from pipeline metadata for audit report
|
|
603
620
|
pipelineTableCatalog = pipelineResult.metadata?.tableCatalog;
|
|
@@ -659,6 +676,22 @@ export function createAuditRunner(options = {}) {
|
|
|
659
676
|
}
|
|
660
677
|
}
|
|
661
678
|
}
|
|
679
|
+
// ── Normalize the analyzer field on every violation to its result key ──
|
|
680
|
+
// Violations are emitted by analyzers that don't always stamp `analyzer`
|
|
681
|
+
// correctly: the react analyzer omits it entirely, and the schema sub-visitor
|
|
682
|
+
// helpers (createSchemaViolation) hardcode `analyzer: 'schema'` for every
|
|
683
|
+
// sub-visitor, collapsing schema-code/schema-sql/schema-prisma/schema-json
|
|
684
|
+
// into the parent 'schema'. Both surface in the baseline metadata as
|
|
685
|
+
// `unknown: 338` / an over-broad `schema` bucket. The result key IS the
|
|
686
|
+
// analyzer that emitted the finding, so stamp it here as the single source
|
|
687
|
+
// of truth — a no-op for every analyzer that already labels itself, and it
|
|
688
|
+
// also fixes any future analyzer that forgets. Runs before baseline
|
|
689
|
+
// classification (Spec 18) so fingerprints and analyzerCounts are correct.
|
|
690
|
+
for (const [resultName, result] of Object.entries(orderedAnalyzerResults)) {
|
|
691
|
+
for (const v of result.violations) {
|
|
692
|
+
v.analyzer = resultName;
|
|
693
|
+
}
|
|
694
|
+
}
|
|
662
695
|
// ── Spec 13 R5 Phase 1: Persist seeded DRY pairs ──────────────────
|
|
663
696
|
// Store pairs from DRY analysis in dry_pair_history for divergence tracking.
|
|
664
697
|
// Pair identity is fingerprint-based (file + nodeType + line),
|
|
@@ -687,36 +720,6 @@ export function createAuditRunner(options = {}) {
|
|
|
687
720
|
catch {
|
|
688
721
|
// dry_pair_history persistence is advisory — non-fatal
|
|
689
722
|
}
|
|
690
|
-
// ── Baseline classification (Spec 18 R1) ────────────────────────────
|
|
691
|
-
const projectRoot = path.resolve(mergedOptions.projectRoot || process.cwd());
|
|
692
|
-
const baseline = loadBaseline(projectRoot);
|
|
693
|
-
let baselineMetadata;
|
|
694
|
-
if (baseline) {
|
|
695
|
-
const allViolations = Object.values(orderedAnalyzerResults).flatMap((r) => r.violations);
|
|
696
|
-
// For scoped runs, limit "fixed" to in-scope files
|
|
697
|
-
const scopedFiles = isScoped ? files : undefined;
|
|
698
|
-
const classified = matchFindings(allViolations, baseline, scopedFiles);
|
|
699
|
-
// Tag violations with their baseline status
|
|
700
|
-
for (const v of classified.new) {
|
|
701
|
-
v.new = true;
|
|
702
|
-
}
|
|
703
|
-
for (const v of classified.known) {
|
|
704
|
-
v.new = false;
|
|
705
|
-
}
|
|
706
|
-
baselineMetadata = {
|
|
707
|
-
present: true,
|
|
708
|
-
hash: hashBaseline(baseline),
|
|
709
|
-
newCount: classified.new.length,
|
|
710
|
-
fixedCount: classified.fixed.length,
|
|
711
|
-
knownCount: classified.known.length,
|
|
712
|
-
previousKnownCount: baseline.metadata.totalFindings,
|
|
713
|
-
};
|
|
714
|
-
logMcpInfo('baseline', 'baseline classification complete', {
|
|
715
|
-
new: classified.new.length,
|
|
716
|
-
fixed: classified.fixed.length,
|
|
717
|
-
known: classified.known.length,
|
|
718
|
-
});
|
|
719
|
-
}
|
|
720
723
|
// ── Spec 36 R7 — suppression decay ──────────────────────────────────
|
|
721
724
|
// Inline `code-audit-disable-*` directives suppress a finding only while
|
|
722
725
|
// it fires, carry a required reason, and are themselves an error when
|
|
@@ -897,6 +900,42 @@ export function createAuditRunner(options = {}) {
|
|
|
897
900
|
for (const ar of Object.values(orderedAnalyzerResults)) {
|
|
898
901
|
validateHookContract(ar.violations);
|
|
899
902
|
}
|
|
903
|
+
// ── Baseline classification (Spec 18 R1) ────────────────────────────
|
|
904
|
+
// Runs AFTER validateHookContract (and after divergence tracking) so it
|
|
905
|
+
// classifies exactly the violation set that reaches the report. Previously
|
|
906
|
+
// this ran before the hook-contract guard spliced out malformed violations
|
|
907
|
+
// (empty file / line 0 / missing line), so newCount+knownCount over-counted
|
|
908
|
+
// by the number of spliced findings and no longer reconciled with
|
|
909
|
+
// summary.totalViolations.
|
|
910
|
+
const projectRoot = path.resolve(mergedOptions.projectRoot || process.cwd());
|
|
911
|
+
const baseline = loadBaseline(projectRoot);
|
|
912
|
+
let baselineMetadata;
|
|
913
|
+
if (baseline) {
|
|
914
|
+
const allViolations = Object.values(orderedAnalyzerResults).flatMap((r) => r.violations);
|
|
915
|
+
// For scoped runs, limit "fixed" to in-scope files
|
|
916
|
+
const scopedFiles = isScoped ? files : undefined;
|
|
917
|
+
const classified = matchFindings(allViolations, baseline, scopedFiles);
|
|
918
|
+
// Tag violations with their baseline status
|
|
919
|
+
for (const v of classified.new) {
|
|
920
|
+
v.new = true;
|
|
921
|
+
}
|
|
922
|
+
for (const v of classified.known) {
|
|
923
|
+
v.new = false;
|
|
924
|
+
}
|
|
925
|
+
baselineMetadata = {
|
|
926
|
+
present: true,
|
|
927
|
+
hash: hashBaseline(baseline),
|
|
928
|
+
newCount: classified.new.length,
|
|
929
|
+
fixedCount: classified.fixed.length,
|
|
930
|
+
knownCount: classified.known.length,
|
|
931
|
+
previousKnownCount: baseline.metadata.totalFindings,
|
|
932
|
+
};
|
|
933
|
+
logMcpInfo('baseline', 'baseline classification complete', {
|
|
934
|
+
new: classified.new.length,
|
|
935
|
+
fixed: classified.fixed.length,
|
|
936
|
+
known: classified.known.length,
|
|
937
|
+
});
|
|
938
|
+
}
|
|
900
939
|
// Generate summary
|
|
901
940
|
const summary = generateSummary(orderedAnalyzerResults, files.length);
|
|
902
941
|
// Create result
|