@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.
- package/config/rules/enhanced-rules-registry.json +77 -18
- package/core/cli-program.js +2 -1
- package/core/github-annotate-service.js +89 -0
- package/core/output-service.js +25 -0
- package/core/summary-report-service.js +30 -30
- 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/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/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
|
+
}
|