@sun-asterisk/sunlint 1.3.8 โ†’ 1.3.10

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 (27) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/config/rules/enhanced-rules-registry.json +79 -22
  3. package/core/cli-program.js +1 -1
  4. package/core/file-targeting-service.js +15 -0
  5. package/core/semantic-engine.js +4 -2
  6. package/package.json +1 -1
  7. package/rules/common/C024_no_scatter_hardcoded_constants/symbol-based-analyzer.js +116 -10
  8. package/rules/common/C060_no_override_superclass/analyzer.js +180 -0
  9. package/rules/common/C060_no_override_superclass/config.json +50 -0
  10. package/rules/common/C060_no_override_superclass/symbol-based-analyzer.js +220 -0
  11. package/rules/index.js +1 -0
  12. package/rules/security/S020_no_eval_dynamic_code/README.md +136 -0
  13. package/rules/security/S020_no_eval_dynamic_code/analyzer.js +263 -0
  14. package/rules/security/S020_no_eval_dynamic_code/config.json +54 -0
  15. package/rules/security/S020_no_eval_dynamic_code/regex-based-analyzer.js +307 -0
  16. package/rules/security/S020_no_eval_dynamic_code/symbol-based-analyzer.js +280 -0
  17. package/rules/security/S024_xpath_xxe_protection/symbol-based-analyzer.js +3 -3
  18. package/rules/security/S025_server_side_validation/symbol-based-analyzer.js +3 -4
  19. package/rules/security/S030_directory_browsing_protection/README.md +128 -0
  20. package/rules/security/S030_directory_browsing_protection/analyzer.js +264 -0
  21. package/rules/security/S030_directory_browsing_protection/config.json +63 -0
  22. package/rules/security/S030_directory_browsing_protection/regex-based-analyzer.js +483 -0
  23. package/rules/security/S030_directory_browsing_protection/symbol-based-analyzer.js +539 -0
  24. package/rules/security/S033_samesite_session_cookies/symbol-based-analyzer.js +8 -9
  25. package/rules/security/S039_no_session_tokens_in_url/symbol-based-analyzer.js +33 -26
  26. package/rules/security/S056_log_injection_protection/analyzer.js +2 -2
  27. package/rules/security/S056_log_injection_protection/symbol-based-analyzer.js +77 -118
package/CHANGELOG.md CHANGED
@@ -2,6 +2,31 @@
2
2
 
3
3
  ---
4
4
 
5
+ ## ๐Ÿ”ง **v1.3.9 - File Targeting Regression Fix (October 2, 2025)**
6
+
7
+ **Release Date**: October 2, 2025
8
+ **Type**: Bug Fix
9
+ **Branch**: `feature.sunlint.heuristic_rule_c065`
10
+
11
+ ### ๐Ÿ› **Critical Bug Fixes**
12
+ - **FIXED**: File targeting regression where user-specified source directories were incorrectly optimized
13
+ - **Issue**: When using `--input=examples/project-samples/replace-fe/src`, file count dropped from 2.2K to 254 files
14
+ - **Root Cause**: `optimizeProjectPaths` function incorrectly treated user-specified source directories as project roots
15
+ - **Solution**: Added source directory detection logic to bypass optimization for `src`, `lib`, `app`, `packages`, `test` directories
16
+ - **Impact**: Full file coverage restored - all 1507 .tsx files now properly included
17
+
18
+ ### โšก **Performance Improvements**
19
+ - **ENHANCED**: File targeting logic with smart source directory detection
20
+ - **OPTIMIZED**: Direct targeting for user-specified source paths
21
+
22
+ ### ๐Ÿ“ **Technical Details**
23
+ - Modified `file-targeting-service.js` `optimizeProjectPaths` function
24
+ - Added `sourceDirectoryNames` array for known source directory patterns
25
+ - Implemented basename checking to detect when users specify source directories directly
26
+ - Maintained backward compatibility with existing project structure detection
27
+
28
+ ---
29
+
5
30
  ## ๐Ÿงช **v1.3.8 - C065 Rule Enhancement & Advanced Context Analysis (October 1, 2025)**
