@sun-asterisk/sunlint 1.3.27 → 1.3.29

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 (29) hide show
  1. package/config/rules/enhanced-rules-registry.json +2 -1
  2. package/config/rules/rules-registry-generated.json +22 -22
  3. package/origin-rules/security-en.md +351 -338
  4. package/package.json +1 -1
  5. package/rules/common/C003_no_vague_abbreviations/analyzer.js +73 -21
  6. package/rules/common/C017_constructor_logic/symbol-based-analyzer.js +206 -2
  7. package/rules/common/C024_no_scatter_hardcoded_constants/symbol-based-analyzer.js +553 -58
  8. package/rules/common/C041_no_sensitive_hardcode/symbol-based-analyzer.js +9 -5
  9. package/rules/security/S005_no_origin_auth/analyzer.js +97 -148
  10. package/rules/security/S005_no_origin_auth/config.json +28 -67
  11. package/rules/security/S005_no_origin_auth/symbol-based-analyzer.js +708 -0
  12. package/rules/security/S006_no_plaintext_recovery_codes/symbol-based-analyzer.js +170 -31
  13. package/rules/security/S010_no_insecure_encryption/analyzer.js +8 -2
  14. package/rules/security/S013_tls_enforcement/symbol-based-analyzer.js +87 -0
  15. package/rules/security/S017_use_parameterized_queries/analyzer.js +11 -78
  16. package/rules/security/S017_use_parameterized_queries/symbol-based-analyzer.js +1146 -1
  17. package/rules/security/S020_no_eval_dynamic_code/analyzer.js +55 -130
  18. package/rules/security/S020_no_eval_dynamic_code/symbol-based-analyzer.js +4 -19
  19. package/rules/security/S042_require_re_authentication_for_long_lived/analyzer.js +1 -1
  20. package/rules/security/S043_password_changes_invalidate_all_sessions/README.md +107 -0
  21. package/rules/security/S043_password_changes_invalidate_all_sessions/analyzer.js +153 -0
  22. package/rules/security/S043_password_changes_invalidate_all_sessions/config.json +41 -0
  23. package/rules/security/S043_password_changes_invalidate_all_sessions/symbol-based-analyzer.js +541 -0
  24. package/docs/COMMAND-EXAMPLES.md +0 -390
  25. package/docs/FILE_LIMITS_COMPLETION_REPORT.md +0 -151
  26. package/docs/FOLDER_STRUCTURE.md +0 -59
  27. package/docs/SIMPLIFIED_USAGE_GUIDE.md +0 -208
  28. package/rules/security/S017_use_parameterized_queries/regex-based-analyzer.js +0 -541
  29. package/rules/security/S020_no_eval_dynamic_code/regex-based-analyzer.js +0 -307
@@ -6,7 +6,6 @@
6
6
  */
7
7
 
8
8
  const S020SymbolBasedAnalyzer = require("./symbol-based-analyzer.js");
9
- const S020RegexBasedAnalyzer = require("./regex-based-analyzer.js");
10
9
 
