@sun-asterisk/sunlint 1.3.0 → 1.3.2
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 +115 -1
- package/CONTRIBUTING.md +249 -605
- package/README.md +3 -4
- package/config/ci-cd.json +54 -0
- package/config/development.json +56 -0
- 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 +38 -3
- package/config/rules/enhanced-rules-registry.json +474 -1179
- package/config/rules/rules-registry-generated.json +3 -3
- package/core/cli-action-handler.js +24 -30
- package/core/cli-program.js +11 -3
- package/core/config-merger.js +29 -2
- package/core/enhanced-rules-registry.js +3 -2
- package/core/semantic-engine.js +129 -19
- package/core/semantic-rule-base.js +4 -2
- package/core/unified-rule-registry.js +1 -1
- package/docs/COMMAND-EXAMPLES.md +134 -0
- package/docs/LARGE-PROJECT-GUIDE.md +324 -0
- package/engines/heuristic-engine.js +135 -16
- package/integrations/eslint/plugin/index.js +0 -2
- package/integrations/eslint/plugin/rules/common/c003-no-vague-abbreviations.js +59 -1
- package/integrations/eslint/plugin/rules/common/c006-function-name-verb-noun.js +26 -1
- package/integrations/eslint/plugin/rules/common/c030-use-custom-error-classes.js +54 -19
- package/origin-rules/common-en.md +19 -15
- package/package.json +1 -1
- package/rules/common/C002_no_duplicate_code/analyzer.js +334 -36
- package/rules/common/C003_no_vague_abbreviations/analyzer.js +220 -35
- package/rules/common/C006_function_naming/analyzer.js +29 -3
- package/rules/common/C010_limit_block_nesting/analyzer.js +181 -337
- package/rules/common/C010_limit_block_nesting/config.json +64 -0
- package/rules/common/C010_limit_block_nesting/regex-based-analyzer.js +379 -0
- package/rules/common/C010_limit_block_nesting/symbol-based-analyzer.js +231 -0
- package/rules/common/C013_no_dead_code/analyzer.js +75 -177
- package/rules/common/C013_no_dead_code/config.json +61 -0
- package/rules/common/C013_no_dead_code/regex-based-analyzer.js +345 -0
- package/rules/common/C013_no_dead_code/symbol-based-analyzer.js +640 -0
- package/rules/common/C014_dependency_injection/analyzer.js +48 -313
- package/rules/common/C014_dependency_injection/config.json +26 -0
- package/rules/common/C014_dependency_injection/symbol-based-analyzer.js +751 -0
- package/rules/common/C017_constructor_logic/analyzer.js +254 -17
- package/rules/common/C017_constructor_logic/semantic-analyzer.js +340 -0
- package/rules/common/C018_no_throw_generic_error/analyzer.js +232 -0
- package/rules/common/C018_no_throw_generic_error/config.json +50 -0
- package/rules/common/C018_no_throw_generic_error/regex-based-analyzer.js +387 -0
- package/rules/common/C018_no_throw_generic_error/symbol-based-analyzer.js +314 -0
- package/rules/common/C019_log_level_usage/analyzer.js +110 -317
- package/rules/common/C019_log_level_usage/pattern-analyzer.js +88 -0
- package/rules/common/C019_log_level_usage/system-log-analyzer.js +1267 -0
- package/rules/common/C023_no_duplicate_variable/analyzer.js +180 -0
- package/rules/common/C023_no_duplicate_variable/config.json +50 -0
- package/rules/common/C023_no_duplicate_variable/symbol-based-analyzer.js +158 -0
- package/rules/common/C024_no_scatter_hardcoded_constants/analyzer.js +180 -0
- package/rules/common/C024_no_scatter_hardcoded_constants/config.json +50 -0
- package/rules/common/C024_no_scatter_hardcoded_constants/symbol-based-analyzer.js +181 -0
- package/rules/common/C030_use_custom_error_classes/analyzer.js +200 -0
- 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 +232 -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/{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 +6 -1
- 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/S009_no_insecure_encryption/README.md +158 -0
- package/rules/security/S009_no_insecure_encryption/analyzer.js +319 -0
- package/rules/security/S009_no_insecure_encryption/config.json +55 -0
- package/rules/security/S010_no_insecure_encryption/README.md +224 -0
- package/rules/security/S010_no_insecure_encryption/analyzer.js +493 -0
- package/rules/security/S010_no_insecure_encryption/config.json +48 -0
- package/rules/security/S016_no_sensitive_querystring/STRATEGY.md +149 -0
- package/rules/security/S016_no_sensitive_querystring/analyzer.js +276 -0
- package/rules/security/S016_no_sensitive_querystring/config.json +127 -0
- package/rules/security/S016_no_sensitive_querystring/regex-based-analyzer.js +258 -0
- package/rules/security/S016_no_sensitive_querystring/symbol-based-analyzer.js +495 -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/rules/security/S048_no_current_password_in_reset/README.md +222 -0
- package/rules/security/S048_no_current_password_in_reset/analyzer.js +366 -0
- package/rules/security/S048_no_current_password_in_reset/config.json +48 -0
- package/rules/security/S055_content_type_validation/README.md +176 -0
- package/rules/security/S055_content_type_validation/analyzer.js +312 -0
- package/rules/security/S055_content_type_validation/config.json +48 -0
- package/rules/utils/rule-helpers.js +140 -1
- package/scripts/consolidate-config.js +116 -0
- package/scripts/prepare-release.sh +1 -1
- package/config/rules/rules-registry.json +0 -765
- 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/integrations/eslint/plugin/rules/common/c076-single-behavior-per-test.js +0 -254
- package/rules/common/C006_function_naming/smart-analyzer.js +0 -503
|
@@ -1,338 +1,73 @@
|
|
|
1
|
-
const
|
|
2
|
-
const path = require('path');
|
|
1
|
+
const C014SymbolBasedAnalyzer = require('./symbol-based-analyzer.js');
|
|
3
2
|
|
|
4
3
|
class C014Analyzer {
|
|
5
|
-
constructor() {
|
|
4
|
+
constructor(semanticEngine = null) {
|
|
6
5
|
this.ruleId = 'C014';
|
|
7
6
|
this.ruleName = 'Dependency Injection Pattern';
|
|
8
|
-
this.description = '
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
async analyze(files, language, options = {}) {
|
|
12
|
-
const violations = [];
|
|
13
|
-
|
|
14
|
-
for (const filePath of files) {
|
|
15
|
-
if (options.verbose) {
|
|
16
|
-
console.log(`🔍 Running C014 analysis on ${path.basename(filePath)}`);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
try {
|
|
20
|
-
const content = fs.readFileSync(filePath, 'utf8');
|
|
21
|
-
const fileViolations = await this.analyzeFile(filePath, content, language, options);
|
|
22
|
-
violations.push(...fileViolations);
|
|
23
|
-
} catch (error) {
|
|
24
|
-
console.warn(`⚠️ Failed to analyze ${filePath}: ${error.message}`);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
7
|
+
this.description = 'Use Dependency Injection instead of direct instantiation in business logic';
|
|
8
|
+
this.semanticEngine = semanticEngine;
|
|
9
|
+
this.verbose = false;
|
|
27
10
|
|
|
28
|
-
|
|
11
|
+
// Use symbol-based only for accuracy
|
|
12
|
+
this.symbolAnalyzer = new C014SymbolBasedAnalyzer(semanticEngine);
|
|
29
13
|
}
|
|
30
14
|
|
|
31
|
-
async
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
case 'javascript':
|
|
35
|
-
return this.analyzeTypeScript(filePath, content, config);
|
|
36
|
-
default:
|
|
37
|
-
return [];
|
|
15
|
+
async initialize(semanticEngine = null) {
|
|
16
|
+
if (semanticEngine) {
|
|
17
|
+
this.semanticEngine = semanticEngine;
|
|
38
18
|
}
|
|
19
|
+
this.verbose = semanticEngine?.verbose || false;
|
|
20
|
+
await this.symbolAnalyzer.initialize(semanticEngine);
|
|
39
21
|
}
|
|
40
22
|
|
|
41
|
-
async
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
// Skip comments and imports
|
|
50
|
-
if (this.isCommentOrImport(trimmedLine)) {
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// Find new expressions
|
|
55
|
-
const newExpressions = this.findNewExpressions(trimmedLine, line);
|
|
56
|
-
|
|
57
|
-
newExpressions.forEach(expr => {
|
|
58
|
-
if (this.shouldFlag(expr.className, line, filePath)) {
|
|
59
|
-
violations.push({
|
|
60
|
-
ruleId: this.ruleId,
|
|
61
|
-
file: filePath,
|
|
62
|
-
line: lineNumber,
|
|
63
|
-
column: expr.column,
|
|
64
|
-
message: `Avoid direct instantiation of '${expr.className}'. Use dependency injection or factory pattern instead`,
|
|
65
|
-
severity: 'warning',
|
|
66
|
-
code: trimmedLine,
|
|
67
|
-
type: 'direct_instantiation',
|
|
68
|
-
confidence: expr.confidence,
|
|
69
|
-
suggestion: `Consider injecting ${expr.className} as a dependency or using a factory`
|
|
70
|
-
});
|
|
23
|
+
async analyzeFileBasic(filePath, options = {}) {
|
|
24
|
+
try {
|
|
25
|
+
// Check if symbol engine is ready
|
|
26
|
+
if (!this.semanticEngine?.isSymbolEngineReady?.() || !this.semanticEngine.project) {
|
|
27
|
+
const errorMsg = 'Symbol engine required for C014 analysis - consider enabling semantic analysis';
|
|
28
|
+
if (this.verbose) {
|
|
29
|
+
console.log(`[DEBUG] ❌ C014: ${errorMsg} for ${filePath.split('/').pop()}`);
|
|
71
30
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
return violations;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
isCommentOrImport(line) {
|
|
79
|
-
const trimmed = line.trim();
|
|
80
|
-
return trimmed.startsWith('//') ||
|
|
81
|
-
trimmed.startsWith('/*') ||
|
|
82
|
-
trimmed.startsWith('*') ||
|
|
83
|
-
trimmed.startsWith('import ') ||
|
|
84
|
-
trimmed.startsWith('export ');
|
|
85
|
-
}
|
|
31
|
+
throw new Error(errorMsg);
|
|
32
|
+
}
|
|
86
33
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
let match;
|
|
92
|
-
while ((match = regex.exec(line)) !== null) {
|
|
93
|
-
expressions.push({
|
|
94
|
-
className: match[1],
|
|
95
|
-
column: match.index + 1,
|
|
96
|
-
confidence: this.calculateConfidence(match[1], line)
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
return expressions;
|
|
101
|
-
}
|
|
34
|
+
if (this.verbose) {
|
|
35
|
+
console.log(`[DEBUG] 🎯 C014: Using symbol-based analysis for ${filePath.split('/').pop()}`);
|
|
36
|
+
}
|
|
102
37
|
|
|
103
|
-
|
|
104
|
-
// Don't flag built-in JavaScript/TypeScript classes and common patterns
|
|
105
|
-
const allowedClasses = [
|
|
106
|
-
// Built-in JavaScript classes
|
|
107
|
-
'Date', 'Array', 'Object', 'String', 'Number', 'Boolean', 'RegExp',
|
|
108
|
-
'Map', 'Set', 'WeakMap', 'WeakSet', 'Promise', 'Error', 'TypeError',
|
|
109
|
-
'RangeError', 'SyntaxError', 'ReferenceError', 'EvalError', 'URIError',
|
|
110
|
-
'Function', 'Symbol', 'BigInt', 'ArrayBuffer', 'DataView',
|
|
111
|
-
'Int8Array', 'Uint8Array', 'Uint8ClampedArray', 'Int16Array', 'Uint16Array',
|
|
112
|
-
'Int32Array', 'Uint32Array', 'Float32Array', 'Float64Array',
|
|
113
|
-
|
|
114
|
-
// DOM classes (Frontend)
|
|
115
|
-
'FormData', 'Headers', 'Request', 'Response', 'URLSearchParams',
|
|
116
|
-
'URL', 'Blob', 'File', 'FileReader', 'Image', 'Audio', 'Video',
|
|
117
|
-
'XMLHttpRequest', 'WebSocket', 'Worker', 'MessageChannel',
|
|
118
|
-
'MutationObserver', 'ResizeObserver', 'IntersectionObserver',
|
|
119
|
-
'AbortController', 'CustomEvent', 'Event', 'ErrorEvent',
|
|
120
|
-
|
|
121
|
-
// Node.js classes
|
|
122
|
-
'Buffer', 'URL', 'URLSearchParams', 'ReadableStream', 'WritableStream',
|
|
38
|
+
const violations = await this.symbolAnalyzer.analyzeFileBasic(filePath, options);
|
|
123
39
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
// Common library classes that are okay to instantiate directly
|
|
128
|
-
'Axios', 'HttpClient', 'Logger', 'Configuration', 'Config', 'AppConfig', 'UserSettings',
|
|
129
|
-
'AppConfiguration', 'SystemLogger', 'Cache', 'Monitor', 'Connection', 'DatabaseConfig'
|
|
130
|
-
];
|
|
131
|
-
|
|
132
|
-
if (allowedClasses.includes(className)) {
|
|
133
|
-
return false;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// Don't flag if it's clearly for testing
|
|
137
|
-
if (this.isTestContext(line, filePath)) {
|
|
138
|
-
return false;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// Don't flag if it's in React hooks or state management
|
|
142
|
-
if (this.isReactHookContext(line)) {
|
|
143
|
-
return false;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Don't flag if it's in a factory pattern or builder pattern
|
|
147
|
-
if (this.isFactoryOrBuilderPattern(line)) {
|
|
148
|
-
return false;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Don't flag if it's a utility class or static helper
|
|
152
|
-
if (this.isUtilityClass(className, line)) {
|
|
153
|
-
return false;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// Don't flag if it's in constructor or initialization context
|
|
157
|
-
if (this.isConstructorOrInitContext(line)) {
|
|
158
|
-
return false;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// Don't flag if it's immediate usage pattern (e.g., new Date().getTime())
|
|
162
|
-
if (this.isImmediateUsagePattern(line)) {
|
|
163
|
-
return false;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
return true;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
isTestContext(line, filePath) {
|
|
170
|
-
// Check if file is a test file
|
|
171
|
-
const testPatterns = ['.test.', '.spec.', '__tests__', '/tests/'];
|
|
172
|
-
const isTestFile = testPatterns.some(pattern => filePath.includes(pattern));
|
|
173
|
-
|
|
174
|
-
if (isTestFile) {
|
|
175
|
-
return true;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// Check if line contains test-related keywords - use word boundaries to avoid false matches
|
|
179
|
-
const testKeywords = ['test', 'describe', 'expect', 'mock', 'spy', 'stub'];
|
|
180
|
-
const lowerLine = line.toLowerCase();
|
|
181
|
-
|
|
182
|
-
// Use word boundaries to avoid false positives like "Repository" containing "it"
|
|
183
|
-
const hasTestKeywords = testKeywords.some(keyword => {
|
|
184
|
-
const wordBoundaryRegex = new RegExp(`\\b${keyword}\\b`);
|
|
185
|
-
return wordBoundaryRegex.test(lowerLine);
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
// Special case for "it" - only match as standalone word or in specific contexts
|
|
189
|
-
const hasItKeyword = /\bit\s*\(/.test(lowerLine); // it( function calls
|
|
190
|
-
|
|
191
|
-
return hasTestKeywords || hasItKeyword;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
isFactoryOrBuilderPattern(line) {
|
|
195
|
-
const lowerLine = line.toLowerCase();
|
|
196
|
-
const patterns = [
|
|
197
|
-
'factory',
|
|
198
|
-
'builder',
|
|
199
|
-
'create',
|
|
200
|
-
'build',
|
|
201
|
-
'make',
|
|
202
|
-
'construct',
|
|
203
|
-
'getinstance',
|
|
204
|
-
'newinstance',
|
|
205
|
-
'=> new',
|
|
206
|
-
'return new'
|
|
207
|
-
];
|
|
208
|
-
|
|
209
|
-
return patterns.some(pattern => lowerLine.includes(pattern));
|
|
210
|
-
}
|
|
40
|
+
if (this.verbose) {
|
|
41
|
+
console.log(`[DEBUG] 🎯 C014: Symbol-based analysis found ${violations.length} violations`);
|
|
42
|
+
}
|
|
211
43
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
/Manager$/,
|
|
219
|
-
/Processor$/,
|
|
220
|
-
/Validator$/,
|
|
221
|
-
/Parser$/,
|
|
222
|
-
/Formatter$/,
|
|
223
|
-
/Converter$/
|
|
224
|
-
];
|
|
225
|
-
|
|
226
|
-
// Business logic handlers should not be considered utilities
|
|
227
|
-
const businessLogicExceptions = [
|
|
228
|
-
'EventHandler',
|
|
229
|
-
'CommandHandler',
|
|
230
|
-
'RequestHandler',
|
|
231
|
-
'ActionHandler',
|
|
232
|
-
'MessageHandler',
|
|
233
|
-
'HttpHandler',
|
|
234
|
-
'ApiHandler'
|
|
235
|
-
];
|
|
236
|
-
|
|
237
|
-
if (businessLogicExceptions.includes(className)) {
|
|
238
|
-
return false; // These should be flagged as DI violations
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
const isUtilityName = utilityPatterns.some(pattern => pattern.test(className));
|
|
242
|
-
|
|
243
|
-
if (isUtilityName) {
|
|
244
|
-
return true;
|
|
44
|
+
return violations;
|
|
45
|
+
} catch (error) {
|
|
46
|
+
if (this.verbose) {
|
|
47
|
+
console.error(`[DEBUG] ❌ C014: Analysis failed: ${error.message}`);
|
|
48
|
+
}
|
|
49
|
+
throw new Error(`C014 analysis failed: ${error.message}`);
|
|
245
50
|
}
|
|
246
|
-
|
|
247
|
-
// Check if it's used as return value (factory pattern)
|
|
248
|
-
const factoryContext = [
|
|
249
|
-
'return new',
|
|
250
|
-
'export new',
|
|
251
|
-
'export default new'
|
|
252
|
-
];
|
|
253
|
-
|
|
254
|
-
return factoryContext.some(context => line.includes(context));
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
isConstructorOrInitContext(line) {
|
|
258
|
-
const lowerLine = line.toLowerCase();
|
|
259
|
-
const patterns = [
|
|
260
|
-
'constructor',
|
|
261
|
-
'init',
|
|
262
|
-
'setup',
|
|
263
|
-
'configure',
|
|
264
|
-
'this.',
|
|
265
|
-
'super(',
|
|
266
|
-
'initialize'
|
|
267
|
-
];
|
|
268
|
-
|
|
269
|
-
return patterns.some(pattern => lowerLine.includes(pattern));
|
|
270
51
|
}
|
|
271
52
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
const
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
'useContext(',
|
|
281
|
-
'useReducer(',
|
|
282
|
-
'useImperativeHandle(',
|
|
283
|
-
'useLayoutEffect(',
|
|
284
|
-
'useDebugValue(',
|
|
285
|
-
// Custom hooks
|
|
286
|
-
'use[A-Z]'
|
|
287
|
-
];
|
|
288
|
-
|
|
289
|
-
return reactHookPatterns.some(pattern => {
|
|
290
|
-
if (pattern.includes('[A-Z]')) {
|
|
291
|
-
return new RegExp(pattern).test(line);
|
|
53
|
+
async analyzeFiles(files, options = {}) {
|
|
54
|
+
const allViolations = [];
|
|
55
|
+
for (const filePath of files) {
|
|
56
|
+
try {
|
|
57
|
+
const violations = await this.analyzeFileBasic(filePath, options);
|
|
58
|
+
allViolations.push(...violations);
|
|
59
|
+
} catch (error) {
|
|
60
|
+
console.warn(`C014: Skipping ${filePath}: ${error.message}`);
|
|
292
61
|
}
|
|
293
|
-
return line.includes(pattern);
|
|
294
|
-
});
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
isImmediateUsagePattern(line) {
|
|
298
|
-
// Check for immediate usage patterns like new Date().getTime()
|
|
299
|
-
const immediatePatterns = [
|
|
300
|
-
/new\s+\w+\(\)\./, // new Date().getTime(), new Error().stack
|
|
301
|
-
/new\s+\w+\([^)]*\)\./, // new Date(value).getTime()
|
|
302
|
-
/window\.dispatchEvent\(new\s+/, // window.dispatchEvent(new Event())
|
|
303
|
-
/document\.dispatchEvent\(new\s+/, // document.dispatchEvent(new Event())
|
|
304
|
-
];
|
|
305
|
-
|
|
306
|
-
// Special case: standalone built-in class instantiation (not assignment)
|
|
307
|
-
const standaloneBuiltinPattern = /(?:^|\s|;)\s*new\s+(Date|Error|Array|Set|Map|Promise|RegExp|URL|FormData|Event|FileReader|XMLHttpRequest)\(\)\s*;?\s*$/;
|
|
308
|
-
if (standaloneBuiltinPattern.test(line)) {
|
|
309
|
-
return true;
|
|
310
62
|
}
|
|
311
|
-
|
|
312
|
-
return immediatePatterns.some(pattern => pattern.test(line));
|
|
63
|
+
return allViolations;
|
|
313
64
|
}
|
|
314
65
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
// Increase confidence for business logic classes
|
|
319
|
-
if (className.includes('Service') || className.includes('Repository') ||
|
|
320
|
-
className.includes('Controller') || className.includes('Component')) {
|
|
321
|
-
confidence += 0.2;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
// Decrease confidence if in constructor or initialization context
|
|
325
|
-
if (line.includes('constructor') || line.includes('init') || line.includes('setup')) {
|
|
326
|
-
confidence -= 0.1;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
// Decrease confidence if it looks like configuration
|
|
330
|
-
if (line.includes('config') || line.includes('options') || line.includes('settings')) {
|
|
331
|
-
confidence -= 0.1;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
return Math.max(0.3, Math.min(1.0, confidence));
|
|
66
|
+
// Legacy method for backward compatibility
|
|
67
|
+
async analyze(files, language, options = {}) {
|
|
68
|
+
return this.analyzeFiles(files, options);
|
|
335
69
|
}
|
|
70
|
+
|
|
336
71
|
}
|
|
337
72
|
|
|
338
|
-
module.exports =
|
|
73
|
+
module.exports = C014Analyzer;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"maxNestingLevel": 3,
|
|
3
|
+
"allowedBuiltins": [
|
|
4
|
+
"Date", "Array", "Object", "String", "Number", "Boolean", "RegExp",
|
|
5
|
+
"Map", "Set", "WeakMap", "WeakSet", "Promise", "Error", "TypeError",
|
|
6
|
+
"FormData", "Headers", "Request", "Response", "URLSearchParams",
|
|
7
|
+
"URL", "Blob", "File", "Buffer"
|
|
8
|
+
],
|
|
9
|
+
"allowedValueObjects": [
|
|
10
|
+
"Money", "Price", "Currency", "Quantity", "Amount",
|
|
11
|
+
"Email", "Phone", "Address", "Name", "Id", "UserId",
|
|
12
|
+
"UUID", "Timestamp", "Duration", "Range"
|
|
13
|
+
],
|
|
14
|
+
"infraPatterns": [
|
|
15
|
+
"Client", "Repository", "Service", "Gateway", "Adapter",
|
|
16
|
+
"Provider", "Factory", "Builder", "Manager", "Handler",
|
|
17
|
+
"Controller", "Processor", "Validator", "Logger"
|
|
18
|
+
],
|
|
19
|
+
"excludePatterns": [
|
|
20
|
+
"**/*.test.ts", "**/*.spec.ts", "**/*.test.js", "**/*.spec.js",
|
|
21
|
+
"**/tests/**", "**/test/**", "**/migration/**", "**/scripts/**"
|
|
22
|
+
],
|
|
23
|
+
"includePatterns": [
|
|
24
|
+
"**/*.js", "**/*.ts", "**/*.jsx", "**/*.tsx"
|
|
25
|
+
]
|
|
26
|
+
}
|