@sun-asterisk/sunlint 1.3.8 โ†’ 1.3.9

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 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
@@ -660,16 +660,51 @@
660
660
  "tags": ["security", "email", "injection"]
661
661
  },
662
662
  "S020": {
663
- "name": "No Eval Dynamic Execution",
664
- "description": "Prevent eval and dynamic code execution",
663
+ "name": "Avoid using eval() or executing dynamic code",
664
+ "description": "Avoid using eval() or executing dynamic code as it can lead to code injection vulnerabilities and compromise application security.",
665
665
  "category": "security",
666
666
  "severity": "error",
667
667
  "languages": ["typescript", "javascript"],
668
- "analyzer": "eslint",
669
- "eslintRule": "custom/typescript_s020",
668
+ "analyzer": "./rules/security/S020_no_eval_dynamic_code/analyzer.js",
669
+ "config": "./rules/security/S020_no_eval_dynamic_code/config.json",
670
670
  "version": "1.0.0",
671
- "status": "stable",
672
- "tags": ["security", "eval", "dynamic-execution"]
671
+ "status": "experimental",
672
+ "tags": ["security", "eval", "dynamic-execution", "code-injection"],
673
+ "strategy": {
674
+ "preferred": "ast",
675
+ "fallbacks": ["ast", "regex"],
676
+ "accuracy": { "ast": 95, "regex": 85 }
677
+ },
678
+ "engineMappings": {
679
+ "heuristic": ["rules/security/S020_no_eval_dynamic_code/analyzer.js"]
680
+ }
681
+ },
682
+ "S030": {
683
+ "name": "Disable directory browsing and protect sensitive metadata files",
684
+ "description": "Disable directory browsing and protect sensitive metadata files (.git/, .env, config files, etc.) to prevent information disclosure and potential security vulnerabilities.",
685
+ "category": "security",
686
+ "severity": "error",
687
+ "languages": ["typescript", "javascript"],
688
+ "analyzer": "./rules/security/S030_directory_browsing_protection/analyzer.js",
689
+ "config": "./rules/security/S030_directory_browsing_protection/config.json",
690
+ "version": "1.0.0",
691
+ "status": "experimental",
692
+ "tags": [
693
+ "security",
694
+ "directory-browsing",
695
+ "information-disclosure",
696
+ "metadata-protection"
697
+ ],
698
+ "strategy": {
699
+ "preferred": "ast",
700
+ "fallbacks": ["ast", "regex"],
701
+ "accuracy": { "ast": 90, "regex": 75 }
702
+ },
703
+ "engineMappings": {
704
+ "heuristic": [
705
+ "rules/security/S030_directory_browsing_protection/analyzer.js"
706
+ ]
707
+ }
673
708
  },
674
709
  "S022": {
675
710
  "name": "Output Encoding Required",
@@ -791,18 +826,6 @@
791
826
  "status": "stable",
792
827
  "tags": ["security", "csrf", "protection"]
793
828
  },
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
829
  "S031": {
807
830
  "name": "Set Secure flag for Session Cookies",
808
831
  "description": "Set Secure flag for Session Cookies to protect via HTTPS. This ensures cookies are only transmitted over secure connections, preventing interception.",
@@ -1141,7 +1164,7 @@
1141
1164
  "name": "Password length policy enforcement (12-64 chars recommended, reject >128)",
1142
1165
  "description": "Enforce strong password length policies with multi-signal detection. Prevent weak validators, missing limits, and FE/BE mismatches.",
1143
1166
  "category": "security",
1144
- "severity": "error",
1167
+ "severity": "error",
1145
1168
  "languages": ["typescript", "javascript"],
1146
1169
  "analyzer": "./rules/security/S051_password_length_policy/analyzer.js",
1147
1170
  "config": "./rules/security/S051_password_length_policy/config.json",
@@ -1151,7 +1174,9 @@
1151
1174
  "tags": ["security", "password", "validation", "length", "policy"],
1152
1175
  "engineMappings": {
1153
1176
  "eslint": ["custom/typescript_s051"],
1154
- "heuristic": ["./rules/security/S051_password_length_policy/analyzer.js"]
1177
+ "heuristic": [
1178
+ "./rules/security/S051_password_length_policy/analyzer.js"
1179
+ ]
1155
1180
  }
1156
1181
  },
