@sun-asterisk/sunlint 1.3.18 → 1.3.20

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.
Files changed (35) hide show
  1. package/config/rules/enhanced-rules-registry.json +77 -18
  2. package/core/cli-program.js +9 -1
  3. package/core/github-annotate-service.js +986 -0
  4. package/core/output-service.js +294 -6
  5. package/core/summary-report-service.js +30 -30
  6. package/docs/GITHUB_ACTIONS_INTEGRATION.md +421 -0
  7. package/package.json +2 -1
  8. package/rules/common/C014_dependency_injection/symbol-based-analyzer.js +392 -280
  9. package/rules/common/C017_constructor_logic/analyzer.js +137 -503
  10. package/rules/common/C017_constructor_logic/config.json +50 -0
  11. package/rules/common/C017_constructor_logic/symbol-based-analyzer.js +463 -0
  12. package/rules/security/S006_no_plaintext_recovery_codes/symbol-based-analyzer.js +463 -21
  13. package/rules/security/S011_secure_guid_generation/README.md +255 -0
  14. package/rules/security/S011_secure_guid_generation/analyzer.js +135 -0
  15. package/rules/security/S011_secure_guid_generation/config.json +56 -0
  16. package/rules/security/S011_secure_guid_generation/symbol-based-analyzer.js +609 -0
  17. package/rules/security/S028_file_upload_size_limits/README.md +537 -0
  18. package/rules/security/S028_file_upload_size_limits/analyzer.js +202 -0
  19. package/rules/security/S028_file_upload_size_limits/config.json +186 -0
  20. package/rules/security/S028_file_upload_size_limits/symbol-based-analyzer.js +530 -0
  21. package/rules/security/S041_session_token_invalidation/README.md +303 -0
  22. package/rules/security/S041_session_token_invalidation/analyzer.js +242 -0
  23. package/rules/security/S041_session_token_invalidation/config.json +175 -0
  24. package/rules/security/S041_session_token_invalidation/regex-based-analyzer.js +411 -0
  25. package/rules/security/S041_session_token_invalidation/symbol-based-analyzer.js +674 -0
  26. package/rules/security/S044_re_authentication_required/README.md +136 -0
  27. package/rules/security/S044_re_authentication_required/analyzer.js +242 -0
  28. package/rules/security/S044_re_authentication_required/config.json +161 -0
  29. package/rules/security/S044_re_authentication_required/regex-based-analyzer.js +329 -0
  30. package/rules/security/S044_re_authentication_required/symbol-based-analyzer.js +537 -0
  31. package/rules/security/S045_brute_force_protection/README.md +345 -0
  32. package/rules/security/S045_brute_force_protection/analyzer.js +336 -0
  33. package/rules/security/S045_brute_force_protection/config.json +139 -0
  34. package/rules/security/S045_brute_force_protection/symbol-based-analyzer.js +646 -0
  35. package/rules/common/C017_constructor_logic/semantic-analyzer.js +0 -340
@@ -1,551 +1,185 @@
1
- const fs = require('fs');
2
- const path = require('path');
1
+ /**
2
+ * C017 Main Analyzer - Do not put business logic inside constructors
3
+ * Primary: Ensure constructors only initialize objects, not perform business logic, to improve testability.
4
+ * Fallback: Regex-based for all other cases
5
+ * node cli.js --rules=c017 --input=examples/rule-test-fixtures/rules/C017_constructor_logic/violations --engine=heuristic --verbose
6
+ */
7
+
8
+ const C017SymbolBasedAnalyzer = require('./symbol-based-analyzer');
3
9
 
