@sun-asterisk/sunlint 1.3.17 → 1.3.19
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/config/rules/enhanced-rules-registry.json +77 -18
- package/core/analysis-orchestrator.js +11 -3
- package/core/cli-program.js +2 -1
- package/core/github-annotate-service.js +89 -0
- package/core/output-service.js +52 -9
- package/core/summary-report-service.js +45 -27
- package/package.json +3 -2
- package/rules/common/C014_dependency_injection/symbol-based-analyzer.js +392 -280
- package/rules/common/C017_constructor_logic/analyzer.js +137 -503
- package/rules/common/C017_constructor_logic/config.json +50 -0
- package/rules/common/C017_constructor_logic/symbol-based-analyzer.js +463 -0
- package/rules/common/C047_no_duplicate_retry_logic/analyzer.js +96 -40
- package/rules/common/C047_no_duplicate_retry_logic/symbol-analyzer-enhanced.js +17 -2
- package/rules/security/S006_no_plaintext_recovery_codes/symbol-based-analyzer.js +463 -21
- package/rules/security/S011_secure_guid_generation/README.md +255 -0
- package/rules/security/S011_secure_guid_generation/analyzer.js +135 -0
- package/rules/security/S011_secure_guid_generation/config.json +56 -0
- package/rules/security/S011_secure_guid_generation/symbol-based-analyzer.js +609 -0
- package/rules/security/S028_file_upload_size_limits/README.md +537 -0
- package/rules/security/S028_file_upload_size_limits/analyzer.js +202 -0
- package/rules/security/S028_file_upload_size_limits/config.json +186 -0
- package/rules/security/S028_file_upload_size_limits/symbol-based-analyzer.js +530 -0
- package/rules/security/S041_session_token_invalidation/README.md +303 -0
- package/rules/security/S041_session_token_invalidation/analyzer.js +242 -0
- package/rules/security/S041_session_token_invalidation/config.json +175 -0
- package/rules/security/S041_session_token_invalidation/regex-based-analyzer.js +411 -0
- package/rules/security/S041_session_token_invalidation/symbol-based-analyzer.js +674 -0
- package/rules/security/S044_re_authentication_required/README.md +136 -0
- package/rules/security/S044_re_authentication_required/analyzer.js +242 -0
- package/rules/security/S044_re_authentication_required/config.json +161 -0
- package/rules/security/S044_re_authentication_required/regex-based-analyzer.js +329 -0
- package/rules/security/S044_re_authentication_required/symbol-based-analyzer.js +537 -0
- package/rules/security/S045_brute_force_protection/README.md +345 -0
- package/rules/security/S045_brute_force_protection/analyzer.js +336 -0
- package/rules/security/S045_brute_force_protection/config.json +139 -0
- package/rules/security/S045_brute_force_protection/symbol-based-analyzer.js +646 -0
- package/docs/CONSTANTS-ARCHITECTURE.md +0 -288
- package/docs/DEPLOYMENT-STRATEGIES.md +0 -270
- package/docs/ESLINT_INTEGRATION.md +0 -238
- package/docs/PERFORMANCE_MIGRATION_GUIDE.md +0 -368
- package/docs/PERFORMANCE_OPTIMIZATION_PLAN.md +0 -255
- package/rules/common/C017_constructor_logic/semantic-analyzer.js +0 -340
|
@@ -0,0 +1,537 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* S044 Symbol-Based Analyzer - Re-authentication Required for Sensitive Operations
|
|
3
|
+
* Uses TypeScript compiler API for semantic analysis
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const ts = require("typescript");
|
|
7
|
+
|
|
8
|
+
class S044SymbolBasedAnalyzer {
|
|
9
|
+
constructor(semanticEngine = null) {
|
|
10
|
+
this.semanticEngine = semanticEngine;
|
|
11
|
+
this.ruleId = "S044";
|
|
12
|
+
this.category = "security";
|
|
13
|
+
|
|
14
|
+
// Sensitive operations that require re-authentication
|
|
15
|
+
this.sensitiveOperations = [
|
|
16
|
+
"changePassword",
|
|
17
|
+
"updatePassword",
|
|
18
|
+
"resetPassword",
|
|
19
|
+
"changeEmail",
|
|
20
|
+
"updateEmail",
|
|
21
|
+
"changeProfile",
|
|
22
|
+
"updateProfile",
|
|
23
|
+
"deleteAccount",
|
|
24
|
+
"deactivateAccount",
|
|
25
|
+
"changePhoneNumber",
|
|
26
|
+
"updatePhoneNumber",
|
|
27
|
+
"changeSecurityQuestion",
|
|
28
|
+
"updateSecurityQuestion",
|
|
29
|
+
"changeTwoFactorSettings",
|
|
30
|
+
"updateTwoFactorSettings",
|
|
31
|
+
"changeBillingInfo",
|
|
32
|
+
"updateBillingInfo",
|
|
33
|
+
"changePaymentMethod",
|
|
34
|
+
"updatePaymentMethod"
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
// Re-authentication methods/guards
|
|
38
|
+
this.reAuthMethods = [
|
|
39
|
+
"verifyPassword",
|
|
40
|
+
"confirmPassword",
|
|
41
|
+
"reAuthenticate",
|
|
42
|
+
"verifyCurrentPassword",
|
|
43
|
+
"validatePassword",
|
|
44
|
+
"checkPassword",
|
|
45
|
+
"authenticateUser",
|
|
46
|
+
"verifyIdentity"
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
// Re-authentication middleware/guards
|
|
50
|
+
this.reAuthMiddleware = [
|
|
51
|
+
"requireReAuth",
|
|
52
|
+
"requireReAuthentication",
|
|
53
|
+
"verifyReAuth",
|
|
54
|
+
"checkReAuth",
|
|
55
|
+
"validateReAuth",
|
|
56
|
+
"ReAuthGuard",
|
|
57
|
+
"ReAuthenticationGuard",
|
|
58
|
+
"VerifyReAuthGuard"
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
// NestJS decorators for re-authentication
|
|
62
|
+
this.nestjsDecorators = [
|
|
63
|
+
"RequireReAuth",
|
|
64
|
+
"ReAuthenticationRequired",
|
|
65
|
+
"VerifyReAuth",
|
|
66
|
+
"UseGuards(ReAuthGuard)",
|
|
67
|
+
"UseGuards(ReAuthenticationGuard)",
|
|
68
|
+
"UseGuards(VerifyReAuthGuard)"
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
// Express route patterns that are sensitive
|
|
72
|
+
this.sensitiveRoutePatterns = [
|
|
73
|
+
"/change-password",
|
|
74
|
+
"/update-password",
|
|
75
|
+
"/change-email",
|
|
76
|
+
"/update-email",
|
|
77
|
+
"/change-profile",
|
|
78
|
+
"/update-profile",
|
|
79
|
+
"/delete-account",
|
|
80
|
+
"/deactivate-account",
|
|
81
|
+
"/change-phone",
|
|
82
|
+
"/update-phone",
|
|
83
|
+
"/change-security-question",
|
|
84
|
+
"/update-security-question",
|
|
85
|
+
"/change-two-factor",
|
|
86
|
+
"/update-two-factor",
|
|
87
|
+
"/change-billing",
|
|
88
|
+
"/update-billing",
|
|
89
|
+
"/change-payment",
|
|
90
|
+
"/update-payment"
|
|
91
|
+
];
|
|
92
|
+
|
|
93
|
+
// Exclude patterns (login, register, etc.)
|
|
94
|
+
this.excludePatterns = [
|
|
95
|
+
"login",
|
|
96
|
+
"register",
|
|
97
|
+
"logout",
|
|
98
|
+
"forgot-password",
|
|
99
|
+
"reset-password-request",
|
|
100
|
+
"signin",
|
|
101
|
+
"signup",
|
|
102
|
+
"signout"
|
|
103
|
+
];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Initialize analyzer with semantic engine
|
|
108
|
+
*/
|
|
109
|
+
async initialize(semanticEngine) {
|
|
110
|
+
this.semanticEngine = semanticEngine;
|
|
111
|
+
if (this.verbose) {
|
|
112
|
+
console.log(`🔍 [${this.ruleId}] Symbol: Semantic engine initialized`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async analyze(filePath) {
|
|
117
|
+
if (this.verbose) {
|
|
118
|
+
console.log(
|
|
119
|
+
`🔍 [${this.ruleId}] Symbol: Starting analysis for ${filePath}`
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (!this.semanticEngine) {
|
|
124
|
+
if (this.verbose) {
|
|
125
|
+
console.log(
|
|
126
|
+
`🔍 [${this.ruleId}] Symbol: No semantic engine available, skipping`
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
return [];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
try {
|
|
133
|
+
const sourceFile = this.semanticEngine.getSourceFile(filePath);
|
|
134
|
+
if (!sourceFile) {
|
|
135
|
+
if (this.verbose) {
|
|
136
|
+
console.log(
|
|
137
|
+
`🔍 [${this.ruleId}] Symbol: No source file found, trying ts-morph fallback`
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
return await this.analyzeTsMorph(filePath);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (this.verbose) {
|
|
144
|
+
console.log(`🔧 [${this.ruleId}] Source file found, analyzing...`);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return await this.analyzeSourceFile(sourceFile, filePath);
|
|
148
|
+
} catch (error) {
|
|
149
|
+
if (this.verbose) {
|
|
150
|
+
console.log(
|
|
151
|
+
`🔍 [${this.ruleId}] Symbol: Error in analysis:`,
|
|
152
|
+
error.message
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
return [];
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async analyzeTsMorph(filePath) {
|
|
160
|
+
try {
|
|
161
|
+
if (this.verbose) {
|
|
162
|
+
console.log(`🔍 [${this.ruleId}] Symbol: Starting ts-morph analysis`);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const { Project } = require("ts-morph");
|
|
166
|
+
const project = new Project();
|
|
167
|
+
const sourceFile = project.addSourceFileAtPath(filePath);
|
|
168
|
+
|
|
169
|
+
return await this.analyzeSourceFile(sourceFile, filePath);
|
|
170
|
+
} catch (error) {
|
|
171
|
+
if (this.verbose) {
|
|
172
|
+
console.log(
|
|
173
|
+
`🔍 [${this.ruleId}] Symbol: ts-morph analysis failed:`,
|
|
174
|
+
error.message
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
return [];
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async analyzeSourceFile(sourceFile, filePath) {
|
|
182
|
+
const violations = [];
|
|
183
|
+
|
|
184
|
+
try {
|
|
185
|
+
if (this.verbose) {
|
|
186
|
+
console.log(`🔍 [${this.ruleId}] Symbol: Starting symbol-based analysis`);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Analyze method declarations
|
|
190
|
+
const methodDeclarations = sourceFile.getDescendantsOfKind
|
|
191
|
+
? sourceFile.getDescendantsOfKind(
|
|
192
|
+
require("typescript").SyntaxKind.MethodDeclaration
|
|
193
|
+
)
|
|
194
|
+
: [];
|
|
195
|
+
|
|
196
|
+
for (const methodNode of methodDeclarations) {
|
|
197
|
+
try {
|
|
198
|
+
const violation = this.analyzeMethodDeclaration(methodNode, sourceFile);
|
|
199
|
+
if (violation) {
|
|
200
|
+
violations.push(violation);
|
|
201
|
+
}
|
|
202
|
+
} catch (error) {
|
|
203
|
+
if (this.verbose) {
|
|
204
|
+
console.log(
|
|
205
|
+
`🔍 [${this.ruleId}] Symbol: Error analyzing method declaration:`,
|
|
206
|
+
error.message
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Analyze function declarations
|
|
213
|
+
const functionDeclarations = sourceFile.getDescendantsOfKind
|
|
214
|
+
? sourceFile.getDescendantsOfKind(
|
|
215
|
+
require("typescript").SyntaxKind.FunctionDeclaration
|
|
216
|
+
)
|
|
217
|
+
: [];
|
|
218
|
+
|
|
219
|
+
for (const functionNode of functionDeclarations) {
|
|
220
|
+
try {
|
|
221
|
+
const violation = this.analyzeFunctionDeclaration(functionNode, sourceFile);
|
|
222
|
+
if (violation) {
|
|
223
|
+
violations.push(violation);
|
|
224
|
+
}
|
|
225
|
+
} catch (error) {
|
|
226
|
+
if (this.verbose) {
|
|
227
|
+
console.log(
|
|
228
|
+
`🔍 [${this.ruleId}] Symbol: Error analyzing function declaration:`,
|
|
229
|
+
error.message
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Analyze call expressions (for Express routes)
|
|
236
|
+
const callExpressions = sourceFile.getDescendantsOfKind
|
|
237
|
+
? sourceFile.getDescendantsOfKind(
|
|
238
|
+
require("typescript").SyntaxKind.CallExpression
|
|
239
|
+
)
|
|
240
|
+
: [];
|
|
241
|
+
|
|
242
|
+
for (const callNode of callExpressions) {
|
|
243
|
+
try {
|
|
244
|
+
const violation = this.analyzeCallExpression(callNode, sourceFile);
|
|
245
|
+
if (violation) {
|
|
246
|
+
violations.push(violation);
|
|
247
|
+
}
|
|
248
|
+
} catch (error) {
|
|
249
|
+
if (this.verbose) {
|
|
250
|
+
console.log(
|
|
251
|
+
`🔍 [${this.ruleId}] Symbol: Error analyzing call expression:`,
|
|
252
|
+
error.message
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (this.verbose) {
|
|
259
|
+
console.log(
|
|
260
|
+
`🔍 [${this.ruleId}] Symbol: Analysis completed. Found ${violations.length} violations`
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return violations;
|
|
265
|
+
} catch (error) {
|
|
266
|
+
if (this.verbose) {
|
|
267
|
+
console.log(
|
|
268
|
+
`🔍 [${this.ruleId}] Symbol: Error in source file analysis:`,
|
|
269
|
+
error.message
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
return [];
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
analyzeMethodDeclaration(methodNode, sourceFile) {
|
|
277
|
+
try {
|
|
278
|
+
const methodName = methodNode.getName();
|
|
279
|
+
|
|
280
|
+
if (!this.isSensitiveOperation(methodName)) {
|
|
281
|
+
return null;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Only check methods in controller classes (not service classes)
|
|
285
|
+
const parentClass = methodNode.getParent();
|
|
286
|
+
if (!parentClass || !this.isControllerClass(parentClass)) {
|
|
287
|
+
return null;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (this.verbose) {
|
|
291
|
+
console.log(
|
|
292
|
+
`🔍 [${this.ruleId}] Symbol: Sensitive method detected: ${methodName}`
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Check if method has re-authentication decorators
|
|
297
|
+
const hasReAuthDecorator = this.hasReAuthDecorator(methodNode);
|
|
298
|
+
if (hasReAuthDecorator) {
|
|
299
|
+
return null; // Method is properly protected
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Check if method calls re-authentication methods
|
|
303
|
+
const hasReAuthCall = this.hasReAuthMethodCall(methodNode);
|
|
304
|
+
if (hasReAuthCall) {
|
|
305
|
+
return null; // Method calls re-authentication
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return this.createViolation(
|
|
309
|
+
sourceFile,
|
|
310
|
+
methodNode,
|
|
311
|
+
`Sensitive operation '${methodName}' requires re-authentication before execution`
|
|
312
|
+
);
|
|
313
|
+
} catch (error) {
|
|
314
|
+
if (this.verbose) {
|
|
315
|
+
console.log(
|
|
316
|
+
`🔍 [${this.ruleId}] Symbol: Error analyzing method declaration:`,
|
|
317
|
+
error.message
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
return null;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
analyzeFunctionDeclaration(functionNode, sourceFile) {
|
|
325
|
+
try {
|
|
326
|
+
const functionName = functionNode.getName();
|
|
327
|
+
|
|
328
|
+
if (!functionName || !this.isSensitiveOperation(functionName)) {
|
|
329
|
+
return null;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if (this.verbose) {
|
|
333
|
+
console.log(
|
|
334
|
+
`🔍 [${this.ruleId}] Symbol: Sensitive function detected: ${functionName}`
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Check if function calls re-authentication methods
|
|
339
|
+
const hasReAuthCall = this.hasReAuthMethodCall(functionNode);
|
|
340
|
+
if (hasReAuthCall) {
|
|
341
|
+
return null; // Function calls re-authentication
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
return this.createViolation(
|
|
345
|
+
sourceFile,
|
|
346
|
+
functionNode,
|
|
347
|
+
`Sensitive operation '${functionName}' requires re-authentication before execution`
|
|
348
|
+
);
|
|
349
|
+
} catch (error) {
|
|
350
|
+
if (this.verbose) {
|
|
351
|
+
console.log(
|
|
352
|
+
`🔍 [${this.ruleId}] Symbol: Error analyzing function declaration:`,
|
|
353
|
+
error.message
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
return null;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
analyzeCallExpression(callNode, sourceFile) {
|
|
361
|
+
try {
|
|
362
|
+
const expression = callNode.getExpression();
|
|
363
|
+
const methodName = this.getMethodName(expression);
|
|
364
|
+
|
|
365
|
+
// Check if this is an Express route definition
|
|
366
|
+
if (this.isExpressRouteDefinition(callNode)) {
|
|
367
|
+
const routePath = this.getRoutePath(callNode);
|
|
368
|
+
if (this.isSensitiveRoute(routePath)) {
|
|
369
|
+
const hasReAuthMiddleware = this.hasReAuthMiddleware(callNode);
|
|
370
|
+
if (!hasReAuthMiddleware) {
|
|
371
|
+
return this.createViolation(
|
|
372
|
+
sourceFile,
|
|
373
|
+
callNode,
|
|
374
|
+
`Sensitive route '${routePath}' requires re-authentication middleware`
|
|
375
|
+
);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
return null;
|
|
381
|
+
} catch (error) {
|
|
382
|
+
if (this.verbose) {
|
|
383
|
+
console.log(
|
|
384
|
+
`🔍 [${this.ruleId}] Symbol: Error analyzing call expression:`,
|
|
385
|
+
error.message
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
return null;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
isSensitiveOperation(methodName) {
|
|
393
|
+
return this.sensitiveOperations.some(operation =>
|
|
394
|
+
methodName.toLowerCase().includes(operation.toLowerCase())
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
isSensitiveRoute(routePath) {
|
|
399
|
+
if (!routePath) return false;
|
|
400
|
+
return this.sensitiveRoutePatterns.some(pattern =>
|
|
401
|
+
routePath.toLowerCase().includes(pattern.toLowerCase())
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
isExpressRouteDefinition(callNode) {
|
|
406
|
+
try {
|
|
407
|
+
const expression = callNode.getExpression();
|
|
408
|
+
const methodName = this.getMethodName(expression);
|
|
409
|
+
|
|
410
|
+
const expressMethods = ['get', 'post', 'put', 'patch', 'delete'];
|
|
411
|
+
return expressMethods.includes(methodName.toLowerCase());
|
|
412
|
+
} catch (error) {
|
|
413
|
+
return false;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
getRoutePath(callNode) {
|
|
418
|
+
try {
|
|
419
|
+
const args = callNode.getArguments();
|
|
420
|
+
if (args.length > 0) {
|
|
421
|
+
const firstArg = args[0];
|
|
422
|
+
if (firstArg.getKind() === require("typescript").SyntaxKind.StringLiteral) {
|
|
423
|
+
return firstArg.getText().replace(/['"]/g, '');
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
return null;
|
|
427
|
+
} catch (error) {
|
|
428
|
+
return null;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
hasReAuthDecorator(node) {
|
|
433
|
+
try {
|
|
434
|
+
const decorators = node.getDecorators ? node.getDecorators() : [];
|
|
435
|
+
return decorators.some(decorator => {
|
|
436
|
+
const decoratorText = decorator.getText();
|
|
437
|
+
return this.nestjsDecorators.some(pattern =>
|
|
438
|
+
decoratorText.includes(pattern)
|
|
439
|
+
);
|
|
440
|
+
});
|
|
441
|
+
} catch (error) {
|
|
442
|
+
return false;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
hasReAuthMethodCall(node) {
|
|
447
|
+
try {
|
|
448
|
+
const callExpressions = node.getDescendantsOfKind
|
|
449
|
+
? node.getDescendantsOfKind(require("typescript").SyntaxKind.CallExpression)
|
|
450
|
+
: [];
|
|
451
|
+
|
|
452
|
+
return callExpressions.some(callNode => {
|
|
453
|
+
const methodName = this.getMethodName(callNode.getExpression());
|
|
454
|
+
return this.reAuthMethods.includes(methodName);
|
|
455
|
+
});
|
|
456
|
+
} catch (error) {
|
|
457
|
+
return false;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
hasReAuthMiddleware(callNode) {
|
|
462
|
+
try {
|
|
463
|
+
const args = callNode.getArguments();
|
|
464
|
+
if (args.length < 2) return false;
|
|
465
|
+
|
|
466
|
+
// Check if any argument is a re-authentication middleware
|
|
467
|
+
for (let i = 1; i < args.length; i++) {
|
|
468
|
+
const arg = args[i];
|
|
469
|
+
const argText = arg.getText();
|
|
470
|
+
|
|
471
|
+
if (this.reAuthMiddleware.some(middleware =>
|
|
472
|
+
argText.includes(middleware)
|
|
473
|
+
)) {
|
|
474
|
+
return true;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
return false;
|
|
479
|
+
} catch (error) {
|
|
480
|
+
return false;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
getMethodName(expression) {
|
|
485
|
+
try {
|
|
486
|
+
const ts = require("typescript");
|
|
487
|
+
|
|
488
|
+
if (expression.getKind() === ts.SyntaxKind.PropertyAccessExpression) {
|
|
489
|
+
return expression.getNameNode().getText();
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
if (expression.getKind() === ts.SyntaxKind.Identifier) {
|
|
493
|
+
return expression.getText();
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
return "";
|
|
497
|
+
} catch (error) {
|
|
498
|
+
return "";
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
isControllerClass(classNode) {
|
|
503
|
+
try {
|
|
504
|
+
const className = classNode.getName();
|
|
505
|
+
return className && className.toLowerCase().includes('controller');
|
|
506
|
+
} catch (error) {
|
|
507
|
+
return false;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
createViolation(sourceFile, node, message) {
|
|
512
|
+
try {
|
|
513
|
+
const start = node.getStart();
|
|
514
|
+
const lineAndChar = sourceFile.getLineAndColumnAtPos(start);
|
|
515
|
+
|
|
516
|
+
return {
|
|
517
|
+
rule: this.ruleId,
|
|
518
|
+
source: sourceFile.getFilePath(),
|
|
519
|
+
category: this.category,
|
|
520
|
+
line: lineAndChar.line,
|
|
521
|
+
column: lineAndChar.column,
|
|
522
|
+
message: message,
|
|
523
|
+
severity: "error",
|
|
524
|
+
};
|
|
525
|
+
} catch (error) {
|
|
526
|
+
if (this.verbose) {
|
|
527
|
+
console.log(
|
|
528
|
+
`🔍 [${this.ruleId}] Symbol: Error creating violation:`,
|
|
529
|
+
error.message
|
|
530
|
+
);
|
|
531
|
+
}
|
|
532
|
+
return null;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
module.exports = S044SymbolBasedAnalyzer;
|