@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.
Files changed (69) hide show
  1. package/config/rules/enhanced-rules-registry.json +101 -17
  2. package/config/rules/rules-registry-generated.json +22 -22
  3. package/origin-rules/security-en.md +351 -338
  4. package/package.json +1 -1
  5. package/rules/common/C003_no_vague_abbreviations/analyzer.js +73 -21
  6. package/rules/common/C017_constructor_logic/symbol-based-analyzer.js +206 -2
  7. package/rules/common/C024_no_scatter_hardcoded_constants/symbol-based-analyzer.js +553 -58
  8. package/rules/common/C029_catch_block_logging/analyzer.js +47 -12
  9. package/rules/common/C033_separate_service_repository/symbol-based-analyzer.js +35 -15
  10. package/rules/common/C041_no_sensitive_hardcode/symbol-based-analyzer.js +9 -5
  11. package/rules/security/S003_open_redirect_protection/README.md +371 -0
  12. package/rules/security/S003_open_redirect_protection/analyzer.js +135 -0
  13. package/rules/security/S003_open_redirect_protection/config.json +58 -0
  14. package/rules/security/S003_open_redirect_protection/symbol-based-analyzer.js +884 -0
  15. package/rules/security/S004_sensitive_data_logging/analyzer.js +135 -0
  16. package/rules/security/S004_sensitive_data_logging/config.json +62 -0
  17. package/rules/security/S004_sensitive_data_logging/symbol-based-analyzer.js +592 -0
  18. package/rules/security/S005_no_origin_auth/analyzer.js +97 -148
  19. package/rules/security/S005_no_origin_auth/config.json +28 -67
  20. package/rules/security/S005_no_origin_auth/symbol-based-analyzer.js +708 -0
  21. package/rules/security/S006_no_plaintext_recovery_codes/symbol-based-analyzer.js +170 -31
  22. package/rules/security/S010_no_insecure_encryption/analyzer.js +8 -2
  23. package/rules/security/S012_hardcoded_secrets/analyzer.js +149 -0
  24. package/rules/security/S012_hardcoded_secrets/config.json +75 -0
  25. package/rules/security/S012_hardcoded_secrets/symbol-based-analyzer.js +1204 -0
  26. package/rules/security/S013_tls_enforcement/symbol-based-analyzer.js +87 -0
  27. package/rules/security/S017_use_parameterized_queries/analyzer.js +11 -78
  28. package/rules/security/S017_use_parameterized_queries/symbol-based-analyzer.js +1146 -1
  29. package/rules/security/S019_smtp_injection_protection/analyzer.js +120 -0
  30. package/rules/security/S019_smtp_injection_protection/config.json +35 -0
  31. package/rules/security/S019_smtp_injection_protection/symbol-based-analyzer.js +687 -0
  32. package/rules/security/S020_no_eval_dynamic_code/analyzer.js +55 -130
  33. package/rules/security/S020_no_eval_dynamic_code/symbol-based-analyzer.js +4 -19
  34. package/rules/security/S022_escape_output_context/README.md +254 -0
  35. package/rules/security/S022_escape_output_context/analyzer.js +510 -0
  36. package/rules/security/S022_escape_output_context/config.json +229 -0
  37. package/rules/security/S023_no_json_injection/analyzer.js +15 -0
  38. package/rules/security/S023_no_json_injection/ast-analyzer.js +18 -3
  39. package/rules/security/S023_no_json_injection/config.json +133 -0
  40. package/rules/security/S024_xpath_xxe_protection/regex-based-analyzer.js +41 -0
  41. package/rules/security/S027_no_hardcoded_secrets/analyzer.js +67 -8
  42. package/rules/security/S027_no_hardcoded_secrets/categorized-analyzer.js +29 -6
  43. package/rules/security/S029_csrf_protection/config.json +127 -0
  44. package/rules/security/S030_directory_browsing_protection/regex-based-analyzer.js +160 -28
  45. package/rules/security/S030_directory_browsing_protection/symbol-based-analyzer.js +81 -19
  46. package/rules/security/S031_secure_session_cookies/analyzer.js +20 -2
  47. package/rules/security/S031_secure_session_cookies/regex-based-analyzer.js +100 -0
  48. package/rules/security/S031_secure_session_cookies/symbol-based-analyzer.js +8 -1
  49. package/rules/security/S032_httponly_session_cookies/analyzer.js +2 -2
  50. package/rules/security/S032_httponly_session_cookies/regex-based-analyzer.js +115 -0
  51. package/rules/security/S032_httponly_session_cookies/symbol-based-analyzer.js +39 -10
  52. package/rules/security/S036_lfi_rfi_protection/analyzer.js +224 -0
  53. package/rules/security/S036_lfi_rfi_protection/config.json +20 -0
  54. package/rules/security/S040_session_fixation_protection/analyzer.js +153 -0
  55. package/rules/security/S040_session_fixation_protection/config.json +20 -0
  56. package/rules/security/S042_require_re_authentication_for_long_lived/README.md +83 -0
  57. package/rules/security/S042_require_re_authentication_for_long_lived/analyzer.js +153 -0
  58. package/rules/security/S042_require_re_authentication_for_long_lived/config.json +41 -0
  59. package/rules/security/S042_require_re_authentication_for_long_lived/symbol-based-analyzer.js +1139 -0
  60. package/rules/security/S043_password_changes_invalidate_all_sessions/README.md +107 -0
  61. package/rules/security/S043_password_changes_invalidate_all_sessions/analyzer.js +153 -0
  62. package/rules/security/S043_password_changes_invalidate_all_sessions/config.json +41 -0
  63. package/rules/security/S043_password_changes_invalidate_all_sessions/symbol-based-analyzer.js +541 -0
  64. package/docs/COMMAND-EXAMPLES.md +0 -390
  65. package/docs/FILE_LIMITS_COMPLETION_REPORT.md +0 -151
  66. package/docs/FOLDER_STRUCTURE.md +0 -59
  67. package/docs/SIMPLIFIED_USAGE_GUIDE.md +0 -208
  68. package/rules/security/S017_use_parameterized_queries/regex-based-analyzer.js +0 -541
  69. 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;