@sun-asterisk/sunlint 1.2.1 → 1.2.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.
Files changed (77) hide show
  1. package/config/rule-analysis-strategies.js +18 -2
  2. package/engines/eslint-engine.js +9 -11
  3. package/engines/heuristic-engine.js +55 -31
  4. package/package.json +2 -1
  5. package/rules/README.md +252 -0
  6. package/rules/common/C002_no_duplicate_code/analyzer.js +65 -0
  7. package/rules/common/C002_no_duplicate_code/config.json +23 -0
  8. package/rules/common/C003_no_vague_abbreviations/analyzer.js +418 -0
  9. package/rules/common/C003_no_vague_abbreviations/config.json +35 -0
  10. package/rules/common/C006_function_naming/analyzer.js +504 -0
  11. package/rules/common/C006_function_naming/config.json +86 -0
  12. package/rules/common/C006_function_naming/smart-analyzer.js +503 -0
  13. package/rules/common/C010_limit_block_nesting/analyzer.js +389 -0
  14. package/rules/common/C012_command_query_separation/analyzer.js +481 -0
  15. package/rules/common/C012_command_query_separation/ast-analyzer.js +495 -0
  16. package/rules/common/C013_no_dead_code/analyzer.js +206 -0
  17. package/rules/common/C014_dependency_injection/analyzer.js +338 -0
  18. package/rules/common/C017_constructor_logic/analyzer.js +314 -0
  19. package/rules/common/C019_log_level_usage/analyzer.js +362 -0
  20. package/rules/common/C019_log_level_usage/config.json +121 -0
  21. package/rules/common/C029_catch_block_logging/analyzer-backup.js +426 -0
  22. package/rules/common/C029_catch_block_logging/analyzer-fixed.js +130 -0
  23. package/rules/common/C029_catch_block_logging/analyzer-multi-tech.js +487 -0
  24. package/rules/common/C029_catch_block_logging/analyzer-simple.js +110 -0
  25. package/rules/common/C029_catch_block_logging/analyzer-smart-pipeline.js +755 -0
  26. package/rules/common/C029_catch_block_logging/analyzer.js +129 -0
  27. package/rules/common/C029_catch_block_logging/ast-analyzer-backup.js +441 -0
  28. package/rules/common/C029_catch_block_logging/ast-analyzer-new.js +127 -0
  29. package/rules/common/C029_catch_block_logging/ast-analyzer.js +133 -0
  30. package/rules/common/C029_catch_block_logging/cfg-analyzer.js +408 -0
  31. package/rules/common/C029_catch_block_logging/config.json +59 -0
  32. package/rules/common/C029_catch_block_logging/dataflow-analyzer.js +454 -0
  33. package/rules/common/C029_catch_block_logging/multi-language-ast-engine.js +700 -0
  34. package/rules/common/C029_catch_block_logging/pattern-learning-analyzer.js +568 -0
  35. package/rules/common/C029_catch_block_logging/semantic-analyzer.js +459 -0
  36. package/rules/common/C031_validation_separation/analyzer.js +186 -0
  37. package/rules/common/C041_no_sensitive_hardcode/analyzer.js +292 -0
  38. package/rules/common/C041_no_sensitive_hardcode/ast-analyzer.js +296 -0
  39. package/rules/common/C042_boolean_name_prefix/analyzer.js +300 -0
  40. package/rules/common/C043_no_console_or_print/analyzer.js +431 -0
  41. package/rules/common/C047_no_duplicate_retry_logic/analyzer.js +590 -0
  42. package/rules/common/C075_explicit_return_types/analyzer.js +103 -0
  43. package/rules/common/C076_single_test_behavior/analyzer.js +121 -0
  44. package/rules/docs/C002_no_duplicate_code.md +57 -0
  45. package/rules/docs/C031_validation_separation.md +72 -0
  46. package/rules/index.js +155 -0
  47. package/rules/migration/converter.js +385 -0
  48. package/rules/migration/mapping.json +164 -0
  49. package/rules/parser/constants.js +31 -0
  50. package/rules/parser/file-config.js +80 -0
  51. package/rules/parser/rule-parser-simple.js +305 -0
  52. package/rules/parser/rule-parser.js +527 -0
  53. package/rules/security/S015_insecure_tls_certificate/analyzer.js +150 -0
  54. package/rules/security/S015_insecure_tls_certificate/ast-analyzer.js +237 -0
  55. package/rules/security/S023_no_json_injection/analyzer.js +278 -0
  56. package/rules/security/S023_no_json_injection/ast-analyzer.js +359 -0
  57. package/rules/security/S026_json_schema_validation/analyzer.js +251 -0
  58. package/rules/security/S026_json_schema_validation/config.json +27 -0
  59. package/rules/security/S027_no_hardcoded_secrets/analyzer.js +436 -0
  60. package/rules/security/S027_no_hardcoded_secrets/config.json +29 -0
  61. package/rules/security/S029_csrf_protection/analyzer.js +330 -0
  62. package/rules/tests/C002_no_duplicate_code.test.js +50 -0
  63. package/rules/universal/C010/generic.js +0 -0
  64. package/rules/universal/C010/tree-sitter-analyzer.js +0 -0
  65. package/rules/utils/ast-utils.js +191 -0
  66. package/rules/utils/base-analyzer.js +98 -0
  67. package/rules/utils/pattern-matchers.js +239 -0
  68. package/rules/utils/rule-helpers.js +264 -0
  69. package/rules/utils/severity-constants.js +93 -0
  70. package/scripts/generate_insights.js +188 -0
  71. package/scripts/merge-reports.js +0 -424
  72. package/scripts/test-scripts/README.md +0 -22
  73. package/scripts/test-scripts/test-c041-comparison.js +0 -114
  74. package/scripts/test-scripts/test-c041-eslint.js +0 -67
  75. package/scripts/test-scripts/test-eslint-rules.js +0 -146
  76. package/scripts/test-scripts/test-real-world.js +0 -44
  77. package/scripts/test-scripts/test-rules-on-real-projects.js +0 -86