1157
1182
  "C065": {
@@ -1191,13 +1216,27 @@
1191
1216
  "description": "Prevent use of default or shared accounts. Enforce per-user identities, initial password change, and disabling well-known built-ins.",
1192
1217
  "category": "security",
1193
1218
  "severity": "error",
1194
- "languages": ["typescript", "javascript", "sql", "terraform", "yaml", "dockerfile", "all"],
1219
+ "languages": [
1220
+ "typescript",
1221
+ "javascript",
1222
+ "sql",
1223
+ "terraform",
1224
+ "yaml",
1225
+ "dockerfile",
1226
+ "all"
1227
+ ],
1195
1228
  "analyzer": "./rules/security/S054_no_default_accounts/analyzer.js",
1196
1229
  "config": "./rules/security/S054_no_default_accounts/config.json",
1197
1230
  "eslintRule": "custom/typescript_s054",
1198
1231
  "version": "1.0.0",
1199
1232
  "status": "stable",
1200
- "tags": ["security", "accounts", "default", "authentication", "authorization"],
1233
+ "tags": [
1234
+ "security",
1235
+ "accounts",
1236
+ "default",
1237
+ "authentication",
1238
+ "authorization"
1239
+ ],
1201
1240
  "engines": {
1202
1241
  "eslint": ["custom/typescript_s054"],
1203
1242
  "heuristic": ["./rules/security/S054_no_default_accounts/analyzer.js"]
@@ -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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sun-asterisk/sunlint",
3
- "version": "1.3.8",
3
+ "version": "1.3.9",
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": {
@@ -0,0 +1,136 @@
1
+ # S020 - Avoid using eval() or executing dynamic code
2
+
3
+ ## Overview
4
+
5
+ This rule detects and prevents the use of `eval()` and other dynamic code execution mechanisms that can lead to security vulnerabilities, particularly code injection attacks.
6
+
7
+ ## Why This Rule Matters
8
+
9
+ Dynamic code execution through `eval()` and similar functions poses significant security risks:
10
+
11
+ 1. **Code Injection Vulnerabilities**: Untrusted input can be executed as code
12
+ 2. **Performance Issues**: Dynamic code execution is slower than static code
13
+ 3. **Debugging Difficulties**: Dynamic code is harder to debug and analyze
14
+ 4. **Security Auditing**: Static analysis tools cannot analyze dynamically generated code
15
+
16
+ ## What This Rule Detects
17
+
18
+ ### โŒ Dangerous Functions
19
+
20
+ - `eval()` - Direct code execution
21
+ - `new Function()` - Function constructor with string
22
+ - `setTimeout(string)` - Timer with string code
23
+ - `setInterval(string)` - Interval with string code
24
+ - `execScript()` - Legacy IE function
25
+ - `setImmediate(string)` - Immediate execution with string
26
+
27
+ ### โŒ Global Access Patterns
28
+
29
+ - `window.eval()`
30
+ - `global.eval()`
31
+ - `globalThis.eval()`
32
+ - `self.eval()`
33
+
34
+ ### โŒ Suspicious Variable Patterns
35
+
36
+ Variables containing dynamic code indicators:
37
+
38
+ - Variables named with `code`, `script`, `expression`, `formula`, `template`, `eval`
39
+
40
+ ## Examples
41
+
42
+ ### โŒ Violations
43
+
44
+ ```javascript
45
+ // Direct eval usage
46
+ eval("console.log('Hello')"); // ERROR
47
+
48
+ // Function constructor
49
+ const fn = new Function("return 1 + 1"); // ERROR
50
+
51
+ // setTimeout with string
52
+ setTimeout("console.log('test')", 1000); // WARNING
53
+
54
+ // Global eval access
55
+ window.eval("alert('test')"); // ERROR
56
+
57
+ // Dynamic code variables
58
+ const userCode = req.body.code;
59
+ eval(userCode); // ERROR - code injection risk
60
+ ```
61
+
62
+ ### โœ… Safe Alternatives
63
+
64
+ ```javascript
65
+ // Instead of eval(), use proper parsing and validation
66
+ const result = JSON.parse(jsonString);
67
+
68
+ // Instead of Function constructor, use proper function definitions
69
+ function add(a, b) {
70
+ return a + b;
71
+ }
72
+
73
+ // Instead of setTimeout with string, use function reference
74
+ setTimeout(() => console.log("test"), 1000);
75
+
76
+ // Instead of dynamic code execution, use configuration objects
77
+ const actions = {
78
+ add: (a, b) => a + b,
79
+ multiply: (a, b) => a * b,
80
+ };
81
+ const result = actions[operation](x, y);
82
+
83
+ // For template engines, use safe templating libraries
84
+ const template = handlebars.compile(templateString);
85
+ const result = template(data);
86
+ ```
87
+
88
+ ## Configuration
89
+
90
+ The rule can be configured in `config.json`:
91
+
92
+ ```json
93
+ {
94
+ "validation": {
95
+ "dangerousFunctions": ["eval", "Function", "setTimeout", "setInterval"],
96
+ "dangerousPatterns": ["new Function", "window.eval", "global.eval"],
97
+ "dynamicCodeIndicators": ["code", "script", "expression", "formula"]
98
+ }
99
+ }
100
+ ```
101
+
102
+ ## Security Best Practices
103
+
104
+ 1. **Input Validation**: Always validate and sanitize user input
105
+ 2. **Use Safe Alternatives**: Prefer configuration objects over dynamic code
106
+ 3. **Template Engines**: Use established, secure template libraries
107
+ 4. **Content Security Policy**: Implement CSP headers to prevent code injection
108
+ 5. **Code Review**: Carefully review any dynamic code patterns
109
+
110
+ ## Related Rules
111
+
112
+ - **S023**: JSON Injection Protection
113
+ - **S025**: Server-side Validation
114
+ - **S056**: Log Injection Protection
115
+
116
+ ## Severity Levels
117
+
118
+ - **ERROR**: Direct `eval()`, `Function()` constructor, global eval access
119
+ - **WARNING**: `setTimeout`/`setInterval` with strings, suspicious variable patterns
120
+
121
+ ## Framework-Specific Notes
122
+
123
+ ### Node.js
124
+
125
+ - Be especially careful with `vm` module usage
126
+ - Avoid `child_process.exec()` with user input
127
+
128
+ ### Browser
129
+
130
+ - Consider Content Security Policy to prevent eval
131
+ - Be cautious with `innerHTML` and similar DOM manipulation
132
+
133
+ ### React/Vue
134
+
135
+ - Avoid `dangerouslySetInnerHTML` with user content
136
+ - Use proper event handlers instead of inline scripts
@@ -0,0 +1,263 @@
1
+ /**
2
+ * S020 Main Analyzer - Avoid using eval() or executing dynamic code
3
+ * Primary: Symbol-based analysis (when available)
4
+ * Fallback: Regex-based for all other cases
5
+ * Command: node cli.js --rule=S020 --input=examples/rule-test-fixtures/rules/S020_no_eval_dynamic_code --engine=heuristic
6
+ */
7
+
8
+ const S020SymbolBasedAnalyzer = require("./symbol-based-analyzer.js");
9
+ const S020RegexBasedAnalyzer = require("./regex-based-analyzer.js");
10
+
11
+ class S020Analyzer {
12
+ constructor(options = {}) {
13
+ if (process.env.SUNLINT_DEBUG) {
14
+ console.log(`๐Ÿ”ง [S020] Constructor called with options:`, !!options);
15
+ console.log(
16
+ `๐Ÿ”ง [S020] Options type:`,
17
+ typeof options,
18
+ Object.keys(options || {})
19
+ );
20
+ }
21
+
22
+ this.ruleId = "S020";
23
+ this.ruleName = "Avoid using eval() or executing dynamic code";
24
+ this.description =
25
+ "Avoid using eval() or executing dynamic code as it can lead to code injection vulnerabilities and compromise application security.";
26
+ this.semanticEngine = options.semanticEngine || null;
27
+ this.verbose = options.verbose || false;
28
+
29
+ this.config = {
30
+ useSymbolBased: true,
31
+ fallbackToRegex: true,
32
+ regexBasedOnly: false,
33
+ prioritizeSymbolic: true, // Prefer symbol-based when available
34
+ fallbackToSymbol: true, // Allow symbol analysis even without semantic engine
35
+ };
36
+
37
+ try {
38
+ this.symbolAnalyzer = new S020SymbolBasedAnalyzer(this.semanticEngine);
39
+ if (process.env.SUNLINT_DEBUG)
40
+ console.log(`๐Ÿ”ง [S020] Symbol analyzer created successfully`);
41
+ } catch (error) {
42
+ console.error(`๐Ÿ”ง [S020] Error creating symbol analyzer:`, error);
43
+ }
44
+
45
+ try {
46
+ this.regexAnalyzer = new S020RegexBasedAnalyzer(this.semanticEngine);
47
+ if (process.env.SUNLINT_DEBUG)
48
+ console.log(`๐Ÿ”ง [S020] Regex analyzer created successfully`);
49
+ } catch (error) {
50
+ console.error(`๐Ÿ”ง [S020] Error creating regex analyzer:`, error);
51
+ }
52
+ }
53
+
54
+ async initialize(semanticEngine) {
55
+ this.semanticEngine = semanticEngine;
56
+ if (process.env.SUNLINT_DEBUG)
57
+ console.log(`๐Ÿ”ง [S020] Main analyzer initializing...`);
58
+
59
+ if (this.symbolAnalyzer)
60
+ await this.symbolAnalyzer.initialize?.(semanticEngine);
61
+ if (this.regexAnalyzer)
62
+ await this.regexAnalyzer.initialize?.(semanticEngine);
63
+ if (this.regexAnalyzer) this.regexAnalyzer.cleanup?.();
64
+
65
+ if (process.env.SUNLINT_DEBUG)
66
+ console.log(`๐Ÿ”ง [S020] Main analyzer initialized successfully`);
67
+ }
68
+
69
+ analyzeSingle(filePath, options = {}) {
70
+ if (process.env.SUNLINT_DEBUG)
71
+ console.log(`๐Ÿ” [S020] analyzeSingle() called for: ${filePath}`);
72
+ return this.analyze([filePath], "typescript", options);
73
+ }
74
+
75
+ async analyze(files, language, options = {}) {
76
+ if (process.env.SUNLINT_DEBUG) {
77
+ console.log(
78
+ `๐Ÿ”ง [S020] analyze() method called with ${files.length} files, language: ${language}`
79
+ );
80
+ }
81
+
82
+ const violations = [];
83
+ for (const filePath of files) {
84
+ try {
85
+ if (process.env.SUNLINT_DEBUG)
86
+ console.log(`๐Ÿ”ง [S020] Processing file: ${filePath}`);
87
+ const fileViolations = await this.analyzeFile(filePath, options);
88
+ violations.push(...fileViolations);
89
+ if (process.env.SUNLINT_DEBUG)
90
+ console.log(
91
+ `๐Ÿ”ง [S020] File ${filePath}: Found ${fileViolations.length} violations`
92
+ );
93
+ } catch (error) {
94
+ console.warn(
95
+ `โš  [S020] Analysis failed for ${filePath}:`,
96
+ error.message
97
+ );
98
+ }
99
+ }
100
+
101
+ if (process.env.SUNLINT_DEBUG)
102
+ console.log(`๐Ÿ”ง [S020] Total violations found: ${violations.length}`);
103
+ return violations;
104
+ }
105
+
106
+ async analyzeFile(filePath, options = {}) {
107
+ if (process.env.SUNLINT_DEBUG)
108
+ console.log(`๐Ÿ” [S020] analyzeFile() called for: ${filePath}`);
109
+ const violationMap = new Map();
110
+
111
+ // Try symbol-based analysis first when semantic engine is available OR when explicitly enabled
112
+ if (process.env.SUNLINT_DEBUG) {
113
+ console.log(
114
+ `๐Ÿ”ง [S020] Symbol check: useSymbolBased=${
115
+ this.config.useSymbolBased
116
+ }, semanticEngine=${!!this.semanticEngine}, project=${!!this
117
+ .semanticEngine?.project}, initialized=${!!this.semanticEngine
118
+ ?.initialized}`
119
+ );
120
+ }
121
+
122
+ const canUseSymbol =
123
+ this.config.useSymbolBased &&
124
+ ((this.semanticEngine?.project && this.semanticEngine?.initialized) ||
125
+ (!this.semanticEngine && this.config.fallbackToSymbol !== false));
126
+
127
+ if (canUseSymbol) {
128
+ try {
129
+ if (process.env.SUNLINT_DEBUG)
130
+ console.log(`๐Ÿ”ง [S020] Trying symbol-based analysis...`);
131
+
132
+ let sourceFile = null;
133
+ if (this.semanticEngine?.project) {
134
+ sourceFile = this.semanticEngine.project.getSourceFile(filePath);
135
+ if (process.env.SUNLINT_DEBUG) {
136
+ console.log(
137
+ `๐Ÿ”ง [S020] Checked existing semantic engine project: sourceFile=${!!sourceFile}`
138
+ );
139
+ }
140
+ }
141
+
142
+ if (!sourceFile) {
143
+ // Create a minimal ts-morph project for this analysis
144
+ if (process.env.SUNLINT_DEBUG)
145
+ console.log(
146
+ `๐Ÿ”ง [S020] Creating temporary ts-morph project for: ${filePath}`
147
+ );
148
+ try {
149
+ const fs = require("fs");
150
+ const path = require("path");
151
+ const { Project } = require("ts-morph");
152
+
153
+ // Check if file exists and read content
154
+ if (!fs.existsSync(filePath)) {
155
+ throw new Error(`File not found: ${filePath}`);
156
+ }
157
+
158
+ const fileContent = fs.readFileSync(filePath, "utf8");
159
+ const fileName = path.basename(filePath);
160
+
161
+ const tempProject = new Project({
162
+ useInMemoryFileSystem: true,
163
+ compilerOptions: {
164
+ allowJs: true,
165
+ allowSyntheticDefaultImports: true,
166
+ },
167
+ });
168
+
169
+ // Add file content to in-memory project
170
+ sourceFile = tempProject.createSourceFile(fileName, fileContent);
171
+ if (process.env.SUNLINT_DEBUG)
172
+ console.log(
173
+ `๐Ÿ”ง [S020] Temporary project created successfully with file: ${fileName}`
174
+ );
175
+ } catch (projectError) {
176
+ if (process.env.SUNLINT_DEBUG)
177
+ console.log(
178
+ `๐Ÿ”ง [S020] Failed to create temporary project:`,
179
+ projectError.message
180
+ );
181
+ throw projectError;
182
+ }
183
+ }
184
+
185
+ if (sourceFile) {
186
+ const symbolViolations = await this.symbolAnalyzer.analyze(
187
+ sourceFile,
188
+ filePath
189
+ );
190
+ symbolViolations.forEach((v) => {
191
+ const key = `${v.line}:${v.column}:${v.message}`;
192
+ if (!violationMap.has(key)) violationMap.set(key, v);
193
+ });
194
+ if (process.env.SUNLINT_DEBUG)
195
+ console.log(
196
+ `๐Ÿ”ง [S020] Symbol analysis completed: ${symbolViolations.length} violations`
197
+ );
198
+
199
+ // If symbol-based found violations AND prioritizeSymbolic is true, skip regex
200
+ // But still run regex if symbol-based didn't find any violations
201
+ if (this.config.prioritizeSymbolic && symbolViolations.length > 0) {
202
+ const finalViolations = Array.from(violationMap.values()).map(
203
+ (v) => ({
204
+ ...v,
205
+ filePath,
206
+ file: filePath,
207
+ })
208
+ );
209
+ if (process.env.SUNLINT_DEBUG)
210
+ console.log(
211
+ `๐Ÿ”ง [S020] Symbol-based analysis prioritized: ${finalViolations.length} violations`
212
+ );
213
+ return finalViolations;
214
+ }
215
+ } else {
216
+ if (process.env.SUNLINT_DEBUG)
217
+ console.log(
218
+ `๐Ÿ”ง [S020] No source file found, skipping symbol analysis`
219
+ );
220
+ }
221
+ } catch (error) {
222
+ console.warn(`โš  [S020] Symbol analysis failed:`, error.message);
223
+ }
224
+ }
225
+
226
+ // Fallback to regex-based analysis
227
+ if (this.config.fallbackToRegex || this.config.regexBasedOnly) {
228
+ try {
229
+ if (process.env.SUNLINT_DEBUG)
230
+ console.log(`๐Ÿ”ง [S020] Trying regex-based analysis...`);
231
+ const regexViolations = await this.regexAnalyzer.analyze(filePath);
232
+ regexViolations.forEach((v) => {
233
+ const key = `${v.line}:${v.column}:${v.message}`;
234
+ if (!violationMap.has(key)) violationMap.set(key, v);
235
+ });
236
+ if (process.env.SUNLINT_DEBUG)
237
+ console.log(
238
+ `๐Ÿ”ง [S020] Regex analysis completed: ${regexViolations.length} violations`
239
+ );
240
+ } catch (error) {
241
+ console.warn(`โš  [S020] Regex analysis failed:`, error.message);
242
+ }
243
+ }
244
+
245
+ const finalViolations = Array.from(violationMap.values()).map((v) => ({
246
+ ...v,
247
+ filePath,
248
+ file: filePath,
249
+ }));
250
+ if (process.env.SUNLINT_DEBUG)
251
+ console.log(
252
+ `๐Ÿ”ง [S020] File analysis completed: ${finalViolations.length} unique violations`
253
+ );
254
+ return finalViolations;
255
+ }
256
+
257
+ cleanup() {
258
+ if (this.symbolAnalyzer?.cleanup) this.symbolAnalyzer.cleanup();
259
+ if (this.regexAnalyzer?.cleanup) this.regexAnalyzer.cleanup();
260
+ }
261
+ }
262
+
263
+ module.exports = S020Analyzer;
@@ -0,0 +1,54 @@
1
+ {
2
+ "id": "S020",
3
+ "name": "Avoid using eval() or executing dynamic code",
4
+ "category": "security",
5
+ "description": "S020 - Avoid using eval() or executing dynamic code as it can lead to code injection vulnerabilities and compromise application security.",
6
+ "severity": "error",
7
+ "enabled": true,
8
+ "semantic": {
9
+ "enabled": true,
10
+ "priority": "high",
11
+ "fallback": "heuristic"
12
+ },
13
+ "patterns": {
14
+ "include": ["**/*.js", "**/*.ts", "**/*.jsx", "**/*.tsx"],
15
+ "exclude": [
16
+ "**/*.test.js",
17
+ "**/*.test.ts",
18
+ "**/*.spec.js",
19
+ "**/*.spec.ts",
20
+ "**/node_modules/**",
21
+ "**/dist/**",
22
+ "**/build/**"
23
+ ]
24
+ },
25
+ "analysis": {
26
+ "approach": "symbol-based-primary",
27
+ "fallback": "regex-based",
28
+ "depth": 2,
29
+ "timeout": 5000
30
+ },
31
+ "validation": {
32
+ "dangerousFunctions": [
33
+ "eval",
34
+ "Function",
35
+ "setTimeout",
36
+ "setInterval",
37
+ "execScript",
38
+ "setImmediate"
39
+ ],
40
+ "dangerousPatterns": [
41
+ "new Function",
42
+ "window.eval",
43
+ "global.eval",
44
+ "globalThis.eval"
45
+ ],
46
+ "dynamicCodeIndicators": [
47
+ "code",
48
+ "script",
49
+ "expression",
50
+ "formula",
51
+ "template"
52
+ ]
53
+ }
54
+ }