@sun-asterisk/sunlint 1.3.0 → 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.
- package/CHANGELOG.md +115 -1
- package/CONTRIBUTING.md +249 -605
- package/README.md +3 -4
- package/config/ci-cd.json +54 -0
- package/config/development.json +56 -0
- package/config/large-project.json +143 -0
- package/config/presets/all.json +0 -1
- package/config/release.json +70 -0
- package/config/rule-analysis-strategies.js +38 -3
- package/config/rules/enhanced-rules-registry.json +474 -1179
- package/config/rules/rules-registry-generated.json +3 -3
- package/core/cli-action-handler.js +24 -30
- package/core/cli-program.js +11 -3
- package/core/config-merger.js +29 -2
- package/core/enhanced-rules-registry.js +3 -2
- package/core/semantic-engine.js +129 -19
- package/core/semantic-rule-base.js +4 -2
- package/core/unified-rule-registry.js +1 -1
- package/docs/COMMAND-EXAMPLES.md +134 -0
- package/docs/LARGE-PROJECT-GUIDE.md +324 -0
- package/engines/heuristic-engine.js +135 -16
- package/integrations/eslint/plugin/index.js +0 -2
- package/integrations/eslint/plugin/rules/common/c003-no-vague-abbreviations.js +59 -1
- package/integrations/eslint/plugin/rules/common/c006-function-name-verb-noun.js +26 -1
- package/integrations/eslint/plugin/rules/common/c030-use-custom-error-classes.js +54 -19
- package/origin-rules/common-en.md +19 -15
- package/package.json +1 -1
- package/rules/common/C002_no_duplicate_code/analyzer.js +334 -36
- package/rules/common/C003_no_vague_abbreviations/analyzer.js +220 -35
- package/rules/common/C006_function_naming/analyzer.js +29 -3
- package/rules/common/C010_limit_block_nesting/analyzer.js +181 -337
- package/rules/common/C010_limit_block_nesting/config.json +64 -0
- package/rules/common/C010_limit_block_nesting/regex-based-analyzer.js +379 -0
- package/rules/common/C010_limit_block_nesting/symbol-based-analyzer.js +231 -0
- package/rules/common/C013_no_dead_code/analyzer.js +75 -177
- package/rules/common/C013_no_dead_code/config.json +61 -0
- package/rules/common/C013_no_dead_code/regex-based-analyzer.js +345 -0
- package/rules/common/C013_no_dead_code/symbol-based-analyzer.js +640 -0
- package/rules/common/C014_dependency_injection/analyzer.js +48 -313
- package/rules/common/C014_dependency_injection/config.json +26 -0
- package/rules/common/C014_dependency_injection/symbol-based-analyzer.js +751 -0
- package/rules/common/C017_constructor_logic/analyzer.js +254 -17
- package/rules/common/C017_constructor_logic/semantic-analyzer.js +340 -0
- package/rules/common/C018_no_throw_generic_error/analyzer.js +232 -0
- package/rules/common/C018_no_throw_generic_error/config.json +50 -0
- package/rules/common/C018_no_throw_generic_error/regex-based-analyzer.js +387 -0
- package/rules/common/C018_no_throw_generic_error/symbol-based-analyzer.js +314 -0
- package/rules/common/C019_log_level_usage/analyzer.js +110 -317
- package/rules/common/C019_log_level_usage/pattern-analyzer.js +88 -0
- package/rules/common/C019_log_level_usage/system-log-analyzer.js +1267 -0
- package/rules/common/C023_no_duplicate_variable/analyzer.js +180 -0
- package/rules/common/C023_no_duplicate_variable/config.json +50 -0
- package/rules/common/C023_no_duplicate_variable/symbol-based-analyzer.js +158 -0
- package/rules/common/C024_no_scatter_hardcoded_constants/analyzer.js +180 -0
- package/rules/common/C024_no_scatter_hardcoded_constants/config.json +50 -0
- package/rules/common/C024_no_scatter_hardcoded_constants/symbol-based-analyzer.js +181 -0
- package/rules/common/C030_use_custom_error_classes/analyzer.js +200 -0
- package/rules/common/C033_separate_service_repository/README.md +78 -0
- package/rules/common/C033_separate_service_repository/analyzer.js +160 -0
- package/rules/common/C033_separate_service_repository/config.json +50 -0
- package/rules/common/C033_separate_service_repository/regex-based-analyzer.js +585 -0
- package/rules/common/C033_separate_service_repository/symbol-based-analyzer.js +368 -0
- package/rules/common/C035_error_logging_context/STRATEGY.md +99 -0
- package/rules/common/C035_error_logging_context/analyzer.js +232 -0
- package/rules/common/C035_error_logging_context/config.json +54 -0
- package/rules/common/C035_error_logging_context/regex-based-analyzer.js +299 -0
- package/rules/common/C035_error_logging_context/symbol-based-analyzer.js +454 -0
- package/rules/common/C040_centralized_validation/analyzer.js +165 -0
- package/rules/common/C040_centralized_validation/config.json +46 -0
- package/rules/common/C040_centralized_validation/regex-based-analyzer.js +243 -0
- package/rules/common/C040_centralized_validation/symbol-based-analyzer.js +416 -0
- package/rules/common/{C076_single_test_behavior → C072_single_test_behavior}/analyzer.js +6 -6
- package/rules/common/C076_explicit_function_types/README.md +30 -0
- package/rules/common/C076_explicit_function_types/analyzer.js +172 -0
- package/rules/common/C076_explicit_function_types/config.json +15 -0
- package/rules/common/C076_explicit_function_types/semantic-analyzer.js +341 -0
- package/rules/index.js +6 -1
- package/rules/parser/rule-parser.js +13 -2
- package/rules/security/S005_no_origin_auth/README.md +226 -0
- package/rules/security/S005_no_origin_auth/analyzer.js +184 -0
- package/rules/security/S005_no_origin_auth/ast-analyzer.js +406 -0
- package/rules/security/S005_no_origin_auth/config.json +85 -0
- package/rules/security/S006_no_plaintext_recovery_codes/README.md +139 -0
- package/rules/security/S006_no_plaintext_recovery_codes/analyzer.js +306 -0
- package/rules/security/S006_no_plaintext_recovery_codes/config.json +48 -0
- package/rules/security/S007_no_plaintext_otp/README.md +198 -0
- package/rules/security/S007_no_plaintext_otp/analyzer.js +406 -0
- package/rules/security/S007_no_plaintext_otp/config.json +79 -0
- package/rules/security/S007_no_plaintext_otp/semantic-analyzer.js +609 -0
- package/rules/security/S007_no_plaintext_otp/semantic-config.json +195 -0
- package/rules/security/S007_no_plaintext_otp/semantic-wrapper.js +280 -0
- package/rules/security/S009_no_insecure_encryption/README.md +158 -0
- package/rules/security/S009_no_insecure_encryption/analyzer.js +319 -0
- package/rules/security/S009_no_insecure_encryption/config.json +55 -0
- package/rules/security/S010_no_insecure_encryption/README.md +224 -0
- package/rules/security/S010_no_insecure_encryption/analyzer.js +493 -0
- package/rules/security/S010_no_insecure_encryption/config.json +48 -0
- package/rules/security/S016_no_sensitive_querystring/STRATEGY.md +149 -0
- package/rules/security/S016_no_sensitive_querystring/analyzer.js +276 -0
- package/rules/security/S016_no_sensitive_querystring/config.json +127 -0
- package/rules/security/S016_no_sensitive_querystring/regex-based-analyzer.js +258 -0
- package/rules/security/S016_no_sensitive_querystring/symbol-based-analyzer.js +495 -0
- package/rules/security/S027_no_hardcoded_secrets/analyzer.js +180 -366
- package/rules/security/S027_no_hardcoded_secrets/categories.json +153 -0
- package/rules/security/S027_no_hardcoded_secrets/categorized-analyzer.js +250 -0
- package/rules/security/S048_no_current_password_in_reset/README.md +222 -0
- package/rules/security/S048_no_current_password_in_reset/analyzer.js +366 -0
- package/rules/security/S048_no_current_password_in_reset/config.json +48 -0
- package/rules/security/S055_content_type_validation/README.md +176 -0
- package/rules/security/S055_content_type_validation/analyzer.js +312 -0
- package/rules/security/S055_content_type_validation/config.json +48 -0
- package/rules/utils/rule-helpers.js +140 -1
- package/scripts/consolidate-config.js +116 -0
- package/scripts/prepare-release.sh +1 -1
- package/config/rules/rules-registry.json +0 -765
- package/docs/ESLINT-INTEGRATION-STRATEGY.md +0 -392
- package/docs/FUTURE_PACKAGES.md +0 -83
- package/docs/HEURISTIC_VS_AI.md +0 -113
- package/docs/PRODUCTION_DEPLOYMENT_ANALYSIS.md +0 -112
- package/docs/PRODUCTION_SIZE_IMPACT.md +0 -183
- package/docs/RELEASE_GUIDE.md +0 -230
- package/docs/STANDARDIZED-CATEGORY-FILTERING.md +0 -156
- package/integrations/eslint/plugin/rules/common/c076-single-behavior-per-test.js +0 -254
- 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)
|