@sun-asterisk/sunlint 1.3.1 → 1.3.2

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 (66) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/CONTRIBUTING.md +210 -1691
  3. package/config/rule-analysis-strategies.js +17 -1
  4. package/config/rules/enhanced-rules-registry.json +369 -1135
  5. package/config/rules/rules-registry-generated.json +1 -1
  6. package/core/enhanced-rules-registry.js +2 -1
  7. package/core/semantic-engine.js +15 -3
  8. package/core/semantic-rule-base.js +4 -2
  9. package/engines/heuristic-engine.js +65 -4
  10. package/integrations/eslint/plugin/rules/common/c003-no-vague-abbreviations.js +59 -1
  11. package/integrations/eslint/plugin/rules/common/c006-function-name-verb-noun.js +26 -1
  12. package/integrations/eslint/plugin/rules/common/c030-use-custom-error-classes.js +54 -19
  13. package/origin-rules/common-en.md +11 -7
  14. package/package.json +1 -1
  15. package/rules/common/C002_no_duplicate_code/analyzer.js +334 -36
  16. package/rules/common/C003_no_vague_abbreviations/analyzer.js +220 -35
  17. package/rules/common/C006_function_naming/analyzer.js +29 -3
  18. package/rules/common/C010_limit_block_nesting/analyzer.js +181 -337
  19. package/rules/common/C010_limit_block_nesting/config.json +64 -0
  20. package/rules/common/C010_limit_block_nesting/regex-based-analyzer.js +379 -0
  21. package/rules/common/C010_limit_block_nesting/symbol-based-analyzer.js +231 -0
  22. package/rules/common/C013_no_dead_code/analyzer.js +75 -177
  23. package/rules/common/C013_no_dead_code/config.json +61 -0
  24. package/rules/common/C013_no_dead_code/regex-based-analyzer.js +345 -0
  25. package/rules/common/C013_no_dead_code/symbol-based-analyzer.js +640 -0
  26. package/rules/common/C014_dependency_injection/analyzer.js +48 -313
  27. package/rules/common/C014_dependency_injection/config.json +26 -0
  28. package/rules/common/C014_dependency_injection/symbol-based-analyzer.js +751 -0
  29. package/rules/common/C018_no_throw_generic_error/analyzer.js +232 -0
  30. package/rules/common/C018_no_throw_generic_error/config.json +50 -0
  31. package/rules/common/C018_no_throw_generic_error/regex-based-analyzer.js +387 -0
  32. package/rules/common/C018_no_throw_generic_error/symbol-based-analyzer.js +314 -0
  33. package/rules/common/C019_log_level_usage/analyzer.js +110 -317
  34. package/rules/common/C019_log_level_usage/pattern-analyzer.js +88 -0
  35. package/rules/common/C019_log_level_usage/system-log-analyzer.js +1267 -0
  36. package/rules/common/C023_no_duplicate_variable/analyzer.js +180 -0
  37. package/rules/common/C023_no_duplicate_variable/config.json +50 -0
  38. package/rules/common/C023_no_duplicate_variable/symbol-based-analyzer.js +158 -0
  39. package/rules/common/C024_no_scatter_hardcoded_constants/analyzer.js +180 -0
  40. package/rules/common/C024_no_scatter_hardcoded_constants/config.json +50 -0
  41. package/rules/common/C024_no_scatter_hardcoded_constants/symbol-based-analyzer.js +181 -0
  42. package/rules/common/C030_use_custom_error_classes/analyzer.js +200 -0
  43. package/rules/common/C035_error_logging_context/analyzer.js +3 -1
  44. package/rules/index.js +5 -1
  45. package/rules/security/S009_no_insecure_encryption/README.md +158 -0
  46. package/rules/security/S009_no_insecure_encryption/analyzer.js +319 -0
  47. package/rules/security/S009_no_insecure_encryption/config.json +55 -0
  48. package/rules/security/S010_no_insecure_encryption/README.md +224 -0
  49. package/rules/security/S010_no_insecure_encryption/analyzer.js +493 -0
  50. package/rules/security/S010_no_insecure_encryption/config.json +48 -0
  51. package/rules/security/S016_no_sensitive_querystring/STRATEGY.md +149 -0
  52. package/rules/security/S016_no_sensitive_querystring/analyzer.js +276 -0
  53. package/rules/security/S016_no_sensitive_querystring/config.json +127 -0
  54. package/rules/security/S016_no_sensitive_querystring/regex-based-analyzer.js +258 -0
  55. package/rules/security/S016_no_sensitive_querystring/symbol-based-analyzer.js +495 -0
  56. package/rules/security/S048_no_current_password_in_reset/README.md +222 -0
  57. package/rules/security/S048_no_current_password_in_reset/analyzer.js +366 -0
  58. package/rules/security/S048_no_current_password_in_reset/config.json +48 -0
  59. package/rules/security/S055_content_type_validation/README.md +176 -0
  60. package/rules/security/S055_content_type_validation/analyzer.js +312 -0
  61. package/rules/security/S055_content_type_validation/config.json +48 -0
  62. package/rules/utils/rule-helpers.js +140 -1
  63. package/scripts/consolidate-config.js +116 -0
  64. package/config/rules/S027-categories.json +0 -122
  65. package/config/rules/rules-registry.json +0 -777
  66. package/rules/common/C006_function_naming/smart-analyzer.js +0 -503