6
31
 
7
32
  **Release Date**: October 1, 2025
@@ -375,6 +375,24 @@
375
375
  }
376
376
  }
377
377
  },
378
+ "C060": {
379
+ "name": "Do not override superclass methods and ignore critical logic",
380
+ "description": "Preserve important behavior or lifecycle logic defined in the superclass to ensure correctness and prevent silent errors.",
381
+ "category": "logging",
382
+ "severity": "warning",
383
+ "languages": ["typescript", "javascript", "dart"],
384
+ "analyzer": "./rules/common/C060_no_override_superclass/analyzer.js",
385
+ "version": "1.0.0",
386
+ "status": "stable",
387
+ "tags": ["logging", "production", "debugging", "console"],
388
+ "strategy": {
389
+ "preferred": "regex",
390
+ "fallbacks": ["regex"],
391
+ "accuracy": {
392
+ "regex": 90
393
+ }
394
+ }
395
+ },
378
396
  "S001": {
379
397
  "name": "Fail Securely",
380
398
  "description": "Verify that if there is an error in access control, the system fails securely",
@@ -660,16 +678,51 @@
660
678
  "tags": ["security", "email", "injection"]
661
679
  },
662
680
  "S020": {
663
- "name": "No Eval Dynamic Execution",
664
- "description": "Prevent eval and dynamic code execution",
681
+ "name": "Avoid using eval() or executing dynamic code",
682
+ "description": "Avoid using eval() or executing dynamic code as it can lead to code injection vulnerabilities and compromise application security.",
665
683
  "category": "security",
666
684
  "severity": "error",
667
685
  "languages": ["typescript", "javascript"],
668
- "analyzer": "eslint",
669
- "eslintRule": "custom/typescript_s020",
686
+ "analyzer": "./rules/security/S020_no_eval_dynamic_code/analyzer.js",
687
+ "config": "./rules/security/S020_no_eval_dynamic_code/config.json",
670
688
  "version": "1.0.0",
671
- "status": "stable",
672
- "tags": ["security", "eval", "dynamic-execution"]
689
+ "status": "experimental",
690
+ "tags": ["security", "eval", "dynamic-execution", "code-injection"],
691
+ "strategy": {
692
+ "preferred": "ast",
693
+ "fallbacks": ["ast", "regex"],
694
+ "accuracy": { "ast": 95, "regex": 85 }
695
+ },
696
+ "engineMappings": {
697
+ "heuristic": ["rules/security/S020_no_eval_dynamic_code/analyzer.js"]
698
+ }
699
+ },
700
+ "S030": {
701
+ "name": "Disable directory browsing and protect sensitive metadata files",
702
+ "description": "Disable directory browsing and protect sensitive metadata files (.git/, .env, config files, etc.) to prevent information disclosure and potential security vulnerabilities.",
703
+ "category": "security",
704
+ "severity": "error",
705
+ "languages": ["typescript", "javascript"],
706
+ "analyzer": "./rules/security/S030_directory_browsing_protection/analyzer.js",
707
+ "config": "./rules/security/S030_directory_browsing_protection/config.json",
708
+ "version": "1.0.0",
709
+ "status": "experimental",
710
+ "tags": [
711
+ "security",
712
+ "directory-browsing",
713
+ "information-disclosure",
714
+ "metadata-protection"
715
+ ],
716
+ "strategy": {
717
+ "preferred": "ast",
718
+ "fallbacks": ["ast", "regex"],
719
+ "accuracy": { "ast": 90, "regex": 75 }
720
+ },
721
+ "engineMappings": {
722
+ "heuristic": [
723
+ "rules/security/S030_directory_browsing_protection/analyzer.js"
724
+ ]
725
+ }
673
726
  },
674
727
  "S022": {
675
728
  "name": "Output Encoding Required",
@@ -791,18 +844,6 @@
791
844
  "status": "stable",
792
845
  "tags": ["security", "csrf", "protection"]
793
846
  },
