@sun-asterisk/sunlint 1.3.36 → 1.3.38

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 (113) hide show
  1. package/cli.js +34 -0
  2. package/config/rules/enhanced-rules-registry.json +387 -98
  3. package/config/rules/rules-registry-generated.json +202 -174
  4. package/config/rules-summary.json +1 -1
  5. package/core/architecture-integration.js +115 -17
  6. package/core/cli-action-handler.js +103 -28
  7. package/core/cli-program.js +7 -2
  8. package/core/github-annotate-service.js +62 -0
  9. package/core/impact-integration.js +31 -16
  10. package/core/init-command.js +261 -0
  11. package/core/output-service.js +64 -10
  12. package/core/performance-optimizer.js +1 -1
  13. package/core/summary-report-service.js +46 -0
  14. package/core/unified-rule-registry.js +4 -3
  15. package/docs/DART_RULE_EXECUTION_FLOW.md +1 -1
  16. package/docs/REGISTRY_GENERATION_DIAGRAM.md +289 -0
  17. package/docs/REGISTRY_GENERATION_FLOW.md +486 -0
  18. package/docs/skills/CREATE_NEW_DART_RULE.md +932 -0
  19. package/engines/eslint-engine.js +6 -0
  20. package/engines/heuristic-engine.js +23 -10
  21. package/engines/impact/core/detectors/database-detector.js +1 -1
  22. package/engines/impact/core/detectors/endpoint-detector.js +1 -1
  23. package/engines/impact/core/report-generator.js +235 -73
  24. package/origin-rules/dart-en.md +4 -4
  25. package/origin-rules/security-en.md +470 -282
  26. package/package.json +1 -1
  27. package/rules/dart/D001_recommended_lint_rules/config.json +134 -0
  28. package/rules/index.js +6 -4
  29. package/rules/security/S001_backend_auth_communications/dart/analyzer.js +44 -0
  30. package/rules/security/S001_backend_auth_communications/index.js +87 -0
  31. package/rules/security/S001_backend_auth_communications/typescript/analyzer.js +164 -0
  32. package/rules/security/S002_os_command_injection/dart/analyzer.js +44 -0
  33. package/rules/security/S002_os_command_injection/index.js +87 -0
  34. package/rules/security/S002_os_command_injection/typescript/analyzer.js +194 -0
  35. package/rules/security/S008_svg_content_validation/dart/analyzer.js +44 -0
  36. package/rules/security/S008_svg_content_validation/index.js +87 -0
  37. package/rules/security/S008_svg_content_validation/typescript/analyzer.js +216 -0
  38. package/rules/security/S018_no_sensitive_browser_storage/dart/analyzer.js +44 -0
  39. package/rules/security/S018_no_sensitive_browser_storage/index.js +86 -0
  40. package/rules/security/S018_no_sensitive_browser_storage/typescript/analyzer.js +193 -0
  41. package/rules/security/S021_referrer_policy/dart/analyzer.js +44 -0
  42. package/rules/security/S021_referrer_policy/index.js +86 -0
  43. package/rules/security/S021_referrer_policy/typescript/analyzer.js +183 -0
  44. package/rules/security/S023_no_json_injection/config.json +133 -44
  45. package/rules/security/S023_no_json_injection/dart/analyzer.js +7 -6
  46. package/rules/security/S023_no_json_injection/typescript/analyzer.js +402 -126
  47. package/rules/security/S023_no_json_injection/typescript/ast-analyzer.js +571 -154
  48. package/rules/security/S026_tls_all_connections/config.json +30 -0
  49. package/rules/security/S026_tls_all_connections/typescript/analyzer.js +339 -0
  50. package/rules/security/S027_mtls_certificate_validation/config.json +30 -0
  51. package/rules/security/S027_mtls_certificate_validation/typescript/analyzer.js +225 -0
  52. package/rules/security/S035_separate_app_hostnames/config.json +28 -0
  53. package/rules/security/S035_separate_app_hostnames/typescript/analyzer.js +186 -0
  54. package/rules/security/S036_lfi_rfi_protection/config.json +2 -2
  55. package/rules/security/S039_tls_certificate_validation/config.json +29 -0
  56. package/rules/security/S039_tls_certificate_validation/typescript/analyzer.js +229 -0
  57. package/rules/security/S046_jwt_algorithm_allowlist/config.json +28 -0
  58. package/rules/security/S046_jwt_algorithm_allowlist/dart/analyzer.js +44 -0
  59. package/rules/security/S046_jwt_algorithm_allowlist/index.js +87 -0
  60. package/rules/security/S046_jwt_algorithm_allowlist/typescript/analyzer.js +235 -0
  61. package/rules/security/S047_oauth_pkce_protection/config.json +31 -0
  62. package/rules/security/S047_oauth_pkce_protection/dart/analyzer.js +44 -0
  63. package/rules/security/S047_oauth_pkce_protection/index.js +86 -0
  64. package/rules/security/S047_oauth_pkce_protection/typescript/analyzer.js +78 -0
  65. package/rules/security/S048_oauth_redirect_uri_validation/config.json +30 -0
  66. package/rules/security/S048_oauth_redirect_uri_validation/typescript/analyzer.js +278 -0
  67. package/rules/security/S049_short_validity_tokens/typescript/config.json +10 -3
  68. package/rules/security/S050_reference_tokens_entropy/config.json +28 -0
  69. package/rules/security/S050_reference_tokens_entropy/dart/analyzer.js +45 -0
  70. package/rules/security/S050_reference_tokens_entropy/index.js +86 -0
  71. package/rules/security/S050_reference_tokens_entropy/typescript/analyzer.js +74 -0
  72. package/rules/security/S053_generic_error_messages/config.json +28 -0
  73. package/rules/security/S053_generic_error_messages/dart/analyzer.js +45 -0
  74. package/rules/security/S053_generic_error_messages/index.js +86 -0
  75. package/rules/security/S053_generic_error_messages/typescript/analyzer.js +80 -0
  76. package/rules/security/S055_content_type_validation/typescript/symbol-based-analyzer.js +64 -2
  77. package/rules/security/S059_disable_debug_mode/config.json +28 -0
  78. package/rules/security/S059_disable_debug_mode/dart/analyzer.js +45 -0
  79. package/rules/security/S059_disable_debug_mode/index.js +86 -0
  80. package/rules/security/S059_disable_debug_mode/typescript/analyzer.js +85 -0
  81. package/rules/security/S060_password_minimum_length/config.json +28 -0
  82. package/rules/security/S060_password_minimum_length/dart/analyzer.js +45 -0
  83. package/rules/security/S060_password_minimum_length/index.js +86 -0
  84. package/rules/security/S060_password_minimum_length/typescript/analyzer.js +78 -0
  85. package/rules/security/S026_json_schema_validation/config.json +0 -27
  86. package/rules/security/S026_json_schema_validation/typescript/analyzer.js +0 -251
  87. package/rules/security/S027_no_hardcoded_secrets/config.json +0 -29
  88. package/rules/security/S027_no_hardcoded_secrets/typescript/analyzer.js +0 -309
  89. package/rules/security/S027_no_hardcoded_secrets/typescript/categories.json +0 -153
  90. package/rules/security/S035_path_session_cookies/config.json +0 -99
  91. package/rules/security/S035_path_session_cookies/typescript/analyzer.js +0 -316
  92. package/rules/security/S035_path_session_cookies/typescript/regex-based-analyzer.js +0 -724
  93. package/rules/security/S035_path_session_cookies/typescript/symbol-based-analyzer.js +0 -373
  94. package/rules/security/S039_no_session_tokens_in_url/config.json +0 -92
  95. package/rules/security/S039_no_session_tokens_in_url/typescript/analyzer.js +0 -262
  96. package/rules/security/S039_no_session_tokens_in_url/typescript/regex-based-analyzer.js +0 -337
  97. package/rules/security/S039_no_session_tokens_in_url/typescript/symbol-based-analyzer.js +0 -443
  98. package/rules/security/S048_no_current_password_in_reset/config.json +0 -48
  99. package/rules/security/S048_no_current_password_in_reset/typescript/analyzer.js +0 -366
  100. /package/rules/security/{S026_json_schema_validation → S026_tls_all_connections}/dart/analyzer.js +0 -0
  101. /package/rules/security/{S026_json_schema_validation → S026_tls_all_connections}/index.js +0 -0
  102. /package/rules/security/{S027_no_hardcoded_secrets → S027_mtls_certificate_validation}/dart/analyzer.js +0 -0
  103. /package/rules/security/{S027_no_hardcoded_secrets → S027_mtls_certificate_validation}/index.js +0 -0
  104. /package/rules/security/{S027_no_hardcoded_secrets → S027_mtls_certificate_validation}/typescript/categorized-analyzer.js +0 -0
  105. /package/rules/security/{S035_path_session_cookies → S035_separate_app_hostnames}/dart/analyzer.js +0 -0
  106. /package/rules/security/{S035_path_session_cookies → S035_separate_app_hostnames}/index.js +0 -0
  107. /package/rules/security/{S035_path_session_cookies → S035_separate_app_hostnames}/typescript/README.md +0 -0
  108. /package/rules/security/{S039_no_session_tokens_in_url → S039_tls_certificate_validation}/dart/analyzer.js +0 -0
  109. /package/rules/security/{S039_no_session_tokens_in_url → S039_tls_certificate_validation}/index.js +0 -0
  110. /package/rules/security/{S039_no_session_tokens_in_url → S039_tls_certificate_validation}/typescript/README.md +0 -0
  111. /package/rules/security/{S048_no_current_password_in_reset → S048_oauth_redirect_uri_validation}/dart/analyzer.js +0 -0
  112. /package/rules/security/{S048_no_current_password_in_reset → S048_oauth_redirect_uri_validation}/index.js +0 -0
  113. /package/rules/security/{S048_no_current_password_in_reset → S048_oauth_redirect_uri_validation}/typescript/README.md +0 -0
