@sun-asterisk/sunlint 1.3.26 → 1.3.28
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 +101 -17
- package/config/rules/rules-registry-generated.json +22 -22
- package/origin-rules/security-en.md +351 -338
- package/package.json +1 -1
- package/rules/common/C003_no_vague_abbreviations/analyzer.js +73 -21
- package/rules/common/C017_constructor_logic/symbol-based-analyzer.js +206 -2
- package/rules/common/C024_no_scatter_hardcoded_constants/symbol-based-analyzer.js +553 -58
- package/rules/common/C029_catch_block_logging/analyzer.js +47 -12
- package/rules/common/C033_separate_service_repository/symbol-based-analyzer.js +35 -15
- package/rules/common/C041_no_sensitive_hardcode/symbol-based-analyzer.js +9 -5
- package/rules/security/S003_open_redirect_protection/README.md +371 -0
- package/rules/security/S003_open_redirect_protection/analyzer.js +135 -0
- package/rules/security/S003_open_redirect_protection/config.json +58 -0
- package/rules/security/S003_open_redirect_protection/symbol-based-analyzer.js +884 -0
- package/rules/security/S004_sensitive_data_logging/analyzer.js +135 -0
- package/rules/security/S004_sensitive_data_logging/config.json +62 -0
- package/rules/security/S004_sensitive_data_logging/symbol-based-analyzer.js +592 -0
- package/rules/security/S005_no_origin_auth/analyzer.js +97 -148
- package/rules/security/S005_no_origin_auth/config.json +28 -67
- package/rules/security/S005_no_origin_auth/symbol-based-analyzer.js +708 -0
- package/rules/security/S006_no_plaintext_recovery_codes/symbol-based-analyzer.js +170 -31
- package/rules/security/S010_no_insecure_encryption/analyzer.js +8 -2
- package/rules/security/S012_hardcoded_secrets/analyzer.js +149 -0
- package/rules/security/S012_hardcoded_secrets/config.json +75 -0
- package/rules/security/S012_hardcoded_secrets/symbol-based-analyzer.js +1204 -0
- package/rules/security/S013_tls_enforcement/symbol-based-analyzer.js +87 -0
- package/rules/security/S017_use_parameterized_queries/analyzer.js +11 -78
- package/rules/security/S017_use_parameterized_queries/symbol-based-analyzer.js +1146 -1
- package/rules/security/S019_smtp_injection_protection/analyzer.js +120 -0
- package/rules/security/S019_smtp_injection_protection/config.json +35 -0
- package/rules/security/S019_smtp_injection_protection/symbol-based-analyzer.js +687 -0
- package/rules/security/S020_no_eval_dynamic_code/analyzer.js +55 -130
- package/rules/security/S020_no_eval_dynamic_code/symbol-based-analyzer.js +4 -19
- package/rules/security/S022_escape_output_context/README.md +254 -0
- package/rules/security/S022_escape_output_context/analyzer.js +510 -0
- package/rules/security/S022_escape_output_context/config.json +229 -0
- package/rules/security/S023_no_json_injection/analyzer.js +15 -0
- package/rules/security/S023_no_json_injection/ast-analyzer.js +18 -3
- package/rules/security/S023_no_json_injection/config.json +133 -0
- package/rules/security/S024_xpath_xxe_protection/regex-based-analyzer.js +41 -0
- package/rules/security/S027_no_hardcoded_secrets/analyzer.js +67 -8
- package/rules/security/S027_no_hardcoded_secrets/categorized-analyzer.js +29 -6
- package/rules/security/S029_csrf_protection/config.json +127 -0
- package/rules/security/S030_directory_browsing_protection/regex-based-analyzer.js +160 -28
- package/rules/security/S030_directory_browsing_protection/symbol-based-analyzer.js +81 -19
- package/rules/security/S031_secure_session_cookies/analyzer.js +20 -2
- package/rules/security/S031_secure_session_cookies/regex-based-analyzer.js +100 -0
- package/rules/security/S031_secure_session_cookies/symbol-based-analyzer.js +8 -1
- package/rules/security/S032_httponly_session_cookies/analyzer.js +2 -2
- package/rules/security/S032_httponly_session_cookies/regex-based-analyzer.js +115 -0
- package/rules/security/S032_httponly_session_cookies/symbol-based-analyzer.js +39 -10
- package/rules/security/S036_lfi_rfi_protection/analyzer.js +224 -0
- package/rules/security/S036_lfi_rfi_protection/config.json +20 -0
- package/rules/security/S040_session_fixation_protection/analyzer.js +153 -0
- package/rules/security/S040_session_fixation_protection/config.json +20 -0
- package/rules/security/S042_require_re_authentication_for_long_lived/README.md +83 -0
- package/rules/security/S042_require_re_authentication_for_long_lived/analyzer.js +153 -0
- package/rules/security/S042_require_re_authentication_for_long_lived/config.json +41 -0
- package/rules/security/S042_require_re_authentication_for_long_lived/symbol-based-analyzer.js +1139 -0
- package/rules/security/S043_password_changes_invalidate_all_sessions/README.md +107 -0
- package/rules/security/S043_password_changes_invalidate_all_sessions/analyzer.js +153 -0
- package/rules/security/S043_password_changes_invalidate_all_sessions/config.json +41 -0
- package/rules/security/S043_password_changes_invalidate_all_sessions/symbol-based-analyzer.js +541 -0
- package/docs/COMMAND-EXAMPLES.md +0 -390
- package/docs/FILE_LIMITS_COMPLETION_REPORT.md +0 -151
- package/docs/FOLDER_STRUCTURE.md +0 -59
- package/docs/SIMPLIFIED_USAGE_GUIDE.md +0 -208
- package/rules/security/S017_use_parameterized_queries/regex-based-analyzer.js +0 -541
- package/rules/security/S020_no_eval_dynamic_code/regex-based-analyzer.js +0 -307
|
@@ -1,307 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* S020 Regex-Based Analyzer - Avoid using eval() or executing dynamic code
|
|
3
|
-
* Detects dangerous dynamic code execution patterns across different contexts
|
|
4
|
-
*/
|
|
5
|
-
const fs = require("fs");
|
|
6
|
-
|
|
7
|
-
class S020RegexBasedAnalyzer {
|
|
8
|
-
constructor() {
|
|
9
|
-
this.ruleId = "S020";
|
|
10
|
-
|
|
11
|
-
// Dangerous function patterns
|
|
12
|
-
this.dangerousPatterns = {
|
|
13
|
-
// Direct eval calls
|
|
14
|
-
eval: /\beval\s*\(/g,
|
|
15
|
-
// Function constructor
|
|
16
|
-
functionConstructor: /\bnew\s+Function\s*\(/g,
|
|
17
|
-
// Global eval access
|
|
18
|
-
globalEval: /\b(window|global|globalThis|self)\.eval\s*\(/g,
|
|
19
|
-
// setTimeout/setInterval with strings (but not function references)
|
|
20
|
-
timerWithString: /(setTimeout|setInterval)\s*\(\s*['"`]/g,
|
|
21
|
-
// execScript (IE legacy) - including type casting
|
|
22
|
-
execScript: /(\bexecScript\s*\(|\(\w+\s+as\s+any\)\.execScript\s*\()/g,
|
|
23
|
-
// setImmediate with strings (but not function references) - including type casting
|
|
24
|
-
setImmediateString:
|
|
25
|
-
/(\bsetImmediate\s*\(\s*['"`]|\(\s*setImmediate\s+as\s+any\)\s*\(\s*['"`])/g,
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
// Dynamic code indicators in variable names
|
|
29
|
-
this.dynamicCodeVariables =
|
|
30
|
-
/\b(code|script|expression|formula|template|eval)\w*\s*=/gi;
|
|
31
|
-
|
|
32
|
-
// Execution context patterns
|
|
33
|
-
this.executionPatterns = {
|
|
34
|
-
// Function.prototype.call/apply with dynamic strings
|
|
35
|
-
functionCall: /Function\.prototype\.(call|apply)/g,
|
|
36
|
-
// Code generation patterns
|
|
37
|
-
codeGeneration: /(generate|build|create)\w*\s*(code|script|function)/gi,
|
|
38
|
-
// Template execution
|
|
39
|
-
templateExecution: /(execute|run|eval)\w*\s*(template|expression)/gi,
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
async analyze(filePath) {
|
|
44
|
-
// Skip files that are unlikely to contain dynamic code execution
|
|
45
|
-
const skipPatterns = [
|
|
46
|
-
/\.d\.ts$/,
|
|
47
|
-
/\.types\.ts$/,
|
|
48
|
-
/\.interface\.ts$/,
|
|
49
|
-
/\.constants?\.ts$/,
|
|
50
|
-
/\.config\.ts$/,
|
|
51
|
-
/\.spec\.ts$/,
|
|
52
|
-
/\.test\.ts$/,
|
|
53
|
-
/\.min\.js$/,
|
|
54
|
-
/\.bundle\.js$/,
|
|
55
|
-
];
|
|
56
|
-
|
|
57
|
-
const shouldSkip = skipPatterns.some((pattern) => pattern.test(filePath));
|
|
58
|
-
if (shouldSkip) {
|
|
59
|
-
return [];
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const content = fs.readFileSync(filePath, "utf8");
|
|
63
|
-
const lines = content.split(/\r?\n/);
|
|
64
|
-
const violations = [];
|
|
65
|
-
|
|
66
|
-
for (let i = 0; i < lines.length; i++) {
|
|
67
|
-
const line = lines[i];
|
|
68
|
-
const lineNumber = i + 1;
|
|
69
|
-
|
|
70
|
-
// Skip comments and imports
|
|
71
|
-
if (this.shouldSkipLine(line)) {
|
|
72
|
-
continue;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Check for dangerous function patterns
|
|
76
|
-
this.checkDangerousPatterns(line, lineNumber, violations);
|
|
77
|
-
|
|
78
|
-
// Check for dynamic code variable patterns
|
|
79
|
-
this.checkDynamicCodeVariables(line, lineNumber, violations);
|
|
80
|
-
|
|
81
|
-
// Check for execution context patterns
|
|
82
|
-
this.checkExecutionPatterns(line, lineNumber, violations);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return violations;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
shouldSkipLine(line) {
|
|
89
|
-
const trimmed = line.trim();
|
|
90
|
-
|
|
91
|
-
// Skip empty lines
|
|
92
|
-
if (!trimmed) return true;
|
|
93
|
-
|
|
94
|
-
// Skip single-line comments
|
|
95
|
-
if (trimmed.startsWith("//")) return true;
|
|
96
|
-
|
|
97
|
-
// Skip import/export statements
|
|
98
|
-
if (trimmed.startsWith("import ") || trimmed.startsWith("export "))
|
|
99
|
-
return true;
|
|
100
|
-
|
|
101
|
-
// Skip require statements
|
|
102
|
-
if (trimmed.startsWith("const ") && trimmed.includes("require("))
|
|
103
|
-
return true;
|
|
104
|
-
|
|
105
|
-
// Skip JSDoc comments
|
|
106
|
-
if (
|
|
107
|
-
trimmed.startsWith("*") ||
|
|
108
|
-
trimmed.startsWith("/**") ||
|
|
109
|
-
trimmed.startsWith("*/")
|
|
110
|
-
)
|
|
111
|
-
return true;
|
|
112
|
-
|
|
113
|
-
return false;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
checkDangerousPatterns(line, lineNumber, violations) {
|
|
117
|
-
// Debug log for setImmediate lines specifically
|
|
118
|
-
if (line.includes("setImmediate") && process.env.SUNLINT_DEBUG) {
|
|
119
|
-
console.log(
|
|
120
|
-
`🔍 [S020-Regex] Checking setImmediate line ${lineNumber}: ${line.trim()}`
|
|
121
|
-
);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
for (const [patternName, pattern] of Object.entries(
|
|
125
|
-
this.dangerousPatterns
|
|
126
|
-
)) {
|
|
127
|
-
const matches = [...line.matchAll(pattern)];
|
|
128
|
-
|
|
129
|
-
// Debug log for setImmediate pattern specifically
|
|
130
|
-
if (
|
|
131
|
-
patternName === "setImmediateString" &&
|
|
132
|
-
line.includes("setImmediate") &&
|
|
133
|
-
process.env.SUNLINT_DEBUG
|
|
134
|
-
) {
|
|
135
|
-
console.log(
|
|
136
|
-
`🔍 [S020-Regex] Testing setImmediateString pattern on line ${lineNumber}`
|
|
137
|
-
);
|
|
138
|
-
console.log(`🔍 [S020-Regex] Pattern: ${pattern}`);
|
|
139
|
-
console.log(`🔍 [S020-Regex] Matches found: ${matches.length}`);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
for (const match of matches) {
|
|
143
|
-
let message;
|
|
144
|
-
let severity = "error";
|
|
145
|
-
|
|
146
|
-
switch (patternName) {
|
|
147
|
-
case "eval":
|
|
148
|
-
message =
|
|
149
|
-
"Direct eval() call can execute arbitrary code and poses security risks";
|
|
150
|
-
break;
|
|
151
|
-
case "functionConstructor":
|
|
152
|
-
message =
|
|
153
|
-
"Function constructor can create functions from strings and execute dynamic code";
|
|
154
|
-
break;
|
|
155
|
-
case "globalEval":
|
|
156
|
-
message = `Global eval access '${match[0]}' can execute arbitrary code and poses security risks`;
|
|
157
|
-
break;
|
|
158
|
-
case "timerWithString":
|
|
159
|
-
message = `${match[1]}() with string argument can execute dynamic code - use function reference instead`;
|
|
160
|
-
severity = "warning";
|
|
161
|
-
break;
|
|
162
|
-
case "execScript":
|
|
163
|
-
message =
|
|
164
|
-
"execScript() can execute arbitrary code and poses security risks";
|
|
165
|
-
break;
|
|
166
|
-
case "setImmediateString":
|
|
167
|
-
message =
|
|
168
|
-
"setImmediate() with string argument can execute dynamic code - use function reference instead";
|
|
169
|
-
severity = "warning";
|
|
170
|
-
break;
|
|
171
|
-
default:
|
|
172
|
-
message = "Potential dynamic code execution detected";
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
violations.push({
|
|
176
|
-
ruleId: this.ruleId,
|
|
177
|
-
message: message,
|
|
178
|
-
severity: severity,
|
|
179
|
-
line: lineNumber,
|
|
180
|
-
column: match.index + 1,
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
if (process.env.SUNLINT_DEBUG) {
|
|
184
|
-
console.log(
|
|
185
|
-
`🔧 [S020-Regex] Found ${patternName} at line ${lineNumber}: ${match[0]}`
|
|
186
|
-
);
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
checkDynamicCodeVariables(line, lineNumber, violations) {
|
|
193
|
-
const matches = [...line.matchAll(this.dynamicCodeVariables)];
|
|
194
|
-
|
|
195
|
-
for (const match of matches) {
|
|
196
|
-
// Additional context checking to reduce false positives
|
|
197
|
-
const context = line.toLowerCase();
|
|
198
|
-
|
|
199
|
-
// Check if it's actually related to code execution
|
|
200
|
-
if (this.isLikelyDynamicCode(context)) {
|
|
201
|
-
violations.push({
|
|
202
|
-
ruleId: this.ruleId,
|
|
203
|
-
message: `Variable '${match[0].trim()}' suggests dynamic code handling - review for potential code execution`,
|
|
204
|
-
severity: "warning",
|
|
205
|
-
line: lineNumber,
|
|
206
|
-
column: match.index + 1,
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
if (process.env.SUNLINT_DEBUG) {
|
|
210
|
-
console.log(
|
|
211
|
-
`🔧 [S020-Regex] Found dynamic code variable at line ${lineNumber}: ${match[0]}`
|
|
212
|
-
);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
checkExecutionPatterns(line, lineNumber, violations) {
|
|
219
|
-
for (const [patternName, pattern] of Object.entries(
|
|
220
|
-
this.executionPatterns
|
|
221
|
-
)) {
|
|
222
|
-
const matches = [...line.matchAll(pattern)];
|
|
223
|
-
|
|
224
|
-
for (const match of matches) {
|
|
225
|
-
let message;
|
|
226
|
-
|
|
227
|
-
switch (patternName) {
|
|
228
|
-
case "functionCall":
|
|
229
|
-
message = `Function.prototype.${match[1]} may be used for dynamic code execution - review implementation`;
|
|
230
|
-
break;
|
|
231
|
-
case "codeGeneration":
|
|
232
|
-
message = `Code generation pattern '${match[0]}' detected - ensure no dynamic code execution`;
|
|
233
|
-
break;
|
|
234
|
-
case "templateExecution":
|
|
235
|
-
message = `Template execution pattern '${match[0]}' detected - ensure safe template handling`;
|
|
236
|
-
break;
|
|
237
|
-
default:
|
|
238
|
-
message = "Potential code execution pattern detected";
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
violations.push({
|
|
242
|
-
ruleId: this.ruleId,
|
|
243
|
-
message: message,
|
|
244
|
-
severity: "warning",
|
|
245
|
-
line: lineNumber,
|
|
246
|
-
column: match.index + 1,
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
if (process.env.SUNLINT_DEBUG) {
|
|
250
|
-
console.log(
|
|
251
|
-
`🔧 [S020-Regex] Found execution pattern ${patternName} at line ${lineNumber}: ${match[0]}`
|
|
252
|
-
);
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
isLikelyDynamicCode(context) {
|
|
259
|
-
// Keywords that suggest actual code execution
|
|
260
|
-
const codeExecutionKeywords = [
|
|
261
|
-
"execute",
|
|
262
|
-
"run",
|
|
263
|
-
"eval",
|
|
264
|
-
"compile",
|
|
265
|
-
"interpret",
|
|
266
|
-
"function",
|
|
267
|
-
"method",
|
|
268
|
-
"call",
|
|
269
|
-
"invoke",
|
|
270
|
-
"dynamic",
|
|
271
|
-
"runtime",
|
|
272
|
-
"generated",
|
|
273
|
-
];
|
|
274
|
-
|
|
275
|
-
// Keywords that suggest benign usage (reduce false positives)
|
|
276
|
-
const benignKeywords = [
|
|
277
|
-
"html",
|
|
278
|
-
"css",
|
|
279
|
-
"style",
|
|
280
|
-
"markup",
|
|
281
|
-
"tag",
|
|
282
|
-
"error",
|
|
283
|
-
"message",
|
|
284
|
-
"text",
|
|
285
|
-
"string",
|
|
286
|
-
"config",
|
|
287
|
-
"setting",
|
|
288
|
-
"option",
|
|
289
|
-
"parameter",
|
|
290
|
-
];
|
|
291
|
-
|
|
292
|
-
const hasExecutionKeyword = codeExecutionKeywords.some((keyword) =>
|
|
293
|
-
context.includes(keyword)
|
|
294
|
-
);
|
|
295
|
-
|
|
296
|
-
const hasBenignKeyword = benignKeywords.some((keyword) =>
|
|
297
|
-
context.includes(keyword)
|
|
298
|
-
);
|
|
299
|
-
|
|
300
|
-
// Only flag if there's execution context and no benign indicators
|
|
301
|
-
return hasExecutionKeyword && !hasBenignKeyword;
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
cleanup() {}
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
module.exports = S020RegexBasedAnalyzer;
|