@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
package/CONTRIBUTING.md
CHANGED
|
@@ -49,74 +49,917 @@ When contributing to Sun Lint, please follow these coding rules:
|
|
|
49
49
|
|
|
50
50
|
## 🔧 **Development Workflow**
|
|
51
51
|
|
|
52
|
-
### **
|
|
52
|
+
### **SunLint Architecture Overview**
|
|
53
|
+
|
|
54
|
+
SunLint uses a **multi-engine architecture** with rule mapping system:
|
|
55
|
+
|
|
56
|
+
- **Heuristic Engine**: Pattern-based analysis using AST (Abstract Syntax Tree)
|
|
57
|
+
- **ESLint Engine**: JavaScript/TypeScript linting using ESLint rules
|
|
58
|
+
- **OpenAI Engine**: AI-powered code analysis
|
|
59
|
+
|
|
60
|
+
**Rule Configuration Files:**
|
|
61
|
+
- `rules/` - Unified rule registry (auto-generated from origin-rules)
|
|
62
|
+
- `config/eslint-rule-mapping.json` - ESLint engine rule mappings
|
|
63
|
+
- `origin-rules/` - Original rule definitions (markdown format)
|
|
64
|
+
|
|
65
|
+
### **Adding a New Rule (Modern Approach - 2025)**
|
|
66
|
+
|
|
67
|
+
#### **Step 1: Choose Rule Implementation Strategy**
|
|
68
|
+
|
|
69
|
+
**Modern Approach: Hybrid Symbol + Regex Analysis (Recommended)**
|
|
70
|
+
Following the successful C035 pattern, implement rules with both symbol-based and regex-based analyzers for maximum accuracy and fallback coverage.
|
|
71
|
+
|
|
72
|
+
#### **Step 2: Create Rule Directory Structure**
|
|
53
73
|
|
|
54
|
-
1. **Create Rule Implementation**
|
|
55
74
|
```bash
|
|
56
|
-
# Create
|
|
57
|
-
mkdir -p rules/
|
|
58
|
-
|
|
75
|
+
# Create rule directory
|
|
76
|
+
mkdir -p rules/common/C042_no_hardcoded_config
|
|
77
|
+
|
|
78
|
+
# Create required files
|
|
79
|
+
touch rules/common/C042_no_hardcoded_config/analyzer.js
|
|
80
|
+
touch rules/common/C042_no_hardcoded_config/symbol-based-analyzer.js
|
|
81
|
+
touch rules/common/C042_no_hardcoded_config/regex-based-analyzer.js
|
|
82
|
+
touch rules/common/C042_no_hardcoded_config/config.json
|
|
83
|
+
touch rules/common/C042_no_hardcoded_config/STRATEGY.md
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
#### **Step 3: Implement Main Analyzer (Hybrid Orchestrator)**
|
|
87
|
+
|
|
88
|
+
```javascript
|
|
89
|
+
// rules/common/C042_no_hardcoded_config/analyzer.js
|
|
90
|
+
const C042SymbolBasedAnalyzer = require('./symbol-based-analyzer.js');
|
|
91
|
+
const C042RegexBasedAnalyzer = require('./regex-based-analyzer.js');
|
|
92
|
+
|
|
93
|
+
class C042Analyzer {
|
|
94
|
+
constructor(semanticEngine = null) {
|
|
95
|
+
this.ruleId = 'C042';
|
|
96
|
+
this.ruleName = 'No Hardcoded Configuration Values';
|
|
97
|
+
this.description = 'Avoid hardcoding configuration values in business logic';
|
|
98
|
+
this.semanticEngine = semanticEngine;
|
|
99
|
+
this.verbose = false;
|
|
100
|
+
|
|
101
|
+
// Configuration
|
|
102
|
+
this.config = {
|
|
103
|
+
useSymbolBased: true, // Primary approach
|
|
104
|
+
fallbackToRegex: true, // Only when symbol fails completely
|
|
105
|
+
symbolBasedOnly: false // Can be set to true for pure mode
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
// Initialize both analyzers
|
|
109
|
+
this.symbolAnalyzer = new C042SymbolBasedAnalyzer(semanticEngine);
|
|
110
|
+
this.regexAnalyzer = new C042RegexBasedAnalyzer(semanticEngine);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async initialize(semanticEngine = null) {
|
|
114
|
+
if (semanticEngine) {
|
|
115
|
+
this.semanticEngine = semanticEngine;
|
|
116
|
+
}
|
|
117
|
+
this.verbose = semanticEngine?.verbose || false;
|
|
118
|
+
|
|
119
|
+
// Initialize both analyzers
|
|
120
|
+
await this.symbolAnalyzer.initialize(semanticEngine);
|
|
121
|
+
await this.regexAnalyzer.initialize(semanticEngine);
|
|
122
|
+
|
|
123
|
+
if (this.verbose) {
|
|
124
|
+
console.log(`🔧 [C042 Hybrid] Analyzer initialized`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async analyzeFileBasic(filePath, options = {}) {
|
|
129
|
+
try {
|
|
130
|
+
// Try symbol-based analysis first
|
|
131
|
+
if (this.semanticEngine?.isSymbolEngineReady?.() &&
|
|
132
|
+
this.semanticEngine.project) {
|
|
133
|
+
|
|
134
|
+
if (this.verbose) {
|
|
135
|
+
console.log(`🔍 [C042] Using symbol-based analysis for ${filePath}`);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const violations = await this.symbolAnalyzer.analyzeFileBasic(filePath, options);
|
|
139
|
+
return violations;
|
|
140
|
+
}
|
|
141
|
+
} catch (error) {
|
|
142
|
+
if (this.verbose) {
|
|
143
|
+
console.warn(`⚠️ [C042] Symbol analysis failed: ${error.message}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Fallback to regex-based analysis
|
|
148
|
+
if (this.verbose) {
|
|
149
|
+
console.log(`🔄 [C042] Using regex-based analysis (fallback) for ${filePath}`);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return await this.regexAnalyzer.analyzeFileBasic(filePath, options);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async analyze(files, language, options = {}) {
|
|
156
|
+
const violations = [];
|
|
157
|
+
|
|
158
|
+
for (const filePath of files) {
|
|
159
|
+
try {
|
|
160
|
+
const fileViolations = await this.analyzeFileBasic(filePath, options);
|
|
161
|
+
violations.push(...fileViolations);
|
|
162
|
+
} catch (error) {
|
|
163
|
+
if (this.verbose) {
|
|
164
|
+
console.warn(`❌ [C042] Analysis failed for ${filePath}:`, error.message);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return violations;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
module.exports = C042Analyzer;
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
#### **Step 4: Implement Symbol-Based Analyzer (Primary)**
|
|
177
|
+
|
|
178
|
+
```javascript
|
|
179
|
+
// rules/common/C042_no_hardcoded_config/symbol-based-analyzer.js
|
|
180
|
+
class C042SymbolBasedAnalyzer {
|
|
181
|
+
constructor(semanticEngine = null) {
|
|
182
|
+
this.ruleId = 'C042';
|
|
183
|
+
this.ruleName = 'No Hardcoded Configuration Values (Symbol-Based)';
|
|
184
|
+
this.semanticEngine = semanticEngine;
|
|
185
|
+
this.verbose = false;
|
|
186
|
+
|
|
187
|
+
// Configuration patterns to detect
|
|
188
|
+
this.configPatterns = [
|
|
189
|
+
/^API_/i, /^DATABASE_/i, /^DB_/i,
|
|
190
|
+
/^MAX_/i, /^MIN_/i, /^LIMIT_/i,
|
|
191
|
+
/^TIMEOUT/i, /^PORT$/i, /^HOST$/i,
|
|
192
|
+
/^SECRET/i, /^KEY$/i, /^TOKEN/i
|
|
193
|
+
];
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async initialize(semanticEngine = null) {
|
|
197
|
+
if (semanticEngine) {
|
|
198
|
+
this.semanticEngine = semanticEngine;
|
|
199
|
+
}
|
|
200
|
+
this.verbose = semanticEngine?.verbose || false;
|
|
201
|
+
|
|
202
|
+
if (this.verbose) {
|
|
203
|
+
console.log(`🔧 [C042 Symbol-Based] Analyzer initialized`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async analyzeFileBasic(filePath, options = {}) {
|
|
208
|
+
const violations = [];
|
|
209
|
+
|
|
210
|
+
if (!this.semanticEngine?.project) {
|
|
211
|
+
return violations;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
try {
|
|
215
|
+
const sourceFile = this.semanticEngine.project.getSourceFileByFilePath(filePath);
|
|
216
|
+
if (!sourceFile) {
|
|
217
|
+
return violations;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Analyze variable declarations
|
|
221
|
+
const variableDeclarations = sourceFile.getVariableDeclarations();
|
|
222
|
+
for (const declaration of variableDeclarations) {
|
|
223
|
+
const violation = this.analyzeVariableDeclaration(declaration, filePath);
|
|
224
|
+
if (violation) {
|
|
225
|
+
violations.push(violation);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return violations;
|
|
230
|
+
} catch (error) {
|
|
231
|
+
if (this.verbose) {
|
|
232
|
+
console.warn(`⚠️ [C042 Symbol-Based] Analysis failed for ${filePath}:`, error.message);
|
|
233
|
+
}
|
|
234
|
+
return violations;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
analyzeVariableDeclaration(declaration, filePath) {
|
|
239
|
+
const name = declaration.getName();
|
|
240
|
+
const initializer = declaration.getInitializer();
|
|
241
|
+
|
|
242
|
+
// Check if variable name suggests configuration
|
|
243
|
+
if (!this.isConfigVariable(name)) {
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Check if value is hardcoded
|
|
248
|
+
if (this.isHardcodedValue(initializer)) {
|
|
249
|
+
const startLineNumber = declaration.getStartLineNumber();
|
|
250
|
+
|
|
251
|
+
return {
|
|
252
|
+
ruleId: this.ruleId,
|
|
253
|
+
severity: 'warning',
|
|
254
|
+
message: `Configuration variable '${name}' should not be hardcoded`,
|
|
255
|
+
source: this.ruleId,
|
|
256
|
+
file: filePath,
|
|
257
|
+
line: startLineNumber,
|
|
258
|
+
column: declaration.getStart() - declaration.getStartLinePos() + 1,
|
|
259
|
+
description: `Symbol-based analysis detected hardcoded configuration value. Use environment variables or config files.`,
|
|
260
|
+
suggestion: `Use process.env.${name} or load from configuration file`,
|
|
261
|
+
category: 'maintainability'
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
isConfigVariable(name) {
|
|
269
|
+
return this.configPatterns.some(pattern => pattern.test(name));
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
isHardcodedValue(initializer) {
|
|
273
|
+
if (!initializer) return false;
|
|
274
|
+
|
|
275
|
+
// Check for literal values (strings, numbers)
|
|
276
|
+
if (initializer.getKind() === ts.SyntaxKind.StringLiteral ||
|
|
277
|
+
initializer.getKind() === ts.SyntaxKind.NumericLiteral) {
|
|
278
|
+
return true;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Check for property access to process.env
|
|
282
|
+
if (initializer.getText().includes('process.env')) {
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
module.exports = C042SymbolBasedAnalyzer;
|
|
59
291
|
```
|
|
60
292
|
|
|
61
|
-
|
|
293
|
+
#### **Step 5: Implement Regex-Based Analyzer (Fallback)**
|
|
294
|
+
|
|
62
295
|
```javascript
|
|
63
|
-
// rules/
|
|
64
|
-
class
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
296
|
+
// rules/common/C042_no_hardcoded_config/regex-based-analyzer.js
|
|
297
|
+
class C042RegexBasedAnalyzer {
|
|
298
|
+
constructor(semanticEngine = null) {
|
|
299
|
+
this.ruleId = 'C042';
|
|
300
|
+
this.ruleName = 'No Hardcoded Configuration Values (Regex-Based)';
|
|
301
|
+
this.semanticEngine = semanticEngine;
|
|
302
|
+
this.verbose = false;
|
|
303
|
+
|
|
304
|
+
// Patterns for hardcoded config detection
|
|
305
|
+
this.hardcodedPatterns = [
|
|
306
|
+
/const\s+(API_\w+|DATABASE_\w+|DB_\w+)\s*=\s*["'][^"']+["']/gi,
|
|
307
|
+
/const\s+(MAX_\w+|MIN_\w+|LIMIT_\w+)\s*=\s*\d+/gi,
|
|
308
|
+
/const\s+(TIMEOUT|PORT|HOST)\s*=\s*["']\w+["']|\d+/gi
|
|
309
|
+
];
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
async initialize(semanticEngine = null) {
|
|
313
|
+
if (semanticEngine) {
|
|
314
|
+
this.semanticEngine = semanticEngine;
|
|
315
|
+
}
|
|
316
|
+
this.verbose = semanticEngine?.verbose || false;
|
|
317
|
+
|
|
318
|
+
if (this.verbose) {
|
|
319
|
+
console.log(`🔧 [C042 Regex-Based] Analyzer initialized`);
|
|
320
|
+
}
|
|
68
321
|
}
|
|
69
322
|
|
|
70
|
-
|
|
71
|
-
|
|
323
|
+
async analyzeFileBasic(filePath, options = {}) {
|
|
324
|
+
const fs = require('fs');
|
|
72
325
|
const violations = [];
|
|
73
|
-
|
|
326
|
+
|
|
327
|
+
if (!fs.existsSync(filePath)) {
|
|
328
|
+
return violations;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
332
|
+
const lines = content.split('\n');
|
|
333
|
+
|
|
334
|
+
for (let i = 0; i < lines.length; i++) {
|
|
335
|
+
const line = lines[i];
|
|
336
|
+
|
|
337
|
+
for (const pattern of this.hardcodedPatterns) {
|
|
338
|
+
pattern.lastIndex = 0; // Reset regex
|
|
339
|
+
let match;
|
|
340
|
+
|
|
341
|
+
while ((match = pattern.exec(line)) !== null) {
|
|
342
|
+
// Skip if using process.env
|
|
343
|
+
if (line.includes('process.env')) {
|
|
344
|
+
continue;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
violations.push({
|
|
348
|
+
ruleId: this.ruleId,
|
|
349
|
+
severity: 'warning',
|
|
350
|
+
message: `Configuration variable '${match[1]}' should not be hardcoded`,
|
|
351
|
+
source: this.ruleId,
|
|
352
|
+
file: filePath,
|
|
353
|
+
line: i + 1,
|
|
354
|
+
column: match.index + 1,
|
|
355
|
+
description: `[REGEX-FALLBACK] Hardcoded configuration detected. Use environment variables or config files.`,
|
|
356
|
+
suggestion: `Use process.env.${match[1]} or load from configuration file`,
|
|
357
|
+
category: 'maintainability'
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
return violations;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
async analyze(files, language, options = {}) {
|
|
367
|
+
const violations = [];
|
|
368
|
+
|
|
369
|
+
for (const filePath of files) {
|
|
370
|
+
try {
|
|
371
|
+
const fileViolations = await this.analyzeFileBasic(filePath, options);
|
|
372
|
+
violations.push(...fileViolations);
|
|
373
|
+
} catch (error) {
|
|
374
|
+
if (this.verbose) {
|
|
375
|
+
console.warn(`❌ [C042 Regex] Analysis failed for ${filePath}:`, error.message);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
74
380
|
return violations;
|
|
75
381
|
}
|
|
76
382
|
}
|
|
77
383
|
|
|
78
|
-
module.exports =
|
|
384
|
+
module.exports = C042RegexBasedAnalyzer;
|
|
79
385
|
```
|
|
80
386
|
|
|
81
|
-
|
|
387
|
+
#### **Step 6: Create Rule Configuration**
|
|
388
|
+
|
|
82
389
|
```json
|
|
83
|
-
// rules/
|
|
390
|
+
// rules/common/C042_no_hardcoded_config/config.json
|
|
84
391
|
{
|
|
85
|
-
"
|
|
86
|
-
"name": "
|
|
87
|
-
"
|
|
88
|
-
"
|
|
89
|
-
"
|
|
90
|
-
"languages": ["typescript", "
|
|
91
|
-
"
|
|
392
|
+
"ruleId": "C042",
|
|
393
|
+
"name": "No Hardcoded Configuration Values",
|
|
394
|
+
"description": "Avoid hardcoding configuration values in business logic",
|
|
395
|
+
"category": "maintainability",
|
|
396
|
+
"severity": "warning",
|
|
397
|
+
"languages": ["typescript", "javascript"],
|
|
398
|
+
"version": "1.0.0",
|
|
399
|
+
"tags": ["configuration", "maintainability", "environment"],
|
|
400
|
+
"patterns": {
|
|
401
|
+
"configVariables": [
|
|
402
|
+
"API_", "DATABASE_", "DB_", "MAX_", "MIN_", "LIMIT_",
|
|
403
|
+
"TIMEOUT", "PORT", "HOST", "SECRET", "KEY", "TOKEN"
|
|
404
|
+
],
|
|
405
|
+
"excludePatterns": [
|
|
406
|
+
"process.env", "config.", "getConfig(", "loadConfig("
|
|
407
|
+
]
|
|
408
|
+
},
|
|
409
|
+
"examples": {
|
|
410
|
+
"bad": [
|
|
411
|
+
"const API_URL = 'https://api.example.com';",
|
|
412
|
+
"const MAX_RETRIES = 5;",
|
|
413
|
+
"const DATABASE_PASSWORD = 'secret123';"
|
|
414
|
+
],
|
|
415
|
+
"good": [
|
|
416
|
+
"const API_URL = process.env.API_URL;",
|
|
417
|
+
"const MAX_RETRIES = parseInt(process.env.MAX_RETRIES || '3');",
|
|
418
|
+
"const config = loadConfigFromFile();"
|
|
419
|
+
]
|
|
420
|
+
}
|
|
92
421
|
}
|
|
93
422
|
```
|
|
94
423
|
|
|
95
|
-
|
|
424
|
+
#### **Step 7: Document Analysis Strategy**
|
|
425
|
+
|
|
426
|
+
```markdown
|
|
427
|
+
<!-- rules/common/C042_no_hardcoded_config/STRATEGY.md -->
|
|
428
|
+
# C042 Analysis Strategy: No Hardcoded Configuration Values
|
|
429
|
+
|
|
430
|
+
## Overview
|
|
431
|
+
Hybrid approach using symbol-based analysis as primary with regex fallback.
|
|
432
|
+
|
|
433
|
+
## Symbol-Based Analysis (Primary)
|
|
434
|
+
- Uses ts-morph AST to analyze variable declarations
|
|
435
|
+
- Detects configuration variable patterns by name
|
|
436
|
+
- Checks initializer values for hardcoded literals
|
|
437
|
+
- Excludes process.env and config function calls
|
|
438
|
+
|
|
439
|
+
## Regex-Based Analysis (Fallback)
|
|
440
|
+
- Pattern matching for common hardcoded config scenarios
|
|
441
|
+
- Simpler but broader detection
|
|
442
|
+
- Used when symbol analysis unavailable
|
|
443
|
+
|
|
444
|
+
## Strategy Selection
|
|
445
|
+
1. **Symbol-based**: When ts-morph project available (95% accuracy)
|
|
446
|
+
2. **Regex-fallback**: When symbol analysis fails (80% accuracy)
|
|
447
|
+
|
|
448
|
+
## Testing Approach
|
|
449
|
+
Test both analyzers independently and verify hybrid orchestration.
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
---
|
|
453
|
+
|
|
454
|
+
## 🆕 **Modern Rule Implementation Guide (2025)**
|
|
455
|
+
|
|
456
|
+
Based on the successful C035 implementation, here's the **current recommended approach** for adding new rules:
|
|
457
|
+
|
|
458
|
+
### **Critical Success Patterns**
|
|
459
|
+
|
|
460
|
+
#### **✅ 1. Constructor Pattern (REQUIRED)**
|
|
96
461
|
```javascript
|
|
97
|
-
//
|
|
462
|
+
// ✅ CORRECT - Works with semantic engine injection
|
|
463
|
+
class C042Analyzer {
|
|
464
|
+
constructor(options = {}) {
|
|
465
|
+
this.semanticEngine = options.semanticEngine || null;
|
|
466
|
+
this.verbose = options.verbose || false;
|
|
467
|
+
// ...
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// ❌ WRONG - Causes semantic engine injection failure
|
|
472
|
+
class C042Analyzer {
|
|
473
|
+
constructor(semanticEngine = null) {
|
|
474
|
+
this.semanticEngine = semanticEngine;
|
|
475
|
+
// ...
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
#### **✅ 2. Registry Configuration (REQUIRED)**
|
|
481
|
+
|
|
482
|
+
**Enhanced Rules Registry** (`config/rules/enhanced-rules-registry.json`):
|
|
483
|
+
```json
|
|
98
484
|
{
|
|
99
485
|
"C042": {
|
|
100
|
-
"
|
|
101
|
-
"
|
|
102
|
-
"category": "
|
|
103
|
-
"
|
|
104
|
-
"
|
|
105
|
-
"
|
|
486
|
+
"name": "No Hardcoded Configuration Values",
|
|
487
|
+
"description": "Avoid hardcoding configuration values in business logic",
|
|
488
|
+
"category": "maintainability",
|
|
489
|
+
"severity": "warning",
|
|
490
|
+
"languages": ["typescript", "javascript", "dart", "kotlin"],
|
|
491
|
+
"analyzer": "./rules/common/C042_no_hardcoded_config/analyzer.js",
|
|
492
|
+
"config": "./rules/common/C042_no_hardcoded_config/config.json",
|
|
493
|
+
"version": "1.0.0",
|
|
494
|
+
"status": "stable",
|
|
495
|
+
"tags": ["configuration", "hardcoding", "maintainability"],
|
|
496
|
+
"engineMappings": {
|
|
497
|
+
"eslint": ["no-magic-numbers", "@typescript-eslint/no-magic-numbers"]
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
**Rule Analysis Strategies** (`config/rule-analysis-strategies.js`):
|
|
504
|
+
```javascript
|
|
505
|
+
module.exports = {
|
|
506
|
+
hybridOptimal: {
|
|
507
|
+
'C042': {
|
|
508
|
+
reason: 'Configuration detection requires symbol resolution + pattern matching',
|
|
509
|
+
methods: ['semantic', 'regex'],
|
|
510
|
+
strategy: 'semantic-primary-regex-fallback',
|
|
511
|
+
accuracy: { semantic: 90, regex: 75, combined: 95 }
|
|
512
|
+
}
|
|
106
513
|
}
|
|
107
514
|
}
|
|
108
515
|
```
|
|
109
516
|
|
|
110
|
-
|
|
517
|
+
#### **✅ 3. Hybrid Architecture Template**
|
|
518
|
+
|
|
519
|
+
**Main Analyzer** (`analyzer.js`):
|
|
111
520
|
```javascript
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
521
|
+
const C042SymbolBasedAnalyzer = require('./symbol-based-analyzer.js');
|
|
522
|
+
const C042RegexBasedAnalyzer = require('./regex-based-analyzer.js');
|
|
523
|
+
|
|
524
|
+
class C042Analyzer {
|
|
525
|
+
constructor(options = {}) {
|
|
526
|
+
if (process.env.SUNLINT_DEBUG) {
|
|
527
|
+
console.log(`🔧 [C042] Constructor called with options:`, !!options);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
this.ruleId = 'C042';
|
|
531
|
+
this.semanticEngine = options.semanticEngine || null;
|
|
532
|
+
this.verbose = options.verbose || false;
|
|
533
|
+
|
|
534
|
+
// Initialize analyzers with THIS.semanticEngine
|
|
535
|
+
this.symbolAnalyzer = new C042SymbolBasedAnalyzer(this.semanticEngine);
|
|
536
|
+
this.regexAnalyzer = new C042RegexBasedAnalyzer(this.semanticEngine);
|
|
537
|
+
|
|
538
|
+
this.config = {
|
|
539
|
+
useSymbolBased: true,
|
|
540
|
+
fallbackToRegex: true,
|
|
541
|
+
symbolBasedOnly: false
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
async analyze(files, language = 'javascript', options = {}) {
|
|
546
|
+
const allViolations = [];
|
|
547
|
+
|
|
548
|
+
for (const filePath of files) {
|
|
549
|
+
const violations = await this.analyzeFile(filePath, options);
|
|
550
|
+
allViolations.push(...violations);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
return allViolations;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
async analyzeFile(filePath, options = {}) {
|
|
557
|
+
// 1. Try Symbol-based analysis first (primary)
|
|
558
|
+
if (this.config.useSymbolBased &&
|
|
559
|
+
this.semanticEngine?.project &&
|
|
560
|
+
this.semanticEngine?.initialized) {
|
|
561
|
+
try {
|
|
562
|
+
const sourceFile = this.semanticEngine.project.getSourceFile(filePath);
|
|
563
|
+
if (sourceFile) {
|
|
564
|
+
const violations = await this.symbolAnalyzer.analyzeFileWithSymbols(filePath, options);
|
|
565
|
+
violations.forEach(v => v.analysisStrategy = 'symbol-based');
|
|
566
|
+
return violations;
|
|
567
|
+
}
|
|
568
|
+
} catch (error) {
|
|
569
|
+
console.warn(`⚠️ [C042] Symbol analysis failed: ${error.message}`);
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
// 2. Fallback to regex-based analysis
|
|
574
|
+
if (this.config.fallbackToRegex) {
|
|
575
|
+
const violations = await this.regexAnalyzer.analyzeFileBasic(filePath, options);
|
|
576
|
+
violations.forEach(v => v.analysisStrategy = 'regex-based');
|
|
577
|
+
return violations;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
return [];
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
module.exports = C042Analyzer;
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
#### **✅ 4. Testing & Validation**
|
|
588
|
+
|
|
589
|
+
**Debug Output Verification**:
|
|
590
|
+
```bash
|
|
591
|
+
SUNLINT_DEBUG=true node cli.js --input test-files/c042-test.js --rules C042 --engine=heuristic --debug
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
**Expected Output**:
|
|
595
|
+
```
|
|
596
|
+
🔧 [HeuristicEngine] Rule C042: semantic (approach: hybrid-combined)
|
|
597
|
+
🔧 [C042] Constructor called with options: true
|
|
598
|
+
🔧 [C042] Options type: object [ 'verbose', 'semanticEngine' ]
|
|
599
|
+
🔧 [C042] Trying symbol-based analysis...
|
|
600
|
+
✅ [C042] Symbol-based analysis: X violations
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
#### **✅ 5. Common Pitfalls & Solutions**
|
|
604
|
+
|
|
605
|
+
| ❌ Problem | ✅ Solution |
|
|
606
|
+
|------------|-------------|
|
|
607
|
+
| `constructor(semanticEngine)` | `constructor(options = {})` |
|
|
608
|
+
| Missing from `rule-analysis-strategies.js` | Add to `hybridOptimal` section |
|
|
609
|
+
| `ast (approach: ast-primary)` output | Ensure rule in strategies config |
|
|
610
|
+
| `this.semanticEngine?.project: false` | Check engine injection & initialization |
|
|
611
|
+
| No debug output from constructor | Verify constructor signature |
|
|
612
|
+
|
|
613
|
+
#### **✅ 6. Validation Checklist**
|
|
614
|
+
|
|
615
|
+
Before submitting a new rule:
|
|
616
|
+
|
|
617
|
+
- [ ] Constructor uses `options = {}` pattern
|
|
618
|
+
- [ ] Rule registered in `enhanced-rules-registry.json`
|
|
619
|
+
- [ ] Strategy defined in `rule-analysis-strategies.js` `hybridOptimal`
|
|
620
|
+
- [ ] Symbol-based analyzer implemented with ts-morph
|
|
621
|
+
- [ ] Regex-based analyzer implemented with pattern matching
|
|
622
|
+
- [ ] Debug output shows `semantic (approach: hybrid-combined)`
|
|
623
|
+
- [ ] Test cases detect expected violations accurately
|
|
624
|
+
- [ ] No modifications to engine core files (registry-driven only)
|
|
625
|
+
- [ ] Performance acceptable for large codebases
|
|
626
|
+
|
|
627
|
+
---
|
|
628
|
+
|
|
629
|
+
## 📚 **Legacy Implementation Examples**
|
|
630
|
+
|
|
631
|
+
The following examples show older patterns for reference, but **use the Modern Guide above** for new rules:
|
|
632
|
+
|
|
633
|
+
#### **Step 7: Document Analysis Strategy**
|
|
634
|
+
|
|
635
|
+
```markdown
|
|
636
|
+
<!-- rules/common/C042_no_hardcoded_config/STRATEGY.md -->
|
|
637
|
+
# C042 Analysis Strategy: No Hardcoded Configuration Values
|
|
638
|
+
|
|
639
|
+
## Overview
|
|
640
|
+
Hybrid approach using symbol-based analysis as primary with regex fallback.
|
|
641
|
+
|
|
642
|
+
## Symbol-Based Analysis (Primary)
|
|
643
|
+
- Uses ts-morph AST to analyze variable declarations
|
|
644
|
+
- Detects configuration variable patterns by name
|
|
645
|
+
- Checks initializer values for hardcoded literals
|
|
646
|
+
- Excludes process.env and config function calls
|
|
647
|
+
|
|
648
|
+
## Regex-Based Analysis (Fallback)
|
|
649
|
+
- Pattern matching for common hardcoded config scenarios
|
|
650
|
+
- Simpler but broader detection
|
|
651
|
+
- Used when symbol analysis unavailable
|
|
652
|
+
|
|
653
|
+
## Strategy Selection
|
|
654
|
+
1. **Symbol-based**: When ts-morph project available (95% accuracy)
|
|
655
|
+
2. **Regex-fallback**: When symbol analysis fails (80% accuracy)
|
|
656
|
+
|
|
657
|
+
## Testing Approach
|
|
658
|
+
Test both analyzers independently and verify hybrid orchestration.
|
|
659
|
+
```
|
|
660
|
+
|
|
661
|
+
#### **Step 8: Register Rule in Rules Registry**
|
|
662
|
+
|
|
663
|
+
```json
|
|
664
|
+
// config/rules/rules-registry.json
|
|
665
|
+
{
|
|
666
|
+
"rules": {
|
|
667
|
+
"C042": {
|
|
668
|
+
"name": "No Hardcoded Configuration Values",
|
|
669
|
+
"description": "Avoid hardcoding configuration values in business logic",
|
|
670
|
+
"category": "maintainability",
|
|
671
|
+
"severity": "warning",
|
|
672
|
+
"languages": ["typescript", "javascript"],
|
|
673
|
+
"analyzer": "./rules/common/C042_no_hardcoded_config/analyzer.js",
|
|
674
|
+
"config": "./rules/common/C042_no_hardcoded_config/config.json",
|
|
675
|
+
"version": "1.0.0",
|
|
676
|
+
"status": "experimental",
|
|
677
|
+
"tags": ["configuration", "maintainability", "environment"]
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
```
|
|
682
|
+
|
|
683
|
+
#### **Step 9: Add to Enhanced Rules Registry**
|
|
684
|
+
|
|
685
|
+
```javascript
|
|
686
|
+
// core/enhanced-rules-registry.js
|
|
687
|
+
loadEnginePreferences(options = {}) {
|
|
688
|
+
const preferences = {
|
|
689
|
+
// ... existing rules ...
|
|
690
|
+
'C042': ['heuristic', 'openai'], // Symbol + regex hybrid
|
|
691
|
+
// ... rest of rules ...
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
#### **Step 10: Add to Analysis Strategies**
|
|
697
|
+
|
|
698
|
+
```javascript
|
|
699
|
+
// config/rule-analysis-strategies.js
|
|
700
|
+
module.exports = {
|
|
701
|
+
astPreferred: {
|
|
702
|
+
// ... existing rules ...
|
|
703
|
+
'C042': {
|
|
704
|
+
reason: 'Configuration detection requires precise symbol analysis for variable declarations',
|
|
705
|
+
methods: ['ast', 'regex'],
|
|
706
|
+
accuracy: { ast: 95, regex: 80 }
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
};
|
|
710
|
+
```
|
|
711
|
+
|
|
712
|
+
**Description**: Avoid hardcoding configuration values in business logic
|
|
713
|
+
|
|
714
|
+
**Examples**:
|
|
715
|
+
❌ Bad:
|
|
716
|
+
```javascript
|
|
717
|
+
const API_URL = "https://api.example.com";
|
|
718
|
+
const MAX_RETRIES = 5;
|
|
719
|
+
```
|
|
720
|
+
|
|
721
|
+
✅ Good:
|
|
722
|
+
```javascript
|
|
723
|
+
const API_URL = process.env.API_URL;
|
|
724
|
+
const MAX_RETRIES = parseInt(process.env.MAX_RETRIES || '3');
|
|
725
|
+
```
|
|
726
|
+
|
|
727
|
+
#### **Step 4: Add Tests**
|
|
728
|
+
|
|
729
|
+
```javascript
|
|
730
|
+
// test/rules/c042.test.js
|
|
731
|
+
const { testRule } = require('../test-utils');
|
|
732
|
+
|
|
733
|
+
describe('Rule C042: No Hardcoded Configuration Values', () => {
|
|
734
|
+
testRule('C042', {
|
|
735
|
+
valid: [
|
|
736
|
+
'const apiUrl = process.env.API_URL;',
|
|
737
|
+
'const config = loadConfig();'
|
|
738
|
+
],
|
|
739
|
+
invalid: [
|
|
740
|
+
'const API_URL = "https://api.example.com";',
|
|
741
|
+
'const MAX_RETRIES = 5;'
|
|
742
|
+
]
|
|
743
|
+
});
|
|
744
|
+
});
|
|
745
|
+
```
|
|
746
|
+
|
|
747
|
+
#### **Step 5: Test Your Rule**
|
|
748
|
+
|
|
749
|
+
```bash
|
|
750
|
+
# Test with specific engine
|
|
751
|
+
node cli.js --rule=C042 --engine=eslint --input=test/fixtures
|
|
752
|
+
node cli.js --rule=C042 --engine=heuristic --input=test/fixtures
|
|
753
|
+
|
|
754
|
+
# Test rule behavior
|
|
755
|
+
node cli.js --rule=C042 --input=test/fixtures --verbose
|
|
756
|
+
```
|
|
757
|
+
|
|
758
|
+
### **Engine-Specific Development**
|
|
759
|
+
|
|
760
|
+
#### **Heuristic Engine Rules**
|
|
761
|
+
|
|
762
|
+
For custom pattern-based analysis:
|
|
763
|
+
|
|
764
|
+
1. **Create Rule Class**:
|
|
765
|
+
```javascript
|
|
766
|
+
// rules/custom/your-rule.js
|
|
767
|
+
const HeuristicRuleBase = require('../../core/heuristic-rule-base');
|
|
768
|
+
|
|
769
|
+
class YourCustomRule extends HeuristicRuleBase {
|
|
770
|
+
constructor() {
|
|
771
|
+
super('CXXX', 'Your Rule Name');
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
analyze(node, context) {
|
|
775
|
+
// Your analysis logic
|
|
776
|
+
// Return violation or null
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
```
|
|
780
|
+
|
|
781
|
+
2. **Add to Registry**:
|
|
782
|
+
```json
|
|
783
|
+
{
|
|
784
|
+
"CXXX": {
|
|
785
|
+
"engineMappings": {
|
|
786
|
+
"heuristic": {
|
|
787
|
+
"implementation": "custom/your-rule",
|
|
788
|
+
"supportedLanguages": ["typescript", "javascript"]
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
```
|
|
794
|
+
|
|
795
|
+
#### **ESLint Engine Rules**
|
|
796
|
+
|
|
797
|
+
For JavaScript/TypeScript linting:
|
|
798
|
+
|
|
799
|
+
1. **Use Existing ESLint Rules**:
|
|
800
|
+
```json
|
|
801
|
+
{
|
|
802
|
+
"CXXX": {
|
|
803
|
+
"engineMappings": {
|
|
804
|
+
"eslint": {
|
|
805
|
+
"rules": ["no-console", "prefer-const"],
|
|
806
|
+
"config": {
|
|
807
|
+
"no-console": ["error"],
|
|
808
|
+
"prefer-const": ["warn"]
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
```
|
|
815
|
+
|
|
816
|
+
2. **Create Custom ESLint Rule** (advanced):
|
|
817
|
+
```javascript
|
|
818
|
+
// eslint-custom-rules/your-rule.js
|
|
819
|
+
module.exports = {
|
|
820
|
+
meta: {
|
|
821
|
+
type: 'problem',
|
|
822
|
+
docs: { description: 'Your rule description' }
|
|
823
|
+
},
|
|
824
|
+
create(context) {
|
|
825
|
+
return {
|
|
826
|
+
FunctionDeclaration(node) {
|
|
827
|
+
// Your ESLint rule logic
|
|
828
|
+
}
|
|
829
|
+
};
|
|
830
|
+
}
|
|
831
|
+
};
|
|
832
|
+
```
|
|
833
|
+
|
|
834
|
+
#### **OpenAI Engine Rules**
|
|
835
|
+
|
|
836
|
+
For AI-powered analysis:
|
|
837
|
+
|
|
838
|
+
```json
|
|
839
|
+
{
|
|
840
|
+
"CXXX": {
|
|
841
|
+
"engineMappings": {
|
|
842
|
+
"openai": {
|
|
843
|
+
"prompt": "Analyze code for specific pattern or anti-pattern",
|
|
844
|
+
"examples": [
|
|
845
|
+
"// Bad example",
|
|
846
|
+
"// Good example"
|
|
847
|
+
],
|
|
848
|
+
"temperature": 0.1,
|
|
849
|
+
"maxTokens": 500
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
}
|
|
115
854
|
```
|
|
116
855
|
|
|
117
856
|
### **Adding a New Security Rule**
|
|
118
857
|
|
|
119
|
-
Same process
|
|
858
|
+
Same process as above, just use `"category": "security"` in the rule definition.
|
|
859
|
+
|
|
860
|
+
### **Testing Your New Rule**
|
|
861
|
+
|
|
862
|
+
```bash
|
|
863
|
+
# Test all engines with your new rule
|
|
864
|
+
node cli.js --rule=C042 --input=test/fixtures --format=json
|
|
865
|
+
|
|
866
|
+
# Test specific engine
|
|
867
|
+
node cli.js --rule=C042 --engine=heuristic --input=test/fixtures
|
|
868
|
+
node cli.js --rule=C042 --engine=eslint --input=test/fixtures
|
|
869
|
+
node cli.js --rule=C042 --engine=openai --input=test/fixtures
|
|
870
|
+
|
|
871
|
+
# Validate rule registry
|
|
872
|
+
node validate-system.js
|
|
873
|
+
```
|
|
874
|
+
|
|
875
|
+
### **Rule Definition Schema**
|
|
876
|
+
|
|
877
|
+
Complete schema for rule definitions:
|
|
878
|
+
|
|
879
|
+
```json
|
|
880
|
+
{
|
|
881
|
+
"CXXX": {
|
|
882
|
+
"id": "CXXX", // Required: Rule ID
|
|
883
|
+
"title": "Rule Title", // Required: Human-readable title
|
|
884
|
+
"description": "Detailed description", // Required: Rule description
|
|
885
|
+
"severity": "error|warning|info", // Required: Severity level
|
|
886
|
+
"category": "quality|security|performance|maintainability", // Required
|
|
887
|
+
"tags": ["tag1", "tag2"], // Optional: Tags for filtering
|
|
888
|
+
"languages": ["typescript", "javascript"], // Optional: Supported languages
|
|
889
|
+
"engineMappings": { // Required: Engine configurations
|
|
890
|
+
"heuristic": {
|
|
891
|
+
"implementation": "custom/rule-name",
|
|
892
|
+
"supportedLanguages": ["typescript"],
|
|
893
|
+
"astTargets": ["FunctionDeclaration"]
|
|
894
|
+
},
|
|
895
|
+
"eslint": {
|
|
896
|
+
"rules": ["eslint-rule-name"],
|
|
897
|
+
"config": { "rule-option": true }
|
|
898
|
+
},
|
|
899
|
+
"openai": {
|
|
900
|
+
"prompt": "AI analysis prompt",
|
|
901
|
+
"examples": ["code example"],
|
|
902
|
+
"temperature": 0.1
|
|
903
|
+
}
|
|
904
|
+
},
|
|
905
|
+
"analysisStrategy": { // Optional: Analysis metadata
|
|
906
|
+
"type": "ast|regex|semantic",
|
|
907
|
+
"patterns": ["pattern1"],
|
|
908
|
+
"astTargets": ["NodeType"],
|
|
909
|
+
"heuristics": ["detection-method"]
|
|
910
|
+
},
|
|
911
|
+
"metadata": { // Optional: Additional metadata
|
|
912
|
+
"author": "Developer Name",
|
|
913
|
+
"created": "2025-08-07",
|
|
914
|
+
"updated": "2025-08-07",
|
|
915
|
+
"version": "1.0.0"
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
```
|
|
920
|
+
|
|
921
|
+
### **Architecture Deep Dive**
|
|
922
|
+
|
|
923
|
+
#### **Unified Rule Registry**
|
|
924
|
+
- **Location**: `rules/` (auto-generated from origin-rules)
|
|
925
|
+
- **Source**: `origin-rules/` (markdown files)
|
|
926
|
+
- **ESLint Mappings**: `config/eslint-rule-mapping.json`
|
|
927
|
+
- **Loader**: `core/unified-rule-registry.js`
|
|
928
|
+
|
|
929
|
+
#### **Engine Architecture**
|
|
930
|
+
```
|
|
931
|
+
┌─────────────────────────────────────────┐
|
|
932
|
+
│ SunLint CLI │
|
|
933
|
+
├─────────────────────────────────────────┤
|
|
934
|
+
│ Analysis Orchestrator │
|
|
935
|
+
│ (Strict Engine Mode Support) │
|
|
936
|
+
├─────────────────────────────────────────┤
|
|
937
|
+
│ ┌─────────────┬─────────────┬─────────┐ │
|
|
938
|
+
│ │ Heuristic │ ESLint │ OpenAI │ │
|
|
939
|
+
│ │ Engine │ Engine │ Engine │ │
|
|
940
|
+
│ │ (244 rules) │ (57 rules) │(256 all)│ │
|
|
941
|
+
│ └─────────────┴─────────────┴─────────┘ │
|
|
942
|
+
├─────────────────────────────────────────┤
|
|
943
|
+
│ Rule Configuration │
|
|
944
|
+
│ ┌─────────────┬─────────────────────────┐ │
|
|
945
|
+
│ │ ESLint │ Unified Registry │ │
|
|
946
|
+
│ │ Mappings │ (Generated Rules) │ │
|
|
947
|
+
│ │ (.json) │ (origin-rules) │ │
|
|
948
|
+
│ └─────────────┴─────────────────────────┘ │
|
|
949
|
+
└─────────────────────────────────────────┘
|
|
950
|
+
```
|
|
951
|
+
|
|
952
|
+
#### **How Engines Load Rules**
|
|
953
|
+
1. **Initialization**: Each engine calls `getInstance()` from unified registry
|
|
954
|
+
2. **Rule Loading**: Registry loads rules from auto-generated `rules/` directory
|
|
955
|
+
3. **ESLint Mapping**: ESLint engine loads rule mappings from `config/eslint-rule-mapping.json`
|
|
956
|
+
4. **Engine Filtering**: Each engine filters rules based on their capabilities
|
|
957
|
+
5. **Analysis**: Engines analyze code using their specific rule implementations
|
|
958
|
+
|
|
959
|
+
**Key Features:**
|
|
960
|
+
- ✅ **Strict Engine Mode**: `--engine=eslint` only runs ESLint, skips unsupported rules
|
|
961
|
+
- ✅ **Fallback Mode**: Auto-engine selection with fallback (ESLint → Heuristic → OpenAI)
|
|
962
|
+
- ✅ **Rule Skipping**: Graceful handling of unsupported rules by specific engines
|
|
120
963
|
|
|
121
964
|
## 🧪 **Testing**
|
|
122
965
|
|
|
@@ -125,25 +968,50 @@ Same process but in `rules/security/` directory with `security` category.
|
|
|
125
968
|
npm test
|
|
126
969
|
```
|
|
127
970
|
|
|
128
|
-
### **
|
|
971
|
+
### **Test Rule Registry System**
|
|
129
972
|
```bash
|
|
130
|
-
#
|
|
131
|
-
|
|
973
|
+
# Validate unified rule registry
|
|
974
|
+
node validate-system.js
|
|
975
|
+
|
|
976
|
+
# Check rule loading for each engine
|
|
977
|
+
npm run test:engines
|
|
978
|
+
```
|
|
132
979
|
|
|
133
|
-
|
|
134
|
-
|
|
980
|
+
### **Test Specific Rules**
|
|
981
|
+
```bash
|
|
982
|
+
# Test specific rule with all engines
|
|
983
|
+
node cli.js --rule=C042 --input=test/fixtures
|
|
135
984
|
|
|
136
|
-
# Test
|
|
137
|
-
|
|
985
|
+
# Test with specific engine
|
|
986
|
+
node cli.js --rule=C042 --engine=heuristic --input=test/fixtures
|
|
987
|
+
node cli.js --rule=C042 --engine=eslint --input=test/fixtures
|
|
988
|
+
node cli.js --rule=C042 --engine=openai --input=test/fixtures
|
|
138
989
|
|
|
139
|
-
# Test
|
|
140
|
-
|
|
990
|
+
# Test multiple rules
|
|
991
|
+
node cli.js --rule=C006,C019,C042 --input=test/fixtures
|
|
141
992
|
```
|
|
142
993
|
|
|
143
|
-
### **Test
|
|
994
|
+
### **Test Rule Development**
|
|
144
995
|
```bash
|
|
145
|
-
#
|
|
146
|
-
|
|
996
|
+
# Create test fixtures
|
|
997
|
+
mkdir -p test/fixtures/c042
|
|
998
|
+
echo 'const API_URL = "https://api.example.com";' > test/fixtures/c042/invalid.ts
|
|
999
|
+
echo 'const apiUrl = process.env.API_URL;' > test/fixtures/c042/valid.ts
|
|
1000
|
+
|
|
1001
|
+
# Test your rule
|
|
1002
|
+
node cli.js --rule=C042 --input=test/fixtures/c042 --format=json
|
|
1003
|
+
```
|
|
1004
|
+
|
|
1005
|
+
### **Integration Testing**
|
|
1006
|
+
```bash
|
|
1007
|
+
# Test all engines work together
|
|
1008
|
+
npm run test:integration
|
|
1009
|
+
|
|
1010
|
+
# Test rule registry loading
|
|
1011
|
+
npm run test:registry
|
|
1012
|
+
|
|
1013
|
+
# Performance testing
|
|
1014
|
+
npm run test:performance
|
|
147
1015
|
```
|
|
148
1016
|
|
|
149
1017
|
## 📊 **Code Review Process**
|
|
@@ -158,13 +1026,16 @@ node cli.js --rule=C042 --input=test/fixtures --format=eslint
|
|
|
158
1026
|
|
|
159
1027
|
2. **Submit Pull Request**
|
|
160
1028
|
- Clear title and description
|
|
161
|
-
- Reference related issues
|
|
1029
|
+
- Reference related issues
|
|
162
1030
|
- Include test results
|
|
163
1031
|
- Follow template
|
|
1032
|
+
- **NEW**: Validate rule registry with `node validate-system.js`
|
|
164
1033
|
|
|
165
1034
|
3. **Review Criteria**
|
|
166
1035
|
- Code quality (follows our own rules!)
|
|
167
|
-
-
|
|
1036
|
+
- Rule properly defined in `enhanced-rules-registry.json`
|
|
1037
|
+
- All engines can load the rule correctly
|
|
1038
|
+
- Test coverage for all supported engines
|
|
168
1039
|
- Documentation completeness
|
|
169
1040
|
- Performance impact
|
|
170
1041
|
- Backward compatibility
|
|
@@ -174,32 +1045,88 @@ node cli.js --rule=C042 --input=test/fixtures --format=eslint
|
|
|
174
1045
|
### **Update Documentation**
|
|
175
1046
|
When adding features:
|
|
176
1047
|
- Update `README.md`
|
|
177
|
-
- Add rule documentation
|
|
1048
|
+
- Add rule to `enhanced-rules-registry.json` (this is your main documentation!)
|
|
178
1049
|
- Update configuration examples
|
|
179
1050
|
- Add usage examples
|
|
1051
|
+
- Update `RULE_MIGRATION_SUMMARY.md` if changing rule system
|
|
180
1052
|
|
|
181
1053
|
### **Rule Documentation Template**
|
|
182
|
-
```markdown
|
|
183
|
-
## Rule C042: New Rule Name
|
|
184
1054
|
|
|
185
|
-
|
|
186
|
-
**Severity**: Error
|
|
187
|
-
**Languages**: TypeScript, Dart, Kotlin
|
|
1055
|
+
All rule documentation is now centralized in `enhanced-rules-registry.json`:
|
|
188
1056
|
|
|
189
|
-
|
|
190
|
-
|
|
1057
|
+
```json
|
|
1058
|
+
{
|
|
1059
|
+
"C042": {
|
|
1060
|
+
"title": "Clear, descriptive rule title",
|
|
1061
|
+
"description": "Detailed explanation of what the rule checks, why it matters, and how to fix violations. Follow Rule C015 (domain language) - use clear business terms.",
|
|
1062
|
+
"examples": {
|
|
1063
|
+
"invalid": [
|
|
1064
|
+
"// Bad example that violates the rule",
|
|
1065
|
+
"const API_URL = 'https://hardcoded-url.com';"
|
|
1066
|
+
],
|
|
1067
|
+
"valid": [
|
|
1068
|
+
"// Good example that follows the rule",
|
|
1069
|
+
"const apiUrl = process.env.API_URL;"
|
|
1070
|
+
]
|
|
1071
|
+
},
|
|
1072
|
+
"fixSuggestions": [
|
|
1073
|
+
"Move configuration to environment variables",
|
|
1074
|
+
"Use a configuration management system",
|
|
1075
|
+
"Extract constants to a separate config file"
|
|
1076
|
+
],
|
|
1077
|
+
"relatedRules": ["C031", "C034"]
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
```
|
|
191
1081
|
|
|
192
|
-
###
|
|
1082
|
+
### **Engine-Specific Documentation**
|
|
193
1083
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
1084
|
+
#### **Heuristic Engine Rules**
|
|
1085
|
+
Document custom analysis patterns:
|
|
1086
|
+
```json
|
|
1087
|
+
{
|
|
1088
|
+
"analysisStrategy": {
|
|
1089
|
+
"type": "ast",
|
|
1090
|
+
"description": "Analyzes AST nodes for hardcoded configuration patterns",
|
|
1091
|
+
"astTargets": ["VariableDeclaration", "PropertyAssignment"],
|
|
1092
|
+
"patterns": ["literal-values-in-config-context"],
|
|
1093
|
+
"complexity": "O(n) where n is number of variable declarations"
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
197
1096
|
```
|
|
198
1097
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
1098
|
+
#### **ESLint Engine Rules**
|
|
1099
|
+
Document ESLint rule mappings:
|
|
1100
|
+
```json
|
|
1101
|
+
{
|
|
1102
|
+
"engineMappings": {
|
|
1103
|
+
"eslint": {
|
|
1104
|
+
"rules": ["no-magic-numbers"],
|
|
1105
|
+
"rationale": "ESLint's no-magic-numbers rule catches hardcoded values",
|
|
1106
|
+
"limitations": "May have false positives for legitimate constants",
|
|
1107
|
+
"customConfig": {
|
|
1108
|
+
"no-magic-numbers": ["error", { "ignore": [0, 1, -1] }]
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
202
1113
|
```
|
|
1114
|
+
|
|
1115
|
+
#### **OpenAI Engine Rules**
|
|
1116
|
+
Document AI prompts and examples:
|
|
1117
|
+
```json
|
|
1118
|
+
{
|
|
1119
|
+
"engineMappings": {
|
|
1120
|
+
"openai": {
|
|
1121
|
+
"prompt": "Identify hardcoded configuration values that should be externalized to environment variables or config files",
|
|
1122
|
+
"context": "Look for URLs, timeouts, limits, and other configuration that might change between environments",
|
|
1123
|
+
"examples": [
|
|
1124
|
+
"❌ const API_URL = 'https://api.example.com';",
|
|
1125
|
+
"✅ const API_URL = process.env.API_URL || 'https://default-api.com';"
|
|
1126
|
+
]
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
203
1130
|
```
|
|
204
1131
|
|
|
205
1132
|
## 🐛 **Bug Reports**
|
|
@@ -210,6 +1137,49 @@ When reporting bugs:
|
|
|
210
1137
|
3. Provide sample code
|
|
211
1138
|
4. Include environment details
|
|
212
1139
|
5. Include sunlint output
|
|
1140
|
+
6. **NEW**: Run `node validate-system.js` and include output
|
|
1141
|
+
|
|
1142
|
+
## 🔧 **Troubleshooting**
|
|
1143
|
+
|
|
1144
|
+
### **Common Issues**
|
|
1145
|
+
|
|
1146
|
+
#### **Rule Not Loading**
|
|
1147
|
+
```bash
|
|
1148
|
+
# Check if rule exists in registry
|
|
1149
|
+
node -e "const registry = require('./core/unified-rule-registry'); registry.getInstance().initialize().then(() => console.log('Rule C042:', registry.getInstance().rules.has('C042')))"
|
|
1150
|
+
|
|
1151
|
+
# Validate registry syntax
|
|
1152
|
+
node validate-system.js
|
|
1153
|
+
```
|
|
1154
|
+
|
|
1155
|
+
#### **Engine Not Finding Rule**
|
|
1156
|
+
```bash
|
|
1157
|
+
# Check engine-specific mapping
|
|
1158
|
+
node -e "const registry = require('./core/unified-rule-registry'); registry.getInstance().initialize().then(r => { const rule = r.rules.get('C042'); console.log('ESLint mapping:', rule?.engineMappings?.eslint); })"
|
|
1159
|
+
```
|
|
1160
|
+
|
|
1161
|
+
#### **Rule Registry Errors**
|
|
1162
|
+
```bash
|
|
1163
|
+
# Common issues:
|
|
1164
|
+
# 1. JSON syntax errors in enhanced-rules-registry.json
|
|
1165
|
+
# 2. Missing required fields (id, title, description, severity, category)
|
|
1166
|
+
# 3. Invalid engine mapping structure
|
|
1167
|
+
|
|
1168
|
+
# Validate JSON syntax
|
|
1169
|
+
node -c config/enhanced-rules-registry.json
|
|
1170
|
+
|
|
1171
|
+
# Check required fields
|
|
1172
|
+
node validate-system.js
|
|
1173
|
+
```
|
|
1174
|
+
|
|
1175
|
+
### **Performance Issues**
|
|
1176
|
+
```bash
|
|
1177
|
+
# Profile rule loading
|
|
1178
|
+
node --prof cli.js --rule=C042 --input=large-project/
|
|
1179
|
+
|
|
1180
|
+
# Check memory usage
|
|
1181
|
+
node --inspect cli.js --rule=C042 --input=test/fixtures/
|
|
1182
|
+
```
|
|
213
1183
|
|
|
214
1184
|
## 💡 **Feature Requests**
|
|
215
1185
|
|
|
@@ -220,6 +1190,624 @@ For new features:
|
|
|
220
1190
|
4. Consider implementation complexity
|
|
221
1191
|
5. Think about backward compatibility
|
|
222
1192
|
|
|
1193
|
+
## 📋 **Quick Reference**
|
|
1194
|
+
|
|
1195
|
+
### **Essential Commands**
|
|
1196
|
+
```bash
|
|
1197
|
+
# Add ESLint engine rule mapping
|
|
1198
|
+
vim config/eslint-rule-mapping.json
|
|
1199
|
+
|
|
1200
|
+
# Create custom heuristic rule
|
|
1201
|
+
vim custom-rules/c042-rule-name.js
|
|
1202
|
+
|
|
1203
|
+
# Add rule documentation
|
|
1204
|
+
vim origin-rules/common-en.md
|
|
1205
|
+
|
|
1206
|
+
# Test new rule with specific engine
|
|
1207
|
+
node cli.js --rule=CXXX --engine=eslint --input=test/fixtures
|
|
1208
|
+
node cli.js --rule=CXXX --engine=heuristic --input=test/fixtures
|
|
1209
|
+
|
|
1210
|
+
# Test strict engine mode (no fallback)
|
|
1211
|
+
node cli.js --rule=CXXX --engine=eslint --input=test/fixtures
|
|
1212
|
+
|
|
1213
|
+
# Test fallback mode (auto engine selection)
|
|
1214
|
+
node cli.js --rule=CXXX --input=test/fixtures
|
|
1215
|
+
```
|
|
1216
|
+
|
|
1217
|
+
### **Key Files**
|
|
1218
|
+
- `config/eslint-rule-mapping.json` - **ESLint engine rule mappings**
|
|
1219
|
+
- `origin-rules/` - **Original rule definitions** (markdown format)
|
|
1220
|
+
- `rules/` - **Generated rule registry** (auto-generated)
|
|
1221
|
+
- `core/unified-rule-registry.js` - Rule registry loader
|
|
1222
|
+
- `engines/heuristic-engine.js` - Custom pattern analysis
|
|
1223
|
+
- `engines/eslint-engine.js` - JavaScript/TypeScript linting
|
|
1224
|
+
- `engines/openai-engine.js` - AI-powered analysis
|
|
1225
|
+
- `integrations/eslint/plugin/` - Custom ESLint rules
|
|
1226
|
+
- `custom-rules/` - Heuristic engine custom rules
|
|
1227
|
+
|
|
1228
|
+
### **Rule Development Checklist**
|
|
1229
|
+
- [ ] Choose appropriate engine (ESLint for JS/TS, Heuristic for universal)
|
|
1230
|
+
- [ ] Add ESLint mapping to `config/eslint-rule-mapping.json` (if using ESLint engine)
|
|
1231
|
+
- [ ] Create custom rule implementation (if needed)
|
|
1232
|
+
- [ ] Add rule definition to `origin-rules/` (markdown format)
|
|
1233
|
+
- [ ] Add test cases and examples
|
|
1234
|
+
- [ ] Test with `node cli.js --rule=CXXX --input=test/fixtures`
|
|
1235
|
+
- [ ] Test engine-specific behavior (`--engine=eslint`, `--engine=heuristic`)
|
|
1236
|
+
- [ ] Update documentation if needed
|
|
1237
|
+
|
|
1238
|
+
---
|
|
1239
|
+
|
|
1240
|
+
**🚀 Ready to contribute? Start by choosing your engine and editing the appropriate mapping file!**
|
|
1241
|
+
|
|
1242
|
+
**For ESLint rules**: Edit `config/eslint-rule-mapping.json`
|
|
1243
|
+
**For Heuristic rules**: Create files in `custom-rules/`
|
|
1244
|
+
**For documentation**: Add to `origin-rules/` markdown files
|
|
1245
|
+
|
|
1246
|
+
---
|
|
1247
|
+
|
|
1248
|
+
## 🔬 **Modern Rule Development Guide (2024)**
|
|
1249
|
+
|
|
1250
|
+
*This section provides a comprehensive, step-by-step guide for implementing new rules using the modern hybrid architecture pattern (symbol-based + regex fallback), based on the successful C035 implementation.*
|
|
1251
|
+
|
|
1252
|
+
### **Quick Start: Creating a New Rule**
|
|
1253
|
+
|
|
1254
|
+
#### **Step 1: Create Rule Directory Structure**
|
|
1255
|
+
|
|
1256
|
+
```bash
|
|
1257
|
+
# Example: Creating C042 (No Hardcoded Configuration Values)
|
|
1258
|
+
mkdir -p rules/common/C042_no_hardcoded_config
|
|
1259
|
+
cd rules/common/C042_no_hardcoded_config
|
|
1260
|
+
```
|
|
1261
|
+
|
|
1262
|
+
#### **Step 2: Implement Main Analyzer (Hybrid Orchestrator)**
|
|
1263
|
+
|
|
1264
|
+
```javascript
|
|
1265
|
+
// analyzer.js - Main hybrid orchestrator
|
|
1266
|
+
const SymbolBasedAnalyzer = require('./symbol-based-analyzer');
|
|
1267
|
+
const RegexBasedAnalyzer = require('./regex-based-analyzer');
|
|
1268
|
+
|
|
1269
|
+
class C042NoHardcodedConfigAnalyzer {
|
|
1270
|
+
constructor(verbose = false) {
|
|
1271
|
+
this.verbose = verbose;
|
|
1272
|
+
this.symbolAnalyzer = new SymbolBasedAnalyzer(verbose);
|
|
1273
|
+
this.regexAnalyzer = new RegexBasedAnalyzer(verbose);
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
async analyzeFileBasic(filePath, options = {}) {
|
|
1277
|
+
if (this.verbose) {
|
|
1278
|
+
console.log(`🔍 [C042] Analyzing file: ${filePath}`);
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
try {
|
|
1282
|
+
// Primary: Symbol-based analysis
|
|
1283
|
+
const violations = await this.symbolAnalyzer.analyzeFileBasic(filePath, options);
|
|
1284
|
+
|
|
1285
|
+
if (this.verbose) {
|
|
1286
|
+
console.log(`✅ [C042] Symbol-based analysis completed. Found ${violations.length} violations.`);
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
return violations;
|
|
1290
|
+
} catch (error) {
|
|
1291
|
+
if (this.verbose) {
|
|
1292
|
+
console.warn(`⚠️ [C042] Symbol analysis failed: ${error.message}`);
|
|
1293
|
+
console.log(`🔄 [C042] Falling back to regex analysis...`);
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
// Fallback: Regex-based analysis
|
|
1297
|
+
try {
|
|
1298
|
+
const violations = await this.regexAnalyzer.analyzeFileBasic(filePath, options);
|
|
1299
|
+
|
|
1300
|
+
if (this.verbose) {
|
|
1301
|
+
console.log(`✅ [C042] Regex-fallback analysis completed. Found ${violations.length} violations.`);
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
return violations;
|
|
1305
|
+
} catch (fallbackError) {
|
|
1306
|
+
if (this.verbose) {
|
|
1307
|
+
console.error(`❌ [C042] Both symbol and regex analysis failed: ${fallbackError.message}`);
|
|
1308
|
+
}
|
|
1309
|
+
return [];
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
module.exports = C042NoHardcodedConfigAnalyzer;
|
|
1316
|
+
```
|
|
1317
|
+
|
|
1318
|
+
#### **Step 3: Implement Symbol-Based Analyzer (Primary)**
|
|
1319
|
+
|
|
1320
|
+
```javascript
|
|
1321
|
+
// symbol-based-analyzer.js - High-accuracy AST analysis
|
|
1322
|
+
const { Project } = require('ts-morph');
|
|
1323
|
+
|
|
1324
|
+
class C042SymbolBasedAnalyzer {
|
|
1325
|
+
constructor(verbose = false) {
|
|
1326
|
+
this.verbose = verbose;
|
|
1327
|
+
this.project = new Project({
|
|
1328
|
+
useInMemoryFileSystem: true,
|
|
1329
|
+
compilerOptions: {
|
|
1330
|
+
allowJs: true,
|
|
1331
|
+
checkJs: false,
|
|
1332
|
+
},
|
|
1333
|
+
});
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
async analyzeFileBasic(filePath, options = {}) {
|
|
1337
|
+
const violations = [];
|
|
1338
|
+
|
|
1339
|
+
try {
|
|
1340
|
+
const sourceFile = this.project.addSourceFileAtPath(filePath);
|
|
1341
|
+
|
|
1342
|
+
// Analyze variable declarations
|
|
1343
|
+
sourceFile.getVariableDeclarations().forEach(declaration => {
|
|
1344
|
+
const initializer = declaration.getInitializer();
|
|
1345
|
+
if (initializer && this.isHardcodedConfig(initializer, declaration.getName())) {
|
|
1346
|
+
violations.push({
|
|
1347
|
+
line: declaration.getStartLineNumber(),
|
|
1348
|
+
column: declaration.getStart() - declaration.getStartLinePos() + 1,
|
|
1349
|
+
message: `Hardcoded configuration value detected in variable '${declaration.getName()}'`,
|
|
1350
|
+
severity: 'warning',
|
|
1351
|
+
rule: 'C042'
|
|
1352
|
+
});
|
|
1353
|
+
}
|
|
1354
|
+
});
|
|
1355
|
+
|
|
1356
|
+
// Analyze property assignments
|
|
1357
|
+
sourceFile.getDescendantsOfKind(ts.SyntaxKind.PropertyAssignment).forEach(prop => {
|
|
1358
|
+
const value = prop.getInitializer();
|
|
1359
|
+
if (value && this.isHardcodedConfig(value, prop.getName())) {
|
|
1360
|
+
violations.push({
|
|
1361
|
+
line: prop.getStartLineNumber(),
|
|
1362
|
+
column: prop.getStart() - prop.getStartLinePos() + 1,
|
|
1363
|
+
message: `Hardcoded configuration value in property '${prop.getName()}'`,
|
|
1364
|
+
severity: 'warning',
|
|
1365
|
+
rule: 'C042'
|
|
1366
|
+
});
|
|
1367
|
+
}
|
|
1368
|
+
});
|
|
1369
|
+
|
|
1370
|
+
this.project.removeSourceFile(sourceFile);
|
|
1371
|
+
return violations;
|
|
1372
|
+
} catch (error) {
|
|
1373
|
+
throw new Error(`Symbol analysis failed: ${error.message}`);
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
isHardcodedConfig(node, name) {
|
|
1378
|
+
// Check if the value looks like configuration
|
|
1379
|
+
const configPatterns = [
|
|
1380
|
+
/url/i, /host/i, /port/i, /timeout/i, /secret/i,
|
|
1381
|
+
/api[_-]?key/i, /password/i, /connection/i, /endpoint/i
|
|
1382
|
+
];
|
|
1383
|
+
|
|
1384
|
+
const nameMatches = configPatterns.some(pattern => pattern.test(name));
|
|
1385
|
+
|
|
1386
|
+
if (!nameMatches) return false;
|
|
1387
|
+
|
|
1388
|
+
// Check if value is hardcoded (string/number literal)
|
|
1389
|
+
return node.getKind() === ts.SyntaxKind.StringLiteral ||
|
|
1390
|
+
node.getKind() === ts.SyntaxKind.NumericLiteral;
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
module.exports = C042SymbolBasedAnalyzer;
|
|
1395
|
+
```
|
|
1396
|
+
|
|
1397
|
+
#### **Step 4: Implement Regex-Based Analyzer (Fallback)**
|
|
1398
|
+
|
|
1399
|
+
```javascript
|
|
1400
|
+
// regex-based-analyzer.js - Broad coverage pattern matching
|
|
1401
|
+
const fs = require('fs');
|
|
1402
|
+
|
|
1403
|
+
class C042RegexBasedAnalyzer {
|
|
1404
|
+
constructor(verbose = false) {
|
|
1405
|
+
this.verbose = verbose;
|
|
1406
|
+
|
|
1407
|
+
// Configuration-related patterns
|
|
1408
|
+
this.configPatterns = [
|
|
1409
|
+
/(?:const|let|var)\s+(\w*(?:url|host|port|timeout|secret|key|password|connection|endpoint)\w*)\s*=\s*['"`]([^'"`]+)['"`]/gi,
|
|
1410
|
+
/(\w*(?:url|host|port|timeout|secret|key|password|connection|endpoint)\w*)\s*:\s*['"`]([^'"`]+)['"`]/gi,
|
|
1411
|
+
/(\w*(?:url|host|port|timeout|secret|key|password|connection|endpoint)\w*)\s*=\s*(\d+)/gi
|
|
1412
|
+
];
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
async analyzeFileBasic(filePath, options = {}) {
|
|
1416
|
+
const violations = [];
|
|
1417
|
+
|
|
1418
|
+
try {
|
|
1419
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
1420
|
+
const lines = content.split('\n');
|
|
1421
|
+
|
|
1422
|
+
this.configPatterns.forEach(pattern => {
|
|
1423
|
+
lines.forEach((line, index) => {
|
|
1424
|
+
let match;
|
|
1425
|
+
while ((match = pattern.exec(line)) !== null) {
|
|
1426
|
+
const variableName = match[1];
|
|
1427
|
+
const value = match[2];
|
|
1428
|
+
|
|
1429
|
+
if (this.isLikelyHardcodedConfig(variableName, value)) {
|
|
1430
|
+
violations.push({
|
|
1431
|
+
line: index + 1,
|
|
1432
|
+
column: match.index + 1,
|
|
1433
|
+
message: `Potential hardcoded configuration: '${variableName}' = '${value}'`,
|
|
1434
|
+
severity: 'warning',
|
|
1435
|
+
rule: 'C042'
|
|
1436
|
+
});
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
pattern.lastIndex = 0; // Reset regex
|
|
1440
|
+
});
|
|
1441
|
+
});
|
|
1442
|
+
|
|
1443
|
+
return violations;
|
|
1444
|
+
} catch (error) {
|
|
1445
|
+
throw new Error(`Regex analysis failed: ${error.message}`);
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
isLikelyHardcodedConfig(name, value) {
|
|
1450
|
+
// Skip obvious non-config values
|
|
1451
|
+
const skipPatterns = [
|
|
1452
|
+
/^(true|false|null|undefined)$/i,
|
|
1453
|
+
/^process\.env\./i,
|
|
1454
|
+
/^require\(/i,
|
|
1455
|
+
/^\w+\(/i // Function calls
|
|
1456
|
+
];
|
|
1457
|
+
|
|
1458
|
+
return !skipPatterns.some(pattern => pattern.test(value));
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1462
|
+
module.exports = C042RegexBasedAnalyzer;
|
|
1463
|
+
```
|
|
1464
|
+
|
|
1465
|
+
#### **Step 5: Create Rule Configuration**
|
|
1466
|
+
|
|
1467
|
+
```json
|
|
1468
|
+
// config.json - Rule metadata and examples
|
|
1469
|
+
{
|
|
1470
|
+
"id": "C042",
|
|
1471
|
+
"name": "No Hardcoded Configuration Values",
|
|
1472
|
+
"category": "Maintainability",
|
|
1473
|
+
"severity": "warning",
|
|
1474
|
+
"description": "Detects hardcoded configuration values that should use environment variables or config files",
|
|
1475
|
+
"rationale": "Hardcoded configuration makes applications less flexible and harder to deploy across different environments",
|
|
1476
|
+
"examples": {
|
|
1477
|
+
"bad": [
|
|
1478
|
+
"const API_URL = 'https://api.example.com';",
|
|
1479
|
+
"const DB_HOST = 'localhost';",
|
|
1480
|
+
"const MAX_CONNECTIONS = 100;",
|
|
1481
|
+
"config: { port: 3000, host: 'localhost' }"
|
|
1482
|
+
],
|
|
1483
|
+
"good": [
|
|
1484
|
+
"const API_URL = process.env.API_URL;",
|
|
1485
|
+
"const DB_HOST = process.env.DB_HOST || 'localhost';",
|
|
1486
|
+
"const config = loadConfigFromFile();",
|
|
1487
|
+
"const PORT = parseInt(process.env.PORT || '3000');"
|
|
1488
|
+
]
|
|
1489
|
+
},
|
|
1490
|
+
"tags": ["configuration", "deployment", "security", "maintainability"],
|
|
1491
|
+
"accuracy": {
|
|
1492
|
+
"symbol_based": "high",
|
|
1493
|
+
"regex_based": "medium"
|
|
1494
|
+
},
|
|
1495
|
+
"performance": {
|
|
1496
|
+
"symbol_based": "medium",
|
|
1497
|
+
"regex_based": "fast"
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
```
|
|
1501
|
+
|
|
1502
|
+
#### **Step 6: Document Analysis Strategy**
|
|
1503
|
+
|
|
1504
|
+
```markdown
|
|
1505
|
+
<!-- STRATEGY.md - Analysis approach documentation -->
|
|
1506
|
+
# C042: No Hardcoded Configuration Values - Analysis Strategy
|
|
1507
|
+
|
|
1508
|
+
## Overview
|
|
1509
|
+
This rule detects hardcoded configuration values using a hybrid approach combining symbol-based AST analysis with regex-based pattern matching.
|
|
1510
|
+
|
|
1511
|
+
## Analysis Approaches
|
|
1512
|
+
|
|
1513
|
+
### Symbol-Based Analysis (Primary)
|
|
1514
|
+
- **Tool**: ts-morph AST parser
|
|
1515
|
+
- **Accuracy**: High (95%+)
|
|
1516
|
+
- **Coverage**: Variable declarations, property assignments
|
|
1517
|
+
- **Strengths**:
|
|
1518
|
+
- Understands code context
|
|
1519
|
+
- Low false positives
|
|
1520
|
+
- Handles complex expressions
|
|
1521
|
+
- **Limitations**:
|
|
1522
|
+
- Requires valid syntax
|
|
1523
|
+
- Slower performance
|
|
1524
|
+
|
|
1525
|
+
### Regex-Based Analysis (Fallback)
|
|
1526
|
+
- **Tool**: Regular expressions
|
|
1527
|
+
- **Accuracy**: Medium (80%+)
|
|
1528
|
+
- **Coverage**: String/number patterns in config-like names
|
|
1529
|
+
- **Strengths**:
|
|
1530
|
+
- Fast execution
|
|
1531
|
+
- Works with any file
|
|
1532
|
+
- Broad pattern coverage
|
|
1533
|
+
- **Limitations**:
|
|
1534
|
+
- Higher false positives
|
|
1535
|
+
- Limited context understanding
|
|
1536
|
+
|
|
1537
|
+
## Detection Patterns
|
|
1538
|
+
|
|
1539
|
+
### Configuration Indicators
|
|
1540
|
+
- Variable/property names containing: url, host, port, timeout, secret, key, password, connection, endpoint
|
|
1541
|
+
- Literal string/number values
|
|
1542
|
+
- Assignment patterns
|
|
1543
|
+
|
|
1544
|
+
### Exclusions
|
|
1545
|
+
- Environment variable references (process.env.*)
|
|
1546
|
+
- Function calls
|
|
1547
|
+
- Boolean/null literals
|
|
1548
|
+
- Computed values
|
|
1549
|
+
|
|
1550
|
+
## Testing Strategy
|
|
1551
|
+
- Unit tests for each analyzer
|
|
1552
|
+
- Integration tests for hybrid orchestration
|
|
1553
|
+
- Real-world project validation
|
|
1554
|
+
- Performance benchmarking
|
|
1555
|
+
```
|
|
1556
|
+
|
|
1557
|
+
#### **Step 7: Add to Rules Registry**
|
|
1558
|
+
|
|
1559
|
+
```json
|
|
1560
|
+
// config/rules/rules-registry.json - Add rule definition
|
|
1561
|
+
{
|
|
1562
|
+
"C042": {
|
|
1563
|
+
"id": "C042",
|
|
1564
|
+
"name": "No Hardcoded Configuration Values",
|
|
1565
|
+
"category": "Maintainability",
|
|
1566
|
+
"severity": "warning",
|
|
1567
|
+
"description": "Detects hardcoded configuration values that should use environment variables or config files",
|
|
1568
|
+
"engine": "heuristic",
|
|
1569
|
+
"analyzerPath": "rules/common/C042_no_hardcoded_config/analyzer.js",
|
|
1570
|
+
"configPath": "rules/common/C042_no_hardcoded_config/config.json",
|
|
1571
|
+
"enabled": true,
|
|
1572
|
+
"languages": ["javascript", "typescript"],
|
|
1573
|
+
"tags": ["configuration", "deployment", "security"]
|
|
1574
|
+
}
|
|
1575
|
+
}
|
|
1576
|
+
```
|
|
1577
|
+
|
|
1578
|
+
#### **Step 8: Register with Enhanced Rules Registry**
|
|
1579
|
+
|
|
1580
|
+
```javascript
|
|
1581
|
+
// core/enhanced-rules-registry.js - Add engine preferences
|
|
1582
|
+
const enginePreferences = {
|
|
1583
|
+
// ... existing rules
|
|
1584
|
+
'C042': ['heuristic'], // Add your rule here
|
|
1585
|
+
};
|
|
1586
|
+
```
|
|
1587
|
+
|
|
1588
|
+
#### **Step 9: Add to Analysis Strategies**
|
|
1589
|
+
|
|
1590
|
+
```javascript
|
|
1591
|
+
// config/rule-analysis-strategies.js - Add to AST preferred rules
|
|
1592
|
+
const strategies = {
|
|
1593
|
+
astPreferred: [
|
|
1594
|
+
// ... existing rules
|
|
1595
|
+
'C042', // Add your rule here
|
|
1596
|
+
],
|
|
1597
|
+
regexOnly: [
|
|
1598
|
+
// Rules that only use regex
|
|
1599
|
+
],
|
|
1600
|
+
hybridOnly: [
|
|
1601
|
+
// Rules requiring both approaches
|
|
1602
|
+
]
|
|
1603
|
+
};
|
|
1604
|
+
```
|
|
1605
|
+
|
|
1606
|
+
#### **Step 10: Create Test Script for Direct Testing**
|
|
1607
|
+
|
|
1608
|
+
```javascript
|
|
1609
|
+
// test-c042-direct.js - Direct analyzer testing
|
|
1610
|
+
const C042Analyzer = require('./rules/common/C042_no_hardcoded_config/analyzer');
|
|
1611
|
+
|
|
1612
|
+
async function testC042() {
|
|
1613
|
+
const analyzer = new C042Analyzer(true); // Enable verbose mode
|
|
1614
|
+
|
|
1615
|
+
try {
|
|
1616
|
+
const violations = await analyzer.analyzeFileBasic('test-files/c042-test.js');
|
|
1617
|
+
console.log(`\n📊 C042 Analysis Results:`);
|
|
1618
|
+
console.log(` Found ${violations.length} violations`);
|
|
1619
|
+
|
|
1620
|
+
violations.forEach((violation, index) => {
|
|
1621
|
+
console.log(` ${index + 1}. Line ${violation.line}: ${violation.message}`);
|
|
1622
|
+
});
|
|
1623
|
+
} catch (error) {
|
|
1624
|
+
console.error('❌ C042 test failed:', error.message);
|
|
1625
|
+
}
|
|
1626
|
+
}
|
|
1627
|
+
|
|
1628
|
+
testC042();
|
|
1629
|
+
```
|
|
1630
|
+
|
|
1631
|
+
#### **Step 11: Create Test File**
|
|
1632
|
+
|
|
1633
|
+
```javascript
|
|
1634
|
+
// test-files/c042-test.js
|
|
1635
|
+
/**
|
|
1636
|
+
* Test file for C042 No Hardcoded Configuration Values rule
|
|
1637
|
+
* Contains various configuration patterns to validate analyzer
|
|
1638
|
+
*/
|
|
1639
|
+
|
|
1640
|
+
// Good Examples: Using environment variables
|
|
1641
|
+
const API_URL = process.env.API_URL;
|
|
1642
|
+
const MAX_RETRIES = parseInt(process.env.MAX_RETRIES || '3');
|
|
1643
|
+
const config = loadConfigFromFile();
|
|
1644
|
+
|
|
1645
|
+
// Bad Examples: Hardcoded values (should trigger violations)
|
|
1646
|
+
const DATABASE_URL = 'mongodb://localhost:27017/myapp';
|
|
1647
|
+
const API_SECRET = 'sk-1234567890abcdef';
|
|
1648
|
+
const MAX_CONNECTIONS = 100;
|
|
1649
|
+
const TIMEOUT_MS = 5000;
|
|
1650
|
+
|
|
1651
|
+
// Edge Cases
|
|
1652
|
+
const DEBUG_MODE = true; // Should not trigger (not config pattern)
|
|
1653
|
+
const DEFAULT_CONFIG = { // Should trigger for nested hardcoded values
|
|
1654
|
+
port: 3000,
|
|
1655
|
+
host: 'localhost'
|
|
1656
|
+
};
|
|
1657
|
+
|
|
1658
|
+
module.exports = {
|
|
1659
|
+
goodConfig,
|
|
1660
|
+
badConfig
|
|
1661
|
+
};
|
|
1662
|
+
```
|
|
1663
|
+
|
|
1664
|
+
#### **Step 12: Test Your Rule**
|
|
1665
|
+
|
|
1666
|
+
```bash
|
|
1667
|
+
# Test the new rule
|
|
1668
|
+
node cli.js --rule=C042 --input=test-files/c042-test.js --verbose --engine=heuristic
|
|
1669
|
+
|
|
1670
|
+
# Expected output: Should detect 4-5 violations for hardcoded config values
|
|
1671
|
+
|
|
1672
|
+
# Test rule in real projects
|
|
1673
|
+
node cli.js --rule=C042 --input=src --engine=heuristic
|
|
1674
|
+
|
|
1675
|
+
# Test with different engines
|
|
1676
|
+
node cli.js --rule=C042 --input=test-files/c042-test.js --engine=openai
|
|
1677
|
+
```
|
|
1678
|
+
|
|
1679
|
+
#### **Step 13: Validate Integration**
|
|
1680
|
+
|
|
1681
|
+
```bash
|
|
1682
|
+
# Check rule is loaded
|
|
1683
|
+
node cli.js --rule=C042 --input=test-files/c042-test.js --verbose | grep "Loaded.*rule: C042"
|
|
1684
|
+
|
|
1685
|
+
# Verify both analyzers work
|
|
1686
|
+
node cli.js --rule=C042 --input=test-files/c042-test.js --verbose | grep -E "(Symbol-based|Regex-fallback)"
|
|
1687
|
+
|
|
1688
|
+
# Check for violations
|
|
1689
|
+
node cli.js --rule=C042 --input=test-files/c042-test.js --format=json | jq '.results[].violations | length'
|
|
1690
|
+
```
|
|
1691
|
+
|
|
1692
|
+
### **Modern Rule Development Best Practices**
|
|
1693
|
+
|
|
1694
|
+
#### **Architecture Patterns**
|
|
1695
|
+
|
|
1696
|
+
1. **Hybrid Symbol + Regex Approach** (Recommended)
|
|
1697
|
+
- Primary: Symbol-based analysis (high accuracy)
|
|
1698
|
+
- Fallback: Regex-based analysis (broad coverage)
|
|
1699
|
+
- Example: C035, C033
|
|
1700
|
+
|
|
1701
|
+
2. **Symbol-Only Approach**
|
|
1702
|
+
- For complex AST analysis requiring deep context
|
|
1703
|
+
- Higher accuracy but limited fallback
|
|
1704
|
+
- Example: Advanced OOP rules
|
|
1705
|
+
|
|
1706
|
+
3. **Regex-Only Approach**
|
|
1707
|
+
- For simple pattern matching
|
|
1708
|
+
- Fast but lower accuracy
|
|
1709
|
+
- Example: Basic naming conventions
|
|
1710
|
+
|
|
1711
|
+
#### **Code Quality Standards for Rules**
|
|
1712
|
+
|
|
1713
|
+
- **Rule C005**: Each analyzer should have single responsibility
|
|
1714
|
+
- **Rule C006**: Method names should be verb-noun (e.g., `analyzeVariableDeclaration`)
|
|
1715
|
+
- **Rule C031**: Keep validation logic separate from detection logic
|
|
1716
|
+
- **Rule C035**: Log detailed information for debugging (verbose mode)
|
|
1717
|
+
|
|
1718
|
+
#### **File Structure Standards**
|
|
1719
|
+
|
|
1720
|
+
```
|
|
1721
|
+
rules/common/C042_rule_name/
|
|
1722
|
+
├── analyzer.js # Main hybrid orchestrator
|
|
1723
|
+
├── symbol-based-analyzer.js # Primary AST analysis
|
|
1724
|
+
├── regex-based-analyzer.js # Fallback pattern matching
|
|
1725
|
+
├── config.json # Rule configuration
|
|
1726
|
+
├── STRATEGY.md # Analysis approach documentation
|
|
1727
|
+
└── README.md # Rule documentation (optional)
|
|
1728
|
+
```
|
|
1729
|
+
|
|
1730
|
+
#### **Error Handling Standards**
|
|
1731
|
+
|
|
1732
|
+
```javascript
|
|
1733
|
+
// Always include proper error handling
|
|
1734
|
+
try {
|
|
1735
|
+
const violations = await this.symbolAnalyzer.analyzeFileBasic(filePath, options);
|
|
1736
|
+
return violations;
|
|
1737
|
+
} catch (error) {
|
|
1738
|
+
if (this.verbose) {
|
|
1739
|
+
console.warn(`⚠️ [C042] Symbol analysis failed: ${error.message}`);
|
|
1740
|
+
}
|
|
1741
|
+
// Graceful fallback to regex analysis
|
|
1742
|
+
}
|
|
1743
|
+
```
|
|
1744
|
+
|
|
1745
|
+
#### **Testing Standards**
|
|
1746
|
+
|
|
1747
|
+
1. **Unit Tests**: Test each analyzer independently
|
|
1748
|
+
2. **Integration Tests**: Test hybrid orchestration
|
|
1749
|
+
3. **Real Project Tests**: Validate on actual codebases
|
|
1750
|
+
4. **Performance Tests**: Ensure reasonable execution time
|
|
1751
|
+
|
|
1752
|
+
#### **Documentation Standards**
|
|
1753
|
+
|
|
1754
|
+
- **STRATEGY.md**: Document analysis approach and accuracy
|
|
1755
|
+
- **config.json**: Include examples of good/bad patterns
|
|
1756
|
+
- **CONTRIBUTING.md**: Update with new patterns learned
|
|
1757
|
+
- **Inline comments**: Explain complex detection logic
|
|
1758
|
+
|
|
1759
|
+
### **Rule Development Checklist**
|
|
1760
|
+
|
|
1761
|
+
- [ ] Created rule directory structure
|
|
1762
|
+
- [ ] Implemented main analyzer (hybrid orchestrator)
|
|
1763
|
+
- [ ] Implemented symbol-based analyzer (primary)
|
|
1764
|
+
- [ ] Implemented regex-based analyzer (fallback)
|
|
1765
|
+
- [ ] Created rule configuration (config.json)
|
|
1766
|
+
- [ ] Documented analysis strategy (STRATEGY.md)
|
|
1767
|
+
- [ ] Added to rules registry
|
|
1768
|
+
- [ ] Added to enhanced rules registry
|
|
1769
|
+
- [ ] Added to analysis strategies
|
|
1770
|
+
- [ ] Created test file
|
|
1771
|
+
- [ ] Tested rule individually
|
|
1772
|
+
- [ ] Tested rule integration
|
|
1773
|
+
- [ ] Validated on real projects
|
|
1774
|
+
- [ ] Updated documentation
|
|
1775
|
+
|
|
1776
|
+
### **Common Issues and Solutions**
|
|
1777
|
+
|
|
1778
|
+
#### **Issue: Rule not loaded**
|
|
1779
|
+
```bash
|
|
1780
|
+
# Check if rule exists in registry
|
|
1781
|
+
node cli.js --rule=C042 --input=test-files --verbose | grep "No compatible analyzer found for C042"
|
|
1782
|
+
|
|
1783
|
+
# Solution: Add rule to config/rules/rules-registry.json
|
|
1784
|
+
```
|
|
1785
|
+
|
|
1786
|
+
#### **Issue: Symbol analysis fails**
|
|
1787
|
+
```bash
|
|
1788
|
+
# Check semantic engine availability
|
|
1789
|
+
node cli.js --rule=C042 --input=test-files --verbose | grep "Symbol analysis failed"
|
|
1790
|
+
|
|
1791
|
+
# Solution: Ensure proper error handling and regex fallback
|
|
1792
|
+
```
|
|
1793
|
+
|
|
1794
|
+
#### **Issue: No violations detected**
|
|
1795
|
+
```bash
|
|
1796
|
+
# Test analyzers independently
|
|
1797
|
+
node test-c042-direct.js
|
|
1798
|
+
|
|
1799
|
+
# Check file patterns match expectations
|
|
1800
|
+
```
|
|
1801
|
+
|
|
1802
|
+
#### **Issue: Too many false positives**
|
|
1803
|
+
```bash
|
|
1804
|
+
# Refine detection patterns
|
|
1805
|
+
# Add exclusion rules
|
|
1806
|
+
# Test on diverse codebases
|
|
1807
|
+
```
|
|
1808
|
+
|
|
1809
|
+
---
|
|
1810
|
+
|
|
223
1811
|
## 🤝 **Community**
|
|
224
1812
|
|
|
225
1813
|
- **Discord**: [Sun Engineering Discord](https://discord.gg/sun-engineering)
|