794
- "S030": {
795
- "name": "No Directory Browsing",
796
- "description": "Prevent directory browsing vulnerabilities",
797
- "category": "security",
798
- "severity": "error",
799
- "languages": ["typescript", "javascript"],
800
- "analyzer": "eslint",
801
- "eslintRule": "custom/typescript_s030",
802
- "version": "1.0.0",
803
- "status": "stable",
804
- "tags": ["security", "directory-browsing", "information-disclosure"]
805
- },
806
847
  "S031": {
807
848
  "name": "Set Secure flag for Session Cookies",
808
849
  "description": "Set Secure flag for Session Cookies to protect via HTTPS. This ensures cookies are only transmitted over secure connections, preventing interception.",
@@ -1141,7 +1182,7 @@
1141
1182
  "name": "Password length policy enforcement (12-64 chars recommended, reject >128)",
1142
1183
  "description": "Enforce strong password length policies with multi-signal detection. Prevent weak validators, missing limits, and FE/BE mismatches.",
1143
1184
  "category": "security",
1144
- "severity": "error",
1185
+ "severity": "error",
1145
1186
  "languages": ["typescript", "javascript"],
1146
1187
  "analyzer": "./rules/security/S051_password_length_policy/analyzer.js",
1147
1188
  "config": "./rules/security/S051_password_length_policy/config.json",
@@ -1151,7 +1192,9 @@
1151
1192
  "tags": ["security", "password", "validation", "length", "policy"],
1152
1193
  "engineMappings": {
1153
1194
  "eslint": ["custom/typescript_s051"],
1154
- "heuristic": ["./rules/security/S051_password_length_policy/analyzer.js"]
1195
+ "heuristic": [
1196
+ "./rules/security/S051_password_length_policy/analyzer.js"
1197
+ ]
1155
1198
  }
1156
1199
  },