@@ -0,0 +1,319 @@
1
+ /**
2
+ * Heuristic analyzer for S009 - No Insecure Encryption Modes, Padding, or Cryptographic Algorithms
3
+ * Purpose: Detect usage of insecure encryption algorithms, modes, and padding schemes
4
+ * Based on OWASP A02:2021 - Cryptographic Failures
5
+ */
6
+
7
+ class S009Analyzer {
8
+ constructor() {
9
+ this.ruleId = 'S009';
10
+ this.ruleName = 'No Insecure Encryption Modes, Padding, or Cryptographic Algorithms';
11
+ this.description = 'Do not use insecure encryption modes, padding, or cryptographic algorithms';
12
+
13
+ // Insecure symmetric encryption algorithms
14
+ this.insecureSymmetricAlgorithms = [
15
+ 'des', 'des-cbc', 'des-ecb', 'des-cfb', 'des-ofb',
16
+ '3des', 'des-ede', 'des-ede-cbc', 'des-ede-cfb', 'des-ede-ofb',
17
+ 'des-ede3', 'des-ede3-cbc', 'des-ede3-cfb', 'des-ede3-ofb',
18
+ 'rc2', 'rc2-cbc', 'rc2-ecb', 'rc2-cfb', 'rc2-ofb',
19
+ 'rc4', 'rc4-40', 'rc4-hmac-md5',
20
+ 'blowfish', 'bf', 'bf-cbc', 'bf-ecb', 'bf-cfb', 'bf-ofb'
21
+ ];
22
+
23
+ // Insecure block cipher modes
24
+ this.insecureModes = [
25
+ 'ecb', 'electronic-codebook'
26
+ ];
27
+
28
+ // Insecure padding schemes
29
+ this.insecurePadding = [
30
+ 'pkcs1', 'pkcs1_5', 'pkcs1-padding', 'rsa_pkcs1_padding',
31
+ 'no-padding', 'nopadding'
32
+ ];
33
+
34
+ // Insecure hash algorithms
35
+ this.insecureHashAlgorithms = [
36
+ 'md2', 'md4', 'md5',
37
+ 'sha', 'sha1', 'sha-1',
38
+ 'ripemd', 'ripemd128', 'ripemd160'
39
+ ];
40
+
41
+ // Insecure key derivation functions
42
+ this.insecureKDFs = [
43
+ 'pbkdf1', 'simple-hash', 'plain-hash'
44
+ ];
45
+
46
+ // Common crypto libraries and their patterns
47
+ this.cryptoPatterns = [
48
+ // Node.js crypto module
49
+ /crypto\.createCipher\(['"`]([^'"`]+)['"`]/i,
50
+ /crypto\.createDecipher\(['"`]([^'"`]+)['"`]/i,
51
+ /crypto\.createHash\(['"`]([^'"`]+)['"`]/i,
52
+ /crypto\.createHmac\(['"`]([^'"`]+)['"`]/i,
53
+ /crypto\.pbkdf2\(['"`]([^'"`]+)['"`]/i,
54
+
55
+ // CryptoJS patterns
56
+ /CryptoJS\.DES\./i,
57
+ /CryptoJS\.TripleDES\./i,
58
+ /CryptoJS\.RC4\./i,
59
+ /CryptoJS\.MD5\(/i,
60
+ /CryptoJS\.SHA1\(/i,
61
+ /CryptoJS\.mode\.ECB/i,
62
+
63
+ // Java crypto patterns
64
+ /Cipher\.getInstance\(['"`]([^'"`]+)['"`]/i,
65
+ /MessageDigest\.getInstance\(['"`]([^'"`]+)['"`]/i,
66
+ /Mac\.getInstance\(['"`]([^'"`]+)['"`]/i,
67
+ /KeyGenerator\.getInstance\(['"`]([^'"`]+)['"`]/i,
68
+
69
+ // .NET crypto patterns
70
+ /new\s+(DES|TripleDES|RC2|MD5|SHA1)CryptoServiceProvider/i,
71
+ /\.Create\(['"`](DES|TripleDES|RC2|MD5|SHA1)['"`]\)/i,
72
+
73
+ // OpenSSL command patterns
74
+ /-des\b|-des3\b|-rc4\b|-md5\b|-sha1\b/i,
75
+
76
+ // Generic algorithm references
77
+ /'(des|3des|rc4|md5|sha1|ecb)'/i,
78
+ /"(des|3des|rc4|md5|sha1|ecb)"/i,
79
+ /`(des|3des|rc4|md5|sha1|ecb)`/i,
80
+
81
+ // Configuration patterns
82
+ /algorithm\s*[:=]\s*['"`]([^'"`]+)['"`]/i,
83
+ /cipher\s*[:=]\s*['"`]([^'"`]+)['"`]/i,
84
+ /hash\s*[:=]\s*['"`]([^'"`]+)['"`]/i,
85
+ /encryption\s*[:=]\s*['"`]([^'"`]+)['"`]/i,
86
+ ];
87
+
88
+ // Safe patterns to avoid false positives
89
+ this.safePatterns = [
90
+ // Comments and documentation
91
+ /\/\/|\/\*|\*\/|@param|@return|@example|@deprecated|TODO|FIXME/i,
92
+
93
+ // Import/export statements
94
+ /import|export|require|module\.exports/i,
95
+
96
+ // Type definitions
97
+ /interface|type|enum|class.*\{/i,
98
+
99
+ // Test files and mock data
100
+ /\.test\.|\.spec\.|mock|stub|fake|dummy/i,
101
+
102
+ // Safe modern algorithms mentioned in context
103
+ /aes|rsa-oaep|sha256|sha384|sha512|bcrypt|scrypt|argon2|pbkdf2/i,
104
+
105
+ // Configuration examples or documentation
106
+ /example|sample|demo|deprecated|legacy|old|insecure|weak|avoid|don't use|do not use/i,
107
+
108
+ // Variable names that might contain keywords but are safe
109
+ /const\s+\w*[Nn]ame|let\s+\w*[Nn]ame|var\s+\w*[Nn]ame/i,
110
+
111
+ // Safe usage in educational context
112
+ /educational|tutorial|learning|history|comparison/i,
113
+ ];
114
+
115
+ // Context patterns that increase confidence of violations
116
+ this.violationContexts = [
117
+ // Direct algorithm usage
118
+ /encrypt|decrypt|cipher|hash|digest|sign|verify/i,
119
+
120
+ // Configuration settings
121
+ /config|setting|option|parameter/i,
122
+
123
+ // API calls
124
+ /\.create|\.get|\.set|\.use|\.apply/i,
125
+
126
+ // Assignment patterns
127
+ /=\s*['"`]|:\s*['"`]/,
128
+
129
+ // Function calls
130
+ /\([^)]*['"`][^'"`]*['"`][^)]*\)/,
131
+ ];
132
+ }
133
+
134
+ async analyze(files, language, options = {}) {
135
+ const violations = [];
136
+
137
+ for (const filePath of files) {
138
+ // Skip test files, build directories, and node_modules
139
+ if (this.shouldSkipFile(filePath)) {
140
+ continue;
141
+ }
142
+
143
+ try {
144
+ const content = require('fs').readFileSync(filePath, 'utf8');
145
+ const fileViolations = this.analyzeFile(content, filePath, options);
146
+ violations.push(...fileViolations);
147
+ } catch (error) {
148
+ if (options.verbose) {
149
+ console.warn(`⚠️ Failed to analyze ${filePath}: ${error.message}`);
150
+ }
151
+ }
152
+ }
153
+
154
+ return violations;
155
+ }
156
+
157
+ shouldSkipFile(filePath) {
158
+ const skipPatterns = [
159
+ 'test/', 'tests/', '__tests__/', '.test.', '.spec.',
160
+ 'node_modules/', 'build/', 'dist/', '.next/', 'coverage/',
161
+ 'vendor/', 'mocks/', '.mock.', 'docs/', 'documentation/'
162
+ ];
163
+
164
+ return skipPatterns.some(pattern => filePath.includes(pattern));
165
+ }
166
+
167
+ analyzeFile(content, filePath, options = {}) {
168
+ const violations = [];
169
+ const lines = content.split('\n');
170
+
171
+ lines.forEach((line, index) => {
172
+ const lineNumber = index + 1;
173
+ const trimmedLine = line.trim();
174
+
175
+ // Skip comments, imports, and empty lines
176
+ if (this.shouldSkipLine(trimmedLine)) {
177
+ return;
178
+ }
179
+
180
+ // Check for insecure cryptographic usage
181
+ const violation = this.checkForInsecureCrypto(line, lineNumber, filePath);
182
+ if (violation) {
183
+ violations.push(violation);
184
+ }
185
+ });
186
+
187
+ return violations;
188
+ }
189
+
190
+ shouldSkipLine(line) {
191
+ // Skip comments, imports, and other non-code lines
192
+ return (
193
+ line.length === 0 ||
194
+ line.startsWith('//') ||
195
+ line.startsWith('/*') ||
196
+ line.startsWith('*') ||
197
+ line.startsWith('import ') ||
198
+ line.startsWith('export ') ||
199
+ line.startsWith('require(') ||
200
+ line.includes('module.exports') ||
201
+ line.startsWith('#') // Python/shell comments
202
+ );
203
+ }
204
+
205
+ checkForInsecureCrypto(line, lineNumber, filePath) {
206
+ // First check if line contains safe patterns (early exit)
207
+ if (this.containsSafePattern(line)) {
208
+ return null;
209
+ }
210
+
211
+ // Check each crypto pattern
212
+ for (const pattern of this.cryptoPatterns) {
213
+ const match = pattern.exec(line);
214
+ if (match) {
215
+ const algorithm = match[1] ? match[1].toLowerCase() : '';
216
+ const fullMatch = match[0];
217
+
218
+ // Check if the algorithm is insecure
219
+ const insecureAlgorithm = this.identifyInsecureAlgorithm(algorithm, fullMatch);
220
+ if (insecureAlgorithm) {
221
+ // Additional context check to reduce false positives
222
+ if (this.hasViolationContext(line)) {
223
+ return {
224
+ ruleId: this.ruleId,
225
+ severity: 'error',
226
+ message: `Insecure cryptographic algorithm detected: ${insecureAlgorithm.algorithm}. ${insecureAlgorithm.reason}`,
227
+ line: lineNumber,
228
+ column: line.indexOf(fullMatch) + 1,
229
+ filePath: filePath,
230
+ type: insecureAlgorithm.type,
231
+ algorithm: insecureAlgorithm.algorithm,
232
+ details: insecureAlgorithm.recommendation
233
+ };
234
+ }
235
+ }
236
+ }
237
+ }
238
+
239
+ return null;
240
+ }
241
+
242
+ containsSafePattern(line) {
243
+ return this.safePatterns.some(pattern => pattern.test(line));
244
+ }
245
+
246
+ identifyInsecureAlgorithm(algorithm, fullMatch) {
247
+ const lowerAlgorithm = algorithm.toLowerCase();
248
+ const lowerFullMatch = fullMatch.toLowerCase();
249
+
250
+ // Check symmetric encryption algorithms
251
+ if (this.insecureSymmetricAlgorithms.some(alg =>
252
+ lowerAlgorithm.includes(alg) || lowerFullMatch.includes(alg))) {
253
+ const matchedAlg = this.insecureSymmetricAlgorithms.find(alg =>
254
+ lowerAlgorithm.includes(alg) || lowerFullMatch.includes(alg));
255
+
256
+ return {
257
+ algorithm: matchedAlg.toUpperCase(),
258
+ type: 'insecure_symmetric_encryption',
259
+ reason: 'This algorithm is cryptographically weak and vulnerable to attacks.',
260
+ recommendation: 'Use AES-256-GCM or ChaCha20-Poly1305 for symmetric encryption.'
261
+ };
262
+ }
263
+
264
+ // Check block cipher modes
265
+ if (this.insecureModes.some(mode =>
266
+ lowerAlgorithm.includes(mode) || lowerFullMatch.includes(mode))) {
267
+ return {
268
+ algorithm: 'ECB Mode',
269
+ type: 'insecure_cipher_mode',
270
+ reason: 'ECB mode reveals patterns in plaintext and is not semantically secure.',
271
+ recommendation: 'Use CBC, CTR, or GCM modes with proper initialization vectors.'
272
+ };
273
+ }
274
+
275
+ // Check hash algorithms
276
+ if (this.insecureHashAlgorithms.some(hash =>
277
+ lowerAlgorithm.includes(hash) || lowerFullMatch.includes(hash))) {
278
+ const matchedHash = this.insecureHashAlgorithms.find(hash =>
279
+ lowerAlgorithm.includes(hash) || lowerFullMatch.includes(hash));
280
+
281
+ return {
282
+ algorithm: matchedHash.toUpperCase(),
283
+ type: 'insecure_hash_algorithm',
284
+ reason: 'This hash algorithm is vulnerable to collision attacks.',
285
+ recommendation: 'Use SHA-256, SHA-384, or SHA-512 for cryptographic hashing.'
286
+ };
287
+ }
288
+
289
+ // Check padding schemes
290
+ if (this.insecurePadding.some(padding =>
291
+ lowerAlgorithm.includes(padding) || lowerFullMatch.includes(padding))) {
292
+ return {
293
+ algorithm: 'PKCS#1 v1.5 Padding',
294
+ type: 'insecure_padding_scheme',
295
+ reason: 'PKCS#1 v1.5 padding is vulnerable to padding oracle attacks.',
296
+ recommendation: 'Use OAEP padding for RSA encryption.'
297
+ };
298
+ }
299
+
300
+ // Check key derivation functions
301
+ if (this.insecureKDFs.some(kdf =>
302
+ lowerAlgorithm.includes(kdf) || lowerFullMatch.includes(kdf))) {
303
+ return {
304
+ algorithm: 'PBKDF1',
305
+ type: 'insecure_key_derivation',
306
+ reason: 'This key derivation function is weak and vulnerable to attacks.',
307
+ recommendation: 'Use PBKDF2 with high iteration count, scrypt, or Argon2.'
308
+ };
309
+ }
310
+
311
+ return null;
312
+ }
313
+
314
+ hasViolationContext(line) {
315
+ return this.violationContexts.some(context => context.test(line));
316
+ }
317
+ }
318
+
319
+ module.exports = S009Analyzer;
@@ -0,0 +1,55 @@
1
+ {
2
+ "ruleId": "S009",
3
+ "name": "No Insecure Encryption Modes, Padding, or Cryptographic Algorithms",
4
+ "description": "Do not use insecure encryption modes, padding, or cryptographic algorithms",
5
+ "category": "security",
6
+ "severity": "error",
7
+ "languages": ["All languages"],
8
+ "tags": ["security", "owasp", "cryptographic-failures", "encryption"],
9
+ "enabled": true,
10
+ "fixable": false,
11
+ "engine": "heuristic",
12
+ "metadata": {
13
+ "owaspCategory": "A02:2021 - Cryptographic Failures",
14
+ "cweId": "CWE-327",
15
+ "description": "Using insecure cryptographic algorithms, cipher modes, or padding schemes can lead to data exposure and compromise. Weak algorithms like DES, 3DES, RC4, MD5, and SHA1 are vulnerable to various attacks including collision attacks, brute force, and cryptanalysis.",
16
+ "impact": "High - Data exposure, integrity compromise, authentication bypass",
17
+ "likelihood": "Medium",
18
+ "remediation": "Use strong cryptographic algorithms (AES-256, RSA-2048+, SHA-256+), secure cipher modes (GCM, CBC with IV), and proper padding schemes (OAEP)"
19
+ },
20
+ "patterns": {
21
+ "vulnerable": [
22
+ "Using DES or 3DES for encryption",
23
+ "Using RC4 stream cipher",
24
+ "Using ECB cipher mode",
25
+ "Using MD5 or SHA1 for cryptographic purposes",
26
+ "Using PKCS#1 v1.5 padding for RSA",
27
+ "Using weak key derivation functions"
28
+ ],
29
+ "secure": [
30
+ "Using AES-256-GCM for symmetric encryption",
31
+ "Using RSA with OAEP padding",
32
+ "Using SHA-256 or stronger hash algorithms",
33
+ "Using proper cipher modes with initialization vectors",
34
+ "Using PBKDF2, scrypt, or Argon2 for key derivation"
35
+ ]
36
+ },
37
+ "examples": {
38
+ "violations": [
39
+ "crypto.createCipher('des', key);",
40
+ "crypto.createHash('md5');",
41
+ "Cipher.getInstance('DES/ECB/PKCS5Padding');",
42
+ "CryptoJS.DES.encrypt(data, key);",
43
+ "algorithm: 'rc4'",
44
+ "new DESCryptoServiceProvider();"
45
+ ],
46
+ "fixes": [
47
+ "crypto.createCipher('aes-256-gcm', key);",
48
+ "crypto.createHash('sha256');",
49
+ "Cipher.getInstance('AES/GCM/NoPadding');",
50
+ "CryptoJS.AES.encrypt(data, key);",
51
+ "algorithm: 'aes-256-gcm'",
52
+ "new AesCryptoServiceProvider();"
53
+ ]
54
+ }
55
+ }
@@ -0,0 +1,224 @@
1
+ # S010 - Must use cryptographically secure random number generators (CSPRNG)
2
+
3
+ ## Overview
4
+ Quy tắc này phát hiện việc sử dụng các hàm tạo số ngẫu nhiên không an toàn (như `Math.random()`) cho các mục đích bảo mật, có thể dẫn đến các lỗ hổng bảo mật nghiêm trọng do tính có thể dự đoán được.
5
+
6
+ ## OWASP Classification
7
+ - **Category**: A02:2021 - Cryptographic Failures
8
+ - **CWE**: CWE-338 - Use of Cryptographically Weak Pseudo-Random Number Generator (PRNG)
9
+ - **Severity**: Error
10
+ - **Impact**: High (Predictable tokens, weak encryption keys, authentication bypass)
11
+
12
+ ## Vấn đề
13
+ Khi sử dụng các hàm tạo số ngẫu nhiên không an toàn cho mục đích bảo mật:
14
+
15
+ 1. **Tính dự đoán**: `Math.random()` và các hàm tương tự có thể được dự đoán bởi kẻ tấn công
16
+ 2. **Weak entropy**: Không đủ entropy để tạo ra các giá trị thực sự ngẫu nhiên
17
+ 3. **Seed attacks**: Có thể bị tấn công thông qua việc dự đoán seed
18
+ 4. **Brute force**: Dễ dàng bị brute force do không gian giá trị hạn chế
19
+
20
+ ## Các trường hợp vi phạm
21
+
22
+ ### 1. Sử dụng Math.random() cho security tokens
23
+ ```javascript
24
+ // ❌ Vi phạm - Math.random() không an toàn cho security
25
+ const sessionToken = Math.random().toString(36).substring(2);
26
+ const apiKey = Math.floor(Math.random() * 1000000);
27
+ const resetCode = Math.random().toString().substring(2, 8);
28
+
29
+ // ❌ Vi phạm - tạo password reset token
30
+ function generateResetToken() {
31
+ return Math.random().toString(36).substring(2, 15) +
32
+ Math.random().toString(36).substring(2, 15);
33
+ }
34
+
35
+ // ❌ Vi phạm - tạo JWT secret
36
+ const jwtSecret = Math.random().toString(36);
37
+ ```
38
+
39
+ ### 2. Sử dụng timestamp cho random generation
40
+ ```javascript
41
+ // ❌ Vi phạm - timestamp có thể dự đoán được
42
+ const userId = Date.now().toString();
43
+ const sessionId = new Date().getTime().toString();
44
+ const nonce = performance.now().toString();
45
+
46
+ // ❌ Vi phạm - kết hợp timestamp với Math.random()
47
+ const token = Date.now() + '-' + Math.random().toString(36);
48
+ ```
49
+
50
+ ### 3. Sử dụng cho mã hóa và authentication
51
+ ```javascript
52
+ // ❌ Vi phạm - tạo encryption key không an toàn
53
+ const encryptionKey = Math.random().toString(36).repeat(3);
54
+
55
+ // ❌ Vi phạm - tạo salt cho password hashing
56
+ const salt = Math.random().toString(36).substring(2);
57
+ bcrypt.hash(password, salt); // Nguy hiểm!
58
+
59
+ // ❌ Vi phạm - tạo CSRF token
60
+ const csrfToken = Math.floor(Math.random() * 1000000000);
61
+ ```
62
+
63
+ ### 4. Tạo unique identifiers không an toàn
64
+ ```javascript
65
+ // ❌ Vi phạm - tạo user ID
66
+ const generateUserId = () => Math.random().toString(36).substring(2);
67
+
68
+ // ❌ Vi phạm - tạo file upload path
69
+ const uploadPath = `/uploads/${Math.random().toString(36)}/file.jpg`;
70
+
71
+ // ❌ Vi phạm - tạo temporary filename
72
+ const tempFile = `temp_${Date.now()}_${Math.random()}.tmp`;
73
+ ```
74
+
75
+ ## Giải pháp an toàn
76
+
77
+ ### 1. Sử dụng crypto.randomBytes()
78
+ ```javascript
79
+ // ✅ An toàn - sử dụng crypto.randomBytes()
80
+ const crypto = require('crypto');
81
+
82
+ const sessionToken = crypto.randomBytes(32).toString('hex');
83
+ const apiKey = crypto.randomBytes(16).toString('base64');
84
+ const resetCode = crypto.randomBytes(3).toString('hex'); // 6 hex chars
85
+ ```
86
+
87
+ ### 2. Sử dụng crypto.randomUUID()
88
+ ```javascript
89
+ // ✅ An toàn - sử dụng crypto.randomUUID()
90
+ const sessionId = crypto.randomUUID();
91
+ const userId = crypto.randomUUID();
92
+ const requestId = crypto.randomUUID();
93
+ ```
94
+
95
+ ### 3. Sử dụng crypto.randomInt()
96
+ ```javascript
97
+ // ✅ An toàn - sử dụng crypto.randomInt()
98
+ const otpCode = crypto.randomInt(100000, 999999); // 6-digit OTP
99
+ const randomPort = crypto.randomInt(3000, 9000);
100
+ const challengeNumber = crypto.randomInt(1, 1000000);
101
+ ```
102
+
103
+ ### 4. Sử dụng cho mã hóa an toàn
104
+ ```javascript
105
+ // ✅ An toàn - tạo encryption key
106
+ const encryptionKey = crypto.randomBytes(32); // 256-bit key
107
+
108
+ // ✅ An toàn - tạo salt cho password hashing
109
+ const saltRounds = 12;
110
+ const hashedPassword = await bcrypt.hash(password, saltRounds);
111
+
112
+ // ✅ An toàn - tạo IV cho encryption
113
+ const iv = crypto.randomBytes(16);
114
+ const cipher = crypto.createCipher('aes-256-cbc', key, iv);
115
+ ```
116
+
117
+ ### 5. Sử dụng thư viện an toàn
118
+ ```javascript
119
+ // ✅ An toàn - sử dụng nanoid
120
+ const { nanoid } = require('nanoid');
121
+ const id = nanoid(); // URL-safe, secure ID
122
+
123
+ // ✅ An toàn - sử dụng uuid v4
124
+ const { v4: uuidv4 } = require('uuid');
125
+ const userId = uuidv4();
126
+
127
+ // ✅ An toàn - sử dụng secure-random
128
+ const secureRandom = require('secure-random');
129
+ const randomBytes = secureRandom(32, { type: 'Buffer' });
130
+ ```
131
+
132
+ ### 6. Express.js session security
133
+ ```javascript
134
+ // ✅ An toàn - cấu hình Express session
135
+ const session = require('express-session');
136
+
137
+ app.use(session({
138
+ genid: () => crypto.randomUUID(), // Secure session ID
139
+ secret: crypto.randomBytes(64).toString('hex'), // Secure secret
140
+ resave: false,
141
+ saveUninitialized: false,
142
+ cookie: {
143
+ secure: true, // HTTPS only
144
+ httpOnly: true,
145
+ maxAge: 1800000 // 30 minutes
146
+ }
147
+ }));
148
+ ```
149
+
150
+ ### 7. JWT token generation
151
+ ```javascript
152
+ // ✅ An toàn - tạo JWT với secure secret
153
+ const jwtSecret = crypto.randomBytes(64).toString('hex');
154
+
155
+ const token = jwt.sign(
156
+ { userId, exp: Math.floor(Date.now() / 1000) + 3600 },
157
+ jwtSecret,
158
+ { algorithm: 'HS256' }
159
+ );
160
+ ```
161
+
162
+ ## Phương pháp phát hiện
163
+
164
+ Rule này sử dụng heuristic analysis để phát hiện:
165
+
166
+ 1. **Pattern matching**: Tìm kiếm các pattern như `Math.random()`, `Date.now()`, `performance.now()`
167
+ 2. **Context analysis**: Phân tích ngữ cảnh để xác định có phải mục đích bảo mật không
168
+ 3. **Function analysis**: Kiểm tra tên function có chứa từ khóa bảo mật
169
+ 4. **Variable analysis**: Phân tích tên biến có liên quan đến bảo mật
170
+ 5. **Surrounding context**: Kiểm tra các dòng xung quanh để xác định context
171
+
172
+ ## Cấu hình
173
+
174
+ ```json
175
+ {
176
+ "S010": {
177
+ "enabled": true,
178
+ "severity": "error",
179
+ "excludePatterns": [
180
+ "test/**",
181
+ "**/*.test.js",
182
+ "**/*.spec.js",
183
+ "**/demo/**",
184
+ "**/examples/**"
185
+ ]
186
+ }
187
+ }
188
+ ```
189
+
190
+ ## Best Practices
191
+
192
+ 1. **Luôn sử dụng CSPRNG**: Sử dụng `crypto.randomBytes()`, `crypto.randomUUID()`, `crypto.randomInt()` cho mục đích bảo mật
193
+ 2. **Đủ entropy**: Đảm bảo đủ entropy cho các giá trị random (ít nhất 128 bits cho tokens)
194
+ 3. **Secure storage**: Lưu trữ các giá trị random một cách an toàn
195
+ 4. **Time-limited**: Sử dụng expiration time cho các tokens
196
+ 5. **Proper seeding**: Đảm bảo CSPRNG được seed đúng cách
197
+ 6. **Regular rotation**: Thường xuyên rotate các keys và secrets
198
+
199
+ ## Các trường hợp ngoại lệ
200
+
201
+ Rule này sẽ KHÔNG báo lỗi trong các trường hợp sau:
202
+
203
+ ```javascript
204
+ // ✅ Không báo lỗi - sử dụng cho UI/animation
205
+ const animationDelay = Math.random() * 1000;
206
+ const chartColor = `hsl(${Math.random() * 360}, 50%, 50%)`;
207
+
208
+ // ✅ Không báo lỗi - game logic
209
+ const diceRoll = Math.floor(Math.random() * 6) + 1;
210
+ const enemySpawn = Math.random() < 0.3;
211
+
212
+ // ✅ Không báo lỗi - demo/test data
213
+ const mockData = Array.from({length: 10}, () => ({
214
+ value: Math.random() * 100
215
+ }));
216
+ ```
217
+
218
+ ## Tài liệu tham khảo
219
+
220
+ - [OWASP A02:2021 - Cryptographic Failures](https://owasp.org/Top10/A02_2021-Cryptographic_Failures/)
221
+ - [CWE-338: Use of Cryptographically Weak Pseudo-Random Number Generator](https://cwe.mitre.org/data/definitions/338.html)
222
+ - [Node.js Crypto Documentation](https://nodejs.org/api/crypto.html)
223
+ - [NIST Guidelines for Random Number Generation](https://csrc.nist.gov/publications/detail/sp/800-90a/rev-1/final)
224
+ - [Secure Random Number Generation Best Practices](https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html)