@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.
Files changed (42) hide show
  1. package/config/rules/enhanced-rules-registry.json +77 -18
  2. package/core/analysis-orchestrator.js +11 -3
  3. package/core/cli-program.js +2 -1
  4. package/core/github-annotate-service.js +89 -0
  5. package/core/output-service.js +52 -9
  6. package/core/summary-report-service.js +45 -27
  7. package/package.json +3 -2
  8. package/rules/common/C014_dependency_injection/symbol-based-analyzer.js +392 -280
  9. package/rules/common/C017_constructor_logic/analyzer.js +137 -503
  10. package/rules/common/C017_constructor_logic/config.json +50 -0
  11. package/rules/common/C017_constructor_logic/symbol-based-analyzer.js +463 -0
  12. package/rules/common/C047_no_duplicate_retry_logic/analyzer.js +96 -40
  13. package/rules/common/C047_no_duplicate_retry_logic/symbol-analyzer-enhanced.js +17 -2
  14. package/rules/security/S006_no_plaintext_recovery_codes/symbol-based-analyzer.js +463 -21
  15. package/rules/security/S011_secure_guid_generation/README.md +255 -0
  16. package/rules/security/S011_secure_guid_generation/analyzer.js +135 -0
  17. package/rules/security/S011_secure_guid_generation/config.json +56 -0
  18. package/rules/security/S011_secure_guid_generation/symbol-based-analyzer.js +609 -0
  19. package/rules/security/S028_file_upload_size_limits/README.md +537 -0
  20. package/rules/security/S028_file_upload_size_limits/analyzer.js +202 -0
  21. package/rules/security/S028_file_upload_size_limits/config.json +186 -0
  22. package/rules/security/S028_file_upload_size_limits/symbol-based-analyzer.js +530 -0
  23. package/rules/security/S041_session_token_invalidation/README.md +303 -0
  24. package/rules/security/S041_session_token_invalidation/analyzer.js +242 -0
  25. package/rules/security/S041_session_token_invalidation/config.json +175 -0
  26. package/rules/security/S041_session_token_invalidation/regex-based-analyzer.js +411 -0
  27. package/rules/security/S041_session_token_invalidation/symbol-based-analyzer.js +674 -0
  28. package/rules/security/S044_re_authentication_required/README.md +136 -0
  29. package/rules/security/S044_re_authentication_required/analyzer.js +242 -0
  30. package/rules/security/S044_re_authentication_required/config.json +161 -0
  31. package/rules/security/S044_re_authentication_required/regex-based-analyzer.js +329 -0
  32. package/rules/security/S044_re_authentication_required/symbol-based-analyzer.js +537 -0
  33. package/rules/security/S045_brute_force_protection/README.md +345 -0
  34. package/rules/security/S045_brute_force_protection/analyzer.js +336 -0
  35. package/rules/security/S045_brute_force_protection/config.json +139 -0
  36. package/rules/security/S045_brute_force_protection/symbol-based-analyzer.js +646 -0
  37. package/docs/CONSTANTS-ARCHITECTURE.md +0 -288
  38. package/docs/DEPLOYMENT-STRATEGIES.md +0 -270
  39. package/docs/ESLINT_INTEGRATION.md +0 -238
  40. package/docs/PERFORMANCE_MIGRATION_GUIDE.md +0 -368
  41. package/docs/PERFORMANCE_OPTIMIZATION_PLAN.md +0 -255
  42. 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;