1157
1200
  "C065": {
@@ -1191,13 +1234,27 @@
1191
1234
  "description": "Prevent use of default or shared accounts. Enforce per-user identities, initial password change, and disabling well-known built-ins.",
1192
1235
  "category": "security",
1193
1236
  "severity": "error",
1194
- "languages": ["typescript", "javascript", "sql", "terraform", "yaml", "dockerfile", "all"],
1237
+ "languages": [
1238
+ "typescript",
1239
+ "javascript",
1240
+ "sql",
1241
+ "terraform",
1242
+ "yaml",
1243
+ "dockerfile",
1244
+ "all"
1245
+ ],
1195
1246
  "analyzer": "./rules/security/S054_no_default_accounts/analyzer.js",
1196
1247
  "config": "./rules/security/S054_no_default_accounts/config.json",
1197
1248
  "eslintRule": "custom/typescript_s054",
1198
1249
  "version": "1.0.0",
1199
1250
  "status": "stable",
1200
- "tags": ["security", "accounts", "default", "authentication", "authorization"],
1251
+ "tags": [
1252
+ "security",
1253
+ "accounts",
1254
+ "default",
1255
+ "authentication",
1256
+ "authorization"
1257
+ ],
1201
1258
  "engines": {
1202
1259
  "eslint": ["custom/typescript_s054"],
1203
1260
  "heuristic": ["./rules/security/S054_no_default_accounts/analyzer.js"]
@@ -71,7 +71,7 @@ function createCliProgram() {
71
71
  .option('--debug', 'Enable debug mode')
72
72
  .option('--ai', 'Enable AI-powered analysis')
73
73
  .option('--no-ai', 'Force disable AI analysis')
74
- .option('--max-semantic-files <number>', 'Symbol table file limit for TypeScript analysis (default: auto)', '0')
74
+ .option('--max-semantic-files <number>', 'Symbol table file limit for TypeScript analysis (default: 1000, -1 for unlimited)')
75
75
  .option('--list-engines', 'List available analysis engines');
76
76
 
77
77
  // ESLint Integration options
@@ -163,6 +163,21 @@ class FileTargetingService {
163
163
  for (const inputPath of inputPaths) {
164
164
  // If targeting entire project directory, try to find source/test subdirectories
165
165
  if (fs.existsSync(inputPath) && fs.statSync(inputPath).isDirectory()) {
166
+ const absoluteInputPath = path.resolve(inputPath);
167
+ const inputDirName = path.basename(absoluteInputPath);
168
+
169
+ // If user already specified a source directory (src, lib, app, packages, test, etc.),
170
+ // don't try to optimize further - use it as is
171
+ const sourceDirectoryNames = ['src', 'lib', 'app', 'packages', 'test', 'tests', '__tests__', 'spec', 'specs'];
172
+ if (sourceDirectoryNames.includes(inputDirName)) {
173
+ if (cliOptions.verbose) {
174
+ console.log(chalk.blue(`๐ŸŽฏ Direct targeting: Using specified source directory ${inputDirName}`));
175
+ }
176
+ optimizedPaths.push(inputPath);
177
+ continue;
178
+ }
179
+
180
+ // Only optimize if this appears to be a project root directory
166
181
  const projectOptimization = this.findProjectSourceDirs(inputPath, cliOptions);
167
182
  if (projectOptimization.length > 0) {
168
183
  if (cliOptions.verbose) {
@@ -102,7 +102,7 @@ class SemanticEngine {
102
102
  );
103
103
 
104
104
  if (targetFiles) {
105
- console.log(`๐ŸŽฏ Targeted files received: ${targetFiles.length} total, ${semanticFiles.length} TS/JS files`);
105
+ console.log(`๐ŸŽฏ Targeted files received: ${targetFiles.length} total, ${semanticFiles.length} TypeScript/JavaScript files (TS/TSX/JS/JSX)`);
106
106
  if (semanticFiles.length < 10) {
107
107
  console.log(` Files: ${semanticFiles.map(f => path.basename(f)).join(', ')}`);
108
108
  }
@@ -119,7 +119,9 @@ class SemanticEngine {
119
119
  } else if (userMaxFiles === 0) {
120
120
  // Disable semantic analysis
121
121
  maxFiles = 0;
122
- console.log(`๐Ÿ”ง Semantic Engine config: DISABLED semantic analysis (heuristic only)`);
122
+ console.log(`๐Ÿ”ง Semantic Engine config: DISABLED semantic analysis (heuristic only mode)`);
123
+ console.log(` ๐Ÿ’ก Semantic analysis explicitly disabled with --max-semantic-files=0`);
124
+ console.log(` ๐Ÿ’ก To enable: omit the option (default: 1000) or use --max-semantic-files=1000 (or higher)`);
123
125
  } else if (userMaxFiles > 0) {
124
126
  // User-specified limit
125
127
  maxFiles = Math.min(userMaxFiles, semanticFiles.length);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sun-asterisk/sunlint",
3
- "version": "1.3.8",
3
+ "version": "1.3.10",
4
4
  "description": "โ˜€๏ธ SunLint - Multi-language static analysis tool for code quality and security | Sun* Engineering Standards",
5
5
  "main": "cli.js",
6
6
  "bin": {
@@ -48,6 +48,14 @@ class C024SymbolBasedAnalyzer {
48
48
  }
49
49
 
50
50
  try {
51
+ // skip ignored files
52
+ if (this.isIgnoredFile(filePath)) {
53
+ if (verbose) {
54
+ console.log(`๐Ÿ” [C024 Symbol-Based] Skipping ignored file: ${filePath}`);
55
+ }
56
+ return violations;
57
+ }
58
+
51
59
  const sourceFile = this.semanticEngine.project.getSourceFile(filePath);
52
60
  if (!sourceFile) {
53
61
  return violations;
@@ -99,9 +107,7 @@ class C024SymbolBasedAnalyzer {
99
107
  const kind = node.getKind();
100
108
  if (
101
109
  kind === SyntaxKind.StringLiteral ||
102
- kind === SyntaxKind.NumericLiteral ||
103
- kind === SyntaxKind.TrueKeyword ||
104
- kind === SyntaxKind.FalseKeyword
110
+ kind === SyntaxKind.NumericLiteral
105
111
  ) {
106
112
  const text = node.getText().replace(/['"`]/g, ""); // strip quotes
107
113
  if (this.isAllowedLiteral(node, text)) return;
@@ -120,9 +126,27 @@ class C024SymbolBasedAnalyzer {
120
126
  const kind = node.getKind();
121
127
  if (kind === SyntaxKind.VariableDeclaration) {
122
128
  const parentKind = node.getParent()?.getKind();
129
+ // Skip detection for `for ... of` loop variable
130
+ const loopAncestor = node.getFirstAncestor((ancestor) => {
131
+ const kind = ancestor.getKind?.();
132
+ return (
133
+ kind === SyntaxKind.ForOfStatement ||
134
+ kind === SyntaxKind.ForInStatement ||
135
+ kind === SyntaxKind.ForStatement ||
136
+ kind === SyntaxKind.WhileStatement ||
137
+ kind === SyntaxKind.DoStatement ||
138
+ kind === SyntaxKind.SwitchStatement
139
+ );
140
+ });
141
+
142
+ if (loopAncestor) {
143
+ return; // skip for all loop/switch contexts, no matter how nested
144
+ }
145
+
123
146
  if (
124
147
  parentKind === SyntaxKind.VariableDeclarationList &&
125
- node.getParent().getDeclarationKind() === "const"
148
+ node.getParent().getDeclarationKind() === "const" &&
149
+ !node.getInitializer()
126
150
  ) {
127
151
  this.pushViolation(
128
152
  violations,
@@ -154,27 +178,109 @@ class C024SymbolBasedAnalyzer {
154
178
 
155
179
  // --- helper: allow safe literals ---
156
180
  isAllowedLiteral(node, text) {
157
- // skip imports
158
- if (node.getParent()?.getKind() === SyntaxKind.ImportDeclaration) {
181
+ const parent = node.getParent();
182
+
183
+ // 1 Skip imports/exports
184
+ if (parent?.getKind() === SyntaxKind.ImportDeclaration) return true;
185
+ if (parent?.getKind() === SyntaxKind.ExportDeclaration) return true;
186
+
187
+ // 2 Skip literals that are inside call expressions (direct or nested)
188
+ if (
189
+ parent?.getKind() === SyntaxKind.CallExpression ||
190
+ parent?.getFirstAncestorByKind(SyntaxKind.CallExpression)
191
+ ) {
159
192
  return true;
160
193
  }
161
194
 
162
- // allow short strings
195
+ if (
196
+ parent?.getKind() === SyntaxKind.ElementAccessExpression &&
197
+ parent.getArgumentExpression?.() === node
198
+ ) {
199
+ return true; // skip array/object key
200
+ }
201
+
202
+ // 3 Allow short strings
163
203
  if (typeof text === "string" && text.length <= 1) return true;
164
204
 
165
- // allow sentinel numbers
205
+ // 4 Allow sentinel numbers
166
206
  if (text === "0" || text === "1" || text === "-1") return true;
167
207
 
168
- // allow known safe strings (like "UNKNOWN")
208
+ // 5 Allow known safe strings (like "UNKNOWN")
169
209
  if (this.safeStrings.includes(text)) return true;
170
210
 
211
+ // 6 Allow SQL-style placeholders (:variable) inside string/template
212
+ if (typeof text === "string" && /:\w+/.test(text)) {
213
+ return true;
214
+ }
215
+
171
216
  return false;
172
217
  }
173
218
 
174
219
  // helper to check if file is a constants file
175
220
  isConstantsFile(filePath) {
176
221
  const lower = filePath.toLowerCase();
177
- return lower.endsWith("constants.ts") || lower.includes("/constants/");
222
+
223
+ // common suffixes/patterns for utility or structural files
224
+ const ignoredSuffixes = [
225
+ ".constants.ts",
226
+ ".const.ts",
227
+ ".enum.ts",
228
+ ".interface.ts",
229
+ ".response.ts",
230
+ ".request.ts",
231
+ ".res.ts",
232
+ ".req.ts",
233
+ ];
234
+
235
+ // 1 direct suffix match
236
+ if (ignoredSuffixes.some(suffix => lower.endsWith(suffix))) {
237
+ return true;
238
+ }
239
+
240
+ // 2 matches dto.xxx.ts (multi-dot dto files)
241
+ if (/\.dto\.[^.]+\.ts$/.test(lower)) {
242
+ return true;
243
+ }
244
+
245
+ // 3 matches folder-based conventions
246
+ if (
247
+ lower.includes("/constants/") ||
248
+ lower.includes("/enums/") ||
249
+ lower.includes("/interfaces/")
250
+ ) {
251
+ return true;
252
+ }
253
+
254
+ return false;
255
+ }
256
+
257
+
258
+ isIgnoredFile(filePath) {
259
+ const ignoredPatterns = [
260
+ /\.test\./i,
261
+ /\.tests\./i,
262
+ /\.spec\./i,
263
+ /\.mock\./i,
264
+ /\.css$/i,
265
+ /\.scss$/i,
266
+ /\.html$/i,
267
+ /\.json$/i,
268
+ /\.md$/i,
269
+ /\.svg$/i,
270
+ /\.png$/i,
271
+ /\.jpg$/i,
272
+ /\.jpeg$/i,
273
+ /\.gif$/i,
274
+ /\.bmp$/i,
275
+ /\.ico$/i,
276
+ /\.lock$/i,
277
+ /\.log$/i,
278
+ /\/test\//i,
279
+ /\/tests\//i,
280
+ /\/spec\//i
281
+ ];
282
+
283
+ return ignoredPatterns.some((regex) => regex.test(filePath));
178
284
  }
179
285
  }
180
286
 
@@ -0,0 +1,180 @@
1
+ /**
2
+ * C060 Main Analyzer - Do not override superclass methods and ignore critical logic
3
+ * Primary: Preserve important behavior or lifecycle logic defined in the superclass to ensure correctness and prevent silent errors.
4
+ * Fallback: Regex-based for all other cases
5
+ */
6
+
7
+ const C060SymbolBasedAnalyzer = require('./symbol-based-analyzer');
8
+
9
+ class C060Analyzer {
10
+ constructor(options = {}) {
11
+ if (process.env.SUNLINT_DEBUG) {
12
+ console.log(`๐Ÿ”ง [C060] Constructor called with options:`, !!options);
13
+ console.log(`๐Ÿ”ง [C060] Options type:`, typeof options, Object.keys(options || {}));
14
+ }
15
+
16
+ this.ruleId = 'C060';
17
+ this.ruleName = 'Do not override superclass methods and ignore critical logic';
18
+ this.description = 'Preserve important behavior or lifecycle logic defined in the superclass to ensure correctness and prevent silent errors.';
19
+ this.semanticEngine = options.semanticEngine || null;
20
+ this.verbose = options.verbose || false;
21
+
22
+ // Configuration
23
+ this.config = {
24
+ useSymbolBased: true, // Primary approach
25
+ fallbackToRegex: false, // Only when symbol fails completely
26
+ symbolBasedOnly: false // Can be set to true for pure mode
27
+ };
28
+
29
+ // Initialize both analyzers
30
+ try {
31
+ this.symbolAnalyzer = new C060SymbolBasedAnalyzer(this.semanticEngine);
32
+ if (process.env.SUNLINT_DEBUG) {
33
+ console.log(`๐Ÿ”ง [C060] Symbol analyzer created successfully`);
34
+ }
35
+ } catch (error) {
36
+ console.error(`๐Ÿ”ง [C060] Error creating symbol analyzer:`, error);
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Initialize with semantic engine
42
+ */
43
+ async initialize(semanticEngine = null) {
44
+ if (semanticEngine) {
45
+ this.semanticEngine = semanticEngine;
46
+ }
47
+ this.verbose = semanticEngine?.verbose || false;
48
+
49
+ // Initialize both analyzers
50
+ await this.symbolAnalyzer.initialize(semanticEngine);
51
+
52
+ // Ensure verbose flag is propagated
53
+ this.symbolAnalyzer.verbose = this.verbose;
54
+
55
+ if (this.verbose) {
56
+ console.log(`๐Ÿ”ง [C060 Hybrid] Analyzer initialized - verbose: ${this.verbose}`);
57
+ }
58
+ }
59
+
60
+ async analyze(files, language, options = {}) {
61
+ if (process.env.SUNLINT_DEBUG) {
62
+ console.log(`๐Ÿ”ง [C060] analyze() method called with ${files.length} files, language: ${language}`);
63
+ }
64
+
65
+ const violations = [];
66
+
67
+ for (const filePath of files) {
68
+ try {
69
+ if (process.env.SUNLINT_DEBUG) {
70
+ console.log(`๐Ÿ”ง [C060] Processing file: ${filePath}`);
71
+ }
72
+
73
+ const fileViolations = await this.analyzeFile(filePath, options);
74
+ violations.push(...fileViolations);
75
+
76
+ if (process.env.SUNLINT_DEBUG) {
77
+ console.log(`๐Ÿ”ง [C060] File ${filePath}: Found ${fileViolations.length} violations`);
78
+ }
79
+ } catch (error) {
80
+ console.warn(`โŒ [C060] Analysis failed for ${filePath}:`, error.message);
81
+ }
82
+ }
83
+
84
+ if (process.env.SUNLINT_DEBUG) {
85
+ console.log(`๐Ÿ”ง [C060] Total violations found: ${violations.length}`);
86
+ }
87
+
88
+ return violations;
89
+ }
90
+
91
+ async analyzeFile(filePath, options = {}) {
92
+ if (process.env.SUNLINT_DEBUG) {
93
+ console.log(`๐Ÿ”ง [C060] analyzeFile() called for: ${filePath}`);
94
+ }
95
+
96
+ // 1. Try Symbol-based analysis first (primary)
97
+ if (this.config.useSymbolBased &&
98
+ this.semanticEngine?.project &&
99
+ this.semanticEngine?.initialized) {
100
+ try {
101
+ if (process.env.SUNLINT_DEBUG) {
102
+ console.log(`๐Ÿ”ง [C060] Trying symbol-based analysis...`);
103
+ }
104
+ const sourceFile = this.semanticEngine.project.getSourceFile(filePath);
105
+ if (sourceFile) {
106
+ if (process.env.SUNLINT_DEBUG) {
107
+ console.log(`๐Ÿ”ง [C060] Source file found, analyzing with symbol-based...`);
108
+ }
109
+ const violations = await this.symbolAnalyzer.analyzeFileWithSymbols(filePath, { ...options, verbose: options.verbose });
110
+
111
+ // Mark violations with analysis strategy
112
+ violations.forEach(v => v.analysisStrategy = 'symbol-based');
113
+
114
+ if (process.env.SUNLINT_DEBUG) {
115
+ console.log(`โœ… [C060] Symbol-based analysis: ${violations.length} violations`);
116
+ }
117
+ return violations; // Return even if 0 violations - symbol analysis completed successfully
118
+ } else {
119
+ if (process.env.SUNLINT_DEBUG) {
120
+ console.log(`โš ๏ธ [C060] Source file not found in project`);
121
+ }
122
+ }
123
+ } catch (error) {
124
+ console.warn(`โš ๏ธ [C060] Symbol analysis failed: ${error.message}`);
125
+ // Continue to fallback
126
+ }
127
+ } else {
128
+ if (process.env.SUNLINT_DEBUG) {
129
+ console.log(`๐Ÿ”„ [C060] Symbol analysis conditions check:`);
130
+ console.log(` - useSymbolBased: ${this.config.useSymbolBased}`);
131
+ console.log(` - semanticEngine: ${!!this.semanticEngine}`);
132
+ console.log(` - semanticEngine.project: ${!!this.semanticEngine?.project}`);
133
+ console.log(` - semanticEngine.initialized: ${this.semanticEngine?.initialized}`);
134
+ console.log(`๐Ÿ”„ [C060] Symbol analysis unavailable, using regex fallback`);
135
+ }
136
+ }
137
+
138
+ if (options?.verbose) {
139
+ console.log(`๐Ÿ”ง [C060] No analysis methods succeeded, returning empty`);
140
+ }
141
+ return [];
142
+ }
143
+
144
+ async analyzeFileBasic(filePath, options = {}) {
145
+ console.log(`๐Ÿ”ง [C060] analyzeFileBasic() called for: ${filePath}`);
146
+ console.log(`๐Ÿ”ง [C060] semanticEngine exists: ${!!this.semanticEngine}`);
147
+ console.log(`๐Ÿ”ง [C060] symbolAnalyzer exists: ${!!this.symbolAnalyzer}`);
148
+
149
+ try {
150
+ // Try symbol-based analysis first
151
+ if (this.semanticEngine?.isSymbolEngineReady?.() &&
152
+ this.semanticEngine.project) {
153
+
154
+ if (this.verbose) {
155
+ console.log(`๐Ÿ” [C060] Using symbol-based analysis for ${filePath}`);
156
+ }
157
+
158
+ const violations = await this.symbolAnalyzer.analyzeFileBasic(filePath, options);
159
+ return violations;
160
+ }
161
+ } catch (error) {
162
+ if (this.verbose) {
163
+ console.warn(`โš ๏ธ [C060] Symbol analysis failed: ${error.message}`);
164
+ }
165
+ }
166
+ }
167
+
168
+ /**
169
+ * Methods for compatibility with different engine invocation patterns
170
+ */
171
+ async analyzeFileWithSymbols(filePath, options = {}) {
172
+ return this.analyzeFile(filePath, options);
173
+ }
174
+
175
+ async analyzeWithSemantics(filePath, options = {}) {
176
+ return this.analyzeFile(filePath, options);
177
+ }
178
+ }
179
+
180
+ module.exports = C060Analyzer;
@@ -0,0 +1,50 @@
1
+ {
2
+ "id": "C060",
3
+ "name": "C060_do_not_override_superclass_methods_and_ignore_critical_logic",
4
+ "category": "architecture",
5
+ "description": "C060 - Do not override superclass methods and ignore critical logic",
6
+ "severity": "warning",
7
+ "enabled": true,
8
+ "semantic": {
9
+ "enabled": true,
10
+ "priority": "high",
11
+ "fallback": "heuristic"
12
+ },
13
+ "patterns": {
14
+ "include": [
15
+ "**/*.js",
16
+ "**/*.ts",
17
+ "**/*.jsx",
18
+ "**/*.tsx"
19
+ ],
20
+ "exclude": [
21
+ "**/*.test.*",
22
+ "**/*.spec.*",
23
+ "**/*.mock.*",
24
+ "**/test/**",
25
+ "**/tests/**",
26
+ "**/spec/**"
27
+ ]
28
+ },
29
+ "options": {
30
+ "strictMode": false,
31
+ "allowedDbMethods": [],
32
+ "repositoryPatterns": [
33
+ "*Repository*",
34
+ "*Repo*",
35
+ "*DAO*",
36
+ "*Store*"
37
+ ],
38
+ "servicePatterns": [
39
+ "*Service*",
40
+ "*UseCase*",
41
+ "*Handler*",
42
+ "*Manager*"
43
+ ],
44
+ "complexityThreshold": {
45
+ "methodLength": 200,
46
+ "cyclomaticComplexity": 5,
47
+ "nestedDepth": 3
48
+ }
49
+ }
50
+ }