@sun-asterisk/sunlint 1.2.2 → 1.3.1
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/CHANGELOG.md +107 -1
- package/CONTRIBUTING.md +1654 -66
- package/README.md +19 -6
- package/config/ci-cd.json +54 -0
- package/config/development.json +56 -0
- package/config/engines/engines-enhanced.json +86 -0
- package/config/engines/semantic-config.json +114 -0
- package/config/eslint-rule-mapping.json +50 -38
- package/config/large-project.json +143 -0
- package/config/presets/all.json +0 -1
- package/config/release.json +70 -0
- package/config/rule-analysis-strategies.js +23 -4
- package/config/rules/S027-categories.json +122 -0
- package/config/rules/enhanced-rules-registry.json +2564 -0
- package/config/rules/rules-registry-generated.json +785 -837
- package/config/rules/rules-registry.json +13 -1
- package/core/adapters/sunlint-rule-adapter.js +25 -30
- package/core/analysis-orchestrator.js +42 -2
- package/core/categories.js +52 -0
- package/core/category-constants.js +39 -0
- package/core/cli-action-handler.js +53 -32
- package/core/cli-program.js +11 -3
- package/core/config-manager.js +111 -0
- package/core/config-merger.js +88 -0
- package/core/constants/categories.js +168 -0
- package/core/constants/defaults.js +165 -0
- package/core/constants/engines.js +185 -0
- package/core/constants/index.js +30 -0
- package/core/constants/rules.js +215 -0
- package/core/enhanced-rules-registry.js +3 -3
- package/core/file-targeting-service.js +128 -7
- package/core/interfaces/rule-plugin.interface.js +207 -0
- package/core/plugin-manager.js +448 -0
- package/core/rule-selection-service.js +42 -15
- package/core/semantic-engine.js +658 -0
- package/core/semantic-rule-base.js +433 -0
- package/core/unified-rule-registry.js +484 -0
- package/docs/COMMAND-EXAMPLES.md +134 -0
- package/docs/CONSTANTS-ARCHITECTURE.md +288 -0
- package/docs/LARGE-PROJECT-GUIDE.md +324 -0
- package/engines/core/base-engine.js +249 -0
- package/engines/engine-factory.js +275 -0
- package/engines/eslint-engine.js +171 -19
- package/engines/heuristic-engine.js +569 -78
- package/integrations/eslint/plugin/index.js +26 -28
- package/origin-rules/common-en.md +8 -8
- package/package.json +10 -6
- package/rules/common/C003_no_vague_abbreviations/analyzer.js +1 -1
- package/rules/common/C017_constructor_logic/analyzer.js +254 -17
- package/rules/common/C017_constructor_logic/semantic-analyzer.js +340 -0
- package/rules/common/C029_catch_block_logging/analyzer.js +17 -5
- package/rules/common/C033_separate_service_repository/README.md +78 -0
- package/rules/common/C033_separate_service_repository/analyzer.js +160 -0
- package/rules/common/C033_separate_service_repository/config.json +50 -0
- package/rules/common/C033_separate_service_repository/regex-based-analyzer.js +585 -0
- package/rules/common/C033_separate_service_repository/symbol-based-analyzer.js +368 -0
- package/rules/common/C035_error_logging_context/STRATEGY.md +99 -0
- package/rules/common/C035_error_logging_context/analyzer.js +230 -0
- package/rules/common/C035_error_logging_context/config.json +54 -0
- package/rules/common/C035_error_logging_context/regex-based-analyzer.js +299 -0
- package/rules/common/C035_error_logging_context/symbol-based-analyzer.js +454 -0
- package/rules/common/C040_centralized_validation/analyzer.js +165 -0
- package/rules/common/C040_centralized_validation/config.json +46 -0
- package/rules/common/C040_centralized_validation/regex-based-analyzer.js +243 -0
- package/rules/common/C040_centralized_validation/symbol-based-analyzer.js +416 -0
- package/rules/common/C047_no_duplicate_retry_logic/c047-semantic-rule.js +278 -0
- package/rules/common/C047_no_duplicate_retry_logic/symbol-analyzer-enhanced.js +968 -0
- package/rules/common/C047_no_duplicate_retry_logic/symbol-config.json +71 -0
- package/rules/common/{C076_single_test_behavior → C072_single_test_behavior}/analyzer.js +6 -6
- package/rules/common/C076_explicit_function_types/README.md +30 -0
- package/rules/common/C076_explicit_function_types/analyzer.js +172 -0
- package/rules/common/C076_explicit_function_types/config.json +15 -0
- package/rules/common/C076_explicit_function_types/semantic-analyzer.js +341 -0
- package/rules/index.js +8 -0
- package/rules/parser/rule-parser.js +13 -2
- package/rules/security/S005_no_origin_auth/README.md +226 -0
- package/rules/security/S005_no_origin_auth/analyzer.js +184 -0
- package/rules/security/S005_no_origin_auth/ast-analyzer.js +406 -0
- package/rules/security/S005_no_origin_auth/config.json +85 -0
- package/rules/security/S006_no_plaintext_recovery_codes/README.md +139 -0
- package/rules/security/S006_no_plaintext_recovery_codes/analyzer.js +306 -0
- package/rules/security/S006_no_plaintext_recovery_codes/config.json +48 -0
- package/rules/security/S007_no_plaintext_otp/README.md +198 -0
- package/rules/security/S007_no_plaintext_otp/analyzer.js +406 -0
- package/rules/security/S007_no_plaintext_otp/config.json +79 -0
- package/rules/security/S007_no_plaintext_otp/semantic-analyzer.js +609 -0
- package/rules/security/S007_no_plaintext_otp/semantic-config.json +195 -0
- package/rules/security/S007_no_plaintext_otp/semantic-wrapper.js +280 -0
- package/rules/security/S027_no_hardcoded_secrets/analyzer.js +180 -366
- package/rules/security/S027_no_hardcoded_secrets/categories.json +153 -0
- package/rules/security/S027_no_hardcoded_secrets/categorized-analyzer.js +250 -0
- package/scripts/category-manager.js +150 -0
- package/scripts/generate-rules-registry.js +88 -0
- package/scripts/migrate-rule-registry.js +157 -0
- package/scripts/prepare-release.sh +1 -1
- package/scripts/validate-system.js +48 -0
- package/.sunlint.json +0 -35
- package/config/README.md +0 -88
- package/config/engines/eslint-rule-mapping.json +0 -74
- package/config/schemas/sunlint-schema.json +0 -0
- package/config/testing/test-s005-working.ts +0 -22
- package/core/multi-rule-runner.js +0 -0
- package/docs/ESLINT-INTEGRATION-STRATEGY.md +0 -392
- package/docs/FUTURE_PACKAGES.md +0 -83
- package/docs/HEURISTIC_VS_AI.md +0 -113
- package/docs/PRODUCTION_DEPLOYMENT_ANALYSIS.md +0 -112
- package/docs/PRODUCTION_SIZE_IMPACT.md +0 -183
- package/docs/RELEASE_GUIDE.md +0 -230
- package/docs/STANDARDIZED-CATEGORY-FILTERING.md +0 -156
- package/engines/tree-sitter-parser.js +0 -0
- package/engines/universal-ast-engine.js +0 -0
- package/integrations/eslint/plugin/rules/common/c076-single-behavior-per-test.js +0 -254
- package/rules/common/C029_catch_block_logging/analyzer-backup.js +0 -426
- package/rules/common/C029_catch_block_logging/analyzer-fixed.js +0 -130
- package/rules/common/C029_catch_block_logging/analyzer-multi-tech.js +0 -487
- package/rules/common/C029_catch_block_logging/analyzer-simple.js +0 -110
- package/rules/common/C029_catch_block_logging/ast-analyzer-backup.js +0 -441
- package/rules/common/C029_catch_block_logging/ast-analyzer-new.js +0 -127
- package/rules/common/C029_catch_block_logging/ast-analyzer.js +0 -133
- package/rules/common/C029_catch_block_logging/cfg-analyzer.js +0 -408
- package/rules/common/C029_catch_block_logging/dataflow-analyzer.js +0 -454
- package/rules/common/C029_catch_block_logging/multi-language-ast-engine.js +0 -700
- package/rules/common/C029_catch_block_logging/pattern-learning-analyzer.js +0 -568
- package/rules/common/C029_catch_block_logging/semantic-analyzer.js +0 -459
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* C029 Analyzer - Smart Pipeline Integration
|
|
3
|
-
*
|
|
4
|
-
* This analyzer forwards to the Smart Pipeline for superior accuracy and performance
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const fs = require('fs');
|
|
8
|
-
const path = require('path');
|
|
9
|
-
|
|
10
|
-
class C029Analyzer {
|
|
11
|
-
constructor() {
|
|
12
|
-
this.ruleId = 'C029';
|
|
13
|
-
this.ruleName = 'Enhanced Catch Block Error Logging';
|
|
14
|
-
this.description = 'Mọi catch block phải log nguyên nhân lỗi đầy đủ và bảo toàn context (Smart Pipeline 3-stage analysis)';
|
|
15
|
-
|
|
16
|
-
// Load Smart Pipeline as primary analyzer
|
|
17
|
-
this.smartPipeline = null;
|
|
18
|
-
|
|
19
|
-
try {
|
|
20
|
-
const SmartPipelineClass = require('./analyzer-smart-pipeline.js');
|
|
21
|
-
this.smartPipeline = new SmartPipelineClass();
|
|
22
|
-
console.log('🎯 C029: Smart Pipeline loaded (3-stage: Regex → AST → Data Flow)');
|
|
23
|
-
} catch (error) {
|
|
24
|
-
console.warn('⚠️ C029: Smart Pipeline failed, using fallback:', error.message);
|
|
25
|
-
this.smartPipeline = null;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
async analyze(files, language, options = {}) {
|
|
30
|
-
// Use Smart Pipeline as primary choice
|
|
31
|
-
if (this.smartPipeline) {
|
|
32
|
-
console.log('🎯 C029: Using Smart Pipeline (3-stage analysis)...');
|
|
33
|
-
return await this.smartPipeline.analyze(files, language, options);
|
|
34
|
-
} else {
|
|
35
|
-
console.log('🔍 C029: Using fallback regex analysis...');
|
|
36
|
-
return await this.analyzeWithRegex(files, language, options);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
async analyzeWithRegex(files, language, options = {}) {
|
|
41
|
-
const violations = [];
|
|
42
|
-
|
|
43
|
-
for (const filePath of files) {
|
|
44
|
-
if (options.verbose) {
|
|
45
|
-
console.log(`🔍 C029 Regex: Processing ${path.basename(filePath)}...`);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
try {
|
|
49
|
-
const content = fs.readFileSync(filePath, 'utf8');
|
|
50
|
-
const fileViolations = await this.analyzeFile(filePath, content, language);
|
|
51
|
-
violations.push(...fileViolations);
|
|
52
|
-
} catch (error) {
|
|
53
|
-
console.warn(`⚠️ C029: Error processing ${filePath}:`, error.message);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
return violations;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
async analyzeFile(filePath, content, language) {
|
|
61
|
-
const violations = [];
|
|
62
|
-
const lines = content.split('\n');
|
|
63
|
-
|
|
64
|
-
for (let i = 0; i < lines.length; i++) {
|
|
65
|
-
const line = lines[i];
|
|
66
|
-
|
|
67
|
-
// Simple catch block detection
|
|
68
|
-
if (line.includes('catch') && line.includes('(')) {
|
|
69
|
-
const catchBlock = this.extractCatchBlock(lines, i);
|
|
70
|
-
|
|
71
|
-
if (this.isCatchBlockEmpty(catchBlock.content)) {
|
|
72
|
-
violations.push({
|
|
73
|
-
file: filePath,
|
|
74
|
-
line: i + 1,
|
|
75
|
-
column: line.indexOf('catch') + 1,
|
|
76
|
-
message: 'Empty catch block detected',
|
|
77
|
-
severity: 'error',
|
|
78
|
-
ruleId: this.ruleId,
|
|
79
|
-
type: 'empty_catch'
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return violations;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
extractCatchBlock(lines, startIndex) {
|
|
89
|
-
const content = [];
|
|
90
|
-
let braceCount = 0;
|
|
91
|
-
let inBlock = false;
|
|
92
|
-
|
|
93
|
-
for (let i = startIndex; i < lines.length; i++) {
|
|
94
|
-
const line = lines[i];
|
|
95
|
-
content.push(line);
|
|
96
|
-
|
|
97
|
-
for (const char of line) {
|
|
98
|
-
if (char === '{') {
|
|
99
|
-
braceCount++;
|
|
100
|
-
inBlock = true;
|
|
101
|
-
} else if (char === '}') {
|
|
102
|
-
braceCount--;
|
|
103
|
-
if (braceCount === 0 && inBlock) {
|
|
104
|
-
return { content, endIndex: i };
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
return { content, endIndex: startIndex };
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
isCatchBlockEmpty(content) {
|
|
114
|
-
const blockContent = content.join('\n');
|
|
115
|
-
|
|
116
|
-
// Remove comments and whitespace
|
|
117
|
-
const cleanContent = blockContent
|
|
118
|
-
.replace(/\/\*[\s\S]*?\*\//g, '') // Remove multi-line comments
|
|
119
|
-
.replace(/\/\/.*$/gm, '') // Remove single-line comments
|
|
120
|
-
.replace(/\s+/g, ' ') // Normalize whitespace
|
|
121
|
-
.trim();
|
|
122
|
-
|
|
123
|
-
// Check if only contains catch declaration and braces
|
|
124
|
-
const hasOnlyStructure = /^catch\s*\([^)]*\)\s*\{\s*\}$/.test(cleanContent);
|
|
125
|
-
|
|
126
|
-
return hasOnlyStructure;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
module.exports = C029Analyzer;
|
|
@@ -1,487 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* C029 Catch Block Logging Analyzer
|
|
3
|
-
*
|
|
4
|
-
* Enhanced multi-technology analyzer combining:
|
|
5
|
-
* - Regex pattern matching (basic detection)
|
|
6
|
-
* - AST-based analysis (structural understanding)
|
|
7
|
-
* - Data flow analysis (variable tracking)
|
|
8
|
-
* - Control flow analysis (execution path understanding)
|
|
9
|
-
* - Semantic analysis (intent understanding)
|
|
10
|
-
* - Pattern learning (adaptive rules)
|
|
11
|
-
* - Multi-language support (cross-language compatibility)
|
|
12
|
-
*
|
|
13
|
-
* Technology Showcase: Demonstrates SunLint's advanced analysis capabilities
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
const fs = require('fs');
|
|
17
|
-
const path = require('path');
|
|
18
|
-
|
|
19
|
-
class C029CatchBlockAnalyzer {
|
|
20
|
-
constructor() {
|
|
21
|
-
this.ruleId = 'C029';
|
|
22
|
-
this.ruleName = 'Advanced Catch Block Logging';
|
|
23
|
-
this.description = 'Multi-technology analysis for catch block error handling';
|
|
24
|
-
|
|
25
|
-
// Load analyzers with graceful fallback
|
|
26
|
-
this.analyzers = this.loadAnalyzers();
|
|
27
|
-
|
|
28
|
-
// Technology orchestration configuration
|
|
29
|
-
this.analysisConfig = {
|
|
30
|
-
enableRegex: true,
|
|
31
|
-
enableAST: true,
|
|
32
|
-
enableDataflow: true,
|
|
33
|
-
enableControlflow: true,
|
|
34
|
-
enableSemantic: true,
|
|
35
|
-
enablePatternLearning: true,
|
|
36
|
-
enableMultiLanguage: true,
|
|
37
|
-
confidenceThreshold: 0.5,
|
|
38
|
-
combinationStrategy: 'weighted_voting' // Options: 'union', 'intersection', 'weighted_voting'
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
// Technology weights for voting
|
|
42
|
-
this.technologyWeights = {
|
|
43
|
-
regex: 0.15,
|
|
44
|
-
ast: 0.25,
|
|
45
|
-
dataflow: 0.20,
|
|
46
|
-
controlflow: 0.15,
|
|
47
|
-
semantic: 0.15,
|
|
48
|
-
patternLearning: 0.10
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
// Performance tracking
|
|
52
|
-
this.performanceMetrics = {
|
|
53
|
-
technologyExecutionTimes: new Map(),
|
|
54
|
-
accuracyComparisons: new Map(),
|
|
55
|
-
combinationEffectiveness: new Map()
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Load all available analyzers with graceful fallback
|
|
61
|
-
*/
|
|
62
|
-
loadAnalyzers() {
|
|
63
|
-
const analyzers = {};
|
|
64
|
-
|
|
65
|
-
const analyzerConfigs = [
|
|
66
|
-
{ name: 'ast', path: './ast-analyzer.js', description: 'AST-based analysis' },
|
|
67
|
-
{ name: 'dataflow', path: './dataflow-analyzer.js', description: 'Data flow analysis' },
|
|
68
|
-
{ name: 'cfg', path: './cfg-analyzer.js', description: 'Control flow analysis' },
|
|
69
|
-
{ name: 'semantic', path: './semantic-analyzer.js', description: 'Semantic analysis' },
|
|
70
|
-
{ name: 'patternLearning', path: './pattern-learning-analyzer.js', description: 'Pattern learning' },
|
|
71
|
-
{ name: 'multiLanguage', path: './multi-language-ast-engine.js', description: 'Multi-language support' }
|
|
72
|
-
];
|
|
73
|
-
|
|
74
|
-
for (const config of analyzerConfigs) {
|
|
75
|
-
try {
|
|
76
|
-
analyzers[config.name] = require(config.path);
|
|
77
|
-
console.log(`✅ C029: ${config.description} loaded`);
|
|
78
|
-
} catch (error) {
|
|
79
|
-
console.warn(`⚠️ C029: ${config.description} not available:`, error.message);
|
|
80
|
-
analyzers[config.name] = null;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
return analyzers;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
async analyze(files, language, options = {}) {
|
|
88
|
-
console.log(`🔬 C029 Multi-Technology Analysis: Processing ${files.length} files with advanced techniques...`);
|
|
89
|
-
|
|
90
|
-
const startTime = Date.now();
|
|
91
|
-
const allResults = new Map(); // technology -> violations
|
|
92
|
-
const executionTimes = new Map();
|
|
93
|
-
|
|
94
|
-
// Run all available technologies in parallel
|
|
95
|
-
const analysisPromises = [];
|
|
96
|
-
|
|
97
|
-
// 1. Regex Analysis (always available)
|
|
98
|
-
if (this.analysisConfig.enableRegex) {
|
|
99
|
-
analysisPromises.push(
|
|
100
|
-
this.runTechnologyAnalysis('regex', () => this.analyzeWithRegex(files, language, options))
|
|
101
|
-
);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// 2. AST Analysis
|
|
105
|
-
if (this.analysisConfig.enableAST && this.analyzers.ast) {
|
|
106
|
-
analysisPromises.push(
|
|
107
|
-
this.runTechnologyAnalysis('ast', () => this.analyzers.ast.analyze(files, language, options))
|
|
108
|
-
);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// 3. Data Flow Analysis
|
|
112
|
-
if (this.analysisConfig.enableDataflow && this.analyzers.dataflow) {
|
|
113
|
-
analysisPromises.push(
|
|
114
|
-
this.runTechnologyAnalysis('dataflow', () => this.analyzers.dataflow.analyze(files, language, options))
|
|
115
|
-
);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// 4. Control Flow Analysis
|
|
119
|
-
if (this.analysisConfig.enableControlflow && this.analyzers.cfg) {
|
|
120
|
-
analysisPromises.push(
|
|
121
|
-
this.runTechnologyAnalysis('controlflow', () => this.analyzers.cfg.analyze(files, language, options))
|
|
122
|
-
);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// 5. Semantic Analysis
|
|
126
|
-
if (this.analysisConfig.enableSemantic && this.analyzers.semantic) {
|
|
127
|
-
analysisPromises.push(
|
|
128
|
-
this.runTechnologyAnalysis('semantic', () => this.analyzers.semantic.analyze(files, language, options))
|
|
129
|
-
);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// 6. Pattern Learning Analysis
|
|
133
|
-
if (this.analysisConfig.enablePatternLearning && this.analyzers.patternLearning) {
|
|
134
|
-
analysisPromises.push(
|
|
135
|
-
this.runTechnologyAnalysis('patternLearning', () => this.analyzers.patternLearning.analyze(files, language, options))
|
|
136
|
-
);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// 7. Multi-Language Analysis
|
|
140
|
-
if (this.analysisConfig.enableMultiLanguage && this.analyzers.multiLanguage) {
|
|
141
|
-
analysisPromises.push(
|
|
142
|
-
this.runTechnologyAnalysis('multiLanguage', () => this.analyzers.multiLanguage.analyze(files, language, options))
|
|
143
|
-
);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Wait for all analyses to complete
|
|
147
|
-
const results = await Promise.allSettled(analysisPromises);
|
|
148
|
-
|
|
149
|
-
// Process results
|
|
150
|
-
for (const result of results) {
|
|
151
|
-
if (result.status === 'fulfilled') {
|
|
152
|
-
const { technology, violations, executionTime } = result.value;
|
|
153
|
-
allResults.set(technology, violations);
|
|
154
|
-
executionTimes.set(technology, executionTime);
|
|
155
|
-
console.log(`✅ ${technology}: ${violations.length} issues found (${executionTime}ms)`);
|
|
156
|
-
} else {
|
|
157
|
-
console.error(`❌ Analysis failed:`, result.reason);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// Combine results using configured strategy
|
|
162
|
-
const finalViolations = this.combineResults(allResults, executionTimes);
|
|
163
|
-
|
|
164
|
-
const totalTime = Date.now() - startTime;
|
|
165
|
-
console.log(`🎯 C029 Final Results: ${finalViolations.length} issues identified (${totalTime}ms total)`);
|
|
166
|
-
|
|
167
|
-
// Update performance metrics
|
|
168
|
-
this.updatePerformanceMetrics(allResults, executionTimes, totalTime);
|
|
169
|
-
|
|
170
|
-
return finalViolations;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Run a technology analysis with timing and error handling
|
|
175
|
-
*/
|
|
176
|
-
async runTechnologyAnalysis(technology, analysisFunction) {
|
|
177
|
-
const startTime = Date.now();
|
|
178
|
-
|
|
179
|
-
try {
|
|
180
|
-
const violations = await analysisFunction();
|
|
181
|
-
const executionTime = Date.now() - startTime;
|
|
182
|
-
|
|
183
|
-
return {
|
|
184
|
-
technology,
|
|
185
|
-
violations: violations || [],
|
|
186
|
-
executionTime,
|
|
187
|
-
success: true
|
|
188
|
-
};
|
|
189
|
-
} catch (error) {
|
|
190
|
-
const executionTime = Date.now() - startTime;
|
|
191
|
-
console.error(`❌ ${technology} analysis failed:`, error.message);
|
|
192
|
-
|
|
193
|
-
return {
|
|
194
|
-
technology,
|
|
195
|
-
violations: [],
|
|
196
|
-
executionTime,
|
|
197
|
-
success: false,
|
|
198
|
-
error: error.message
|
|
199
|
-
};
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* Combine results from multiple technologies
|
|
205
|
-
*/
|
|
206
|
-
combineResults(allResults, executionTimes) {
|
|
207
|
-
switch (this.analysisConfig.combinationStrategy) {
|
|
208
|
-
case 'union':
|
|
209
|
-
return this.combineUnion(allResults);
|
|
210
|
-
|
|
211
|
-
case 'intersection':
|
|
212
|
-
return this.combineIntersection(allResults);
|
|
213
|
-
|
|
214
|
-
case 'weighted_voting':
|
|
215
|
-
default:
|
|
216
|
-
return this.combineWeightedVoting(allResults);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* Union strategy: Include all violations from all technologies
|
|
222
|
-
*/
|
|
223
|
-
combineUnion(allResults) {
|
|
224
|
-
const allViolations = [];
|
|
225
|
-
const seenViolations = new Set();
|
|
226
|
-
|
|
227
|
-
for (const [technology, violations] of allResults) {
|
|
228
|
-
for (const violation of violations) {
|
|
229
|
-
const key = `${violation.file}:${violation.line}:${violation.type}`;
|
|
230
|
-
|
|
231
|
-
if (!seenViolations.has(key)) {
|
|
232
|
-
seenViolations.add(key);
|
|
233
|
-
allViolations.push({
|
|
234
|
-
...violation,
|
|
235
|
-
detectedBy: [technology],
|
|
236
|
-
combinationStrategy: 'union'
|
|
237
|
-
});
|
|
238
|
-
} else {
|
|
239
|
-
// Find existing violation and add technology
|
|
240
|
-
const existing = allViolations.find(v =>
|
|
241
|
-
v.file === violation.file &&
|
|
242
|
-
v.line === violation.line &&
|
|
243
|
-
v.type === violation.type
|
|
244
|
-
);
|
|
245
|
-
if (existing) {
|
|
246
|
-
existing.detectedBy.push(technology);
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
return allViolations;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* Intersection strategy: Only include violations found by multiple technologies
|
|
257
|
-
*/
|
|
258
|
-
combineIntersection(allResults) {
|
|
259
|
-
const violationCounts = new Map();
|
|
260
|
-
const violationData = new Map();
|
|
261
|
-
|
|
262
|
-
// Count how many technologies detected each violation
|
|
263
|
-
for (const [technology, violations] of allResults) {
|
|
264
|
-
for (const violation of violations) {
|
|
265
|
-
const key = `${violation.file}:${violation.line}:${violation.type}`;
|
|
266
|
-
|
|
267
|
-
violationCounts.set(key, (violationCounts.get(key) || 0) + 1);
|
|
268
|
-
|
|
269
|
-
if (!violationData.has(key)) {
|
|
270
|
-
violationData.set(key, {
|
|
271
|
-
...violation,
|
|
272
|
-
detectedBy: [technology],
|
|
273
|
-
combinationStrategy: 'intersection'
|
|
274
|
-
});
|
|
275
|
-
} else {
|
|
276
|
-
violationData.get(key).detectedBy.push(technology);
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
// Only return violations detected by at least 2 technologies
|
|
282
|
-
const minDetections = Math.max(2, Math.floor(allResults.size / 2));
|
|
283
|
-
return Array.from(violationData.values())
|
|
284
|
-
.filter(violation => violationCounts.get(`${violation.file}:${violation.line}:${violation.type}`) >= minDetections);
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
/**
|
|
288
|
-
* Weighted voting strategy: Use confidence scores and technology weights
|
|
289
|
-
*/
|
|
290
|
-
combineWeightedVoting(allResults) {
|
|
291
|
-
const violationScores = new Map();
|
|
292
|
-
const violationData = new Map();
|
|
293
|
-
|
|
294
|
-
// Calculate weighted scores for each violation
|
|
295
|
-
for (const [technology, violations] of allResults) {
|
|
296
|
-
const weight = this.technologyWeights[technology] || 0.1;
|
|
297
|
-
|
|
298
|
-
for (const violation of violations) {
|
|
299
|
-
const key = `${violation.file}:${violation.line}:${violation.type}`;
|
|
300
|
-
const score = (violation.confidence || 0.7) * weight;
|
|
301
|
-
|
|
302
|
-
violationScores.set(key, (violationScores.get(key) || 0) + score);
|
|
303
|
-
|
|
304
|
-
if (!violationData.has(key)) {
|
|
305
|
-
violationData.set(key, {
|
|
306
|
-
...violation,
|
|
307
|
-
detectedBy: [technology],
|
|
308
|
-
weightedScore: score,
|
|
309
|
-
combinationStrategy: 'weighted_voting'
|
|
310
|
-
});
|
|
311
|
-
} else {
|
|
312
|
-
const existing = violationData.get(key);
|
|
313
|
-
existing.detectedBy.push(technology);
|
|
314
|
-
existing.weightedScore = violationScores.get(key);
|
|
315
|
-
|
|
316
|
-
// Use highest confidence message
|
|
317
|
-
if ((violation.confidence || 0.7) > (existing.confidence || 0.7)) {
|
|
318
|
-
existing.message = violation.message;
|
|
319
|
-
existing.suggestion = violation.suggestion;
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
// Return violations above confidence threshold
|
|
326
|
-
return Array.from(violationData.values())
|
|
327
|
-
.filter(violation => violation.weightedScore >= this.analysisConfig.confidenceThreshold)
|
|
328
|
-
.sort((a, b) => b.weightedScore - a.weightedScore);
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
/**
|
|
332
|
-
* Regex-based analysis (fallback)
|
|
333
|
-
*/
|
|
334
|
-
async analyzeWithRegex(files, language, options = {}) {
|
|
335
|
-
const violations = [];
|
|
336
|
-
console.log(`📄 C029 Regex: Analyzing ${files.length} files with pattern matching...`);
|
|
337
|
-
|
|
338
|
-
for (const filePath of files) {
|
|
339
|
-
try {
|
|
340
|
-
const content = fs.readFileSync(filePath, 'utf8');
|
|
341
|
-
const regexViolations = this.findCatchBlocksRegex(content, filePath);
|
|
342
|
-
violations.push(...regexViolations);
|
|
343
|
-
} catch (error) {
|
|
344
|
-
console.warn(`C029 Regex skipping ${filePath}: ${error.message}`);
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
return violations;
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
/**
|
|
352
|
-
* Find catch blocks using regex patterns
|
|
353
|
-
*/
|
|
354
|
-
findCatchBlocksRegex(content, filePath) {
|
|
355
|
-
const violations = [];
|
|
356
|
-
const lines = content.split('\n');
|
|
357
|
-
|
|
358
|
-
for (let i = 0; i < lines.length; i++) {
|
|
359
|
-
const line = lines[i];
|
|
360
|
-
|
|
361
|
-
// Basic catch block detection
|
|
362
|
-
if (/catch\s*\([^)]*\)\s*\{/.test(line)) {
|
|
363
|
-
const catchBlock = this.extractCatchBlock(lines, i);
|
|
364
|
-
|
|
365
|
-
if (this.isCatchBlockViolation(catchBlock, filePath)) {
|
|
366
|
-
violations.push({
|
|
367
|
-
ruleId: this.ruleId,
|
|
368
|
-
file: filePath,
|
|
369
|
-
line: i + 1,
|
|
370
|
-
column: 1,
|
|
371
|
-
message: 'Empty or inadequate error handling in catch block',
|
|
372
|
-
severity: 'error',
|
|
373
|
-
code: catchBlock.code,
|
|
374
|
-
type: 'regex_catch_violation',
|
|
375
|
-
confidence: 0.6,
|
|
376
|
-
suggestion: 'Add error logging or appropriate error handling',
|
|
377
|
-
detectedBy: ['regex']
|
|
378
|
-
});
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
return violations;
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
/**
|
|
387
|
-
* Extract catch block content
|
|
388
|
-
*/
|
|
389
|
-
extractCatchBlock(lines, startIndex) {
|
|
390
|
-
let braceCount = 0;
|
|
391
|
-
let content = '';
|
|
392
|
-
let endIndex = startIndex;
|
|
393
|
-
|
|
394
|
-
for (let i = startIndex; i < lines.length; i++) {
|
|
395
|
-
const line = lines[i];
|
|
396
|
-
content += line + '\n';
|
|
397
|
-
|
|
398
|
-
braceCount += (line.match(/\{/g) || []).length;
|
|
399
|
-
braceCount -= (line.match(/\}/g) || []).length;
|
|
400
|
-
|
|
401
|
-
if (braceCount === 0 && i > startIndex) {
|
|
402
|
-
endIndex = i;
|
|
403
|
-
break;
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
return {
|
|
408
|
-
code: content.trim(),
|
|
409
|
-
startLine: startIndex + 1,
|
|
410
|
-
endLine: endIndex + 1,
|
|
411
|
-
content: content.trim()
|
|
412
|
-
};
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
/**
|
|
416
|
-
* Check if catch block is a violation
|
|
417
|
-
*/
|
|
418
|
-
isCatchBlockViolation(catchBlock, filePath) {
|
|
419
|
-
const content = catchBlock.content;
|
|
420
|
-
|
|
421
|
-
// Empty catch block
|
|
422
|
-
if (!content || content.replace(/[{}\s]/g, '').length === 0) {
|
|
423
|
-
return true;
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
// Check for logging patterns
|
|
427
|
-
const hasLogging = /console\.(log|error|warn|debug)|logger?\.(error|warn|info|debug)|print/.test(content);
|
|
428
|
-
|
|
429
|
-
// Skip test files (less strict)
|
|
430
|
-
const isTestFile = /(__tests__|\.test\.|\.spec\.|\/test\/|\/tests\/)/.test(filePath);
|
|
431
|
-
|
|
432
|
-
return !hasLogging && !isTestFile;
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
/**
|
|
436
|
-
* Update performance metrics for analysis
|
|
437
|
-
*/
|
|
438
|
-
updatePerformanceMetrics(allResults, executionTimes, totalTime) {
|
|
439
|
-
// Track execution times
|
|
440
|
-
for (const [technology, time] of executionTimes) {
|
|
441
|
-
if (!this.performanceMetrics.technologyExecutionTimes.has(technology)) {
|
|
442
|
-
this.performanceMetrics.technologyExecutionTimes.set(technology, []);
|
|
443
|
-
}
|
|
444
|
-
this.performanceMetrics.technologyExecutionTimes.get(technology).push(time);
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
// Track accuracy comparisons (simplified)
|
|
448
|
-
const technologiesUsed = Array.from(allResults.keys());
|
|
449
|
-
for (let i = 0; i < technologiesUsed.length; i++) {
|
|
450
|
-
for (let j = i + 1; j < technologiesUsed.length; j++) {
|
|
451
|
-
const tech1 = technologiesUsed[i];
|
|
452
|
-
const tech2 = technologiesUsed[j];
|
|
453
|
-
const pair = `${tech1}-${tech2}`;
|
|
454
|
-
|
|
455
|
-
const violations1 = allResults.get(tech1).length;
|
|
456
|
-
const violations2 = allResults.get(tech2).length;
|
|
457
|
-
const agreement = Math.abs(violations1 - violations2) / Math.max(violations1, violations2, 1);
|
|
458
|
-
|
|
459
|
-
if (!this.performanceMetrics.accuracyComparisons.has(pair)) {
|
|
460
|
-
this.performanceMetrics.accuracyComparisons.set(pair, []);
|
|
461
|
-
}
|
|
462
|
-
this.performanceMetrics.accuracyComparisons.get(pair).push(agreement);
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
/**
|
|
468
|
-
* Get performance insights
|
|
469
|
-
*/
|
|
470
|
-
getPerformanceInsights() {
|
|
471
|
-
const insights = {
|
|
472
|
-
averageExecutionTimes: new Map(),
|
|
473
|
-
technologiesUsed: Array.from(this.performanceMetrics.technologyExecutionTimes.keys()),
|
|
474
|
-
totalAnalysesRun: 0
|
|
475
|
-
};
|
|
476
|
-
|
|
477
|
-
for (const [technology, times] of this.performanceMetrics.technologyExecutionTimes) {
|
|
478
|
-
const avg = times.reduce((sum, time) => sum + time, 0) / times.length;
|
|
479
|
-
insights.averageExecutionTimes.set(technology, avg);
|
|
480
|
-
insights.totalAnalysesRun += times.length;
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
return insights;
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
module.exports = new C029CatchBlockAnalyzer();
|