@@ -1,366 +0,0 @@
1
- /**
2
- * Heuristic analyzer for S048 - No Current Password in Reset Process
3
- * Purpose: Detect requiring current password during password reset process
4
- * Based on OWASP A04:2021 - Insecure Design
5
- */
6
-
7
- class S048Analyzer {
8
- constructor() {
9
- this.ruleId = 'S048';
10
- this.ruleName = 'No Current Password in Reset Process';
11
- this.description = 'Do not require current password during password reset process';
12
-
13
- // Keywords that indicate password reset functionality
14
- this.resetKeywords = [
15
- 'reset', 'forgot', 'recover', 'change', 'update', 'modify',
16
- 'resetpassword', 'forgotpassword', 'changepassword', 'updatepassword'
17
- ];
18
-
19
- // Keywords that indicate current password requirement
20
- this.currentPasswordKeywords = [
21
- 'currentpassword', 'current_password', 'oldpassword', 'old_password',
22
- 'existingpassword', 'existing_password', 'presentpassword', 'present_password',
23
- 'previouspassword', 'previous_password', 'originalpassword', 'original_password'
24
- ];
25
-
26
- // API endpoint patterns for password reset
27
- this.resetEndpointPatterns = [
28
- /\/reset[-_]?password/i,
29
- /\/forgot[-_]?password/i,
30
- /\/change[-_]?password/i,
31
- /\/update[-_]?password/i,
32
- /\/password[-_]?reset/i,
33
- /\/password[-_]?change/i,
34
- /\/password[-_]?update/i,
35
- /\/user\/password/i,
36
- /\/auth\/reset/i,
37
- /\/auth\/forgot/i
38
- ];
39
-
40
- // Function/method patterns related to password reset
41
- this.resetFunctionPatterns = [
42
- /resetpassword/i,
43
- /forgotpassword/i,
44
- /changepassword/i,
45
- /updatepassword/i,
46
- /passwordreset/i,
47
- /passwordchange/i,
48
- /passwordupdate/i,
49
- /handlepasswordreset/i,
50
- /handleforgotpassword/i,
51
- /processpasswordreset/i
52
- ];
53
-
54
- // Patterns for requiring current password in reset context
55
- this.violationPatterns = [
56
- // Validation/requirement patterns
57
- /(?:required?|validate|check|verify).*(?:current|old|existing|present|previous|original).*password/i,
58
- /(?:current|old|existing|present|previous|original).*password.*(?:required?|validate|check|verify)/i,
59
-
60
- // Form field patterns
61
- /(?:input|field|param|body|request).*(?:current|old|existing|present|previous|original).*password/i,
62
- /(?:current|old|existing|present|previous|original).*password.*(?:input|field|param|body|request)/i,
63
-
64
- // Comparison patterns
65
- /(?:compare|match|equal|verify).*(?:current|old|existing|present|previous|original).*password/i,
66
- /(?:current|old|existing|present|previous|original).*password.*(?:compare|match|equal|verify)/i,
67
-
68
- // Database lookup patterns
69
- /(?:select|find|get|fetch|query).*(?:current|old|existing|present|previous|original).*password/i,
70
- /(?:current|old|existing|present|previous|original).*password.*(?:select|find|get|fetch|query)/i,
71
-
72
- // Error message patterns
73
- /(?:current|old|existing|present|previous|original).*password.*(?:incorrect|wrong|invalid|mismatch)/i,
74
- /(?:incorrect|wrong|invalid|mismatch).*(?:current|old|existing|present|previous|original).*password/i,
75
-
76
- // Schema/model field patterns
77
- /currentPassword|current_password|oldPassword|old_password|existingPassword|existing_password/,
78
-
79
- // Template/HTML patterns
80
- /"[^"]*(?:current|old|existing|present|previous|original)[^"]*password[^"]*"/i,
81
- /'[^']*(?:current|old|existing|present|previous|original)[^']*password[^']*'/i,
82
- /`[^`]*(?:current|old|existing|present|previous|original)[^`]*password[^`]*`/i
83
- ];
84
-
85
- // Safe patterns that should be excluded
86
- this.safePatterns = [
87
- // Comments and documentation
88
- /\/\/|\/\*|\*\/|@param|@return|@example|@deprecated/,
89
-
90
- // Import/export statements
91
- /import|export|require|module\.exports/i,
92
-
93
- // Type definitions
94
- /interface|type|enum|class.*\{/i,
95
-
96
- // Configuration files
97
- /config|setting|option|constant|env/i,
98
-
99
- // Test files patterns
100
- /test|spec|mock|fixture|stub/i,
101
-
102
- // Logging patterns (acceptable for debugging)
103
- /log|debug|trace|console|logger/i,
104
-
105
- // Historical/audit patterns (not current validation)
106
- /history|audit|backup|archive|previous.*login/i,
107
-
108
- // Password change (not reset) - legitimate to require current password
109
- /changepassword.*current/i,
110
- /updatepassword.*current/i,
111
-
112
- // Safe messages about security
113
- /for security|security purposes|secure|protection|best practice/i,
114
-
115
- // Documentation patterns
116
- /should not|avoid|don't|never|security risk|vulnerability/i
117
- ];
118
-
119
- // Context keywords that indicate password reset (not change)
120
- this.resetContextKeywords = [
121
- 'reset', 'forgot', 'forgotten', 'recover', 'recovery', 'token', 'link', 'email',
122
- 'verification', 'verify', 'code', 'otp', 'temporary'
123
- ];
124
-
125
- // Keywords that indicate password change (legitimate to require current password)
126
- this.changeContextKeywords = [
127
- 'profile', 'settings', 'account', 'preferences', 'dashboard', 'authenticated',
128
- 'logged', 'session'
129
- ];
130
- }
131
-
132
- async analyze(files, language, options = {}) {
133
- const violations = [];
134
-
135
- for (const filePath of files) {
136
- // Skip test files, build directories, and node_modules
137
- if (this.shouldSkipFile(filePath)) {
138
- continue;
139
- }
140
-
141
- try {
142
- const content = require('fs').readFileSync(filePath, 'utf8');
143
- const fileViolations = this.analyzeFile(content, filePath, options);
144
- violations.push(...fileViolations);
145
- } catch (error) {
146
- if (options.verbose) {
147
- console.warn(`⚠️ Failed to analyze ${filePath}: ${error.message}`);
148
- }
149
- }
150
- }
151
-
152
- return violations;
153
- }
154
-
155
- shouldSkipFile(filePath) {
156
- const skipPatterns = [
157
- 'test/', 'tests/', '__tests__/', '.test.', '.spec.',
158
- 'node_modules/', 'build/', 'dist/', '.next/', 'coverage/',
159
- 'vendor/', 'mocks/', '.mock.'
160
- ];
161
-
162
- return skipPatterns.some(pattern => filePath.includes(pattern));
163
- }
164
-
165
- analyzeFile(content, filePath, options = {}) {
166
- const violations = [];
167
- const lines = content.split('\n');
168
-
169
- lines.forEach((line, index) => {
170
- const lineNumber = index + 1;
171
- const trimmedLine = line.trim();
172
-
173
- // Skip comments, imports, and empty lines
174
- if (this.shouldSkipLine(trimmedLine)) {
175
- return;
176
- }
177
-
178
- // Check for password reset context
179
- if (this.isPasswordResetContext(content, line, lineNumber)) {
180
- // Check for current password requirement violation
181
- const violation = this.checkForCurrentPasswordRequirement(line, lineNumber, filePath, content);
182
- if (violation) {
183
- violations.push(violation);
184
- }
185
- }
186
- });
187
-
188
- return violations;
189
- }
190
-
191
- shouldSkipLine(line) {
192
- // Skip comments, imports, and other non-code lines
193
- return (
194
- line.length === 0 ||
195
- line.startsWith('//') ||
196
- line.startsWith('/*') ||
197
- line.startsWith('*') ||
198
- line.startsWith('import ') ||
199
- line.startsWith('export ') ||
200
- line.startsWith('require(') ||
201
- line.includes('module.exports')
202
- );
203
- }
204
-
205
- isPasswordResetContext(content, line, lineNumber) {
206
- const lowerContent = content.toLowerCase();
207
- const lowerLine = line.toLowerCase();
208
-
209
- // Check if this is in a password reset context
210
- const hasResetContext = (
211
- // Check current line for reset keywords
212
- this.resetKeywords.some(keyword => lowerLine.includes(keyword)) ||
213
-
214
- // Check for reset endpoint patterns
215
- this.resetEndpointPatterns.some(pattern => pattern.test(line)) ||
216
-
217
- // Check for reset function patterns
218
- this.resetFunctionPatterns.some(pattern => pattern.test(line)) ||
219
-
220
- // Check surrounding context (within 10 lines)
221
- this.hasResetContextNearby(content, lineNumber)
222
- );
223
-
224
- // Exclude if it's clearly a password change context (not reset)
225
- const hasChangeContext = this.changeContextKeywords.some(keyword =>
226
- lowerContent.includes(keyword) || lowerLine.includes(keyword)
227
- );
228
-
229
- return hasResetContext && !hasChangeContext;
230
- }
231
-
232
- hasResetContextNearby(content, lineNumber) {
233
- const lines = content.split('\n');
234
- const start = Math.max(0, lineNumber - 10);
235
- const end = Math.min(lines.length, lineNumber + 10);
236
-
237
- for (let i = start; i < end; i++) {
238
- const nearbyLine = lines[i].toLowerCase();
239
-
240
- // Check for reset context keywords
241
- if (this.resetContextKeywords.some(keyword => nearbyLine.includes(keyword))) {
242
- return true;
243
- }
244
-
245
- // Check for reset endpoints
246
- if (this.resetEndpointPatterns.some(pattern => pattern.test(lines[i]))) {
247
- return true;
248
- }
249
-
250
- // Check for reset function names
251
- if (this.resetFunctionPatterns.some(pattern => pattern.test(lines[i]))) {
252
- return true;
253
- }
254
- }
255
-
256
- return false;
257
- }
258
-
259
- checkForCurrentPasswordRequirement(line, lineNumber, filePath, content) {
260
- // First check if line contains safe patterns (early exit)
261
- if (this.containsSafePattern(line)) {
262
- return null;
263
- }
264
-
265
- // Check for direct violation patterns
266
- for (const pattern of this.violationPatterns) {
267
- if (pattern.test(line)) {
268
- // Additional context validation to reduce false positives
269
- if (this.isValidViolationContext(line, content, lineNumber)) {
270
- return {
271
- ruleId: this.ruleId,
272
- severity: 'error',
273
- message: 'Password reset process should not require current password. Use secure token-based reset instead.',
274
- line: lineNumber,
275
- column: this.findPatternColumn(line, pattern),
276
- filePath: filePath,
277
- type: 'current_password_in_reset',
278
- details: 'Requiring current password during reset defeats the purpose of password reset and creates security issues. Use email/SMS verification with secure tokens instead.'
279
- };
280
- }
281
- }
282
- }
283
-
284
- // Check for variable/field names that suggest current password requirement
285
- const currentPasswordField = this.checkCurrentPasswordField(line, lineNumber, filePath);
286
- if (currentPasswordField) {
287
- return currentPasswordField;
288
- }
289
-
290
- return null;
291
- }
292
-
293
- containsSafePattern(line) {
294
- return this.safePatterns.some(pattern => pattern.test(line));
295
- }
296
-
297
- isValidViolationContext(line, content, lineNumber) {
298
- const lowerLine = line.toLowerCase();
299
-
300
- // Check if this is actually about password reset (not change)
301
- const hasResetIndicators = this.resetContextKeywords.some(keyword =>
302
- content.toLowerCase().includes(keyword)
303
- );
304
-
305
- // Check if it's in a validation/requirement context
306
- const hasRequirementContext = [
307
- 'required', 'validate', 'check', 'verify', 'input', 'field', 'param',
308
- 'body', 'request', 'schema', 'model', 'form'
309
- ].some(keyword => lowerLine.includes(keyword));
310
-
311
- // Check if it's actually requiring/validating current password
312
- const hasCurrentPasswordRequirement = this.currentPasswordKeywords.some(keyword =>
313
- lowerLine.includes(keyword)
314
- );
315
-
316
- return hasResetIndicators && hasRequirementContext && hasCurrentPasswordRequirement;
317
- }
318
-
319
- checkCurrentPasswordField(line, lineNumber, filePath) {
320
- // Look for variable declarations, object properties, or field definitions
321
- // that suggest current password fields in reset context
322
-
323
- const fieldPatterns = [
324
- // Variable declarations
325
- /(?:const|let|var)\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*=.*(?:current|old|existing).*password/i,
326
-
327
- // Object properties
328
- /['"']?(currentPassword|current_password|oldPassword|old_password|existingPassword|existing_password)['"']?\s*:/,
329
-
330
- // Form field names
331
- /name\s*=\s*['"](current|old|existing)[-_]?password['"]/i,
332
-
333
- // Schema/model fields
334
- /(?:currentPassword|current_password|oldPassword|old_password|existingPassword|existing_password)\s*:\s*(?:String|type|required)/i,
335
-
336
- // Validation rules
337
- /(?:currentPassword|current_password|oldPassword|old_password|existingPassword|existing_password).*(?:required|validate)/i
338
- ];
339
-
340
- for (const pattern of fieldPatterns) {
341
- const match = line.match(pattern);
342
- if (match) {
343
- return {
344
- ruleId: this.ruleId,
345
- severity: 'warning',
346
- message: `Field '${match[1] || match[0]}' suggests requiring current password in reset process. This should be avoided.`,
347
- line: lineNumber,
348
- column: line.indexOf(match[0]) + 1,
349
- filePath: filePath,
350
- type: 'current_password_field',
351
- fieldName: match[1] || match[0],
352
- details: 'Password reset should use token-based verification, not current password validation.'
353
- };
354
- }
355
- }
356
-
357
- return null;
358
- }
359
-
360
- findPatternColumn(line, pattern) {
361
- const match = pattern.exec(line);
362
- return match ? match.index + 1 : 1;
363
- }
364
- }
365
-
366
- module.exports = S048Analyzer;