4
10
  class C017Analyzer {
5
- constructor() {
11
+ constructor(options = {}) {
12
+ if (process.env.SUNLINT_DEBUG) {
13
+ console.log(`🔧 [C017] Constructor called with options:`, !!options);
14
+ console.log(`🔧 [C017] Options type:`, typeof options, Object.keys(options || {}));
15
+ }
16
+
6
17
  this.ruleId = 'C017';
7
- this.ruleName = 'Constructor Logic Limitation';
8
- this.description = 'Không gán logic xử vào constructor';
9
- }
18
+ this.ruleName = 'Do not put business logic inside constructors';
19
+ this.description = 'Ensure constructors only initialize objects, not perform business logic, to improve testability.';
20
+ this.semanticEngine = options.semanticEngine || null;
21
+ this.verbose = options.verbose || false;
22
+
23
+ // Configuration
24
+ this.config = {
25
+ useSymbolBased: true, // Primary approach
26
+ fallbackToRegex: false, // Only when symbol fails completely
27
+ symbolBasedOnly: false // Can be set to true for pure mode
28
+ };
10
29
 
11
- async analyze(files, language, options = {}) {
12
- const violations = [];
13
-
14
- for (const filePath of files) {
15
- if (options.verbose) {
16
- console.log(`🔍 Running C017 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}`);
30
+ // Initialize both analyzers
31
+ try {
32
+ this.symbolAnalyzer = new C017SymbolBasedAnalyzer(this.semanticEngine);
33
+ if (process.env.SUNLINT_DEBUG) {
34
+ console.log(`🔧 [C017] Symbol analyzer created successfully`);
25
35
  }
36
+ } catch (error) {
37
+ console.error(`🔧 [C017] Error creating symbol analyzer:`, error);
26
38
  }
27
-
28
- return violations;
29
- }
30
39
 
31
- async analyzeFile(filePath, content, language, config) {
32
- switch (language) {
33
- case 'typescript':
34
- case 'javascript':
35
- return this.analyzeTypeScript(filePath, content, config);
36
- default:
37
- return [];
40
+ if (process.env.SUNLINT_DEBUG) {
41
+ console.log(`🔧 [C017] Constructor completed`);
38
42
  }
39
43
  }
40
44
 
41
- async analyzeTypeScript(filePath, content, config) {
42
- const violations = [];
43
- const lines = content.split('\n');
44
-
45
- // Find constructor blocks and analyze their content
46
- lines.forEach((line, index) => {
47
- const lineNumber = index + 1;
48
- const trimmedLine = line.trim();
49
-
50
- // Detect constructor start
51
- if (this.isConstructorStart(trimmedLine)) {
52
- const constructorInfo = this.extractConstructorInfo(lines, index);
53
-
54
- // Debug logging to understand boundary detection
55
- if (config?.verbose) {
56
- console.log(`[DEBUG] Constructor found at line ${lineNumber}`);
57
- console.log(`[DEBUG] Constructor body lines: ${constructorInfo.body.length}`);
58
- console.log(`[DEBUG] Constructor content:`, constructorInfo.body.map((l, i) => `${lineNumber + i}: ${l}`));
59
- }
60
-
61
- const complexLogic = this.findComplexLogic(constructorInfo.body);
62
-
63
- complexLogic.forEach(logic => {
64
- violations.push({
65
- ruleId: this.ruleId,
66
- file: filePath,
67
- line: lineNumber + logic.lineOffset,
68
- column: logic.column,
69
- message: `Constructor contains complex logic: ${logic.description}. Move to initialization methods`,
70
- severity: 'warning',
71
- code: logic.code,
72
- type: logic.type,
73
- confidence: logic.confidence,
74
- suggestion: 'Move complex logic to separate initialization methods or lifecycle hooks'
75
- });
76
- });
77
- }
78
- });
79
-
80
- return violations;
81
- }
45
+ /**
46
+ * Initialize with semantic engine
47
+ */
48
+ async initialize(semanticEngine = null) {
49
+ if (semanticEngine) {
50
+ this.semanticEngine = semanticEngine;
51
+ }
52
+ this.verbose = semanticEngine?.verbose || false;
82
53
 
83
- isConstructorStart(line) {
84
- return line.includes('constructor(') || line.match(/constructor\s*\(/);
85
- }
54
+ // Initialize both analyzers
55
+ await this.symbolAnalyzer.initialize(semanticEngine);
86
56
 
87
- extractConstructorInfo(lines, startIndex) {
88
- const constructorLines = [];
89
- let braceDepth = 0;
90
- let foundConstructorBrace = false;
91
- let inConstructor = false;
92
- let parenthesesDepth = 0;
93
- let inCallback = false;
57
+ // Ensure verbose flag is propagated
58
+ this.symbolAnalyzer.verbose = this.verbose;
94
59
 
95
- for (let i = startIndex; i < lines.length; i++) {
96
- const line = lines[i];
97
- const trimmedLine = line.trim();
98
-
99
- // Check if this is the constructor line with opening brace
100
- if (this.isConstructorStart(trimmedLine)) {
101
- inConstructor = true;
102
- constructorLines.push(line);
103
-
104
- // Special case: empty constructor () {}
105
- if (trimmedLine.includes(') {}')) {
106
- foundConstructorBrace = true;
107
- braceDepth = 0; // Already closed
108
- break; // Stop immediately for empty constructor
109
- }
110
-
111
- // Count braces and parentheses in the constructor line itself
112
- for (const char of line) {
113
- if (char === '{') {
114
- foundConstructorBrace = true;
115
- braceDepth = 1; // Start counting from 1 for the constructor block
116
- } else if (char === '(') {
117
- parenthesesDepth++;
118
- } else if (char === ')') {
119
- parenthesesDepth--;
120
- }
121
- }
122
- } else if (inConstructor && foundConstructorBrace) {
123
- constructorLines.push(line);
124
-
125
- // Track callback functions and nested structures
126
- for (const char of line) {
127
- if (char === '(') {
128
- parenthesesDepth++;
129
- // Detect callback patterns: .use(, .then(, .catch(, etc.
130
- if (line.includes('.use(') || line.includes('.then(') ||
131
- line.includes('.catch(') || line.includes('.finally(') ||
132
- line.includes('=>')) {
133
- inCallback = true;
134
- }
135
- } else if (char === ')') {
136
- parenthesesDepth--;
137
- if (parenthesesDepth <= 0) {
138
- inCallback = false;
139
- }
140
- } else if (char === '{') {
141
- braceDepth++;
142
- } else if (char === '}') {
143
- braceDepth--;
144
- }
145
- }
146
-
147
- // If we've closed all braces and not in callback, we're done
148
- if (braceDepth === 0 && !inCallback) {
149
- break;
150
- }
151
- } else if (inConstructor) {
152
- // Haven't found the opening brace yet, keep looking
153
- constructorLines.push(line);
154
-
155
- // Check for empty constructor pattern: ) {}
156
- if (trimmedLine.includes(') {}')) {
157
- foundConstructorBrace = true;
158
- braceDepth = 0; // Already closed
159
- break; // Stop immediately
160
- }
161
-
162
- for (const char of line) {
163
- if (char === '{') {
164
- foundConstructorBrace = true;
165
- braceDepth = 1;
166
- break;
167
- }
168
- }
169
- }
170
- }
171
-
172
- for (const char of line) {
173
- if (char === '{') {
174
- foundConstructorBrace = true;
175
- braceDepth = 1;
176
- break;
177
- }
178
- }
179
- }
60
+ if (this.verbose) {
61
+ console.log(`🔧 [C017 Hybrid] Analyzer initialized - verbose: ${this.verbose}`);
180
62
  }
181
-
182
- return {
183
- body: constructorLines,
184
- startLine: startIndex
185
- };
186
63
  }
187
64
 
188
- findComplexLogic(constructorBody) {
189
- const complexLogic = [];
190
- let inCallbackFunction = false;
191
- let callbackDepth = 0;
192
-
193
- // Check if this is an empty constructor first
194
- // TEMPORARILY DISABLED for debugging
195
- // if (this.isEmptyConstructor(constructorBody)) {
196
- // return complexLogic; // Return empty array - no violations for empty constructors
197
- // }
198
-
199
- constructorBody.forEach((line, index) => {
200
- const trimmedLine = line.trim();
201
-
202
- // Skip comments, empty lines, and constructor declaration
203
- if (!trimmedLine ||
204
- trimmedLine.startsWith('//') ||
205
- trimmedLine.startsWith('/*') ||
206
- trimmedLine.startsWith('*') ||
207
- this.isConstructorStart(trimmedLine) ||
208
- trimmedLine === '{' ||
209
- trimmedLine === '}') {
210
- return;
211
- }
65
+ async analyze(files, language, options = {}) {
66
+ if (process.env.SUNLINT_DEBUG) {
67
+ console.log(`🔧 [C017] analyze() method called with ${files.length} files, language: ${language}`);
68
+ }
212
69
 
213
- // Track callback functions to avoid flagging their content
214
- if (this.isCallbackStart(trimmedLine)) {
215
- inCallbackFunction = true;
216
- callbackDepth = 1;
217
- return;
218
- }
70
+ const violations = [];
219
71
 
220
- if (inCallbackFunction) {
221
- // Count braces to track callback boundaries
222
- const openBraces = (trimmedLine.match(/\{/g) || []).length;
223
- const closeBraces = (trimmedLine.match(/\}/g) || []).length;
224
- callbackDepth += openBraces - closeBraces;
225
-
226
- if (callbackDepth <= 0) {
227
- inCallbackFunction = false;
228
- callbackDepth = 0;
72
+ for (const filePath of files) {
73
+ try {
74
+ if (process.env.SUNLINT_DEBUG) {
75
+ console.log(`🔧 [C017] Processing file: ${filePath}`);
229
76
  }
230
- return; // Skip analysis inside callbacks
231
- }
232
-
233
- // Analyze line for complex logic patterns only if not in callback
234
- const logicType = this.analyzeLineComplexity(trimmedLine);
235
-
236
- if (logicType) {
237
- complexLogic.push({
238
- lineOffset: index,
239
- column: line.indexOf(trimmedLine) + 1,
240
- code: trimmedLine,
241
- type: logicType.type,
242
- description: logicType.description,
243
- confidence: logicType.confidence
244
- });
245
- }
246
- });
247
77
 
248
- return complexLogic;
249
- }
78
+ const fileViolations = await this.analyzeFile(filePath, options);
79
+ violations.push(...fileViolations);
250
80
 
251
- isEmptyConstructor(constructorBody) {
252
- // Check if constructor body contains only dependency injection parameters and closing brace
253
- const meaningfulLines = constructorBody.filter(line => {
254
- const trimmed = line.trim();
255
-
256
- // Skip empty lines, comments, and constructor declaration
257
- if (!trimmed ||
258
- trimmed.startsWith('//') ||
259
- trimmed.startsWith('/*') ||
260
- trimmed.startsWith('*') ||
261
- this.isConstructorStart(trimmed) ||
262
- trimmed === '{' ||
263
- trimmed === '}' ||
264
- trimmed === ') {}') {
265
- return false;
266
- }
267
-
268
- // Skip constructor parameter declarations (NestJS style)
269
- if (trimmed.startsWith('@') || // Decorators like @InjectRepository
270
- trimmed.match(/^\s*(private|public|protected)\s+readonly\s+\w+/) || // DI parameters
271
- trimmed.match(/^\s*(private|public|protected)\s+\w+/) || // Other parameters
272
- trimmed.endsWith(',') || // Parameter continuation
273
- trimmed.endsWith(') {')) { // Constructor parameter closing
274
- return false;
81
+ if (process.env.SUNLINT_DEBUG) {
82
+ console.log(`🔧 [C017] File ${filePath}: Found ${fileViolations.length} violations`);
83
+ }
84
+ } catch (error) {
85
+ console.warn(`❌ [C017] Analysis failed for ${filePath}:`, error.message);
275
86
  }
276
-
277
- // This is a meaningful line (logic, assignments, calls, etc.)
278
- return true;
279
- });
280
-
281
- return meaningfulLines.length === 0;
282
- }
283
-
284
- isCallbackStart(line) {
285
- // Patterns that indicate callback function starts
286
- const callbackPatterns = [
287
- /\.(use|then|catch|finally|map|filter|forEach|reduce)\s*\(/,
288
- /=>\s*\{/,
289
- /function\s*\(/,
290
- /\(\s*\w+\s*\)\s*=>/,
291
- /interceptors\.(request|response)\.use\(/,
292
- /\.addEventListener\(/,
293
- /setTimeout\(/,
294
- /setInterval\(/
295
- ];
296
-
297
- return callbackPatterns.some(pattern => pattern.test(line));
298
- }
299
-
300
- analyzeLineComplexity(line) {
301
- // Simple property assignments are OK
302
- if (this.isSimpleAssignment(line)) {
303
- return null;
304
- }
305
-
306
- // Super calls are OK
307
- if (this.isSuperCall(line)) {
308
- return null;
309
87
  }
310
88
 
311
- // Parameter assignments are OK
312
- if (this.isParameterAssignment(line)) {
313
- return null;
89
+ if (process.env.SUNLINT_DEBUG) {
90
+ console.log(`🔧 [C017] Total violations found: ${violations.length}`);
314
91
  }
315
92
 
316
- // MobX decorators/observables setup are OK
317
- if (this.isMobXSetup(line)) {
318
- return null;
319
- }
93
+ return violations;
94
+ }
320
95
 
321
- // Allowed method calls are OK
322
- if (this.isAllowedMethodCall(line)) {
323
- return null;
96
+ async analyzeFile(filePath, options = {}) {
97
+ if (process.env.SUNLINT_DEBUG) {
98
+ console.log(`🔧 [C017] analyzeFile() called for: ${filePath}`);
324
99
  }
325
100
 
326
- // Configuration setup is OK
327
- if (this.isConfigurationSetup(line)) {
328
- return null;
329
- }
101
+ // 1. Try Symbol-based analysis first (primary)
102
+ if (this.config.useSymbolBased &&
103
+ this.semanticEngine?.project &&
104
+ this.semanticEngine?.initialized) {
105
+ try {
106
+ if (process.env.SUNLINT_DEBUG) {
107
+ console.log(`🔧 [C017] Trying symbol-based analysis...`);
108
+ }
109
+ const sourceFile = this.semanticEngine.project.getSourceFile(filePath);
110
+ if (sourceFile) {
111
+ if (process.env.SUNLINT_DEBUG) {
112
+ console.log(`🔧 [C017] Source file found, analyzing with symbol-based...`);
113
+ }
114
+ const violations = await this.symbolAnalyzer.analyzeFileWithSymbols(filePath, { ...options, verbose: options.verbose });
330
115
 
331
- // Interceptor setup is OK (axios, etc.)
332
- if (this.isInterceptorSetup(line)) {
333
- return null;
334
- }
116
+ // Mark violations with analysis strategy
117
+ violations.forEach(v => v.analysisStrategy = 'symbol-based');
335
118
 
336
- // Detect complex logic patterns
337
- const complexPatterns = [
338
- {
339
- pattern: /\bif\s*\(|\belse\s|\bswitch\s*\(/,
340
- type: 'conditional_logic',
341
- description: 'conditional statements (if/else/switch)',
342
- confidence: 0.9
343
- },
344
- {
345
- pattern: /\bfor\s*\(|\bwhile\s*\(|\bdo\s+/,
346
- type: 'loop_logic',
347
- description: 'loops (for/while)',
348
- confidence: 0.95
349
- },
350
- {
351
- pattern: /\btry\s*{|\bcatch\s*\(|\bfinally\s*{/,
352
- type: 'exception_handling',
353
- description: 'exception handling (try/catch/finally)',
354
- confidence: 0.8
355
- },
356
- {
357
- pattern: /\.then\s*\(|\.catch\s*\(|await\s+/,
358
- type: 'async_logic',
359
- description: 'asynchronous operations',
360
- confidence: 0.9
361
- },
362
- {
363
- pattern: /\w+\s*\(\s*[^)]*\)\s*[;{]/,
364
- type: 'method_call',
365
- description: 'complex method calls',
366
- confidence: 0.4 // Reduced confidence to minimize false positives
367
- },
368
- {
369
- pattern: /new\s+\w+\s*\([^)]*\)\s*\./,
370
- type: 'chained_instantiation',
371
- description: 'chained object instantiation',
372
- confidence: 0.8
119
+ if (process.env.SUNLINT_DEBUG) {
120
+ console.log(`✅ [C017] Symbol-based analysis: ${violations.length} violations`);
121
+ }
122
+ return violations; // Return even if 0 violations - symbol analysis completed successfully
123
+ } else {
124
+ if (process.env.SUNLINT_DEBUG) {
125
+ console.log(`⚠️ [C017] Source file not found in project`);
126
+ }
127
+ }
128
+ } catch (error) {
129
+ console.warn(`⚠️ [C017] Symbol analysis failed: ${error.message}`);
130
+ // Continue to fallback
373
131
  }
374
- ];
375
-
376
- for (const { pattern, type, description, confidence } of complexPatterns) {
377
- if (pattern.test(line)) {
378
- return { type, description, confidence };
132
+ } else {
133
+ if (process.env.SUNLINT_DEBUG) {
134
+ console.log(`🔄 [C017] Symbol analysis conditions check:`);
135
+ console.log(` - useSymbolBased: ${this.config.useSymbolBased}`);
136
+ console.log(` - semanticEngine: ${!!this.semanticEngine}`);
137
+ console.log(` - semanticEngine.project: ${!!this.semanticEngine?.project}`);
138
+ console.log(` - semanticEngine.initialized: ${this.semanticEngine?.initialized}`);
139
+ console.log(`🔄 [C017] Symbol analysis unavailable, using regex fallback`);
379
140
  }
380
141
  }
381
142
 
382
- // Check for complex expressions
383
- if (this.hasComplexExpression(line)) {
384
- return {
385
- type: 'complex_expression',
386
- description: 'complex expression or calculation',
387
- confidence: 0.6
388
- };
143
+ if (options?.verbose) {
144
+ console.log(`🔧 [C017] No analysis methods succeeded, returning empty`);
389
145
  }
390
-
391
- return null;
146
+ return [];
392
147
  }
393
148
 
394
- isSimpleAssignment(line) {
395
- // Patterns for simple assignments - expanded to catch more legitimate patterns
396
- const simplePatterns = [
397
- /^this\.\w+\s*=\s*[^;]+;?$/, // this.property = value;
398
- /^this\.\w+\s*=\s*\w+;?$/, // this.property = parameter;
399
- /^this\.\w+\s*=\s*(null|undefined|true|false|\d+|'[^']*'|"[^"]*");?$/, // this.property = literal;
400
- /^this\.\w+\s*=\s*this\.\w+\s*\.\s*get\s*\(/, // this.property = this.configService.get(
401
- /^this\.\w+\s*=\s*new\s+\w+\s*\(/, // this.property = new SomeClass(
402
- /^this\.\w+\s*=\s*\w+\s*\.\s*\w+\s*\(/, // this.property = service.method(
403
- // Enhanced patterns for configuration initialization
404
- /^this\.\w+\s*=\s*new\s+\w+Client\s*\(/, // this.s3 = new S3Client(
405
- /^this\.\w+\s*=\s*new\s+\w+\s*\(\s*\{/, // this.prop = new Class({ config })
406
- /^this\.\w+\s*=\s*\w+\s*\.\s*create\s*\(/, // this.prop = Factory.create(
407
- /^this\.\w+\s*=\s*\w+\s*\.\s*getInstance\s*\(/, // this.prop = Service.getInstance(
408
- ];
149
+ async analyzeFileBasic(filePath, options = {}) {
150
+ console.log(`🔧 [C017] analyzeFileBasic() called for: ${filePath}`);
151
+ console.log(`🔧 [C017] semanticEngine exists: ${!!this.semanticEngine}`);
152
+ console.log(`🔧 [C017] symbolAnalyzer exists: ${!!this.symbolAnalyzer}`);
409
153
 
410
- return simplePatterns.some(pattern => pattern.test(line));
411
- }
412
-
413
- isConfigurationSetup(line) {
414
- // Check if this is configuration/options object setup
415
- const configPatterns = [
416
- /^\s*\w+:\s*/, // Property definition in object literal
417
- /level:\s*/, // Log level setting
418
- /timestamp:\s*/, // Timestamp configuration
419
- /formatters:\s*/, // Formatter configuration
420
- /mixin:\s*/, // Mixin configuration
421
- /transport:\s*/, // Transport configuration
422
- /options:\s*/, // Options configuration
423
- /target:\s*/, // Target configuration
424
- /baseURL:\s*/, // Axios baseURL
425
- /timeout:\s*/, // Axios timeout
426
- /region:\s*/, // AWS region configuration
427
- /credentials:\s*/, // AWS credentials
428
- /endpoint:\s*/, // API endpoint
429
- /apiVersion:\s*/, // API version
430
- /maxRetries:\s*/, // Retry configuration
431
- /headers:\s*/, // HTTP headers
432
- /defaultHeaders:\s*/, // Default headers
433
- /interceptors:\s*/, // Request/response interceptors
434
- /transformRequest:\s*/, // Request transformation
435
- /transformResponse:\s*/, // Response transformation
436
- /validateStatus:\s*/, // Status validation
437
- /responseType:\s*/, // Response type
438
- /maxContentLength:\s*/, // Content length limit
439
- /ssl:\s*/, // SSL configuration
440
- /auth:\s*/, // Authentication
441
- /withCredentials:\s*/, // CORS credentials
442
- /maxRedirects:\s*/, // Max redirects
443
- ];
444
-
445
- return configPatterns.some(pattern => pattern.test(line));
446
- }
447
-
448
- isInterceptorSetup(line) {
449
- // Check if this is axios interceptor setup (should be allowed in constructor)
450
- const interceptorPatterns = [
451
- /interceptors\.(request|response)\.use/,
452
- /\.use\s*\(/,
453
- /\.then\s*\(/,
454
- /\.catch\s*\(/,
455
- /\.finally\s*\(/,
456
- /=>\s*\{?/,
457
- /=>\s*[^{]/, // Arrow function without braces
458
- /function\s*\(/
459
- ];
154
+ try {
155
+ // Try symbol-based analysis first
156
+ if (this.semanticEngine?.isSymbolEngineReady?.() &&
157
+ this.semanticEngine.project) {
460
158
 
461
- return interceptorPatterns.some(pattern => pattern.test(line));
462
- }
463
-
464
- isSuperCall(line) {
465
- return line.includes('super(') || line.startsWith('super.');
466
- }
467
-
468
- isParameterAssignment(line) {
469
- // Check if it's just assigning constructor parameters to properties
470
- return /^this\.\w+\s*=\s*\w+;?$/.test(line);
471
- }
472
-
473
- isMobXSetup(line) {
474
- // MobX patterns that are OK in constructor
475
- const mobxPatterns = [
476
- 'makeObservable',
477
- 'makeAutoObservable',
478
- 'observable',
479
- 'action',
480
- 'computed',
481
- 'reaction',
482
- 'autorun',
483
- '@observable',
484
- '@action',
485
- '@computed'
486
- ];
159
+ if (this.verbose) {
160
+ console.log(`🔍 [C017] Using symbol-based analysis for ${filePath}`);
161
+ }
487
162
 
488
- const lowerLine = line.toLowerCase();
489
- return mobxPatterns.some(pattern =>
490
- lowerLine.includes(pattern.toLowerCase())
491
- );
163
+ const violations = await this.symbolAnalyzer.analyzeFileBasic(filePath, options);
164
+ return violations;
165
+ }
166
+ } catch (error) {
167
+ if (this.verbose) {
168
+ console.warn(`⚠️ [C017] Symbol analysis failed: ${error.message}`);
169
+ }
170
+ }
492
171
  }
493
172
 
494
- isAllowedMethodCall(line) {
495
- // Method calls that are typically OK in constructors - expanded list
496
- const allowedMethods = [
497
- 'makeObservable',
498
- 'makeAutoObservable',
499
- 'bind',
500
- 'addEventListener',
501
- 'removeEventListener',
502
- 'Object.assign',
503
- 'Object.defineProperty',
504
- 'console.log',
505
- 'console.warn',
506
- // Common initialization patterns
507
- 'createLogger',
508
- 'initializeLogger',
509
- 'setupConfiguration',
510
- 'initializeService',
511
- 'configure',
512
- 'init',
513
- 'setup',
514
- // Common service/config patterns
515
- 'get',
516
- 'getService',
517
- 'getInstance',
518
- // Dependency injection patterns
519
- 'inject',
520
- 'resolve',
521
- // Axios/HTTP setup patterns
522
- 'interceptors',
523
- 'use',
524
- 'defaults',
525
- 'timeout',
526
- 'baseURL',
527
- 'headers'
528
- ];
529
-
530
- const lowerLine = line.toLowerCase();
531
- return allowedMethods.some(method =>
532
- lowerLine.includes(method.toLowerCase())
533
- );
173
+ /**
174
+ * Methods for compatibility with different engine invocation patterns
175
+ */
176
+ async analyzeFileWithSymbols(filePath, options = {}) {
177
+ return this.analyzeFile(filePath, options);
534
178
  }
535
179
 
536
- hasComplexExpression(line) {
537
- // Check for complex expressions (multiple operators, complex calculations)
538
- // Made more restrictive to reduce false positives
539
- const complexityIndicators = [
540
- /[+\-*/]\s*[+\-*/]/, // Multiple arithmetic operators
541
- /\?\s*.*\s*:/, // Ternary operators
542
- /&&.*&&|\|\|.*\|\|/, // Multiple logical operators (more restrictive)
543
- /\[[^\]]*\].*\[[^\]]*\]/, // Multiple array accesses
544
- /\.[^.]*\.[^.]*\./ // Triple+ chained property access (more restrictive)
545
- ];
546
-
547
- return complexityIndicators.some(pattern => pattern.test(line));
180
+ async analyzeWithSemantics(filePath, options = {}) {
181
+ return this.analyzeFile(filePath, options);
548
182
  }
549
183
  }
550
184
 
551
- module.exports = new C017Analyzer();
185
+ module.exports = C017Analyzer;