@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.
- package/CHANGELOG.md +25 -0
- package/config/rules/enhanced-rules-registry.json +79 -22
- package/core/cli-program.js +1 -1
- package/core/file-targeting-service.js +15 -0
- package/core/semantic-engine.js +4 -2
- package/package.json +1 -1
- package/rules/common/C024_no_scatter_hardcoded_constants/symbol-based-analyzer.js +116 -10
- package/rules/common/C060_no_override_superclass/analyzer.js +180 -0
- package/rules/common/C060_no_override_superclass/config.json +50 -0
- package/rules/common/C060_no_override_superclass/symbol-based-analyzer.js +220 -0
- package/rules/index.js +1 -0
- package/rules/security/S020_no_eval_dynamic_code/README.md +136 -0
- package/rules/security/S020_no_eval_dynamic_code/analyzer.js +263 -0
- package/rules/security/S020_no_eval_dynamic_code/config.json +54 -0
- package/rules/security/S020_no_eval_dynamic_code/regex-based-analyzer.js +307 -0
- package/rules/security/S020_no_eval_dynamic_code/symbol-based-analyzer.js +280 -0
- package/rules/security/S024_xpath_xxe_protection/symbol-based-analyzer.js +3 -3
- package/rules/security/S025_server_side_validation/symbol-based-analyzer.js +3 -4
- package/rules/security/S030_directory_browsing_protection/README.md +128 -0
- package/rules/security/S030_directory_browsing_protection/analyzer.js +264 -0
- package/rules/security/S030_directory_browsing_protection/config.json +63 -0
- package/rules/security/S030_directory_browsing_protection/regex-based-analyzer.js +483 -0
- package/rules/security/S030_directory_browsing_protection/symbol-based-analyzer.js +539 -0
- package/rules/security/S033_samesite_session_cookies/symbol-based-analyzer.js +8 -9
- package/rules/security/S039_no_session_tokens_in_url/symbol-based-analyzer.js +33 -26
- package/rules/security/S056_log_injection_protection/analyzer.js +2 -2
- 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": "
|
|
664
|
-
"description": "
|
|
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": "
|
|
669
|
-
"
|
|
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": "
|
|
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": [
|
|
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": [
|
|
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": [
|
|
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"]
|
package/core/cli-program.js
CHANGED
|
@@ -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:
|
|
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) {
|
package/core/semantic-engine.js
CHANGED
|
@@ -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
|
|
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
|
@@ -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
|
-
|
|
158
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
205
|
+
// 4 Allow sentinel numbers
|
|
166
206
|
if (text === "0" || text === "1" || text === "-1") return true;
|
|
167
207
|
|
|
168
|
-
//
|
|
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
|
-
|
|
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
|
+
}
|