11
10
  class S020Analyzer {
12
11
  constructor(options = {}) {
@@ -26,14 +25,6 @@ class S020Analyzer {
26
25
  this.semanticEngine = options.semanticEngine || null;
27
26
  this.verbose = options.verbose || false;
28
27
 
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
28
  try {
38
29
  this.symbolAnalyzer = new S020SymbolBasedAnalyzer(this.semanticEngine);
39
30
  if (process.env.SUNLINT_DEBUG)
@@ -41,14 +32,6 @@ class S020Analyzer {
41
32
  } catch (error) {
42
33
  console.error(`🔧 [S020] Error creating symbol analyzer:`, error);
43
34
  }
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
35
  }
53
36
 
54
37
  async initialize(semanticEngine) {
@@ -58,9 +41,6 @@ class S020Analyzer {
58
41
 
59
42
  if (this.symbolAnalyzer)
60
43
  await this.symbolAnalyzer.initialize?.(semanticEngine);
61
- if (this.regexAnalyzer)
62
- await this.regexAnalyzer.initialize?.(semanticEngine);
63
- if (this.regexAnalyzer) this.regexAnalyzer.cleanup?.();
64
44
 
65
45
  if (process.env.SUNLINT_DEBUG)
66
46
  console.log(`🔧 [S020] Main analyzer initialized successfully`);
@@ -108,138 +88,84 @@ class S020Analyzer {
108
88
  console.log(`🔍 [S020] analyzeFile() called for: ${filePath}`);
109
89
  const violationMap = new Map();
110
90
 
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
- }
91
+ try {
92
+ if (process.env.SUNLINT_DEBUG)
93
+ console.log(`🔧 [S020] Running symbol-based analysis...`);
121
94
 
122
- const canUseSymbol =
123
- this.config.useSymbolBased &&
124
- ((this.semanticEngine?.project && this.semanticEngine?.initialized) ||
125
- (!this.semanticEngine && this.config.fallbackToSymbol !== false));
95
+ let sourceFile = null;
96
+ if (this.semanticEngine?.project) {
97
+ sourceFile = this.semanticEngine.project.getSourceFile(filePath);
98
+ if (process.env.SUNLINT_DEBUG) {
99
+ console.log(
100
+ `🔧 [S020] Checked existing semantic engine project: sourceFile=${!!sourceFile}`
101
+ );
102
+ }
103
+ }
126
104
 
127
- if (canUseSymbol) {
128
- try {
105
+ if (!sourceFile) {
106
+ // Create a minimal ts-morph project for this analysis
129
107
  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
- );
108
+ console.log(
109
+ `🔧 [S020] Creating temporary ts-morph project for: ${filePath}`
110
+ );
111
+ try {
112
+ const fs = require("fs");
113
+ const path = require("path");
114
+ const { Project } = require("ts-morph");
115
+
116
+ // Check if file exists and read content
117
+ if (!fs.existsSync(filePath)) {
118
+ throw new Error(`File not found: ${filePath}`);
139
119
  }
140
- }
141
120
 
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
- }
121
+ const fileContent = fs.readFileSync(filePath, "utf8");
122
+ const fileName = path.basename(filePath);
184
123
 
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);
124
+ const tempProject = new Project({
125
+ useInMemoryFileSystem: true,
126
+ compilerOptions: {
127
+ allowJs: true,
128
+ allowSyntheticDefaultImports: true,
129
+ },
193
130
  });
131
+
132
+ // Add file content to in-memory project
133
+ sourceFile = tempProject.createSourceFile(fileName, fileContent);
194
134
  if (process.env.SUNLINT_DEBUG)
195
135
  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
- })
136
+ `🔧 [S020] Temporary project created successfully with file: ${fileName}`
208
137
  );
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 {
138
+ } catch (projectError) {
216
139
  if (process.env.SUNLINT_DEBUG)
217
140
  console.log(
218
- `🔧 [S020] No source file found, skipping symbol analysis`
141
+ `🔧 [S020] Failed to create temporary project:`,
142
+ projectError.message
219
143
  );
144
+ throw projectError;
220
145
  }
221
- } catch (error) {
222
- console.warn(`⚠ [S020] Symbol analysis failed:`, error.message);
223
146
  }
224
- }
225
147
 
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) => {
148
+ if (sourceFile) {
149
+ const symbolViolations = await this.symbolAnalyzer.analyze(
150
+ sourceFile,
151
+ filePath
152
+ );
153
+ symbolViolations.forEach((v) => {
233
154
  const key = `${v.line}:${v.column}:${v.message}`;
234
155
  if (!violationMap.has(key)) violationMap.set(key, v);
235
156
  });
236
157
  if (process.env.SUNLINT_DEBUG)
237
158
  console.log(
238
- `🔧 [S020] Regex analysis completed: ${regexViolations.length} violations`
159
+ `🔧 [S020] Symbol analysis completed: ${symbolViolations.length} violations`
160
+ );
161
+ } else {
162
+ if (process.env.SUNLINT_DEBUG)
163
+ console.log(
164
+ `🔧 [S020] No source file found, skipping symbol analysis`
239
165
  );
240
- } catch (error) {
241
- console.warn(`⚠ [S020] Regex analysis failed:`, error.message);
242
166
  }
167
+ } catch (error) {
168
+ console.warn(`⚠ [S020] Symbol analysis failed:`, error.message);
243
169
  }
244
170
 
