@sun-asterisk/sunlint 1.3.18 → 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 (34) hide show
  1. package/config/rules/enhanced-rules-registry.json +77 -18
  2. package/core/cli-program.js +2 -1
  3. package/core/github-annotate-service.js +89 -0
  4. package/core/output-service.js +25 -0
  5. package/core/summary-report-service.js +30 -30
  6. package/package.json +3 -2
  7. package/rules/common/C014_dependency_injection/symbol-based-analyzer.js +392 -280
  8. package/rules/common/C017_constructor_logic/analyzer.js +137 -503
  9. package/rules/common/C017_constructor_logic/config.json +50 -0
  10. package/rules/common/C017_constructor_logic/symbol-based-analyzer.js +463 -0
  11. package/rules/security/S006_no_plaintext_recovery_codes/symbol-based-analyzer.js +463 -21
  12. package/rules/security/S011_secure_guid_generation/README.md +255 -0
  13. package/rules/security/S011_secure_guid_generation/analyzer.js +135 -0
  14. package/rules/security/S011_secure_guid_generation/config.json +56 -0
  15. package/rules/security/S011_secure_guid_generation/symbol-based-analyzer.js +609 -0
  16. package/rules/security/S028_file_upload_size_limits/README.md +537 -0
  17. package/rules/security/S028_file_upload_size_limits/analyzer.js +202 -0
  18. package/rules/security/S028_file_upload_size_limits/config.json +186 -0
  19. package/rules/security/S028_file_upload_size_limits/symbol-based-analyzer.js +530 -0
  20. package/rules/security/S041_session_token_invalidation/README.md +303 -0
  21. package/rules/security/S041_session_token_invalidation/analyzer.js +242 -0
  22. package/rules/security/S041_session_token_invalidation/config.json +175 -0
  23. package/rules/security/S041_session_token_invalidation/regex-based-analyzer.js +411 -0
  24. package/rules/security/S041_session_token_invalidation/symbol-based-analyzer.js +674 -0
  25. package/rules/security/S044_re_authentication_required/README.md +136 -0
  26. package/rules/security/S044_re_authentication_required/analyzer.js +242 -0
  27. package/rules/security/S044_re_authentication_required/config.json +161 -0
  28. package/rules/security/S044_re_authentication_required/regex-based-analyzer.js +329 -0
  29. package/rules/security/S044_re_authentication_required/symbol-based-analyzer.js +537 -0
  30. package/rules/security/S045_brute_force_protection/README.md +345 -0
  31. package/rules/security/S045_brute_force_protection/analyzer.js +336 -0
  32. package/rules/security/S045_brute_force_protection/config.json +139 -0
  33. package/rules/security/S045_brute_force_protection/symbol-based-analyzer.js +646 -0
  34. package/rules/common/C017_constructor_logic/semantic-analyzer.js +0 -340
