@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,255 @@
1
+ # S011 - Secure GUID Generation
2
+
3
+ ## Overview
4
+
5
+ Quy tắc này phát hiện việc sử dụng các phương pháp yếu hoặc có thể dự đoán được để tạo GUIDs/UUIDs cho mục đích bảo mật. GUIDs dùng cho session tokens, API keys, reset tokens, hoặc các mục đích bảo mật khác phải được tạo theo chuẩn UUID v4 với CSPRNG (Cryptographically Secure Pseudo-Random Number Generator).
6
+
7
+ ## OWASP Classification
8
+
9
+ - **Category**: A02:2021 - Cryptographic Failures
10
+ - **CWE**: CWE-338 - Use of Cryptographically Weak Pseudo-Random Number Generator (PRNG)
11
+ - **Severity**: Error
12
+ - **Impact**: High (Session hijacking, token prediction, unauthorized access)
13
+
14
+ ## Vấn đề
15
+
16
+ Khi sử dụng các phương pháp yếu để tạo GUIDs cho mục đích bảo mật:
17
+
18
+ 1. **Dễ dự đoán**: Kẻ tấn công có thể dự đoán GUIDs tiếp theo
19
+ 2. **Session hijacking**: Session tokens yếu có thể bị đoán và chiếm quyền
20
+ 3. **Token collision**: Xác suất trùng lặp cao hơn với PRNG yếu
21
+ 4. **Brute force attacks**: Tokens dễ bị tấn công brute force
22
+
23
+ ## Các trường hợp vi phạm
24
+
25
+ ### 1. Sử dụng Math.random()
26
+
27
+ ```javascript
28
+ // ❌ Vi phạm - Math.random() không an toàn cho mục đích bảo mật
29
+ const sessionId = Math.random().toString(36).substring(2, 15);
30
+ const apiKey = Math.random().toString(36) + Math.random().toString(36);
31
+ const resetToken = `${Date.now()}-${Math.random()}`;
32
+
33
+ // ❌ Vi phạm - Timestamp-based không đủ random
34
+ const token = Date.now().toString(36);
35
+ const userId = new Date().getTime();
36
+ ```
37
+
38
+ ### 2. Sử dụng UUID v1 (time-based)
39
+
40
+ ```javascript
41
+ // ❌ Vi phạm - UUID v1 sử dụng timestamp và MAC address
42
+ import { v1 as uuidv1 } from "uuid";
43
+
44
+ const sessionToken = uuidv1(); // Time-based, có thể dự đoán
45
+ const apiKey = uuidv1(); // Không an toàn cho security purposes
46
+ ```
47
+
48
+ ### 3. Custom GUID generation yếu
49
+
50
+ ```javascript
51
+ // ❌ Vi phạm - Custom implementation không sử dụng CSPRNG
52
+ function generateGuid() {
53
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
54
+ const r = (Math.random() * 16) | 0; // Math.random() không an toàn
55
+ const v = c === "x" ? r : (r & 0x3) | 0x8;
56
+ return v.toString(16);
57
+ });
58
+ }
59
+
60
+ const resetToken = generateGuid();
61
+ const sessionId = generateGuid();
62
+ ```
63
+
64
+ ### 4. Sử dụng cho security-critical purposes
65
+
66
+ ```javascript
67
+ // ❌ Vi phạm - Weak GUID cho authentication
68
+ app.post("/login", (req, res) => {
69
+ const sessionToken = Math.random().toString(36);
70
+ req.session.token = sessionToken;
71
+ });
72
+
73
+ // ❌ Vi phạm - Weak GUID cho password reset
74
+ const resetToken = Date.now() + "-" + Math.random();
75
+ await sendPasswordResetEmail(user.email, resetToken);
76
+
77
+ // ❌ Vi phạm - Weak GUID cho API key
78
+ const apiKey = uuidv1(); // Time-based UUID
79
+ await saveApiKey(userId, apiKey);
80
+ ```
81
+
82
+ ## Giải pháp an toàn
83
+
84
+ ### 1. Sử dụng crypto.randomUUID() (Node.js 14.17+)
85
+
86
+ ```javascript
87
+ // ✅ An toàn - crypto.randomUUID() sử dụng CSPRNG
88
+ import { randomUUID } from "crypto";
89
+
90
+ const sessionId = randomUUID();
91
+ const apiKey = randomUUID();
92
+ const resetToken = randomUUID();
93
+ ```
94
+
95
+ ### 2. Sử dụng uuid v4 library
96
+
97
+ ```javascript
98
+ // ✅ An toàn - uuid v4 sử dụng CSPRNG
99
+ import { v4 as uuidv4 } from "uuid";
100
+
101
+ const sessionToken = uuidv4();
102
+ const apiKey = uuidv4();
103
+ const resetToken = uuidv4();
104
+ ```
105
+
106
+ ### 3. Sử dụng crypto.randomBytes()
107
+
108
+ ```javascript
109
+ // ✅ An toàn - crypto.randomBytes() cho custom implementation
110
+ import { randomBytes } from "crypto";
111
+
112
+ const sessionId = randomBytes(16).toString("hex");
113
+ const apiKey = randomBytes(32).toString("base64url");
114
+ const resetToken = randomBytes(48).toString("base64url");
115
+ ```
116
+
117
+ ### 4. Best practices cho security tokens
118
+
119
+ ```javascript
120
+ // ✅ An toàn - Session management
121
+ import { randomUUID } from "crypto";
122
+
123
+ app.post("/login", async (req, res) => {
124
+ const sessionToken = randomUUID();
125
+ await redis.set(`session:${sessionToken}`, userId, "EX", 3600);
126
+ res.cookie("sessionId", sessionToken, { httpOnly: true, secure: true });
127
+ });
128
+
129
+ // ✅ An toàn - Password reset token
130
+ import { randomBytes } from "crypto";
131
+
132
+ async function generateResetToken() {
133
+ const token = randomBytes(32).toString("hex");
134
+ const hashedToken = await bcrypt.hash(token, 10);
135
+ await saveToDatabase({ token: hashedToken, expiresAt: Date.now() + 3600000 });
136
+ return token;
137
+ }
138
+
139
+ // ✅ An toàn - API key generation
140
+ import { v4 as uuidv4 } from "uuid";
141
+
142
+ const apiKey = `sk_${uuidv4().replace(/-/g, "")}`;
143
+ const hashedKey = await bcrypt.hash(apiKey, 10);
144
+ await saveApiKey(userId, hashedKey);
145
+ ```
146
+
147
+ ## Phương pháp phát hiện
148
+
149
+ Rule này sử dụng symbol-based analysis để phát hiện:
150
+
151
+ 1. **Math.random() usage**: Detect `Math.random()` trong context tạo tokens/IDs
152
+ 2. **Timestamp-based IDs**: Detect `Date.now()`, `new Date().getTime()` cho security purposes
153
+ 3. **UUID v1 usage**: Detect `uuidv1()` hoặc `uuid.v1()` cho authentication/authorization
154
+ 4. **Weak GUID patterns**: Detect custom implementations không sử dụng CSPRNG
155
+ 5. **Variable naming context**: Phân tích tên biến như `sessionId`, `token`, `apiKey`, `resetToken`
156
+
157
+ ### Detection patterns:
158
+
159
+ - Variable names: `session`, `token`, `api`, `key`, `reset`, `auth`, `secret`
160
+ - Unsafe methods: `Math.random()`, `Date.now()`, `getTime()`, `uuidv1()`
161
+ - Secure methods: `crypto.randomUUID()`, `crypto.randomBytes()`, `uuidv4()`
162
+
163
+ ## Safe contexts (không báo lỗi)
164
+
165
+ ```javascript
166
+ // ✅ OK - Sử dụng Math.random() cho non-security purposes
167
+ const displayId = Math.random().toString(36); // UI display ID
168
+ const tempId = Date.now(); // Temporary client-side ID
169
+ const orderId = `ORD-${Date.now()}`; // Business identifier, not security token
170
+
171
+ // ✅ OK - UUID v1 cho non-security purposes
172
+ const recordId = uuidv1(); // Database record ID (không dùng cho authentication)
173
+ const traceId = uuidv1(); // Distributed tracing ID
174
+ ```
175
+
176
+ ## Cấu hình
177
+
178
+ ```json
179
+ {
180
+ "S011": {
181
+ "enabled": true,
182
+ "severity": "error",
183
+ "excludePatterns": ["test/**", "**/*.test.js", "**/*.spec.js"],
184
+ "securityKeywords": [
185
+ "session",
186
+ "token",
187
+ "api",
188
+ "key",
189
+ "reset",
190
+ "auth",
191
+ "secret",
192
+ "credential"
193
+ ]
194
+ }
195
+ }
196
+ ```
197
+
198
+ ## Best Practices
199
+
200
+ 1. **Luôn dùng CSPRNG**: Sử dụng `crypto.randomUUID()` hoặc `crypto.randomBytes()` cho security tokens
201
+ 2. **UUID v4 only**: Chỉ sử dụng UUID v4 cho security purposes, tránh UUID v1
202
+ 3. **Sufficient entropy**: Đảm bảo đủ entropy (ít nhất 128 bits cho tokens)
203
+ 4. **No predictable patterns**: Tránh patterns có thể dự đoán (timestamp, sequential IDs)
204
+ 5. **Token expiration**: Luôn set thời hạn cho security tokens
205
+ 6. **Hash before storage**: Hash tokens trước khi lưu vào database
206
+
207
+ ## Platform-specific implementations
208
+
209
+ ### Node.js
210
+
211
+ ```javascript
212
+ import { randomUUID, randomBytes } from "crypto";
213
+ const token = randomUUID(); // UUID v4 với CSPRNG
214
+ const key = randomBytes(32).toString("hex");
215
+ ```
216
+
217
+ ### Python
218
+
219
+ ```python
220
+ import secrets
221
+ import uuid
222
+
223
+ token = str(uuid.uuid4()) # UUID v4 với CSPRNG
224
+ key = secrets.token_urlsafe(32) # Secure random token
225
+ ```
226
+
227
+ ### Java
228
+
229
+ ```java
230
+ import java.security.SecureRandom;
231
+ import java.util.UUID;
232
+
233
+ UUID token = UUID.randomUUID(); // UUID v4 với CSPRNG
234
+ SecureRandom random = new SecureRandom();
235
+ byte[] bytes = new byte[32];
236
+ random.nextBytes(bytes);
237
+ ```
238
+
239
+ ### .NET/C#
240
+
241
+ ```csharp
242
+ using System;
243
+ using System.Security.Cryptography;
244
+
245
+ var token = Guid.NewGuid(); // GUID với CSPRNG
246
+ var bytes = RandomNumberGenerator.GetBytes(32); // Secure random
247
+ ```
248
+
249
+ ## Tài liệu tham khảo
250
+
251
+ - [OWASP A02:2021 - Cryptographic Failures](https://owasp.org/Top10/A02_2021-Cryptographic_Failures/)
252
+ - [CWE-338: Use of Cryptographically Weak PRNG](https://cwe.mitre.org/data/definitions/338.html)
253
+ - [RFC 4122: UUID Standard](https://www.rfc-editor.org/rfc/rfc4122)
254
+ - [Node.js Crypto Documentation](https://nodejs.org/api/crypto.html)
255
+ - [NIST SP 800-90A: Random Number Generation](https://csrc.nist.gov/publications/detail/sp/800-90a/rev-1/final)
@@ -0,0 +1,135 @@
1
+ /**
2
+ * S011 - Secure GUID Generation
3
+ *
4
+ * Main analyzer using symbol-based analysis to detect weak GUID/UUID generation
5
+ * for security purposes.
6
+ *
7
+ * Based on:
8
+ * - OWASP A02:2021 - Cryptographic Failures
9
+ * - CWE-338: Use of Cryptographically Weak Pseudo-Random Number Generator
10
+ */
11
+
12
+ // Command: node cli.js --rule=S011 --input=examples/rule-test-fixtures/rules/S011_secure_guid_generation --engine=heuristic
13
+
14
+ const S011SymbolBasedAnalyzer = require("./symbol-based-analyzer");
15
+
16
+ class S011Analyzer {
17
+ constructor(options = {}) {
18
+ this.ruleId = "S011";
19
+ this.semanticEngine = options.semanticEngine || null;
20
+ this.verbose = options.verbose || false;
21
+
22
+ try {
23
+ this.symbolAnalyzer = new S011SymbolBasedAnalyzer(this.semanticEngine);
24
+ } catch (e) {
25
+ console.warn(`⚠ [S011] Failed to create symbol analyzer: ${e.message}`);
26
+ }
27
+ }
28
+
29
+ async initialize(semanticEngine) {
30
+ this.semanticEngine = semanticEngine;
31
+ if (this.symbolAnalyzer && this.symbolAnalyzer.initialize) {
32
+ await this.symbolAnalyzer.initialize(semanticEngine);
33
+ }
34
+ }
35
+
36
+ analyzeSingle(filePath, options = {}) {
37
+ return this.analyze([filePath], "typescript", options);
38
+ }
39
+
40
+ async analyze(files, language, options = {}) {
41
+ const violations = [];
42
+ for (const filePath of files) {
43
+ try {
44
+ const vs = await this.analyzeFile(filePath, options);
45
+ violations.push(...vs);
46
+ } catch (e) {
47
+ console.warn(`⚠ [S011] Analysis error for ${filePath}: ${e.message}`);
48
+ }
49
+ }
50
+ return violations;
51
+ }
52
+
53
+ async analyzeFile(filePath, options = {}) {
54
+ const violationMap = new Map();
55
+
56
+ if (!this.symbolAnalyzer) {
57
+ return [];
58
+ }
59
+
60
+ // Skip test files, build directories, and node_modules
61
+ if (this.shouldSkipFile(filePath)) {
62
+ return [];
63
+ }
64
+
65
+ try {
66
+ let sourceFile = null;
67
+ if (this.semanticEngine?.project) {
68
+ sourceFile = this.semanticEngine.project.getSourceFile(filePath);
69
+ }
70
+
71
+ if (!sourceFile) {
72
+ // Create temporary ts-morph source file
73
+ const fs = require("fs");
74
+ const path = require("path");
75
+ const { Project } = require("ts-morph");
76
+ if (!fs.existsSync(filePath)) {
77
+ throw new Error(`File not found: ${filePath}`);
78
+ }
79
+ const content = fs.readFileSync(filePath, "utf8");
80
+ const tmp = new Project({
81
+ useInMemoryFileSystem: true,
82
+ compilerOptions: { allowJs: true },
83
+ });
84
+ sourceFile = tmp.createSourceFile(path.basename(filePath), content);
85
+ }
86
+
87
+ if (sourceFile) {
88
+ const symbolViolations = await this.symbolAnalyzer.analyze(
89
+ sourceFile,
90
+ filePath
91
+ );
92
+ symbolViolations.forEach((v) => {
93
+ const key = `${v.line}:${v.column}:${v.message}`;
94
+ if (!violationMap.has(key)) violationMap.set(key, v);
95
+ });
96
+ }
97
+ } catch (e) {
98
+ console.warn(`⚠ [S011] Symbol analysis failed: ${e.message}`);
99
+ }
100
+
101
+ return Array.from(violationMap.values()).map((v) => ({
102
+ ...v,
103
+ filePath,
104
+ file: filePath,
105
+ }));
106
+ }
107
+
108
+ shouldSkipFile(filePath) {
109
+ const skipPatterns = [
110
+ "test/",
111
+ "tests/",
112
+ "__tests__/",
113
+ ".test.",
114
+ ".spec.",
115
+ "node_modules/",
116
+ "build/",
117
+ "dist/",
118
+ ".next/",
119
+ "coverage/",
120
+ "vendor/",
121
+ "mocks/",
122
+ ".mock.",
123
+ ];
124
+
125
+ return skipPatterns.some((pattern) => filePath.includes(pattern));
126
+ }
127
+
128
+ cleanup() {
129
+ if (this.symbolAnalyzer?.cleanup) {
130
+ this.symbolAnalyzer.cleanup();
131
+ }
132
+ }
133
+ }
134
+
135
+ module.exports = S011Analyzer;
@@ -0,0 +1,56 @@
1
+ {
2
+ "ruleId": "S011",
3
+ "name": "Secure GUID Generation",
4
+ "description": "GUIDs used for security purposes must be generated according to UUID v4 standard with CSPRNG",
5
+ "category": "security",
6
+ "severity": "error",
7
+ "languages": ["All languages"],
8
+ "tags": [
9
+ "security",
10
+ "owasp",
11
+ "cryptographic-failures",
12
+ "uuid",
13
+ "guid",
14
+ "randomness"
15
+ ],
16
+ "enabled": true,
17
+ "fixable": false,
18
+ "engine": "heuristic",
19
+ "metadata": {
20
+ "owaspCategory": "A02:2021 - Cryptographic Failures",
21
+ "cweId": "CWE-338",
22
+ "description": "Using weak or predictable methods to generate GUIDs/UUIDs for security purposes (session tokens, API keys, reset tokens) can lead to security vulnerabilities. Security-critical GUIDs must be generated using UUID v4 with Cryptographically Secure Pseudo-Random Number Generator (CSPRNG).",
23
+ "impact": "High - Session hijacking, token prediction, unauthorized access",
24
+ "likelihood": "Medium",
25
+ "remediation": "Use UUID v4 with CSPRNG libraries: crypto.randomUUID() (Node.js 14.17+), uuid v4, or equivalent secure random generators"
26
+ },
27
+ "patterns": {
28
+ "vulnerable": [
29
+ "Using Math.random() for GUID generation",
30
+ "Using Date.now() or timestamp-based GUIDs for security tokens",
31
+ "Using non-cryptographic UUID libraries",
32
+ "Using UUID v1 (time-based) for security purposes",
33
+ "Custom GUID generation without CSPRNG"
34
+ ],
35
+ "secure": [
36
+ "crypto.randomUUID() for Node.js 14.17+",
37
+ "uuid v4 library with proper CSPRNG",
38
+ "crypto.randomBytes() for custom implementation",
39
+ "Platform-specific secure random: SecureRandom (Java), secrets (Python)"
40
+ ]
41
+ },
42
+ "examples": {
43
+ "violations": [
44
+ "const sessionId = Math.random().toString(36);",
45
+ "const token = Date.now() + '-' + Math.random();",
46
+ "const apiKey = uuidv1(); // Time-based UUID",
47
+ "const resetToken = generateGuid(); // Custom weak implementation"
48
+ ],
49
+ "fixes": [
50
+ "const sessionId = crypto.randomUUID();",
51
+ "const token = require('uuid').v4();",
52
+ "const apiKey = crypto.randomBytes(32).toString('hex');",
53
+ "const resetToken = crypto.randomUUID();"
54
+ ]
55
+ }
56
+ }