@@ -0,0 +1,459 @@
1
+ /**
2
+ * C029 Semantic Analyzer
3
+ *
4
+ * Uses semantic analysis to understand the meaning and intent of error handling code
5
+ * Goes beyond syntax to understand developer intent and code semantics
6
+ *
7
+ * Technology Showcase: Understanding code intent, not just structure
8
+ */
9
+
10
+ class C029SemanticAnalyzer {
11
+ constructor() {
12
+ this.ruleId = 'C029';
13
+ this.ruleName = 'Semantic Intent-Based Catch Analysis';
14
+ this.description = 'Understands developer intent in error handling through semantic analysis';
15
+
16
+ // Semantic patterns for different error handling intents
17
+ this.semanticPatterns = {
18
+ // Intent: Ignore errors intentionally
19
+ intentionalIgnore: [
20
+ /catch\s*\(\s*[_][\w]*\s*\)/, // catch(_), catch(_ignored)
21
+ /\/\/\s*(ignore|suppress|intentional)/i,
22
+ /\/\*\s*(ignore|suppress|intentional)/i,
23
+ /catch\s*\([^)]*ignored[^)]*\)/i
24
+ ],
25
+
26
+ // Intent: Log and continue
27
+ logAndContinue: [
28
+ /console\.(log|error|warn)/,
29
+ /logger\.(log|error|warn)/,
30
+ /log\.(error|warn|info)/
31
+ ],
32
+
33
+ // Intent: Transform and rethrow
34
+ transformAndRethrow: [
35
+ /throw\s+new\s+\w+Error/,
36
+ /throw\s+.*Error\(/,
37
+ /rethrow/i
38
+ ],
39
+
40
+ // Intent: Handle gracefully
41
+ gracefulHandling: [
42
+ /return\s+.*default/i,
43
+ /return\s+.*fallback/i,
44
+ /return\s+null/,
45
+ /return\s+undefined/,
46
+ /return\s+\[\]/,
47
+ /return\s+\{\}/
48
+ ],
49
+
50
+ // Intent: Notify external systems
51
+ externalNotification: [
52
+ /notify/i,
53
+ /alert/i,
54
+ /report/i,
55
+ /track/i,
56
+ /analytics/i,
57
+ /monitoring/i,
58
+ /sentry/i,
59
+ /bugsnag/i
60
+ ],
61
+
62
+ // Intent: Test verification
63
+ testVerification: [
64
+ /expect\(/,
65
+ /assert\(/,
66
+ /should\./,
67
+ /toBe\(/,
68
+ /toEqual\(/,
69
+ /toThrow\(/
70
+ ],
71
+
72
+ // Intent: State management
73
+ stateManagement: [
74
+ /setState/,
75
+ /dispatch/,
76
+ /commit/,
77
+ /rejectWithValue/,
78
+ /setError/,
79
+ /setLoading/
80
+ ]
81
+ };
82
+ }
83
+
84
+ async analyze(files, language, options = {}) {
85
+ const violations = [];
86
+ console.log(`🧠 C029 Semantic: Analyzing ${files.length} files with semantic understanding...`);
87
+
88
+ for (const filePath of files) {
89
+ try {
90
+ const content = require('fs').readFileSync(filePath, 'utf8');
91
+ const semanticViolations = await this.analyzeSemanticIntent(content, filePath, language);
92
+ violations.push(...semanticViolations);
93
+
94
+ } catch (error) {
95
+ console.warn(`C029 Semantic skipping ${filePath}: ${error.message}`);
96
+ }
97
+ }
98
+
99
+ return violations;
100
+ }
101
+
102
+ /**
103
+ * Analyze semantic intent of error handling
104
+ */
105
+ async analyzeSemanticIntent(content, filePath, language) {
106
+ const violations = [];
107
+ const isTestFile = this.isTestFile(filePath);
108
+
109
+ // Find all catch blocks with context
110
+ const catchBlocks = this.extractCatchBlocksWithContext(content);
111
+
112
+ for (const catchBlock of catchBlocks) {
113
+ const semanticAnalysis = this.analyzeBlockSemantic(catchBlock, isTestFile, content);
114
+
115
+ if (semanticAnalysis.isViolation) {
116
+ violations.push({
117
+ ruleId: this.ruleId,
118
+ file: filePath,
119
+ line: catchBlock.lineNumber,
120
+ column: 1,
121
+ message: semanticAnalysis.message,
122
+ severity: semanticAnalysis.severity,
123
+ code: catchBlock.code,
124
+ type: semanticAnalysis.type,
125
+ confidence: semanticAnalysis.confidence,
126
+ suggestion: semanticAnalysis.suggestion,
127
+ semanticIntent: semanticAnalysis.intent,
128
+ contextAnalysis: semanticAnalysis.context
129
+ });
130
+ }
131
+ }
132
+
133
+ return violations;
134
+ }
135
+
136
+ /**
137
+ * Extract catch blocks with surrounding context for semantic analysis
138
+ */
139
+ extractCatchBlocksWithContext(content) {
140
+ const catchBlocks = [];
141
+ const lines = content.split('\n');
142
+
143
+ for (let i = 0; i < lines.length; i++) {
144
+ const line = lines[i];
145
+
146
+ if (this.isCatchBlockStart(line)) {
147
+ const catchBlock = this.extractFullCatchBlock(lines, i);
148
+
149
+ // Add surrounding context for semantic analysis
150
+ const context = this.extractSurroundingContext(lines, i, catchBlock.endLine);
151
+
152
+ catchBlocks.push({
153
+ ...catchBlock,
154
+ lineNumber: i + 1,
155
+ context
156
+ });
157
+ }
158
+ }
159
+
160
+ return catchBlocks;
161
+ }
162
+
163
+ /**
164
+ * Analyze semantic intent of a catch block
165
+ */
166
+ analyzeBlockSemantic(catchBlock, isTestFile, fullContent) {
167
+ const blockContent = catchBlock.content;
168
+ const blockCode = catchBlock.code;
169
+ const context = catchBlock.context;
170
+
171
+ // 1. Detect semantic intent
172
+ const intent = this.detectSemanticIntent(blockContent, context, isTestFile);
173
+
174
+ // 2. Validate intent appropriateness
175
+ const validation = this.validateSemanticIntent(intent, catchBlock, isTestFile, fullContent);
176
+
177
+ if (validation.isValid) {
178
+ return { isViolation: false, intent, context: validation };
179
+ }
180
+
181
+ // 3. Generate semantic-aware violation
182
+ return {
183
+ isViolation: true,
184
+ type: validation.violationType,
185
+ message: validation.message,
186
+ severity: validation.severity,
187
+ confidence: validation.confidence,
188
+ suggestion: validation.suggestion,
189
+ intent,
190
+ context: validation
191
+ };
192
+ }
193
+
194
+ /**
195
+ * Detect the semantic intent of error handling
196
+ */
197
+ detectSemanticIntent(blockContent, context, isTestFile) {
198
+ const intents = [];
199
+
200
+ // Check each semantic pattern category
201
+ for (const [intentType, patterns] of Object.entries(this.semanticPatterns)) {
202
+ for (const pattern of patterns) {
203
+ if (pattern.test(blockContent) || pattern.test(context.before) || pattern.test(context.after)) {
204
+ intents.push({
205
+ type: intentType,
206
+ confidence: this.calculateIntentConfidence(intentType, blockContent, context, isTestFile)
207
+ });
208
+ }
209
+ }
210
+ }
211
+
212
+ // Handle empty catch blocks
213
+ if (this.isEmpty(blockContent)) {
214
+ intents.push({
215
+ type: 'silentIgnore',
216
+ confidence: 0.95
217
+ });
218
+ }
219
+
220
+ // Handle unknown intent
221
+ if (intents.length === 0) {
222
+ intents.push({
223
+ type: 'unknown',
224
+ confidence: 0.1
225
+ });
226
+ }
227
+
228
+ // Return primary intent (highest confidence)
229
+ return intents.sort((a, b) => b.confidence - a.confidence)[0];
230
+ }
231
+
232
+ /**
233
+ * Validate if the detected intent is appropriate for the context
234
+ */
235
+ validateSemanticIntent(intent, catchBlock, isTestFile, fullContent) {
236
+ const intentType = intent.type;
237
+ const confidence = intent.confidence;
238
+
239
+ switch (intentType) {
240
+ case 'intentionalIgnore':
241
+ return this.validateIntentionalIgnore(catchBlock, confidence);
242
+
243
+ case 'logAndContinue':
244
+ return this.validateLogAndContinue(catchBlock, confidence);
245
+
246
+ case 'transformAndRethrow':
247
+ return this.validateTransformAndRethrow(catchBlock, confidence);
248
+
249
+ case 'gracefulHandling':
250
+ return this.validateGracefulHandling(catchBlock, confidence);
251
+
252
+ case 'externalNotification':
253
+ return this.validateExternalNotification(catchBlock, confidence);
254
+
255
+ case 'testVerification':
256
+ return this.validateTestVerification(catchBlock, isTestFile, confidence);
257
+
258
+ case 'stateManagement':
259
+ return this.validateStateManagement(catchBlock, confidence);
260
+
261
+ case 'silentIgnore':
262
+ return this.validateSilentIgnore(catchBlock, isTestFile, confidence);
263
+
264
+ default:
265
+ return this.validateUnknownIntent(catchBlock, confidence);
266
+ }
267
+ }
268
+
269
+ /**
270
+ * Validate intentional ignore patterns
271
+ */
272
+ validateIntentionalIgnore(catchBlock, confidence) {
273
+ // Intentional ignore is usually acceptable if properly documented
274
+ const hasDocumentation = this.hasProperDocumentation(catchBlock);
275
+
276
+ if (hasDocumentation) {
277
+ return { isValid: true, reason: 'documented_ignore' };
278
+ }
279
+
280
+ return {
281
+ isValid: false,
282
+ violationType: 'undocumented_ignore',
283
+ message: 'Intentional error ignore should be documented with reason',
284
+ severity: 'warning',
285
+ confidence: confidence * 0.8,
286
+ suggestion: 'Add comment explaining why error is intentionally ignored'
287
+ };
288
+ }
289
+
290
+ /**
291
+ * Validate log and continue patterns
292
+ */
293
+ validateLogAndContinue(catchBlock, confidence) {
294
+ // Check if logging actually includes error information
295
+ const hasErrorInfo = this.loggingIncludesErrorInfo(catchBlock);
296
+
297
+ if (hasErrorInfo) {
298
+ return { isValid: true, reason: 'proper_logging' };
299
+ }
300
+
301
+ return {
302
+ isValid: false,
303
+ violationType: 'incomplete_logging',
304
+ message: 'Error logging should include error details (message, stack, context)',
305
+ severity: 'warning',
306
+ confidence: confidence * 0.9,
307
+ suggestion: 'Include error.message, error.stack, or relevant error properties in logging'
308
+ };
309
+ }
310
+
311
+ /**
312
+ * Validate silent ignore (empty catch)
313
+ */
314
+ validateSilentIgnore(catchBlock, isTestFile, confidence) {
315
+ // Silent ignore is usually problematic except in specific contexts
316
+ if (isTestFile && this.isExpectedErrorInTest(catchBlock)) {
317
+ return { isValid: true, reason: 'expected_test_error' };
318
+ }
319
+
320
+ return {
321
+ isValid: false,
322
+ violationType: 'silent_error_suppression',
323
+ message: 'Silent error suppression hides bugs and makes debugging difficult',
324
+ severity: 'error',
325
+ confidence: confidence,
326
+ suggestion: 'Add error logging, rethrowing, or explicit ignore with documentation'
327
+ };
328
+ }
329
+
330
+ /**
331
+ * Calculate confidence score for intent detection
332
+ */
333
+ calculateIntentConfidence(intentType, blockContent, context, isTestFile) {
334
+ let confidence = 0.5; // Base confidence
335
+
336
+ // Adjust based on pattern strength
337
+ const patternCount = this.countMatchingPatterns(intentType, blockContent);
338
+ confidence += Math.min(patternCount * 0.2, 0.4);
339
+
340
+ // Adjust based on context
341
+ if (isTestFile && intentType === 'testVerification') {
342
+ confidence += 0.3;
343
+ }
344
+
345
+ // Adjust based on code quality indicators
346
+ if (this.hasGoodNaming(blockContent)) {
347
+ confidence += 0.1;
348
+ }
349
+
350
+ if (this.hasDocumentation(context)) {
351
+ confidence += 0.1;
352
+ }
353
+
354
+ return Math.min(confidence, 1.0);
355
+ }
356
+
357
+ // Helper methods (simplified implementations)
358
+
359
+ isCatchBlockStart(line) {
360
+ return line.trim().includes('catch (') || line.trim().includes('catch(');
361
+ }
362
+
363
+ extractFullCatchBlock(lines, startIndex) {
364
+ // Simplified catch block extraction
365
+ return {
366
+ content: 'catch block content',
367
+ code: lines[startIndex],
368
+ endLine: startIndex + 5
369
+ };
370
+ }
371
+
372
+ extractSurroundingContext(lines, startLine, endLine) {
373
+ const before = lines.slice(Math.max(0, startLine - 3), startLine).join('\n');
374
+ const after = lines.slice(endLine + 1, Math.min(lines.length, endLine + 4)).join('\n');
375
+
376
+ return { before, after };
377
+ }
378
+
379
+ isEmpty(content) {
380
+ return content.trim().replace(/[{}]/g, '').trim().length === 0;
381
+ }
382
+
383
+ hasProperDocumentation(catchBlock) {
384
+ const content = catchBlock.content + (catchBlock.context?.before || '');
385
+ return /\/\/|\/\*/.test(content);
386
+ }
387
+
388
+ loggingIncludesErrorInfo(catchBlock) {
389
+ const content = catchBlock.content;
390
+ return /error\.(message|stack|name)/.test(content) || /\$\{error\}/.test(content);
391
+ }
392
+
393
+ isExpectedErrorInTest(catchBlock) {
394
+ const context = catchBlock.context || {};
395
+ return /expect.*toThrow|assertThrows|shouldThrow/.test(context.before + context.after);
396
+ }
397
+
398
+ countMatchingPatterns(intentType, content) {
399
+ const patterns = this.semanticPatterns[intentType] || [];
400
+ return patterns.filter(pattern => pattern.test(content)).length;
401
+ }
402
+
403
+ hasGoodNaming(content) {
404
+ return /\b(error|err|exception|ex)\b/.test(content);
405
+ }
406
+
407
+ hasDocumentation(context) {
408
+ return /\/\/|\/\*/.test((context?.before || '') + (context?.after || ''));
409
+ }
410
+
411
+ isTestFile(filePath) {
412
+ const testPatterns = ['__tests__', '.test.', '.spec.', '/test/', '/tests/'];
413
+ return testPatterns.some(pattern => filePath.includes(pattern));
414
+ }
415
+
416
+ // Placeholder validation methods
417
+ validateTransformAndRethrow(catchBlock, confidence) {
418
+ return { isValid: true, reason: 'proper_transform' };
419
+ }
420
+
421
+ validateGracefulHandling(catchBlock, confidence) {
422
+ return { isValid: true, reason: 'graceful_handling' };
423
+ }
424
+
425
+ validateExternalNotification(catchBlock, confidence) {
426
+ return { isValid: true, reason: 'external_notification' };
427
+ }
428
+
429
+ validateTestVerification(catchBlock, isTestFile, confidence) {
430
+ if (!isTestFile) {
431
+ return {
432
+ isValid: false,
433
+ violationType: 'test_code_in_production',
434
+ message: 'Test-style error handling found in production code',
435
+ severity: 'warning',
436
+ confidence: confidence * 0.9,
437
+ suggestion: 'Use proper error handling instead of test assertions'
438
+ };
439
+ }
440
+ return { isValid: true, reason: 'valid_test_verification' };
441
+ }
442
+
443
+ validateStateManagement(catchBlock, confidence) {
444
+ return { isValid: true, reason: 'state_management' };
445
+ }
446
+
447
+ validateUnknownIntent(catchBlock, confidence) {
448
+ return {
449
+ isValid: false,
450
+ violationType: 'unclear_error_handling_intent',
451
+ message: 'Error handling intent is unclear - consider explicit error handling',
452
+ severity: 'info',
453
+ confidence: confidence,
454
+ suggestion: 'Make error handling intent clear through logging, rethrowing, or documentation'
455
+ };
456
+ }
457
+ }
458
+
459
+ module.exports = new C029SemanticAnalyzer();
@@ -0,0 +1,186 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ /**
5
+ * Rule C031 - Validation Logic Separation
6
+ * Kiểm tra logic validation có bị trộn lẫn với business logic không
7
+ */
8
+ class ValidationSeparationAnalyzer {
9
+ constructor() {
10
+ this.ruleId = 'C031';
11
+ this.ruleName = 'Validation Logic Separation';
12
+ this.category = 'architecture';
13
+ this.severity = 'warning';
14
+ this.description = 'Logic kiểm tra dữ liệu (validate) phải nằm riêng biệt';
15
+ }
16
+
17
+ analyzeFile(filePath, options = {}) {
18
+ const violations = [];
19
+
20
+ try {
21
+ if (!fs.existsSync(filePath)) {
22
+ return violations;
23
+ }
24
+
25
+ const content = fs.readFileSync(filePath, 'utf8');
26
+ const lines = content.split('\n');
27
+
28
+ // Detect functions with mixed validation and business logic
29
+ const functions = this.extractFunctions(content);
30
+
31
+ for (const func of functions) {
32
+ const validationCount = this.countValidationStatements(func.body);
33
+ const businessLogicCount = this.countBusinessLogicStatements(func.body);
34
+
35
+ // If both validation and business logic exist in same function
36
+ if (validationCount > 0 && businessLogicCount > 0) {
37
+ const maxValidationAllowed = options.maxValidationStatementsInFunction || 3;
38
+
39
+ if (validationCount > maxValidationAllowed) {
40
+ violations.push({
41
+ line: func.startLine,
42
+ column: 1,
43
+ message: `Function '${func.name}' has ${validationCount} validation statements mixed with business logic. Consider separating validation logic.`,
44
+ ruleId: this.ruleId,
45
+ severity: this.severity,
46
+ source: lines[func.startLine - 1]?.trim() || ''
47
+ });
48
+ }
49
+ }
50
+ }
51
+
52
+ } catch (error) {
53
+ console.error(`Error analyzing ${filePath}:`, error.message);
54
+ }
55
+
56
+ return violations;
57
+ }
58
+
59
+ extractFunctions(content) {
60
+ const functions = [];
61
+ const lines = content.split('\n');
62
+
63
+ // Simple function detection patterns
64
+ const functionPatterns = [
65
+ /function\s+(\w+)\s*\(/g,
66
+ /const\s+(\w+)\s*=\s*\(/g,
67
+ /(\w+)\s*\(\s*[^)]*\s*\)\s*=>/g,
68
+ /(\w+)\s*:\s*function\s*\(/g
69
+ ];
70
+
71
+ for (let i = 0; i < lines.length; i++) {
72
+ const line = lines[i];
73
+
74
+ for (const pattern of functionPatterns) {
75
+ const matches = line.matchAll(pattern);
76
+ for (const match of matches) {
77
+ const functionName = match[1];
78
+ const startLine = i + 1;
79
+
80
+ // Extract function body (simple approach)
81
+ const body = this.extractFunctionBody(lines, i);
82
+
83
+ functions.push({
84
+ name: functionName,
85
+ startLine,
86
+ body
87
+ });
88
+ }
89
+ }
90
+ }
91
+
92
+ return functions;
93
+ }
94
+
95
+ extractFunctionBody(lines, startIndex) {
96
+ let body = '';
97
+ let braceCount = 0;
98
+ let inFunction = false;
99
+
100
+ for (let i = startIndex; i < lines.length; i++) {
101
+ const line = lines[i];
102
+
103
+ if (line.includes('{')) {
104
+ braceCount += (line.match(/\{/g) || []).length;
105
+ inFunction = true;
106
+ }
107
+
108
+ if (inFunction) {
109
+ body += line + '\n';
110
+ }
111
+
112
+ if (line.includes('}')) {
113
+ braceCount -= (line.match(/\}/g) || []).length;
114
+ if (braceCount <= 0 && inFunction) {
115
+ break;
116
+ }
117
+ }
118
+ }
119
+
120
+ return body;
121
+ }
122
+
123
+ countValidationStatements(code) {
124
+ const validationPatterns = [
125
+ /if\s*\(\s*!.*\)\s*\{?\s*throw/g,
126
+ /if\s*\(.*\.\s*length\s*[<>=]\s*\d+\)/g,
127
+ /if\s*\(.*\s*==\s*null\s*\||\s*.*\s*==\s*undefined\)/g,
128
+ /if\s*\(.*\s*!\s*=\s*null\s*&&\s*.*\s*!\s*=\s*undefined\)/g,
129
+ /throw\s+new\s+Error\s*\(/g,
130
+ /assert\s*\(/g,
131
+ /validate\w*\s*\(/g,
132
+ /check\w*\s*\(/g
133
+ ];
134
+
135
+ let count = 0;
136
+ for (const pattern of validationPatterns) {
137
+ const matches = code.match(pattern);
138
+ if (matches) {
139
+ count += matches.length;
140
+ }
141
+ }
142
+
143
+ return count;
144
+ }
145
+
146
+ countBusinessLogicStatements(code) {
147
+ const businessLogicPatterns = [
148
+ /calculate\w*\s*\(/g,
149
+ /process\w*\s*\(/g,
150
+ /save\w*\s*\(/g,
151
+ /update\w*\s*\(/g,
152
+ /delete\w*\s*\(/g,
153
+ /send\w*\s*\(/g,
154
+ /return\s+\w+\s*\(/g,
155
+ /await\s+\w+\s*\(/g
156
+ ];
157
+
158
+ let count = 0;
159
+ for (const pattern of businessLogicPatterns) {
160
+ const matches = code.match(pattern);
161
+ if (matches) {
162
+ count += matches.length;
163
+ }
164
+ }
165
+
166
+ return count;
167
+ }
168
+
169
+ // Main analyze method expected by CLI
170
+ async analyze(files, language, config) {
171
+ const violations = [];
172
+
173
+ for (const filePath of files) {
174
+ try {
175
+ const fileViolations = this.analyzeFile(filePath, config);
176
+ violations.push(...fileViolations);
177
+ } catch (error) {
178
+ console.error(`Error analyzing file ${filePath}:`, error.message);
179
+ }
180
+ }
181
+
182
+ return violations;
183
+ }
184
+ }
185
+
186
+ module.exports = new ValidationSeparationAnalyzer();