245
171
  const finalViolations = Array.from(violationMap.values()).map((v) => ({
@@ -256,7 +182,6 @@ class S020Analyzer {
256
182
 
257
183
  cleanup() {
258
184
  if (this.symbolAnalyzer?.cleanup) this.symbolAnalyzer.cleanup();
259
- if (this.regexAnalyzer?.cleanup) this.regexAnalyzer.cleanup();
260
185
  }
261
186
  }
262
187
 
@@ -182,7 +182,8 @@ class S020SymbolBasedAnalyzer {
182
182
  if (args.length > 0) {
183
183
  const firstArg = args[0];
184
184
 
185
- // Check if first argument is a string literal
185
+ // ONLY flag if first argument is a string literal
186
+ // This is the only truly dangerous case for setTimeout/setInterval
186
187
  if (firstArg.getKind() === SyntaxKind.StringLiteral) {
187
188
  const startLine = call.getStartLineNumber();
188
189
  violations.push({
@@ -193,24 +194,8 @@ class S020SymbolBasedAnalyzer {
193
194
  column: 1,
194
195
  });
195
196
  }
196
- // Check if first argument contains dynamic code indicators
197
- else {
198
- const argText = firstArg.getText().toLowerCase();
199
- const hasDynamicIndicator = this.dynamicCodeIndicators.some(
200
- (indicator) => argText.includes(indicator)
201
- );
202
-
203
- if (hasDynamicIndicator) {
204
- const startLine = call.getStartLineNumber();
205
- violations.push({
206
- ruleId: this.ruleId,
207
- message: `Function '${functionName}()' may be executing dynamic code based on variable naming`,
208
- severity: "warning",
209
- line: startLine,
210
- column: 1,
211
- });
212
- }
213
- }
197
+ // NOTE: Removed variable name checking as it causes too many false positives
198
+ // Functions like setTimeout with arrow functions or function references are safe
214
199
  }
215
200
  }
216
201
  }
@@ -2,7 +2,7 @@
2
2
  * S042 Require re-authentication for long-lived sessions or sensitive actions
3
3
  * Primary: Symbol-based analysis (when available)
4
4
  * Fallback: Regex-based for all other cases
5
- * Purpose: Detect REST endpoints that process request body without validating Content-Type
5
+ * Purpose: Reduce the risk of session hijacking or privilege misuse by forcing re-authentication after long idle periods or before critical actions.
6
6
  * Command: node cli.js --rule=S042 --input=examples/rule-test-fixtures/rules/S042_require_re_authentication_for_long_lived --engine=heuristic --verbose
7
7
  */
8
8
 
@@ -0,0 +1,107 @@
1
+ # S043 - Password Changes Must Invalidate All Active Sessions
2
+
3
+ ## Overview
4
+
5
+ **Rule ID:** S043
6
+ **Severity:** Medium
7
+ **Category:** Security
8
+ **Status:** Activated
9
+ **Version:** 1.0
10
+
11
+ ## Objective
12
+
13
+ Ensure that attackers cannot continue using old session tokens after a password change. This rule enforces correct access control after sensitive password updates by requiring all active sessions to be invalidated.
14
+
15
+ ## Description
16
+
17
+ When a user changes their password, all existing session tokens, JWT tokens, and cached credentials must be invalidated immediately. This prevents attackers who may have compromised an old session from continuing to access the account after the password has been changed.
18
+
19
+ This rule performs **deep analysis** across multiple layers:
20
+ - **Controller → Service → Repository → Third Party**
21
+ - Traces up to 4 levels deep in the call chain
22
+ - Resolves NestJS dependency injection to follow service calls
23
+ - Detects session invalidation in any layer of the application
24
+
25
+ ## Violation Criteria
26
+
27
+ A password change function is flagged if it does NOT:
28
+
29
+ 1. Invalidate all other active sessions (except current if necessary)
30
+ 2. Clear all session tokens from database, Redis, or memory storage
31
+ 3. For JWT: use token versioning or timestamp to revoke old tokens
32
+ 4. Require re-login across all devices
33
+ 5. Call logout or sign out operations
34
+ 6. Clear session cache (Redis, in-memory, etc.)
35
+ 7. Use third-party auth service global sign out (AWS Cognito, Auth0, Firebase)
36
+
37
+ ## Detected Patterns
38
+
39
+ ### ✅ Session Invalidation (PASS)
40
+
41
+ The rule detects the following valid patterns:
42
+
43
+ #### 1. Direct Session Invalidation Calls
44
+ ```typescript
45
+ await sessionService.invalidateAllSessions(userId);
46
+ await sessionService.clearAllSessions(userId);
47
+ await sessionService.destroyAllSessions(userId);
48
+ await sessionService.logoutAllDevices(userId);
49
+
50
+ ### 2. Database Session Deletion
51
+ ```typescript
52
+ await sessionRepository.delete({ userId });
53
+ await sessionRepository.deleteAllByUserId(userId);
54
+ await this.sessionRepository
55
+ .createQueryBuilder()
56
+ .delete()
57
+ .where('userId = :userId', { userId })
58
+ .execute();
59
+ ```
60
+
61
+ ### 3. JWT Token Versioning
62
+ ```typescript
63
+ user.tokenVersion = (user.tokenVersion || 0) + 1;
64
+ user.tokenVersion++;
65
+ await userRepository.update(userId, {
66
+ tokenVersion: () => 'tokenVersion + 1'
67
+ });
68
+ ```
69
+
70
+ ### 4. Redis/Cache Clearing
71
+ ```typescript
72
+ await redis.del(`session:${userId}:*`);
73
+ await cacheManager.del(username);
74
+ await redisService.clearUserSessions(userId);
75
+ ```
76
+
77
+ ### Deep Analysis Features
78
+ ```typescript
79
+ // File: auth.controller.ts
80
+ async changePassword(data) {
81
+ await this.authService.changePassword(data); // ← Traces into service
82
+ }
83
+
84
+ // File: auth.service.ts (different file)
85
+ async changePassword(data) {
86
+ await this.userRepo.updatePassword(data);
87
+ await this.sessionService.clearAllSessions(data.userId); // ← Found! ✅
88
+ }
89
+ ```
90
+
91
+ ### NestJS Dependency Injection Resolution
92
+
93
+ ```typescript
94
+ @Injectable()
95
+ export class AuthController {
96
+ constructor(
97
+ private readonly commonChangePasswordService: CommonChangePasswordService
98
+ ) {}
99
+
100
+ async changePassword() {
101
+ // Resolves: commonChangePasswordService → CommonChangePasswordService class
102
+ await this.commonChangePasswordService.changePassword();
103
+ // ↓ Deep traces into CommonChangePasswordService.changePassword() method
104
+ }
105
+ }
106
+ ```
107
+
@@ -0,0 +1,153 @@
1
+ /**
2
+ * S043 Password changes must invalidate all other login sessions
3
+ * Primary: Symbol-based analysis (when available)
4
+ * Fallback: Regex-based for all other cases
5
+ * Purpose: Ensure attackers cannot continue using old session tokens after a password change. Enforce correct access control after sensitive updates.
6
+ * Command: node cli.js --rule=S043 --input=examples/rule-test-fixtures/rules/S043_password_changes_invalidate_all_sessions --engine=heuristic --verbose
7
+ */
8
+
9
+ const S043SymbolBasedAnalyzer = require("./symbol-based-analyzer.js");
10
+
11
+ class S043Analyzer {
12
+ constructor(options = {}) {
13
+ if (process.env.SUNLINT_DEBUG) {
14
+ console.log(`🔧 [S043] Constructor called with options:`, !!options);
15
+ console.log(
16
+ `🔧 [S043] Options type:`,
17
+ typeof options,
18
+ Object.keys(options || {})
19
+ );
20
+ }
21
+
22
+ this.ruleId = "S043";
23
+ this.ruleName = "Password changes must invalidate all other login sessions";
24
+ this.description =
25
+ "Ensure attackers cannot continue using old session tokens after a password change. Enforce correct access control after sensitive updates.";
26
+ this.semanticEngine = options.semanticEngine || null;
27
+ this.verbose = options.verbose || false;
28
+
29
+ this.config = {
30
+ useSymbolBased: true,
31
+ fallbackToRegex: false,
32
+ regexBasedOnly: false,
33
+ fallbackToSymbol: true, // Allow symbol analysis even without semantic engine
34
+ };
35
+
36
+ try {
37
+ this.symbolAnalyzer = new S043SymbolBasedAnalyzer(this.semanticEngine);
38
+ if (process.env.SUNLINT_DEBUG)
39
+ console.log(`🔧 [S043] Symbol analyzer created successfully`);
40
+ } catch (error) {
41
+ console.error(`🔧 [S043] Error creating symbol analyzer:`, error);
42
+ }
43
+ }
44
+
45
+ async initialize(semanticEngine = null) {
46
+ if (semanticEngine) {
47
+ this.semanticEngine = semanticEngine;
48
+ }
49
+ this.verbose = semanticEngine?.verbose || false;
50
+
51
+ // Initialize both analyzers
52
+ await this.symbolAnalyzer.initialize(semanticEngine);
53
+
54
+ // Ensure verbose flag is propagated
55
+ this.symbolAnalyzer.verbose = this.verbose;
56
+
57
+ if (this.verbose) {
58
+ console.log(`🔧 [S043 Hybrid] Analyzer initialized - verbose: ${this.verbose}`);
59
+ }
60
+ }
61
+
62
+ analyzeSingle(filePath, options = {}) {
63
+ if (process.env.SUNLINT_DEBUG)
64
+ console.log(`🔍 [S043] analyzeSingle() called for: ${filePath}`);
65
+ return this.analyze([filePath], "typescript", options);
66
+ }
67
+
68
+ async analyze(files, language, options = {}) {
69
+ if (process.env.SUNLINT_DEBUG) {
70
+ console.log(`🔧 [S043] analyze() method called with ${files.length} files, language: ${language}`);
71
+ }
72
+
73
+ const violations = [];
74
+
75
+ for (const filePath of files) {
76
+ try {
77
+ if (process.env.SUNLINT_DEBUG) {
78
+ console.log(`🔧 [S043] Processing file: ${filePath}`);
79
+ }
80
+
81
+ const fileViolations = await this.analyzeFile(filePath, options);
82
+ violations.push(...fileViolations);
83
+
84
+ if (process.env.SUNLINT_DEBUG) {
85
+ console.log(`🔧 [S043] File ${filePath}: Found ${fileViolations.length} violations`);
86
+ }
87
+ } catch (error) {
88
+ console.warn(`❌ [S043] Analysis failed for ${filePath}:`, error.message);
89
+ }
90
+ }
91
+
92
+ if (process.env.SUNLINT_DEBUG) {
93
+ console.log(`🔧 [S043] Total violations found: ${violations.length}`);
94
+ }
95
+
96
+ return violations;
97
+ }
98
+
99
+ async analyzeFile(filePath, options = {}) {
100
+ if (process.env.SUNLINT_DEBUG) {
101
+ console.log(`🔧 [S043] analyzeFile() called for: ${filePath}`);
102
+ }
103
+
104
+ // 1. Try Symbol-based analysis first (primary)
105
+ if (this.config.useSymbolBased &&
106
+ this.semanticEngine?.project &&
107
+ this.semanticEngine?.initialized) {
108
+ try {
109
+ if (process.env.SUNLINT_DEBUG) {
110
+ console.log(`🔧 [S043] Trying symbol-based analysis...`);
111
+ }
112
+ const sourceFile = this.semanticEngine.project.getSourceFile(filePath);
113
+ if (sourceFile) {
114
+ if (process.env.SUNLINT_DEBUG) {
115
+ console.log(`🔧 [S043] Source file found, analyzing with symbol-based...`);
116
+ }
117
+ const violations = await this.symbolAnalyzer.analyzeFileWithSymbols(filePath, { ...options, verbose: options.verbose });
118
+
119
+ // Mark violations with analysis strategy
120
+ violations.forEach(v => v.analysisStrategy = 'symbol-based');
121
+
122
+ if (process.env.SUNLINT_DEBUG) {
123
+ console.log(`✅ [S043] Symbol-based analysis: ${violations.length} violations`);
124
+ }
125
+ return violations; // Return even if 0 violations - symbol analysis completed successfully
126
+ } else {
127
+ if (process.env.SUNLINT_DEBUG) {
128
+ console.log(`⚠️ [S043] Source file not found in project`);
129
+ }
130
+ }
131
+ } catch (error) {
132
+ console.warn(`⚠️ [S043] Symbol analysis failed: ${error.message}`);
133
+ // Continue to fallback
134
+ }
135
+ } else {
136
+ if (process.env.SUNLINT_DEBUG) {
137
+ console.log(`🔄 [S043] Symbol analysis conditions check:`);
138
+ console.log(` - useSymbolBased: ${this.config.useSymbolBased}`);
139
+ console.log(` - semanticEngine: ${!!this.semanticEngine}`);
140
+ console.log(` - semanticEngine.project: ${!!this.semanticEngine?.project}`);
141
+ console.log(` - semanticEngine.initialized: ${this.semanticEngine?.initialized}`);
142
+ console.log(`🔄 [S043] Symbol analysis unavailable, using regex fallback`);
143
+ }
144
+ }
145
+
146
+ if (options?.verbose) {
147
+ console.log(`🔧 [S043] No analysis methods succeeded, returning empty`);
148
+ }
149
+ return [];
150
+ }
151
+ }
152
+
153
+ module.exports = S043Analyzer;
@@ -0,0 +1,41 @@
1
+ {
2
+ "id": "S043",
3
+ "name": "Password changes must invalidate all other login sessions",
4
+ "category": "security",
5
+ "description": "S043 - Ensure attackers cannot continue using old session tokens after a password change. Enforce correct access control after sensitive updates.",
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": 1,
29
+ "timeout": 4000
30
+ },
31
+ "validation": {
32
+ "httpIndicators": [
33
+ "http://",
34
+ "require('http')",
35
+ "require(\"http\")",
36
+ "http.createServer"
37
+ ],
38
+ "httpsModules": ["https", "tls"],
39
+ "frameworks": ["express", "nextjs", "nuxtjs", "nestjs"]
40
+ }
41
+ }