code-auditor-mcp 3.4.11 → 3.4.13
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 +2 -2
- package/CHANGELOG.md +119 -1
- package/dist/analyzers/__verify_self_probe.d.ts +2 -0
- package/dist/analyzers/__verify_self_probe.d.ts.map +1 -0
- package/dist/analyzers/__verify_self_probe.js +5 -0
- package/dist/analyzers/__verify_self_probe.js.map +1 -0
- package/dist/analyzers/analyzerUtils.d.ts +34 -1
- package/dist/analyzers/analyzerUtils.d.ts.map +1 -1
- package/dist/analyzers/analyzerUtils.js +61 -22
- package/dist/analyzers/analyzerUtils.js.map +1 -1
- package/dist/analyzers/cross-language/APIContractAnalyzer.d.ts +18 -23
- package/dist/analyzers/cross-language/APIContractAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/cross-language/APIContractAnalyzer.js +198 -186
- package/dist/analyzers/cross-language/APIContractAnalyzer.js.map +1 -1
- package/dist/analyzers/cross-language/DependencyGraphBuilder.d.ts +78 -55
- package/dist/analyzers/cross-language/DependencyGraphBuilder.d.ts.map +1 -1
- package/dist/analyzers/cross-language/DependencyGraphBuilder.js +244 -243
- package/dist/analyzers/cross-language/DependencyGraphBuilder.js.map +1 -1
- package/dist/analyzers/cross-language/SchemaValidator.d.ts +27 -37
- package/dist/analyzers/cross-language/SchemaValidator.d.ts.map +1 -1
- package/dist/analyzers/cross-language/SchemaValidator.js +314 -275
- package/dist/analyzers/cross-language/SchemaValidator.js.map +1 -1
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.d.ts +17 -46
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js +620 -523
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js.map +1 -1
- package/dist/analyzers/documentationAnalyzer.d.ts +7 -2
- package/dist/analyzers/documentationAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/documentationAnalyzer.js +259 -198
- package/dist/analyzers/documentationAnalyzer.js.map +1 -1
- package/dist/analyzers/invariantsAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/invariantsAnalyzer.js +59 -46
- package/dist/analyzers/invariantsAnalyzer.js.map +1 -1
- package/dist/analyzers/orm/adapterRegistry.d.ts +10 -0
- package/dist/analyzers/orm/adapterRegistry.d.ts.map +1 -1
- package/dist/analyzers/orm/adapterRegistry.js +10 -0
- package/dist/analyzers/orm/adapterRegistry.js.map +1 -1
- package/dist/analyzers/orm/drizzleAdapter.d.ts +22 -0
- package/dist/analyzers/orm/drizzleAdapter.d.ts.map +1 -1
- package/dist/analyzers/orm/drizzleAdapter.js +42 -44
- package/dist/analyzers/orm/drizzleAdapter.js.map +1 -1
- package/dist/analyzers/orm/prismaAdapter.d.ts +30 -0
- package/dist/analyzers/orm/prismaAdapter.d.ts.map +1 -1
- package/dist/analyzers/orm/prismaAdapter.js +30 -0
- package/dist/analyzers/orm/prismaAdapter.js.map +1 -1
- package/dist/analyzers/provenance.d.ts +72 -2
- package/dist/analyzers/provenance.d.ts.map +1 -1
- package/dist/analyzers/provenance.js +462 -338
- package/dist/analyzers/provenance.js.map +1 -1
- package/dist/analyzers/reactAnalyzer.d.ts +11 -0
- package/dist/analyzers/reactAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/reactAnalyzer.js +188 -125
- package/dist/analyzers/reactAnalyzer.js.map +1 -1
- package/dist/analyzers/ruleRegistry.d.ts +118 -1
- package/dist/analyzers/ruleRegistry.d.ts.map +1 -1
- package/dist/analyzers/ruleRegistry.js +1717 -104
- package/dist/analyzers/ruleRegistry.js.map +1 -1
- package/dist/analyzers/ruleTiming.d.ts +54 -0
- package/dist/analyzers/ruleTiming.d.ts.map +1 -0
- package/dist/analyzers/ruleTiming.js +71 -0
- package/dist/analyzers/ruleTiming.js.map +1 -0
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.d.ts +13 -0
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.js +362 -283
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts +26 -68
- package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalDRYAnalyzer.js +430 -391
- package/dist/analyzers/universal/UniversalDRYAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts +24 -186
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js +1081 -1061
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalDocumentationAnalyzer.d.ts +3 -49
- package/dist/analyzers/universal/UniversalDocumentationAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalDocumentationAnalyzer.js +414 -340
- package/dist/analyzers/universal/UniversalDocumentationAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts +48 -5
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js +262 -134
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts +23 -212
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.js +139 -1518
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts +78 -79
- package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalStylesAnalyzer.js +613 -472
- package/dist/analyzers/universal/UniversalStylesAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/schema/codeAnalysis.d.ts +307 -0
- package/dist/analyzers/universal/schema/codeAnalysis.d.ts.map +1 -0
- package/dist/analyzers/universal/schema/codeAnalysis.js +965 -0
- package/dist/analyzers/universal/schema/codeAnalysis.js.map +1 -0
- package/dist/analyzers/universal/schema/config.d.ts +31 -0
- package/dist/analyzers/universal/schema/config.d.ts.map +1 -0
- package/dist/analyzers/universal/schema/config.js +57 -0
- package/dist/analyzers/universal/schema/config.js.map +1 -0
- package/dist/analyzers/universal/schema/discovery.d.ts +167 -0
- package/dist/analyzers/universal/schema/discovery.d.ts.map +1 -0
- package/dist/analyzers/universal/schema/discovery.js +616 -0
- package/dist/analyzers/universal/schema/discovery.js.map +1 -0
- package/dist/analyzers/universal/schema/jsonSchema.d.ts +27 -0
- package/dist/analyzers/universal/schema/jsonSchema.d.ts.map +1 -0
- package/dist/analyzers/universal/schema/jsonSchema.js +368 -0
- package/dist/analyzers/universal/schema/jsonSchema.js.map +1 -0
- package/dist/analyzers/universal/schema/migrations.d.ts +90 -0
- package/dist/analyzers/universal/schema/migrations.d.ts.map +1 -0
- package/dist/analyzers/universal/schema/migrations.js +238 -0
- package/dist/analyzers/universal/schema/migrations.js.map +1 -0
- package/dist/analyzers/universal/schema/types.d.ts +131 -0
- package/dist/analyzers/universal/schema/types.d.ts.map +1 -0
- package/dist/analyzers/universal/schema/types.js +10 -0
- package/dist/analyzers/universal/schema/types.js.map +1 -0
- package/dist/analyzers/universal/schema/violations.d.ts +44 -0
- package/dist/analyzers/universal/schema/violations.d.ts.map +1 -0
- package/dist/analyzers/universal/schema/violations.js +54 -0
- package/dist/analyzers/universal/schema/violations.js.map +1 -0
- package/dist/auditRunner.d.ts.map +1 -1
- package/dist/auditRunner.js +101 -39
- package/dist/auditRunner.js.map +1 -1
- package/dist/cli.js +348 -40
- package/dist/cli.js.map +1 -1
- package/dist/config/configLoader.d.ts.map +1 -1
- package/dist/config/configLoader.js +9 -7
- package/dist/config/configLoader.js.map +1 -1
- package/dist/config/defaults.d.ts +12 -3
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +15 -5
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/effectiveConfig.d.ts +75 -0
- package/dist/config/effectiveConfig.d.ts.map +1 -0
- package/dist/config/effectiveConfig.js +213 -0
- package/dist/config/effectiveConfig.js.map +1 -0
- package/dist/config/pathProfiles.d.ts +7 -4
- package/dist/config/pathProfiles.d.ts.map +1 -1
- package/dist/config/pathProfiles.js +8 -10
- package/dist/config/pathProfiles.js.map +1 -1
- package/dist/config/thresholdRationales.d.ts +45 -0
- package/dist/config/thresholdRationales.d.ts.map +1 -0
- package/dist/config/thresholdRationales.js +67 -0
- package/dist/config/thresholdRationales.js.map +1 -0
- package/dist/conventions/conventionMiner.d.ts.map +1 -1
- package/dist/conventions/conventionMiner.js +6 -2
- package/dist/conventions/conventionMiner.js.map +1 -1
- package/dist/enforcement/diffGate.d.ts +55 -0
- package/dist/enforcement/diffGate.d.ts.map +1 -0
- package/dist/enforcement/diffGate.js +136 -0
- package/dist/enforcement/diffGate.js.map +1 -0
- package/dist/enforcement/gate.d.ts +36 -0
- package/dist/enforcement/gate.d.ts.map +1 -0
- package/dist/enforcement/gate.js +72 -0
- package/dist/enforcement/gate.js.map +1 -0
- package/dist/enforcement/suppressions.d.ts +41 -0
- package/dist/enforcement/suppressions.d.ts.map +1 -0
- package/dist/enforcement/suppressions.js +170 -0
- package/dist/enforcement/suppressions.js.map +1 -0
- package/dist/fingerprint.d.ts.map +1 -1
- package/dist/fingerprint.js +4 -1
- package/dist/fingerprint.js.map +1 -1
- package/dist/grammars/tree-sitter-scss.wasm +0 -0
- package/dist/invariants/ruleEngine.d.ts +3 -4
- package/dist/invariants/ruleEngine.d.ts.map +1 -1
- package/dist/invariants/ruleEngine.js +2 -2
- package/dist/invariants/ruleEngine.js.map +1 -1
- package/dist/languages/LanguageOrchestrator.d.ts +14 -0
- package/dist/languages/LanguageOrchestrator.d.ts.map +1 -1
- package/dist/languages/LanguageOrchestrator.js +93 -55
- package/dist/languages/LanguageOrchestrator.js.map +1 -1
- package/dist/languages/LanguageRegistry.d.ts +8 -0
- package/dist/languages/LanguageRegistry.d.ts.map +1 -1
- package/dist/languages/LanguageRegistry.js +8 -0
- package/dist/languages/LanguageRegistry.js.map +1 -1
- package/dist/languages/RuntimeManager.d.ts +82 -60
- package/dist/languages/RuntimeManager.d.ts.map +1 -1
- package/dist/languages/RuntimeManager.js +404 -349
- package/dist/languages/RuntimeManager.js.map +1 -1
- package/dist/languages/UniversalAnalyzer.d.ts +51 -6
- package/dist/languages/UniversalAnalyzer.d.ts.map +1 -1
- package/dist/languages/UniversalAnalyzer.js +127 -88
- package/dist/languages/UniversalAnalyzer.js.map +1 -1
- package/dist/languages/adapterBridge.d.ts +36 -0
- package/dist/languages/adapterBridge.d.ts.map +1 -1
- package/dist/languages/adapterBridge.js +89 -35
- package/dist/languages/adapterBridge.js.map +1 -1
- package/dist/languages/go/GoAdapter.d.ts +66 -23
- package/dist/languages/go/GoAdapter.d.ts.map +1 -1
- package/dist/languages/go/GoAdapter.js +553 -474
- package/dist/languages/go/GoAdapter.js.map +1 -1
- package/dist/languages/tree-sitter/TreeSitterCssAdapter.d.ts +38 -9
- package/dist/languages/tree-sitter/TreeSitterCssAdapter.d.ts.map +1 -1
- package/dist/languages/tree-sitter/TreeSitterCssAdapter.js +111 -74
- package/dist/languages/tree-sitter/TreeSitterCssAdapter.js.map +1 -1
- package/dist/languages/tree-sitter/converter.d.ts +18 -0
- package/dist/languages/tree-sitter/converter.d.ts.map +1 -1
- package/dist/languages/tree-sitter/converter.js +18 -0
- package/dist/languages/tree-sitter/converter.js.map +1 -1
- package/dist/languages/tree-sitter/parser.d.ts +39 -2
- package/dist/languages/tree-sitter/parser.d.ts.map +1 -1
- package/dist/languages/tree-sitter/parser.js +95 -12
- package/dist/languages/tree-sitter/parser.js.map +1 -1
- package/dist/languages/types.d.ts +67 -2
- package/dist/languages/types.d.ts.map +1 -1
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.d.ts +253 -62
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.d.ts.map +1 -1
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js +1461 -791
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js.map +1 -1
- package/dist/mcp-tools/whitelistTools.d.ts +1 -1
- package/dist/mcp-tools-shared.d.ts.map +1 -1
- package/dist/mcp-tools-shared.js +1 -0
- package/dist/mcp-tools-shared.js.map +1 -1
- package/dist/mcpAuditJobs.d.ts.map +1 -1
- package/dist/mcpAuditJobs.js +1 -0
- package/dist/mcpAuditJobs.js.map +1 -1
- package/dist/pipeline.d.ts +30 -1
- package/dist/pipeline.d.ts.map +1 -1
- package/dist/pipeline.js +527 -203
- package/dist/pipeline.js.map +1 -1
- package/dist/pipelineAdapters.d.ts +5 -12
- package/dist/pipelineAdapters.d.ts.map +1 -1
- package/dist/pipelineAdapters.js +175 -100
- package/dist/pipelineAdapters.js.map +1 -1
- package/dist/presets/presets.d.ts +54 -0
- package/dist/presets/presets.d.ts.map +1 -0
- package/dist/presets/presets.js +181 -0
- package/dist/presets/presets.js.map +1 -0
- package/dist/reporting/sarifReportGenerator.d.ts.map +1 -1
- package/dist/reporting/sarifReportGenerator.js +0 -2
- package/dist/reporting/sarifReportGenerator.js.map +1 -1
- package/dist/ruleAliases.d.ts +67 -0
- package/dist/ruleAliases.d.ts.map +1 -0
- package/dist/ruleAliases.js +79 -0
- package/dist/ruleAliases.js.map +1 -0
- package/dist/styles/cssAstExtractor.d.ts +20 -5
- package/dist/styles/cssAstExtractor.d.ts.map +1 -1
- package/dist/styles/cssAstExtractor.js +247 -9
- package/dist/styles/cssAstExtractor.js.map +1 -1
- package/dist/styles/styleIndexer.js +13 -4
- package/dist/styles/styleIndexer.js.map +1 -1
- package/dist/styles/tailwindConfigLoader.d.ts +8 -6
- package/dist/styles/tailwindConfigLoader.d.ts.map +1 -1
- package/dist/styles/tailwindConfigLoader.js +61 -15
- package/dist/styles/tailwindConfigLoader.js.map +1 -1
- package/dist/styles/tailwindUtilityExpander.d.ts.map +1 -1
- package/dist/styles/tailwindUtilityExpander.js +13 -1
- package/dist/styles/tailwindUtilityExpander.js.map +1 -1
- package/dist/types.d.ts +201 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -1
- package/package.json +7 -4
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/hooks/hooks.json +1 -1
- package/plugin/scripts/hook-audit.sh +25 -7
- package/plugin/skills/code-auditor/SKILL.md +5 -5
|
@@ -7,7 +7,10 @@ import { promisify } from 'util';
|
|
|
7
7
|
import * as path from 'path';
|
|
8
8
|
import * as fs from 'fs/promises';
|
|
9
9
|
const execAsync = promisify(exec);
|
|
10
|
-
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Detection layer: shared state + runtime discovery (leaf — no cross-layer calls)
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
class RuntimeManagerDetection {
|
|
11
14
|
runtimes = new Map();
|
|
12
15
|
initialized = false;
|
|
13
16
|
config = new Map();
|
|
@@ -35,217 +38,6 @@ export class RuntimeManager {
|
|
|
35
38
|
console.error('[RuntimeManager] Runtime detection complete');
|
|
36
39
|
this.logRuntimeStatus();
|
|
37
40
|
}
|
|
38
|
-
/**
|
|
39
|
-
* Check if a specific runtime is available
|
|
40
|
-
*/
|
|
41
|
-
hasRuntime(name) {
|
|
42
|
-
const runtime = this.runtimes.get(name);
|
|
43
|
-
const config = this.config.get(name);
|
|
44
|
-
return runtime?.available === true &&
|
|
45
|
-
(config?.enabled !== false) &&
|
|
46
|
-
this.checkVersionCompatibility(runtime, config);
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* Get fallback runtime for a language if the primary is unavailable
|
|
50
|
-
*/
|
|
51
|
-
getFallbackRuntime(language) {
|
|
52
|
-
const fallbackMap = {
|
|
53
|
-
'typescript': ['node', 'deno', 'bun'],
|
|
54
|
-
'javascript': ['node', 'deno', 'bun'],
|
|
55
|
-
'go': ['go'],
|
|
56
|
-
'python': ['python'],
|
|
57
|
-
'rust': ['rust']
|
|
58
|
-
};
|
|
59
|
-
const fallbacks = fallbackMap[language] || [];
|
|
60
|
-
for (const fallback of fallbacks) {
|
|
61
|
-
if (this.hasRuntime(fallback)) {
|
|
62
|
-
const runtime = this.runtimes.get(fallback);
|
|
63
|
-
if (runtime?.analyzer?.supports.some(ext => this.getLanguageExtensions(language).includes(ext))) {
|
|
64
|
-
return runtime;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
return null;
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Get file extensions for a language
|
|
72
|
-
*/
|
|
73
|
-
getLanguageExtensions(language) {
|
|
74
|
-
const extensionMap = {
|
|
75
|
-
'typescript': ['.ts', '.tsx'],
|
|
76
|
-
'javascript': ['.js', '.jsx'],
|
|
77
|
-
'go': ['.go'],
|
|
78
|
-
'python': ['.py'],
|
|
79
|
-
'rust': ['.rs']
|
|
80
|
-
};
|
|
81
|
-
return extensionMap[language] || [];
|
|
82
|
-
}
|
|
83
|
-
/**
|
|
84
|
-
* Attempt to analyze with fallback runtime if primary fails
|
|
85
|
-
*/
|
|
86
|
-
async analyzeWithFallback(language, files, options) {
|
|
87
|
-
// Try primary runtime first
|
|
88
|
-
let result = await this.spawnAnalyzer(language, files, options);
|
|
89
|
-
if (result && result.errors?.length === 0) {
|
|
90
|
-
return result;
|
|
91
|
-
}
|
|
92
|
-
// If primary failed, try fallback
|
|
93
|
-
const fallbackRuntime = this.getFallbackRuntime(language);
|
|
94
|
-
if (fallbackRuntime) {
|
|
95
|
-
console.log(`[RuntimeManager] Primary ${language} runtime failed, trying fallback: ${fallbackRuntime.name}`);
|
|
96
|
-
try {
|
|
97
|
-
const fallbackResult = await fallbackRuntime.analyzer.analyze(files, {
|
|
98
|
-
...options,
|
|
99
|
-
fallbackMode: true,
|
|
100
|
-
originalLanguage: language
|
|
101
|
-
});
|
|
102
|
-
// Add warning about fallback usage
|
|
103
|
-
fallbackResult.errors = fallbackResult.errors || [];
|
|
104
|
-
fallbackResult.errors.push({
|
|
105
|
-
message: `Analysis completed using fallback runtime ${fallbackRuntime.name} instead of native ${language} runtime`,
|
|
106
|
-
type: 'fallback_warning',
|
|
107
|
-
language
|
|
108
|
-
});
|
|
109
|
-
return fallbackResult;
|
|
110
|
-
}
|
|
111
|
-
catch (fallbackError) {
|
|
112
|
-
console.error(`[RuntimeManager] Fallback runtime also failed:`, fallbackError);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
// No fallback available or fallback also failed
|
|
116
|
-
return {
|
|
117
|
-
violations: [],
|
|
118
|
-
indexEntries: [],
|
|
119
|
-
metrics: { filesAnalyzed: 0, executionTime: 0 },
|
|
120
|
-
errors: [{
|
|
121
|
-
message: `No working runtime available for ${language}. Install ${language} runtime or enable fallback options.`,
|
|
122
|
-
type: 'no_runtime',
|
|
123
|
-
language,
|
|
124
|
-
suggestions: this.getRuntimeInstallationSuggestions(language)
|
|
125
|
-
}]
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
/**
|
|
129
|
-
* Get installation suggestions for missing runtimes
|
|
130
|
-
*/
|
|
131
|
-
getRuntimeInstallationSuggestions(language) {
|
|
132
|
-
const suggestions = {
|
|
133
|
-
'go': [
|
|
134
|
-
'Install Go: https://golang.org/dl/',
|
|
135
|
-
'Or use package manager: brew install go (macOS), apt install golang (Ubuntu)'
|
|
136
|
-
],
|
|
137
|
-
'python': [
|
|
138
|
-
'Install Python: https://python.org/downloads/',
|
|
139
|
-
'Or use package manager: brew install python (macOS), apt install python3 (Ubuntu)'
|
|
140
|
-
],
|
|
141
|
-
'rust': [
|
|
142
|
-
'Install Rust: https://rustup.rs/',
|
|
143
|
-
'Run: curl --proto "=https" --tlsv1.2 -sSf https://sh.rustup.rs | sh'
|
|
144
|
-
],
|
|
145
|
-
'node': [
|
|
146
|
-
'Install Node.js: https://nodejs.org/',
|
|
147
|
-
'Or use nvm: nvm install node'
|
|
148
|
-
]
|
|
149
|
-
};
|
|
150
|
-
return suggestions[language] || ['Check the official documentation for installation instructions'];
|
|
151
|
-
}
|
|
152
|
-
/**
|
|
153
|
-
* Get runtime information
|
|
154
|
-
*/
|
|
155
|
-
getRuntime(name) {
|
|
156
|
-
return this.runtimes.get(name);
|
|
157
|
-
}
|
|
158
|
-
/**
|
|
159
|
-
* Get all available runtimes
|
|
160
|
-
*/
|
|
161
|
-
getAvailableRuntimes() {
|
|
162
|
-
return Array.from(this.runtimes.values()).filter(r => r.available);
|
|
163
|
-
}
|
|
164
|
-
/**
|
|
165
|
-
* Spawn analyzer for a specific language
|
|
166
|
-
*/
|
|
167
|
-
async spawnAnalyzer(language, files, options) {
|
|
168
|
-
if (!this.initialized) {
|
|
169
|
-
await this.initialize();
|
|
170
|
-
}
|
|
171
|
-
const runtime = this.runtimes.get(language);
|
|
172
|
-
if (!runtime?.available || !runtime.analyzer) {
|
|
173
|
-
console.warn(`[RuntimeManager] No analyzer available for ${language}`);
|
|
174
|
-
return null;
|
|
175
|
-
}
|
|
176
|
-
console.log(`[RuntimeManager] Spawning ${language} analyzer for ${files.length} files`);
|
|
177
|
-
const startTime = Date.now();
|
|
178
|
-
const processTimeout = options?.timeout || 300000; // 5 minutes default
|
|
179
|
-
try {
|
|
180
|
-
const result = await this.runWithTimeout(() => runtime.analyzer.analyze(files, options), processTimeout);
|
|
181
|
-
const executionTime = Date.now() - startTime;
|
|
182
|
-
result.metrics.executionTime = executionTime;
|
|
183
|
-
console.log(`[RuntimeManager] ${language} analysis complete in ${executionTime}ms: ${result.violations.length} violations`);
|
|
184
|
-
return result;
|
|
185
|
-
}
|
|
186
|
-
catch (error) {
|
|
187
|
-
const executionTime = Date.now() - startTime;
|
|
188
|
-
console.error(`[RuntimeManager] Error in ${language} analyzer after ${executionTime}ms:`, error);
|
|
189
|
-
return {
|
|
190
|
-
violations: [],
|
|
191
|
-
indexEntries: [],
|
|
192
|
-
metrics: { filesAnalyzed: 0, executionTime },
|
|
193
|
-
errors: [{
|
|
194
|
-
message: error instanceof Error ? error.message : String(error),
|
|
195
|
-
language,
|
|
196
|
-
type: error instanceof Error && error.message.includes('timeout') ? 'timeout' : 'runtime_error'
|
|
197
|
-
}]
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
/**
|
|
202
|
-
* Run a function with timeout
|
|
203
|
-
*/
|
|
204
|
-
async runWithTimeout(fn, timeoutMs) {
|
|
205
|
-
return new Promise((resolve, reject) => {
|
|
206
|
-
const timeoutId = setTimeout(() => {
|
|
207
|
-
reject(new Error(`Analysis timed out after ${timeoutMs}ms`));
|
|
208
|
-
}, timeoutMs);
|
|
209
|
-
fn()
|
|
210
|
-
.then(result => {
|
|
211
|
-
clearTimeout(timeoutId);
|
|
212
|
-
resolve(result);
|
|
213
|
-
})
|
|
214
|
-
.catch(error => {
|
|
215
|
-
clearTimeout(timeoutId);
|
|
216
|
-
reject(error);
|
|
217
|
-
});
|
|
218
|
-
});
|
|
219
|
-
}
|
|
220
|
-
/**
|
|
221
|
-
* Spawn multiple analyzers in parallel with concurrency control
|
|
222
|
-
*/
|
|
223
|
-
async spawnMultipleAnalyzers(languageFilePairs, options) {
|
|
224
|
-
const maxConcurrency = options?.maxConcurrency || 3;
|
|
225
|
-
const results = [];
|
|
226
|
-
console.log(`[RuntimeManager] Spawning ${languageFilePairs.length} analyzers with max concurrency ${maxConcurrency}`);
|
|
227
|
-
// Process in batches to control concurrency
|
|
228
|
-
for (let i = 0; i < languageFilePairs.length; i += maxConcurrency) {
|
|
229
|
-
const batch = languageFilePairs.slice(i, i + maxConcurrency);
|
|
230
|
-
const batchPromises = batch.map(async ({ language, files }) => {
|
|
231
|
-
const result = await this.spawnAnalyzer(language, files, options);
|
|
232
|
-
return { language, result };
|
|
233
|
-
});
|
|
234
|
-
const batchResults = await Promise.all(batchPromises);
|
|
235
|
-
results.push(...batchResults);
|
|
236
|
-
}
|
|
237
|
-
return results;
|
|
238
|
-
}
|
|
239
|
-
/**
|
|
240
|
-
* Kill all running analyzer processes (for cleanup)
|
|
241
|
-
*/
|
|
242
|
-
async killAllAnalyzers() {
|
|
243
|
-
console.log('[RuntimeManager] Killing all analyzer processes...');
|
|
244
|
-
// This is a placeholder for process management
|
|
245
|
-
// In future implementations, we'll track child processes and kill them here
|
|
246
|
-
// For now, we just log the action since our analyzers run in-process
|
|
247
|
-
console.log('[RuntimeManager] All analyzer processes terminated');
|
|
248
|
-
}
|
|
249
41
|
/**
|
|
250
42
|
* Detect Node.js runtime
|
|
251
43
|
*/
|
|
@@ -426,6 +218,23 @@ export class RuntimeManager {
|
|
|
426
218
|
return false;
|
|
427
219
|
}
|
|
428
220
|
}
|
|
221
|
+
/**
|
|
222
|
+
* Log runtime status
|
|
223
|
+
*/
|
|
224
|
+
logRuntimeStatus() {
|
|
225
|
+
console.log('\n[RuntimeManager] Runtime Status:');
|
|
226
|
+
for (const [name, runtime] of this.runtimes) {
|
|
227
|
+
const status = runtime.available ? '✅' : '❌';
|
|
228
|
+
const analyzer = runtime.analyzer ? '(analyzer available)' : '(no analyzer)';
|
|
229
|
+
console.log(` ${status} ${runtime.name} ${runtime.version} ${analyzer}`);
|
|
230
|
+
}
|
|
231
|
+
console.log('');
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
// ---------------------------------------------------------------------------
|
|
235
|
+
// Version layer: semantic-version comparison and compatibility reporting
|
|
236
|
+
// ---------------------------------------------------------------------------
|
|
237
|
+
class RuntimeManagerVersion extends RuntimeManagerDetection {
|
|
429
238
|
/**
|
|
430
239
|
* Check version compatibility with detailed reporting
|
|
431
240
|
*/
|
|
@@ -453,25 +262,14 @@ export class RuntimeManager {
|
|
|
453
262
|
const config = this.config.get(name);
|
|
454
263
|
const minVersion = config?.minVersion || runtime.minVersion;
|
|
455
264
|
const compatible = this.checkVersionCompatibility(runtime, config);
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
status = 'unknown';
|
|
465
|
-
recommendations.push(`Unable to detect ${runtime.name} version`);
|
|
466
|
-
}
|
|
467
|
-
else if (!compatible) {
|
|
468
|
-
status = 'incompatible';
|
|
469
|
-
recommendations.push(`Update ${runtime.name} to version ${minVersion} or higher`);
|
|
470
|
-
recommendations.push(`Current: ${runtime.version}, Required: ${minVersion}`);
|
|
471
|
-
}
|
|
472
|
-
else {
|
|
473
|
-
status = 'compatible';
|
|
474
|
-
}
|
|
265
|
+
const { status, recommendations } = deriveCompatibilityStatus({
|
|
266
|
+
name,
|
|
267
|
+
available: runtime.available,
|
|
268
|
+
version: runtime.version,
|
|
269
|
+
compatible,
|
|
270
|
+
minVersion,
|
|
271
|
+
installSuggestions: this.getRuntimeInstallationSuggestions(name)
|
|
272
|
+
});
|
|
475
273
|
report.push({
|
|
476
274
|
runtime: runtime.name,
|
|
477
275
|
currentVersion: runtime.version,
|
|
@@ -552,16 +350,90 @@ export class RuntimeManager {
|
|
|
552
350
|
return 0;
|
|
553
351
|
}
|
|
554
352
|
/**
|
|
555
|
-
*
|
|
353
|
+
* Get installation suggestions for missing runtimes
|
|
556
354
|
*/
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
355
|
+
getRuntimeInstallationSuggestions(language) {
|
|
356
|
+
const suggestions = {
|
|
357
|
+
'go': [
|
|
358
|
+
'Install Go: https://golang.org/dl/',
|
|
359
|
+
'Or use package manager: brew install go (macOS), apt install golang (Ubuntu)'
|
|
360
|
+
],
|
|
361
|
+
'python': [
|
|
362
|
+
'Install Python: https://python.org/downloads/',
|
|
363
|
+
'Or use package manager: brew install python (macOS), apt install python3 (Ubuntu)'
|
|
364
|
+
],
|
|
365
|
+
'rust': [
|
|
366
|
+
'Install Rust: https://rustup.rs/',
|
|
367
|
+
'Run: curl --proto "=https" --tlsv1.2 -sSf https://sh.rustup.rs | sh'
|
|
368
|
+
],
|
|
369
|
+
'node': [
|
|
370
|
+
'Install Node.js: https://nodejs.org/',
|
|
371
|
+
'Or use nvm: nvm install node'
|
|
372
|
+
]
|
|
373
|
+
};
|
|
374
|
+
return suggestions[language] || ['Check the official documentation for installation instructions'];
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
// ---------------------------------------------------------------------------
|
|
378
|
+
// Access layer: runtime lookup, fallback resolution, and statistics
|
|
379
|
+
// ---------------------------------------------------------------------------
|
|
380
|
+
class RuntimeManagerAccess extends RuntimeManagerVersion {
|
|
381
|
+
/**
|
|
382
|
+
* Check if a specific runtime is available
|
|
383
|
+
*/
|
|
384
|
+
hasRuntime(name) {
|
|
385
|
+
const runtime = this.runtimes.get(name);
|
|
386
|
+
const config = this.config.get(name);
|
|
387
|
+
return runtime?.available === true &&
|
|
388
|
+
(config?.enabled !== false) &&
|
|
389
|
+
this.checkVersionCompatibility(runtime, config);
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Get fallback runtime for a language if the primary is unavailable
|
|
393
|
+
*/
|
|
394
|
+
getFallbackRuntime(language) {
|
|
395
|
+
const fallbackMap = {
|
|
396
|
+
'typescript': ['node', 'deno', 'bun'],
|
|
397
|
+
'javascript': ['node', 'deno', 'bun'],
|
|
398
|
+
'go': ['go'],
|
|
399
|
+
'python': ['python'],
|
|
400
|
+
'rust': ['rust']
|
|
401
|
+
};
|
|
402
|
+
const fallbacks = fallbackMap[language] || [];
|
|
403
|
+
for (const fallback of fallbacks) {
|
|
404
|
+
if (this.hasRuntime(fallback)) {
|
|
405
|
+
const runtime = this.runtimes.get(fallback);
|
|
406
|
+
if (runtime?.analyzer?.supports.some(ext => this.getLanguageExtensions(language).includes(ext))) {
|
|
407
|
+
return runtime;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
563
410
|
}
|
|
564
|
-
|
|
411
|
+
return null;
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Get file extensions for a language
|
|
415
|
+
*/
|
|
416
|
+
getLanguageExtensions(language) {
|
|
417
|
+
const extensionMap = {
|
|
418
|
+
'typescript': ['.ts', '.tsx'],
|
|
419
|
+
'javascript': ['.js', '.jsx'],
|
|
420
|
+
'go': ['.go'],
|
|
421
|
+
'python': ['.py'],
|
|
422
|
+
'rust': ['.rs']
|
|
423
|
+
};
|
|
424
|
+
return extensionMap[language] || [];
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Get runtime information
|
|
428
|
+
*/
|
|
429
|
+
getRuntime(name) {
|
|
430
|
+
return this.runtimes.get(name);
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Get all available runtimes
|
|
434
|
+
*/
|
|
435
|
+
getAvailableRuntimes() {
|
|
436
|
+
return Array.from(this.runtimes.values()).filter(r => r.available);
|
|
565
437
|
}
|
|
566
438
|
/**
|
|
567
439
|
* Get runtime statistics
|
|
@@ -585,6 +457,156 @@ export class RuntimeManager {
|
|
|
585
457
|
};
|
|
586
458
|
}
|
|
587
459
|
}
|
|
460
|
+
// ---------------------------------------------------------------------------
|
|
461
|
+
// Spawning layer: analyzer process orchestration (public top layer)
|
|
462
|
+
// ---------------------------------------------------------------------------
|
|
463
|
+
/**
|
|
464
|
+
* Runtime manager.
|
|
465
|
+
*/
|
|
466
|
+
export class RuntimeManager extends RuntimeManagerAccess {
|
|
467
|
+
/**
|
|
468
|
+
* Attempt to analyze with fallback runtime if primary fails
|
|
469
|
+
* @param files
|
|
470
|
+
* @param language
|
|
471
|
+
* @param options
|
|
472
|
+
* @returns
|
|
473
|
+
*/
|
|
474
|
+
async analyzeWithFallback(language, files, options) {
|
|
475
|
+
// Try primary runtime first
|
|
476
|
+
let result = await this.spawnAnalyzer(language, files, options);
|
|
477
|
+
if (result && result.errors?.length === 0) {
|
|
478
|
+
return result;
|
|
479
|
+
}
|
|
480
|
+
// If primary failed, try fallback
|
|
481
|
+
const fallbackRuntime = this.getFallbackRuntime(language);
|
|
482
|
+
if (fallbackRuntime) {
|
|
483
|
+
console.log(`[RuntimeManager] Primary ${language} runtime failed, trying fallback: ${fallbackRuntime.name}`);
|
|
484
|
+
try {
|
|
485
|
+
const fallbackResult = await fallbackRuntime.analyzer.analyze(files, {
|
|
486
|
+
...options,
|
|
487
|
+
fallbackMode: true,
|
|
488
|
+
originalLanguage: language
|
|
489
|
+
});
|
|
490
|
+
// Add warning about fallback usage
|
|
491
|
+
fallbackResult.errors = fallbackResult.errors || [];
|
|
492
|
+
fallbackResult.errors.push({
|
|
493
|
+
message: `Analysis completed using fallback runtime ${fallbackRuntime.name} instead of native ${language} runtime`,
|
|
494
|
+
type: 'fallback_warning',
|
|
495
|
+
language
|
|
496
|
+
});
|
|
497
|
+
return fallbackResult;
|
|
498
|
+
}
|
|
499
|
+
catch (fallbackError) {
|
|
500
|
+
console.error(`[RuntimeManager] Fallback runtime also failed:`, fallbackError);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
// No fallback available or fallback also failed
|
|
504
|
+
return {
|
|
505
|
+
violations: [],
|
|
506
|
+
indexEntries: [],
|
|
507
|
+
metrics: { filesAnalyzed: 0, executionTime: 0 },
|
|
508
|
+
errors: [{
|
|
509
|
+
message: `No working runtime available for ${language}. Install ${language} runtime or enable fallback options.`,
|
|
510
|
+
type: 'no_runtime',
|
|
511
|
+
language,
|
|
512
|
+
suggestions: this.getRuntimeInstallationSuggestions(language)
|
|
513
|
+
}]
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
/**
|
|
517
|
+
* Spawn analyzer for a specific language
|
|
518
|
+
* @param files
|
|
519
|
+
* @param language
|
|
520
|
+
* @param options
|
|
521
|
+
* @returns
|
|
522
|
+
*/
|
|
523
|
+
async spawnAnalyzer(language, files, options) {
|
|
524
|
+
if (!this.initialized) {
|
|
525
|
+
await this.initialize();
|
|
526
|
+
}
|
|
527
|
+
const runtime = this.runtimes.get(language);
|
|
528
|
+
if (!runtime?.available || !runtime.analyzer) {
|
|
529
|
+
console.warn(`[RuntimeManager] No analyzer available for ${language}`);
|
|
530
|
+
return null;
|
|
531
|
+
}
|
|
532
|
+
console.log(`[RuntimeManager] Spawning ${language} analyzer for ${files.length} files`);
|
|
533
|
+
const startTime = Date.now();
|
|
534
|
+
const processTimeout = options?.timeout || 300000; // 5 minutes default
|
|
535
|
+
try {
|
|
536
|
+
const result = await this.runWithTimeout(() => runtime.analyzer.analyze(files, options), processTimeout);
|
|
537
|
+
const executionTime = Date.now() - startTime;
|
|
538
|
+
result.metrics.executionTime = executionTime;
|
|
539
|
+
console.log(`[RuntimeManager] ${language} analysis complete in ${executionTime}ms: ${result.violations.length} violations`);
|
|
540
|
+
return result;
|
|
541
|
+
}
|
|
542
|
+
catch (error) {
|
|
543
|
+
const executionTime = Date.now() - startTime;
|
|
544
|
+
console.error(`[RuntimeManager] Error in ${language} analyzer after ${executionTime}ms:`, error);
|
|
545
|
+
return {
|
|
546
|
+
violations: [],
|
|
547
|
+
indexEntries: [],
|
|
548
|
+
metrics: { filesAnalyzed: 0, executionTime },
|
|
549
|
+
errors: [{
|
|
550
|
+
message: error instanceof Error ? error.message : String(error),
|
|
551
|
+
language,
|
|
552
|
+
type: error instanceof Error && error.message.includes('timeout') ? 'timeout' : 'runtime_error'
|
|
553
|
+
}]
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
/**
|
|
558
|
+
* Run a function with timeout
|
|
559
|
+
*/
|
|
560
|
+
async runWithTimeout(fn, timeoutMs) {
|
|
561
|
+
return new Promise((resolve, reject) => {
|
|
562
|
+
const timeoutId = setTimeout(() => {
|
|
563
|
+
reject(new Error(`Analysis timed out after ${timeoutMs}ms`));
|
|
564
|
+
}, timeoutMs);
|
|
565
|
+
fn()
|
|
566
|
+
.then(result => {
|
|
567
|
+
clearTimeout(timeoutId);
|
|
568
|
+
resolve(result);
|
|
569
|
+
})
|
|
570
|
+
.catch(error => {
|
|
571
|
+
clearTimeout(timeoutId);
|
|
572
|
+
reject(error);
|
|
573
|
+
});
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
/**
|
|
577
|
+
* Spawn multiple analyzers in parallel with concurrency control
|
|
578
|
+
* @param languageFilePairs
|
|
579
|
+
* @param options
|
|
580
|
+
* @returns
|
|
581
|
+
*/
|
|
582
|
+
async spawnMultipleAnalyzers(languageFilePairs, options) {
|
|
583
|
+
const maxConcurrency = options?.maxConcurrency || 3;
|
|
584
|
+
const results = [];
|
|
585
|
+
console.log(`[RuntimeManager] Spawning ${languageFilePairs.length} analyzers with max concurrency ${maxConcurrency}`);
|
|
586
|
+
// Process in batches to control concurrency
|
|
587
|
+
for (let i = 0; i < languageFilePairs.length; i += maxConcurrency) {
|
|
588
|
+
const batch = languageFilePairs.slice(i, i + maxConcurrency);
|
|
589
|
+
const batchPromises = batch.map(async ({ language, files }) => {
|
|
590
|
+
const result = await this.spawnAnalyzer(language, files, options);
|
|
591
|
+
return { language, result };
|
|
592
|
+
});
|
|
593
|
+
const batchResults = await Promise.all(batchPromises);
|
|
594
|
+
results.push(...batchResults);
|
|
595
|
+
}
|
|
596
|
+
return results;
|
|
597
|
+
}
|
|
598
|
+
/**
|
|
599
|
+
* Kill all running analyzer processes (for cleanup)
|
|
600
|
+
* @returns
|
|
601
|
+
*/
|
|
602
|
+
async killAllAnalyzers() {
|
|
603
|
+
console.log('[RuntimeManager] Killing all analyzer processes...');
|
|
604
|
+
// This is a placeholder for process management
|
|
605
|
+
// In future implementations, we'll track child processes and kill them here
|
|
606
|
+
// For now, we just log the action since our analyzers run in-process
|
|
607
|
+
console.log('[RuntimeManager] All analyzer processes terminated');
|
|
608
|
+
}
|
|
609
|
+
}
|
|
588
610
|
// Enhanced TypeScript analyzer that integrates with existing infrastructure
|
|
589
611
|
class TypeScriptAnalyzer {
|
|
590
612
|
name = 'typescript';
|
|
@@ -605,73 +627,15 @@ class TypeScriptAnalyzer {
|
|
|
605
627
|
minSeverity: options?.minSeverity || 'warning',
|
|
606
628
|
verbose: false
|
|
607
629
|
});
|
|
608
|
-
// Extract index entries from the audit process
|
|
609
|
-
const indexEntries = [];
|
|
610
|
-
// Try to get functions from the audit metadata if available
|
|
611
|
-
if (auditResult.metadata?.collectedFunctions) {
|
|
612
|
-
for (const func of auditResult.metadata.collectedFunctions) {
|
|
613
|
-
indexEntries.push({
|
|
614
|
-
id: `typescript:function:${func.filePath}:${func.name}:${func.lineNumber || 0}`,
|
|
615
|
-
name: func.name,
|
|
616
|
-
type: 'function',
|
|
617
|
-
language: 'typescript',
|
|
618
|
-
file: func.filePath,
|
|
619
|
-
signature: func.name, // Basic signature
|
|
620
|
-
parameters: [],
|
|
621
|
-
purpose: func.purpose || '',
|
|
622
|
-
context: func.context || '',
|
|
623
|
-
startLine: func.startLine,
|
|
624
|
-
endLine: func.endLine,
|
|
625
|
-
lineNumber: func.lineNumber,
|
|
626
|
-
metadata: func.metadata
|
|
627
|
-
});
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
// Add component information if available
|
|
631
|
-
if (auditResult.metadata?.fileToFunctionsMap) {
|
|
632
|
-
for (const [filePath, funcs] of Object.entries(auditResult.metadata.fileToFunctionsMap)) {
|
|
633
|
-
for (const func of funcs) {
|
|
634
|
-
if (!indexEntries.find(e => e.id === `typescript:function:${func.filePath}:${func.name}:${func.lineNumber || 0}`)) {
|
|
635
|
-
indexEntries.push({
|
|
636
|
-
id: `typescript:function:${func.filePath}:${func.name}:${func.lineNumber || 0}`,
|
|
637
|
-
name: func.name,
|
|
638
|
-
type: 'function',
|
|
639
|
-
language: 'typescript',
|
|
640
|
-
file: func.filePath,
|
|
641
|
-
signature: func.name,
|
|
642
|
-
parameters: [],
|
|
643
|
-
purpose: func.purpose || '',
|
|
644
|
-
context: func.context || '',
|
|
645
|
-
startLine: func.startLine,
|
|
646
|
-
endLine: func.endLine,
|
|
647
|
-
lineNumber: func.lineNumber,
|
|
648
|
-
metadata: func.metadata
|
|
649
|
-
});
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
630
|
const executionTime = Date.now() - startTime;
|
|
655
|
-
// Extract violations from all analyzer results
|
|
656
|
-
const violations = [];
|
|
657
|
-
for (const [analyzerName, analyzerResult] of Object.entries(auditResult.analyzerResults)) {
|
|
658
|
-
violations.push(...analyzerResult.violations);
|
|
659
|
-
}
|
|
660
|
-
// Extract errors from analyzer results
|
|
661
|
-
const errors = [];
|
|
662
|
-
for (const [analyzerName, analyzerResult] of Object.entries(auditResult.analyzerResults)) {
|
|
663
|
-
if (analyzerResult.errors) {
|
|
664
|
-
errors.push(...analyzerResult.errors);
|
|
665
|
-
}
|
|
666
|
-
}
|
|
667
631
|
return {
|
|
668
|
-
violations,
|
|
669
|
-
indexEntries,
|
|
632
|
+
violations: extractViolations(auditResult),
|
|
633
|
+
indexEntries: collectIndexEntries(auditResult.metadata),
|
|
670
634
|
metrics: {
|
|
671
635
|
filesAnalyzed: files.length,
|
|
672
636
|
executionTime
|
|
673
637
|
},
|
|
674
|
-
errors
|
|
638
|
+
errors: extractErrors(auditResult)
|
|
675
639
|
};
|
|
676
640
|
}
|
|
677
641
|
catch (error) {
|
|
@@ -692,6 +656,63 @@ class TypeScriptAnalyzer {
|
|
|
692
656
|
}
|
|
693
657
|
}
|
|
694
658
|
}
|
|
659
|
+
/** Build a single function index entry from a collected-function record. */
|
|
660
|
+
function toFunctionIndexEntry(func) {
|
|
661
|
+
return {
|
|
662
|
+
id: `typescript:function:${func.filePath}:${func.name}:${func.lineNumber || 0}`,
|
|
663
|
+
name: func.name,
|
|
664
|
+
type: 'function',
|
|
665
|
+
language: 'typescript',
|
|
666
|
+
file: func.filePath,
|
|
667
|
+
signature: func.name,
|
|
668
|
+
parameters: [],
|
|
669
|
+
purpose: func.purpose || '',
|
|
670
|
+
context: func.context || '',
|
|
671
|
+
startLine: func.startLine,
|
|
672
|
+
endLine: func.endLine,
|
|
673
|
+
lineNumber: func.lineNumber,
|
|
674
|
+
metadata: func.metadata
|
|
675
|
+
};
|
|
676
|
+
}
|
|
677
|
+
/** Extract function index entries from audit metadata (dedup across both maps). */
|
|
678
|
+
function collectIndexEntries(metadata) {
|
|
679
|
+
const indexEntries = [];
|
|
680
|
+
// Try to get functions from the audit metadata if available
|
|
681
|
+
if (metadata?.collectedFunctions) {
|
|
682
|
+
for (const func of metadata.collectedFunctions) {
|
|
683
|
+
indexEntries.push(toFunctionIndexEntry(func));
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
// Add component information if available
|
|
687
|
+
if (metadata?.fileToFunctionsMap) {
|
|
688
|
+
for (const funcs of Object.values(metadata.fileToFunctionsMap)) {
|
|
689
|
+
for (const func of funcs) {
|
|
690
|
+
if (!indexEntries.find(e => e.id === `typescript:function:${func.filePath}:${func.name}:${func.lineNumber || 0}`)) {
|
|
691
|
+
indexEntries.push(toFunctionIndexEntry(func));
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
return indexEntries;
|
|
697
|
+
}
|
|
698
|
+
/** Flatten violations from all analyzer results. */
|
|
699
|
+
function extractViolations(auditResult) {
|
|
700
|
+
const violations = [];
|
|
701
|
+
for (const analyzerResult of Object.values(auditResult.analyzerResults ?? {})) {
|
|
702
|
+
violations.push(...analyzerResult.violations);
|
|
703
|
+
}
|
|
704
|
+
return violations;
|
|
705
|
+
}
|
|
706
|
+
/** Flatten errors from all analyzer results. */
|
|
707
|
+
function extractErrors(auditResult) {
|
|
708
|
+
const errors = [];
|
|
709
|
+
for (const analyzerResult of Object.values(auditResult.analyzerResults ?? {})) {
|
|
710
|
+
if (analyzerResult.errors) {
|
|
711
|
+
errors.push(...analyzerResult.errors);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
return errors;
|
|
715
|
+
}
|
|
695
716
|
class GoAnalyzer {
|
|
696
717
|
analyzerPath;
|
|
697
718
|
name = 'go';
|
|
@@ -766,55 +787,7 @@ class GoAnalyzer {
|
|
|
766
787
|
}
|
|
767
788
|
async runGoAnalyzer(goDir, files, options) {
|
|
768
789
|
const binaryPath = path.join(goDir, 'analyzer');
|
|
769
|
-
return
|
|
770
|
-
const child = spawn(binaryPath, [], {
|
|
771
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
772
|
-
cwd: goDir
|
|
773
|
-
});
|
|
774
|
-
let stdout = '';
|
|
775
|
-
let stderr = '';
|
|
776
|
-
child.stdout?.on('data', (data) => {
|
|
777
|
-
stdout += data.toString();
|
|
778
|
-
});
|
|
779
|
-
child.stderr?.on('data', (data) => {
|
|
780
|
-
stderr += data.toString();
|
|
781
|
-
});
|
|
782
|
-
child.on('close', (code) => {
|
|
783
|
-
if (code !== 0) {
|
|
784
|
-
reject(new Error(`Go analyzer exited with code ${code}: ${stderr}`));
|
|
785
|
-
return;
|
|
786
|
-
}
|
|
787
|
-
try {
|
|
788
|
-
// Parse the last line as JSON response
|
|
789
|
-
const lines = stdout.trim().split('\n');
|
|
790
|
-
const lastLine = lines[lines.length - 1];
|
|
791
|
-
const response = JSON.parse(lastLine);
|
|
792
|
-
if (response.error) {
|
|
793
|
-
reject(new Error(`Go analyzer error: ${response.error.message}`));
|
|
794
|
-
}
|
|
795
|
-
else {
|
|
796
|
-
resolve(response.result);
|
|
797
|
-
}
|
|
798
|
-
}
|
|
799
|
-
catch (parseError) {
|
|
800
|
-
reject(new Error(`Failed to parse Go analyzer response: ${parseError}`));
|
|
801
|
-
}
|
|
802
|
-
});
|
|
803
|
-
child.on('error', (error) => {
|
|
804
|
-
reject(new Error(`Failed to spawn Go analyzer: ${error}`));
|
|
805
|
-
});
|
|
806
|
-
// Send JSON-RPC request
|
|
807
|
-
const request = {
|
|
808
|
-
method: 'analyze',
|
|
809
|
-
params: {
|
|
810
|
-
files,
|
|
811
|
-
options
|
|
812
|
-
},
|
|
813
|
-
id: 1
|
|
814
|
-
};
|
|
815
|
-
child.stdin?.write(JSON.stringify(request) + '\n');
|
|
816
|
-
child.stdin?.end();
|
|
817
|
-
});
|
|
790
|
+
return spawnGoAnalyzer(binaryPath, goDir, files, options);
|
|
818
791
|
}
|
|
819
792
|
}
|
|
820
793
|
class PythonAnalyzer {
|
|
@@ -838,4 +811,86 @@ class PythonAnalyzer {
|
|
|
838
811
|
};
|
|
839
812
|
}
|
|
840
813
|
}
|
|
814
|
+
/**
|
|
815
|
+
* Derive compatibility status + recommendations for a single runtime.
|
|
816
|
+
*/
|
|
817
|
+
function deriveCompatibilityStatus(input) {
|
|
818
|
+
const recommendations = [];
|
|
819
|
+
if (!input.available) {
|
|
820
|
+
recommendations.push(`Install ${input.name} runtime`);
|
|
821
|
+
recommendations.push(...input.installSuggestions);
|
|
822
|
+
return { status: 'unknown', recommendations };
|
|
823
|
+
}
|
|
824
|
+
if (input.version === 'unknown') {
|
|
825
|
+
recommendations.push(`Unable to detect ${input.name} version`);
|
|
826
|
+
return { status: 'unknown', recommendations };
|
|
827
|
+
}
|
|
828
|
+
if (!input.compatible) {
|
|
829
|
+
recommendations.push(`Update ${input.name} to version ${input.minVersion} or higher`);
|
|
830
|
+
recommendations.push(`Current: ${input.version}, Required: ${input.minVersion}`);
|
|
831
|
+
return { status: 'incompatible', recommendations };
|
|
832
|
+
}
|
|
833
|
+
return { status: 'compatible', recommendations };
|
|
834
|
+
}
|
|
835
|
+
/**
|
|
836
|
+
* Spawn the Go analyzer binary and stream a JSON-RPC analyze request.
|
|
837
|
+
*/
|
|
838
|
+
function spawnGoAnalyzer(binaryPath, goDir, files, options) {
|
|
839
|
+
return new Promise((resolve, reject) => {
|
|
840
|
+
const child = spawn(binaryPath, [], {
|
|
841
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
842
|
+
cwd: goDir
|
|
843
|
+
});
|
|
844
|
+
let stdout = '';
|
|
845
|
+
let stderr = '';
|
|
846
|
+
child.stdout?.on('data', (data) => {
|
|
847
|
+
stdout += data.toString();
|
|
848
|
+
});
|
|
849
|
+
child.stderr?.on('data', (data) => {
|
|
850
|
+
stderr += data.toString();
|
|
851
|
+
});
|
|
852
|
+
child.on('close', (code) => {
|
|
853
|
+
handleGoAnalyzerClose(code, { stdout, stderr }, resolve, reject);
|
|
854
|
+
});
|
|
855
|
+
child.on('error', (error) => {
|
|
856
|
+
reject(new Error(`Failed to spawn Go analyzer: ${error}`));
|
|
857
|
+
});
|
|
858
|
+
// Send JSON-RPC request
|
|
859
|
+
const request = {
|
|
860
|
+
method: 'analyze',
|
|
861
|
+
params: { files, options },
|
|
862
|
+
id: 1
|
|
863
|
+
};
|
|
864
|
+
child.stdin?.write(JSON.stringify(request) + '\n');
|
|
865
|
+
child.stdin?.end();
|
|
866
|
+
});
|
|
867
|
+
}
|
|
868
|
+
/**
|
|
869
|
+
* Handle the `close` event of the Go analyzer child process: reject on a
|
|
870
|
+
* non-zero exit code, otherwise resolve with the parsed JSON-RPC result.
|
|
871
|
+
*/
|
|
872
|
+
function handleGoAnalyzerClose(code, streams, resolve, reject) {
|
|
873
|
+
if (code !== 0) {
|
|
874
|
+
reject(new Error(`Go analyzer exited with code ${code}: ${streams.stderr}`));
|
|
875
|
+
return;
|
|
876
|
+
}
|
|
877
|
+
try {
|
|
878
|
+
resolve(parseGoAnalyzerResponse(streams.stdout));
|
|
879
|
+
}
|
|
880
|
+
catch (parseError) {
|
|
881
|
+
reject(new Error(`Failed to parse Go analyzer response: ${parseError}`));
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
/**
|
|
885
|
+
* Parse the last line of Go analyzer stdout as a JSON-RPC result.
|
|
886
|
+
*/
|
|
887
|
+
function parseGoAnalyzerResponse(stdout) {
|
|
888
|
+
const lines = stdout.trim().split('\n');
|
|
889
|
+
const lastLine = lines[lines.length - 1];
|
|
890
|
+
const response = JSON.parse(lastLine);
|
|
891
|
+
if (response.error) {
|
|
892
|
+
throw new Error(`Go analyzer error: ${response.error.message}`);
|
|
893
|
+
}
|
|
894
|
+
return response.result;
|
|
895
|
+
}
|
|
841
896
|
//# sourceMappingURL=RuntimeManager.js.map
|