@@ -0,0 +1,136 @@
1
+ # S044: Re-authentication Required for Sensitive Operations
2
+
3
+ ## Mô tả
4
+ Rule này yêu cầu xác thực lại trước khi thực hiện các thao tác nhạy cảm như thay đổi mật khẩu, email, thông tin profile và các thao tác quan trọng khác. Điều này ngăn chặn việc truy cập trái phép vào các chức năng tài khoản nhạy cảm ngay cả khi session bị xâm phạm.
5
+
6
+ ## Tại sao cần thiết?
7
+ - **Bảo mật tài khoản**: Ngăn chặn kẻ tấn công thay đổi thông tin quan trọng ngay cả khi có session
8
+ - **Tuân thủ OWASP**: Theo khuyến nghị của OWASP về authentication security
9
+ - **Bảo vệ dữ liệu người dùng**: Đảm bảo chỉ chủ tài khoản mới có thể thay đổi thông tin nhạy cảm
10
+
11
+ ## Các thao tác nhạy cảm được detect
12
+ - `changePassword`, `updatePassword`, `resetPassword`
13
+ - `changeEmail`, `updateEmail`
14
+ - `changeProfile`, `updateProfile`
15
+ - `deleteAccount`, `deactivateAccount`
16
+ - `changePhoneNumber`, `updatePhoneNumber`
17
+ - `changeSecurityQuestion`, `updateSecurityQuestion`
18
+ - `changeTwoFactorSettings`, `updateTwoFactorSettings`
19
+ - `changeBillingInfo`, `updateBillingInfo`
20
+ - `changePaymentMethod`, `updatePaymentMethod`
21
+
22
+ ## Cách sử dụng
23
+
24
+ ### NestJS với Decorators
25
+ ```typescript
26
+ @Controller('user')
27
+ export class UserController {
28
+ // ✅ Đúng: Có re-authentication guard
29
+ @Post('change-password')
30
+ @UseGuards(ReAuthGuard)
31
+ async changePassword(@Body() dto: ChangePasswordDto) {
32
+ return this.userService.changePassword(dto);
33
+ }
34
+
35
+ // ❌ Sai: Không có re-authentication
36
+ @Post('change-password')
37
+ async changePassword(@Body() dto: ChangePasswordDto) {
38
+ return this.userService.changePassword(dto);
39
+ }
40
+ }
41
+ ```
42
+
43
+ ### Express với Middleware
44
+ ```typescript
45
+ // ✅ Đúng: Có re-authentication middleware
46
+ app.put('/change-password', requireReAuth, (req, res) => {
47
+ return userService.changePassword(req.body);
48
+ });
49
+
50
+ // ❌ Sai: Không có re-authentication middleware
51
+ app.put('/change-password', (req, res) => {
52
+ return userService.changePassword(req.body);
53
+ });
54
+ ```
55
+
56
+ ### Method với Re-authentication Call
57
+ ```typescript
58
+ @Controller('user')
59
+ export class UserController {
60
+ // ✅ Đúng: Gọi re-authentication trong method
61
+ @Post('change-password')
62
+ async changePassword(@Body() dto: ChangePasswordDto) {
63
+ await this.userService.verifyCurrentPassword(dto.currentPassword);
64
+ return this.userService.changePassword(dto);
65
+ }
66
+ }
67
+ ```
68
+
69
+ ## Các pattern re-authentication được hỗ trợ
70
+
71
+ ### NestJS Decorators
72
+ - `@UseGuards(ReAuthGuard)`
73
+ - `@UseGuards(ReAuthenticationGuard)`
74
+ - `@UseGuards(VerifyReAuthGuard)`
75
+
76
+ ### Express Middleware
77
+ - `requireReAuth`
78
+ - `requireReAuthentication`
79
+ - `verifyReAuth`
80
+ - `checkReAuth`
81
+ - `validateReAuth`
82
+
83
+ ### Re-authentication Methods
84
+ - `verifyPassword`
85
+ - `confirmPassword`
86
+ - `reAuthenticate`
87
+ - `verifyCurrentPassword`
88
+ - `validatePassword`
89
+ - `checkPassword`
90
+ - `authenticateUser`
91
+ - `verifyIdentity`
92
+
93
+ ## Cấu hình
94
+
95
+ Rule này hỗ trợ cấu hình trong `config.json`:
96
+
97
+ ```json
98
+ {
99
+ "configuration": {
100
+ "enableReAuthenticationDetection": true,
101
+ "sensitiveOperations": [...],
102
+ "authenticationMethods": [...],
103
+ "middlewarePatterns": [...],
104
+ "frameworkSpecific": {
105
+ "express": {...},
106
+ "nestjs": {...}
107
+ }
108
+ }
109
+ }
110
+ ```
111
+
112
+ ## Test Cases
113
+
114
+ Rule được test với các test cases sau:
115
+
116
+ ### Violations (6 cases)
117
+ - `password_change_without_reauth.ts` - Thay đổi mật khẩu không có re-auth
118
+ - `email_change_without_reauth.ts` - Thay đổi email không có re-auth
119
+ - `profile_update_without_reauth.ts` - Cập nhật profile không có re-auth
120
+ - `express_routes_without_reauth.ts` - Express routes không có middleware
121
+
122
+ ### Clean (4 cases)
123
+ - `password_change_with_reauth.ts` - Có @UseGuards decorator
124
+ - `email_change_with_reauth.ts` - Có @UseGuards decorator
125
+ - `profile_update_with_reauth.ts` - Có @UseGuards decorator
126
+ - `express_routes_with_reauth.ts` - Có middleware
127
+ - `method_with_reauth_call.ts` - Có re-auth method call
128
+
129
+ ## Kết quả test
130
+ - **Total violations**: 6 (chỉ từ files violations)
131
+ - **Clean files**: 0 violations (đã được bảo vệ đúng cách)
132
+ - **Accuracy**: 100% - Rule chỉ detect các thao tác thực sự thiếu re-authentication
133
+
134
+ ## Tài liệu tham khảo
135
+ - [OWASP Authentication Cheat Sheet](https://owasp.org/www-community/controls/Authentication_Cheat_Sheet)
136
+ - [OWASP Session Management Cheat Sheet](https://owasp.org/www-community/attacks/Session_Management_Cheat_Sheet)
@@ -0,0 +1,242 @@
1
+ /**
2
+ * S044 Main Analyzer - Re-authentication Required for Sensitive Operations
3
+ * Primary: Symbol-based analysis (when available)
4
+ * Fallback: Regex-based for all other cases
5
+ * Command: node cli.js --rule=S044 --input=examples/rule-test-fixtures/rules/S044_re_authentication_required --engine=heuristic
6
+ */
7
+
8
+ const S044SymbolBasedAnalyzer = require("./symbol-based-analyzer.js");
9
+ const S044RegexBasedAnalyzer = require("./regex-based-analyzer.js");
10
+
11
+ class S044Analyzer {
12
+ constructor(options = {}) {
13
+ if (process.env.SUNLINT_DEBUG) {
14
+ console.log(`🔧 [S044] Constructor called with options:`, !!options);
15
+ console.log(
16
+ `🔧 [S044] Options type:`,
17
+ typeof options,
18
+ Object.keys(options || {})
19
+ );
20
+ }
21
+
22
+ this.ruleId = "S044";
23
+ this.ruleName = "Re-authentication Required for Sensitive Operations";
24
+ this.description =
25
+ "Require re-authentication before performing sensitive operations such as password changes, email changes, profile updates, and other critical account modifications. This prevents unauthorized access to sensitive account functions even if a session is compromised.";
26
+ this.semanticEngine = options.semanticEngine || null;
27
+ this.verbose = options.verbose || false;
28
+
29
+ // Configuration
30
+ this.config = {
31
+ useSymbolBased: true, // Primary approach
32
+ fallbackToRegex: true, // Secondary approach
33
+ regexBasedOnly: false, // Can be set to true for pure mode
34
+ };
35
+
36
+ // Initialize analyzers
37
+ try {
38
+ this.symbolAnalyzer = new S044SymbolBasedAnalyzer(this.semanticEngine);
39
+ if (process.env.SUNLINT_DEBUG) {
40
+ console.log(`🔧 [S044] Symbol analyzer created successfully`);
41
+ }
42
+ } catch (error) {
43
+ console.error(`🔧 [S044] Error creating symbol analyzer:`, error);
44
+ }
45
+
46
+ try {
47
+ this.regexAnalyzer = new S044RegexBasedAnalyzer(this.semanticEngine);
48
+ if (process.env.SUNLINT_DEBUG) {
49
+ console.log(`🔧 [S044] Regex analyzer created successfully`);
50
+ }
51
+ } catch (error) {
52
+ console.error(`🔧 [S044] Error creating regex analyzer:`, error);
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Initialize analyzer with semantic engine
58
+ */
59
+ async initialize(semanticEngine) {
60
+ this.semanticEngine = semanticEngine;
61
+
62
+ if (process.env.SUNLINT_DEBUG) {
63
+ console.log(`🔧 [S044] Main analyzer initializing...`);
64
+ }
65
+
66
+ // Initialize both analyzers
67
+ if (this.symbolAnalyzer) {
68
+ await this.symbolAnalyzer.initialize?.(semanticEngine);
69
+ }
70
+ if (this.regexAnalyzer) {
71
+ await this.regexAnalyzer.initialize?.(semanticEngine);
72
+ }
73
+
74
+ // Clean up if needed
75
+ if (this.regexAnalyzer) {
76
+ this.regexAnalyzer.cleanup?.();
77
+ }
78
+
79
+ if (process.env.SUNLINT_DEBUG) {
80
+ console.log(`🔧 [S044] Main analyzer initialized successfully`);
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Single file analysis method for testing
86
+ */
87
+ analyzeSingle(filePath, options = {}) {
88
+ if (process.env.SUNLINT_DEBUG) {
89
+ console.log(`📊 [S044] analyzeSingle() called for: ${filePath}`);
90
+ }
91
+
92
+ // Return result using same format as analyze method
93
+ return this.analyze([filePath], "typescript", options);
94
+ }
95
+
96
+ async analyze(files, language, options = {}) {
97
+ if (process.env.SUNLINT_DEBUG) {
98
+ console.log(
99
+ `🔧 [S044] analyze() method called with ${files.length} files, language: ${language}`
100
+ );
101
+ }
102
+
103
+ const violations = [];
104
+
105
+ for (const filePath of files) {
106
+ try {
107
+ if (process.env.SUNLINT_DEBUG) {
108
+ console.log(`🔧 [S044] Processing file: ${filePath}`);
109
+ }
110
+
111
+ const fileViolations = await this.analyzeFile(filePath, options);
112
+ violations.push(...fileViolations);
113
+
114
+ if (process.env.SUNLINT_DEBUG) {
115
+ console.log(
116
+ `🔧 [S044] File ${filePath}: Found ${fileViolations.length} violations`
117
+ );
118
+ }
119
+ } catch (error) {
120
+ console.warn(
121
+ `⚠ [S044] Analysis failed for ${filePath}:`,
122
+ error.message
123
+ );
124
+ }
125
+ }
126
+
127
+ if (process.env.SUNLINT_DEBUG) {
128
+ console.log(`🔧 [S044] Total violations found: ${violations.length}`);
129
+ }
130
+
131
+ return violations;
132
+ }
133
+
134
+ async analyzeFile(filePath, options = {}) {
135
+ if (process.env.SUNLINT_DEBUG) {
136
+ console.log(`🔍 [S044] analyzeFile() called for: ${filePath}`);
137
+ }
138
+
139
+ // Create a Map to track unique violations and prevent duplicates
140
+ const violationMap = new Map();
141
+
142
+ // 1. Try Symbol-based analysis first (primary)
143
+ if (
144
+ this.config.useSymbolBased &&
145
+ this.semanticEngine?.project &&
146
+ this.semanticEngine?.initialized
147
+ ) {
148
+ try {
149
+ if (process.env.SUNLINT_DEBUG) {
150
+ console.log(`🔧 [S044] Trying symbol-based analysis...`);
151
+ }
152
+ const sourceFile = this.semanticEngine.project.getSourceFile(filePath);
153
+ if (sourceFile) {
154
+ if (process.env.SUNLINT_DEBUG) {
155
+ console.log(`🔧 [S044] Source file found, analyzing...`);
156
+ }
157
+ const symbolViolations = await this.symbolAnalyzer.analyze(
158
+ sourceFile,
159
+ filePath
160
+ );
161
+
162
+ // Add to violation map with deduplication
163
+ symbolViolations.forEach((violation) => {
164
+ const key = `${violation.line}:${violation.column}:${violation.message}`;
165
+ if (!violationMap.has(key)) {
166
+ violationMap.set(key, violation);
167
+ }
168
+ });
169
+
170
+ if (process.env.SUNLINT_DEBUG) {
171
+ console.log(
172
+ `🔧 [S044] Symbol analysis completed: ${symbolViolations.length} violations`
173
+ );
174
+ }
175
+ } else {
176
+ if (process.env.SUNLINT_DEBUG) {
177
+ console.log(`🔧 [S044] Source file not found, falling back...`);
178
+ }
179
+ }
180
+ } catch (error) {
181
+ console.warn(`⚠ [S044] Symbol analysis failed:`, error.message);
182
+ }
183
+ }
184
+
185
+ // 2. Try Regex-based analysis (fallback or additional)
186
+ if (this.config.fallbackToRegex || this.config.regexBasedOnly) {
187
+ try {
188
+ if (process.env.SUNLINT_DEBUG) {
189
+ console.log(`🔧 [S044] Trying regex-based analysis...`);
190
+ }
191
+ const regexViolations = await this.regexAnalyzer.analyze(filePath);
192
+
193
+ // Add to violation map with deduplication
194
+ regexViolations.forEach((violation) => {
195
+ const key = `${violation.line}:${violation.column}:${violation.message}`;
196
+ if (!violationMap.has(key)) {
197
+ violationMap.set(key, violation);
198
+ }
199
+ });
200
+
201
+ if (process.env.SUNLINT_DEBUG) {
202
+ console.log(
203
+ `🔧 [S044] Regex analysis completed: ${regexViolations.length} violations`
204
+ );
205
+ }
206
+ } catch (error) {
207
+ console.warn(`⚠ [S044] Regex analysis failed:`, error.message);
208
+ }
209
+ }
210
+
211
+ // Convert Map values to array and add filePath to each violation
212
+ const finalViolations = Array.from(violationMap.values()).map(
213
+ (violation) => ({
214
+ ...violation,
215
+ filePath: filePath,
216
+ file: filePath, // Also add 'file' for compatibility
217
+ })
218
+ );
219
+
220
+ if (process.env.SUNLINT_DEBUG) {
221
+ console.log(
222
+ `🔧 [S044] File analysis completed: ${finalViolations.length} unique violations`
223
+ );
224
+ }
225
+
226
+ return finalViolations;
227
+ }
228
+
229
+ /**
230
+ * Clean up resources
231
+ */
232
+ cleanup() {
233
+ if (this.symbolAnalyzer?.cleanup) {
234
+ this.symbolAnalyzer.cleanup();
235
+ }
236
+ if (this.regexAnalyzer?.cleanup) {
237
+ this.regexAnalyzer.cleanup();
238
+ }
239
+ }
240
+ }
241
+
242
+ module.exports = S044Analyzer;
@@ -0,0 +1,161 @@
1
+ {
2
+ "rule": {
3
+ "id": "S044",
4
+ "name": "Re-authentication Required for Sensitive Operations",
5
+ "description": "Require re-authentication before performing sensitive operations such as password changes, email changes, profile updates, and other critical account modifications. This prevents unauthorized access to sensitive account functions even if a session is compromised.",
6
+ "category": "security",
7
+ "severity": "error",
8
+ "languages": ["typescript", "javascript"],
9
+ "frameworks": ["express", "nestjs", "node"],
10
+ "version": "1.0.0",
11
+ "status": "stable",
12
+ "tags": ["security", "authentication", "re-authentication", "sensitive-operations", "owasp"],
13
+ "references": [
14
+ "https://owasp.org/www-community/controls/Authentication_Cheat_Sheet",
15
+ "https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html",
16
+ "https://owasp.org/www-community/attacks/Session_Management_Cheat_Sheet"
17
+ ]
18
+ },
19
+ "configuration": {
20
+ "enableReAuthenticationDetection": true,
21
+ "sensitiveOperations": [
22
+ "changePassword",
23
+ "updatePassword",
24
+ "resetPassword",
25
+ "changeEmail",
26
+ "updateEmail",
27
+ "changeProfile",
28
+ "updateProfile",
29
+ "deleteAccount",
30
+ "deactivateAccount",
31
+ "changePhoneNumber",
32
+ "updatePhoneNumber",
33
+ "changeSecurityQuestion",
34
+ "updateSecurityQuestion",
35
+ "changeTwoFactorSettings",
36
+ "updateTwoFactorSettings",
37
+ "changeBillingInfo",
38
+ "updateBillingInfo",
39
+ "changePaymentMethod",
40
+ "updatePaymentMethod"
41
+ ],
42
+ "authenticationMethods": [
43
+ "verifyPassword",
44
+ "confirmPassword",
45
+ "reAuthenticate",
46
+ "verifyCurrentPassword",
47
+ "validatePassword",
48
+ "checkPassword",
49
+ "authenticateUser",
50
+ "verifyIdentity"
51
+ ],
52
+ "middlewarePatterns": [
53
+ "requireReAuth",
54
+ "requireReAuthentication",
55
+ "verifyReAuth",
56
+ "checkReAuth",
57
+ "validateReAuth"
58
+ ],
59
+ "frameworkSpecific": {
60
+ "express": {
61
+ "routePatterns": [
62
+ "/change-password",
63
+ "/update-password",
64
+ "/change-email",
65
+ "/update-email",
66
+ "/change-profile",
67
+ "/update-profile",
68
+ "/delete-account",
69
+ "/deactivate-account"
70
+ ]
71
+ },
72
+ "nestjs": {
73
+ "decorators": [
74
+ "@RequireReAuth",
75
+ "@ReAuthenticationRequired",
76
+ "@VerifyReAuth"
77
+ ],
78
+ "guards": [
79
+ "ReAuthGuard",
80
+ "ReAuthenticationGuard",
81
+ "VerifyReAuthGuard"
82
+ ]
83
+ }
84
+ },
85
+ "excludePatterns": [
86
+ "login",
87
+ "register",
88
+ "logout",
89
+ "forgot-password",
90
+ "reset-password-request"
91
+ ]
92
+ },
93
+ "examples": {
94
+ "violations": [
95
+ {
96
+ "description": "Password change without re-authentication",
97
+ "code": "@Post('change-password')\nasync changePassword(@Body() changePasswordDto: ChangePasswordDto) {\n return this.userService.changePassword(changePasswordDto);\n}"
98
+ },
99
+ {
100
+ "description": "Email change without re-authentication",
101
+ "code": "@Put('email')\nasync updateEmail(@Body() emailDto: UpdateEmailDto) {\n return this.userService.updateEmail(emailDto);\n}"
102
+ },
103
+ {
104
+ "description": "Profile update without re-authentication",
105
+ "code": "app.put('/profile', (req, res) => {\n return userService.updateProfile(req.body);\n});"
106
+ }
107
+ ],
108
+ "fixes": [
109
+ {
110
+ "description": "Password change with re-authentication",
111
+ "code": "@Post('change-password')\n@UseGuards(ReAuthGuard)\nasync changePassword(@Body() changePasswordDto: ChangePasswordDto) {\n return this.userService.changePassword(changePasswordDto);\n}"
112
+ },
113
+ {
114
+ "description": "Email change with re-authentication",
115
+ "code": "@Put('email')\n@UseGuards(ReAuthGuard)\nasync updateEmail(@Body() emailDto: UpdateEmailDto) {\n return this.userService.updateEmail(emailDto);\n}"
116
+ },
117
+ {
118
+ "description": "Profile update with re-authentication",
119
+ "code": "app.put('/profile', requireReAuth, (req, res) => {\n return userService.updateProfile(req.body);\n});"
120
+ }
121
+ ]
122
+ },
123
+ "testing": {
124
+ "testCases": [
125
+ {
126
+ "name": "password_change_without_reauth",
127
+ "type": "violation",
128
+ "description": "Password change endpoint without re-authentication"
129
+ },
130
+ {
131
+ "name": "email_change_without_reauth",
132
+ "type": "violation",
133
+ "description": "Email change endpoint without re-authentication"
134
+ },
135
+ {
136
+ "name": "profile_update_without_reauth",
137
+ "type": "violation",
138
+ "description": "Profile update endpoint without re-authentication"
139
+ },
140
+ {
141
+ "name": "password_change_with_reauth",
142
+ "type": "clean",
143
+ "description": "Password change endpoint with re-authentication guard"
144
+ },
145
+ {
146
+ "name": "email_change_with_reauth",
147
+ "type": "clean",
148
+ "description": "Email change endpoint with re-authentication guard"
149
+ },
150
+ {
151
+ "name": "profile_update_with_reauth",
152
+ "type": "clean",
153
+ "description": "Profile update endpoint with re-authentication middleware"
154
+ }
155
+ ]
156
+ },
157
+ "performance": {
158
+ "complexity": "O(n)",
159
+ "description": "Linear complexity based on number of route handlers and method calls in the source code"
160
+ }
161
+ }