@sun-asterisk/sunlint 1.3.35 → 1.3.37

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 (103) hide show
  1. package/cli.js +33 -0
  2. package/config/rules/enhanced-rules-registry.json +354 -98
  3. package/config/rules/rules-registry-generated.json +197 -171
  4. package/core/architecture-integration.js +115 -17
  5. package/core/cli-action-handler.js +110 -26
  6. package/core/cli-program.js +14 -3
  7. package/core/github-annotate-service.js +62 -0
  8. package/core/impact-integration.js +309 -176
  9. package/core/init-command.js +227 -0
  10. package/core/output-service.js +53 -5
  11. package/core/summary-report-service.js +46 -0
  12. package/core/unified-rule-registry.js +2 -1
  13. package/engines/eslint-engine.js +6 -0
  14. package/engines/impact/core/detectors/database-detector.js +1 -1
  15. package/engines/impact/core/detectors/endpoint-detector.js +1 -1
  16. package/engines/impact/core/report-generator.js +235 -73
  17. package/origin-rules/security-en.md +470 -282
  18. package/package.json +1 -1
  19. package/rules/security/S001_backend_auth_communications/dart/analyzer.js +44 -0
  20. package/rules/security/S001_backend_auth_communications/index.js +87 -0
  21. package/rules/security/S001_backend_auth_communications/typescript/analyzer.js +164 -0
  22. package/rules/security/S002_os_command_injection/dart/analyzer.js +44 -0
  23. package/rules/security/S002_os_command_injection/index.js +87 -0
  24. package/rules/security/S002_os_command_injection/typescript/analyzer.js +194 -0
  25. package/rules/security/S008_svg_content_validation/dart/analyzer.js +44 -0
  26. package/rules/security/S008_svg_content_validation/index.js +87 -0
  27. package/rules/security/S008_svg_content_validation/typescript/analyzer.js +216 -0
  28. package/rules/security/S018_no_sensitive_browser_storage/dart/analyzer.js +44 -0
  29. package/rules/security/S018_no_sensitive_browser_storage/index.js +86 -0
  30. package/rules/security/S018_no_sensitive_browser_storage/typescript/analyzer.js +193 -0
  31. package/rules/security/S021_referrer_policy/dart/analyzer.js +44 -0
  32. package/rules/security/S021_referrer_policy/index.js +86 -0
  33. package/rules/security/S021_referrer_policy/typescript/analyzer.js +183 -0
  34. package/rules/security/S023_no_json_injection/config.json +133 -44
  35. package/rules/security/S023_no_json_injection/dart/analyzer.js +7 -6
  36. package/rules/security/S023_no_json_injection/typescript/analyzer.js +402 -126
  37. package/rules/security/S023_no_json_injection/typescript/ast-analyzer.js +571 -154
  38. package/rules/security/S026_tls_all_connections/config.json +30 -0
  39. package/rules/security/S026_tls_all_connections/typescript/analyzer.js +339 -0
  40. package/rules/security/S027_mtls_certificate_validation/config.json +30 -0
  41. package/rules/security/S027_mtls_certificate_validation/typescript/analyzer.js +225 -0
  42. package/rules/security/S035_separate_app_hostnames/config.json +28 -0
  43. package/rules/security/S035_separate_app_hostnames/typescript/analyzer.js +186 -0
  44. package/rules/security/S036_lfi_rfi_protection/config.json +2 -2
  45. package/rules/security/S039_tls_certificate_validation/config.json +29 -0
  46. package/rules/security/S039_tls_certificate_validation/typescript/analyzer.js +229 -0
  47. package/rules/security/S046_jwt_algorithm_allowlist/config.json +28 -0
  48. package/rules/security/S046_jwt_algorithm_allowlist/dart/analyzer.js +44 -0
  49. package/rules/security/S046_jwt_algorithm_allowlist/index.js +87 -0
  50. package/rules/security/S046_jwt_algorithm_allowlist/typescript/analyzer.js +235 -0
  51. package/rules/security/S047_oauth_pkce_protection/config.json +31 -0
  52. package/rules/security/S047_oauth_pkce_protection/dart/analyzer.js +44 -0
  53. package/rules/security/S047_oauth_pkce_protection/index.js +86 -0
  54. package/rules/security/S047_oauth_pkce_protection/typescript/analyzer.js +78 -0
  55. package/rules/security/S048_oauth_redirect_uri_validation/config.json +30 -0
  56. package/rules/security/S048_oauth_redirect_uri_validation/typescript/analyzer.js +278 -0
  57. package/rules/security/S049_short_validity_tokens/typescript/config.json +10 -3
  58. package/rules/security/S050_reference_tokens_entropy/config.json +28 -0
  59. package/rules/security/S050_reference_tokens_entropy/dart/analyzer.js +45 -0
  60. package/rules/security/S050_reference_tokens_entropy/index.js +86 -0
  61. package/rules/security/S050_reference_tokens_entropy/typescript/analyzer.js +74 -0
  62. package/rules/security/S053_generic_error_messages/config.json +28 -0
  63. package/rules/security/S053_generic_error_messages/dart/analyzer.js +45 -0
  64. package/rules/security/S053_generic_error_messages/index.js +86 -0
  65. package/rules/security/S053_generic_error_messages/typescript/analyzer.js +80 -0
  66. package/rules/security/S055_content_type_validation/typescript/symbol-based-analyzer.js +64 -2
  67. package/rules/security/S059_disable_debug_mode/config.json +28 -0
  68. package/rules/security/S059_disable_debug_mode/dart/analyzer.js +45 -0
  69. package/rules/security/S059_disable_debug_mode/index.js +86 -0
  70. package/rules/security/S059_disable_debug_mode/typescript/analyzer.js +85 -0
  71. package/rules/security/S060_password_minimum_length/config.json +28 -0
  72. package/rules/security/S060_password_minimum_length/dart/analyzer.js +45 -0
  73. package/rules/security/S060_password_minimum_length/index.js +86 -0
  74. package/rules/security/S060_password_minimum_length/typescript/analyzer.js +78 -0
  75. package/rules/security/S026_json_schema_validation/config.json +0 -27
  76. package/rules/security/S026_json_schema_validation/typescript/analyzer.js +0 -251
  77. package/rules/security/S027_no_hardcoded_secrets/config.json +0 -29
  78. package/rules/security/S027_no_hardcoded_secrets/typescript/analyzer.js +0 -309
  79. package/rules/security/S027_no_hardcoded_secrets/typescript/categories.json +0 -153
  80. package/rules/security/S035_path_session_cookies/config.json +0 -99
  81. package/rules/security/S035_path_session_cookies/typescript/analyzer.js +0 -316
  82. package/rules/security/S035_path_session_cookies/typescript/regex-based-analyzer.js +0 -724
  83. package/rules/security/S035_path_session_cookies/typescript/symbol-based-analyzer.js +0 -373
  84. package/rules/security/S039_no_session_tokens_in_url/config.json +0 -92
  85. package/rules/security/S039_no_session_tokens_in_url/typescript/analyzer.js +0 -262
  86. package/rules/security/S039_no_session_tokens_in_url/typescript/regex-based-analyzer.js +0 -337
  87. package/rules/security/S039_no_session_tokens_in_url/typescript/symbol-based-analyzer.js +0 -443
  88. package/rules/security/S048_no_current_password_in_reset/config.json +0 -48
  89. package/rules/security/S048_no_current_password_in_reset/typescript/analyzer.js +0 -366
  90. /package/rules/security/{S026_json_schema_validation → S026_tls_all_connections}/dart/analyzer.js +0 -0
  91. /package/rules/security/{S026_json_schema_validation → S026_tls_all_connections}/index.js +0 -0
  92. /package/rules/security/{S027_no_hardcoded_secrets → S027_mtls_certificate_validation}/dart/analyzer.js +0 -0
  93. /package/rules/security/{S027_no_hardcoded_secrets → S027_mtls_certificate_validation}/index.js +0 -0
  94. /package/rules/security/{S027_no_hardcoded_secrets → S027_mtls_certificate_validation}/typescript/categorized-analyzer.js +0 -0
  95. /package/rules/security/{S035_path_session_cookies → S035_separate_app_hostnames}/dart/analyzer.js +0 -0
  96. /package/rules/security/{S035_path_session_cookies → S035_separate_app_hostnames}/index.js +0 -0
  97. /package/rules/security/{S035_path_session_cookies → S035_separate_app_hostnames}/typescript/README.md +0 -0
  98. /package/rules/security/{S039_no_session_tokens_in_url → S039_tls_certificate_validation}/dart/analyzer.js +0 -0
  99. /package/rules/security/{S039_no_session_tokens_in_url → S039_tls_certificate_validation}/index.js +0 -0
  100. /package/rules/security/{S039_no_session_tokens_in_url → S039_tls_certificate_validation}/typescript/README.md +0 -0
  101. /package/rules/security/{S048_no_current_password_in_reset → S048_oauth_redirect_uri_validation}/dart/analyzer.js +0 -0
  102. /package/rules/security/{S048_no_current_password_in_reset → S048_oauth_redirect_uri_validation}/index.js +0 -0
  103. /package/rules/security/{S048_no_current_password_in_reset → S048_oauth_redirect_uri_validation}/typescript/README.md +0 -0
@@ -1,373 +0,0 @@
1
- /**
2
- * S035 Symbol-Based Analyzer - Set Path attribute for Session Cookies
3
- * Uses TypeScript compiler API for semantic analysis
4
- */
5
-
6
- const { SyntaxKind } = require("typescript");
7
-
8
- class S035SymbolBasedAnalyzer {
9
- constructor(semanticEngine = null) {
10
- this.semanticEngine = semanticEngine;
11
- this.ruleId = "S035";
12
- this.ruleName = "Set Path attribute for Session Cookies";
13
- this.category = "security";
14
- this.violations = [];
15
-
16
- // Session cookie indicators
17
- this.sessionIndicators = [
18
- "session",
19
- "sessionid",
20
- "sessid",
21
- "jsessionid",
22
- "phpsessid",
23
- "asp.net_sessionid",
24
- "connect.sid",
25
- "auth",
26
- "token",
27
- "jwt",
28
- "csrf",
29
- "refresh",
30
- "user",
31
- "login",
32
- "authentication",
33
- "session_id",
34
- "sid",
35
- "auth_token",
36
- "userid",
37
- "user_id",
38
- ];
39
-
40
- // Cookie methods that need security checking
41
- this.cookieMethods = [
42
- "setCookie",
43
- "cookie",
44
- "set",
45
- "append",
46
- "session",
47
- "setHeader",
48
- "writeHead",
49
- ];
50
-
51
- // Acceptable Path values - should be specific paths, not just "/"
52
- this.acceptableValues = [
53
- "/app",
54
- "/admin",
55
- "/api",
56
- "/auth",
57
- "/user",
58
- "/secure",
59
- "/dashboard",
60
- "/login",
61
- ];
62
-
63
- // Root path "/" is acceptable but not recommended for security
64
- this.rootPath = "/";
65
- }
66
-
67
- /**
68
- * Initialize analyzer with semantic engine
69
- */
70
- async initialize(semanticEngine) {
71
- this.semanticEngine = semanticEngine;
72
- if (process.env.SUNLINT_DEBUG) {
73
- console.log(`🔧 [S035] Symbol analyzer initialized`);
74
- }
75
- }
76
-
77
- /**
78
- * Main analysis method for source file
79
- */
80
- async analyze(sourceFile, filePath) {
81
- if (process.env.SUNLINT_DEBUG) {
82
- console.log(`🔍 [S035] Symbol analysis starting for: ${filePath}`);
83
- }
84
-
85
- this.violations = [];
86
- this.currentFile = sourceFile;
87
- this.currentFilePath = filePath;
88
-
89
- this.visitNode(sourceFile);
90
-
91
- if (process.env.SUNLINT_DEBUG) {
92
- console.log(
93
- `🔍 [S035] Symbol analysis completed: ${this.violations.length} violations`
94
- );
95
- }
96
-
97
- return this.violations;
98
- }
99
-
100
- visitNode(node) {
101
- // Check for res.cookie() calls
102
- if (this.isCallExpression(node)) {
103
- this.checkCookieCall(node);
104
- this.checkSetHeaderCall(node);
105
- }
106
-
107
- // Check for session middleware configuration
108
- if (this.isSessionMiddleware(node)) {
109
- this.checkSessionMiddleware(node);
110
- }
111
-
112
- // Recursively visit child nodes
113
- node.forEachChild((child) => this.visitNode(child));
114
- }
115
-
116
- isCallExpression(node) {
117
- return node.getKind() === SyntaxKind.CallExpression;
118
- }
119
-
120
- isSessionMiddleware(node) {
121
- if (node.getKind() !== SyntaxKind.CallExpression) {
122
- return false;
123
- }
124
-
125
- const expression = node.getExpression();
126
- const text = expression.getText();
127
-
128
- return text === "session" || text.includes("session(");
129
- }
130
-
131
- checkCookieCall(node) {
132
- const expression = node.getExpression();
133
-
134
- // Check if it's res.cookie() call
135
- if (expression.getKind() === SyntaxKind.PropertyAccessExpression) {
136
- const propertyAccess = expression;
137
- const property = propertyAccess.getName();
138
-
139
- if (property === "cookie") {
140
- const args = node.getArguments();
141
- if (args.length >= 1) {
142
- const cookieName = this.extractStringValue(args[0]);
143
-
144
- if (cookieName && this.isSessionCookie(cookieName)) {
145
- this.checkCookieOptions(node, args, cookieName);
146
- }
147
- }
148
- }
149
- }
150
- }
151
-
152
- checkSetHeaderCall(node) {
153
- const expression = node.getExpression();
154
-
155
- if (expression.getKind() === SyntaxKind.PropertyAccessExpression) {
156
- const propertyAccess = expression;
157
- const property = propertyAccess.getName();
158
-
159
- if (property === "setHeader") {
160
- const args = node.getArguments();
161
- if (args.length >= 2) {
162
- const headerName = this.extractStringValue(args[0]);
163
-
164
- if (headerName && headerName.toLowerCase() === "set-cookie") {
165
- const headerValue = this.extractStringValue(args[1]);
166
- if (headerValue) {
167
- this.checkSetCookieHeader(node, headerValue);
168
- }
169
- }
170
- }
171
- }
172
- }
173
- }
174
-
175
- checkSessionMiddleware(node) {
176
- const args = node.getArguments();
177
- if (
178
- args.length >= 1 &&
179
- args[0].getKind() === SyntaxKind.ObjectLiteralExpression
180
- ) {
181
- const config = args[0];
182
- const nameProperty = this.findProperty(config, "name");
183
- const cookieProperty = this.findProperty(config, "cookie");
184
-
185
- if (nameProperty) {
186
- const nameValue = this.extractStringValue(
187
- nameProperty.getInitializer()
188
- );
189
- if (nameValue && this.isSessionCookie(nameValue)) {
190
- // Check if cookie configuration has path
191
- if (
192
- cookieProperty &&
193
- cookieProperty.getInitializer().getKind() ===
194
- SyntaxKind.ObjectLiteralExpression
195
- ) {
196
- const cookieConfig = cookieProperty.getInitializer();
197
- this.checkCookieConfigForPath(node, cookieConfig, nameValue);
198
- } else {
199
- this.addViolation(
200
- node,
201
- `Session middleware cookie "${nameValue}" should specify Path attribute to limit access scope`
202
- );
203
- }
204
- }
205
- }
206
- }
207
- }
208
-
209
- checkCookieOptions(node, args, cookieName) {
210
- if (
211
- args.length >= 3 &&
212
- args[2].getKind() === SyntaxKind.ObjectLiteralExpression
213
- ) {
214
- const options = args[2];
215
- this.checkCookieConfigForPath(node, options, cookieName);
216
- } else {
217
- this.addViolation(
218
- node,
219
- `Session cookie "${cookieName}" should specify Path attribute to limit access scope`
220
- );
221
- }
222
- }
223
-
224
- checkCookieConfigForPath(node, config, cookieName) {
225
- const pathProperty = this.findProperty(config, "path");
226
-
227
- if (!pathProperty) {
228
- this.addViolation(
229
- node,
230
- `Session cookie "${cookieName}" should specify Path attribute to limit access scope`
231
- );
232
- return;
233
- }
234
-
235
- const pathValue = this.extractStringValue(pathProperty.getInitializer());
236
- if (!pathValue) {
237
- this.addViolation(
238
- node,
239
- `Session cookie "${cookieName}" should have a valid Path attribute value`
240
- );
241
- return;
242
- }
243
-
244
- // Check if path is too broad (root path "/")
245
- if (pathValue === this.rootPath) {
246
- this.addViolation(
247
- node,
248
- `Session cookie "${cookieName}" uses root path "/", consider using a more specific path to limit access scope`
249
- );
250
- }
251
- }
252
-
253
- checkSetCookieHeader(node, headerValue) {
254
- // Extract cookie name from Set-Cookie header
255
- const cookieMatch = headerValue.match(/^([^=]+)=/);
256
- if (!cookieMatch) return;
257
-
258
- const cookieName = cookieMatch[1].trim();
259
- if (!this.isSessionCookie(cookieName)) return;
260
-
261
- // Check if Path attribute is present
262
- const pathMatch = headerValue.match(/Path=([^;\\s]*)/i);
263
- if (!pathMatch) {
264
- this.addViolation(
265
- node,
266
- `Session cookie "${cookieName}" in Set-Cookie header should specify Path attribute`
267
- );
268
- return;
269
- }
270
-
271
- const pathValue = pathMatch[1];
272
- if (pathValue === this.rootPath) {
273
- this.addViolation(
274
- node,
275
- `Session cookie "${cookieName}" uses root path "/", consider using a more specific path`
276
- );
277
- }
278
- }
279
-
280
- isSessionCookie(cookieName) {
281
- const lowerName = cookieName.toLowerCase();
282
- return this.sessionIndicators.some((indicator) =>
283
- lowerName.includes(indicator.toLowerCase())
284
- );
285
- }
286
-
287
- extractStringValue(node) {
288
- if (!node) return null;
289
-
290
- const kind = node.getKind();
291
-
292
- if (kind === SyntaxKind.StringLiteral) {
293
- return node.getLiteralValue();
294
- }
295
-
296
- if (kind === SyntaxKind.NoSubstitutionTemplateLiteral) {
297
- return node.getLiteralValue();
298
- }
299
-
300
- return null;
301
- }
302
-
303
- findProperty(objectLiteral, propertyName) {
304
- const properties = objectLiteral.getProperties();
305
-
306
- for (const property of properties) {
307
- if (property.getKind() === SyntaxKind.PropertyAssignment) {
308
- const name = property.getName();
309
- if (name === propertyName) {
310
- return property;
311
- }
312
- }
313
- }
314
-
315
- return null;
316
- }
317
-
318
- addViolation(node, message) {
319
- const start = node.getStart();
320
- const sourceFile = node.getSourceFile();
321
- const lineAndColumn = sourceFile.getLineAndColumnAtPos(start);
322
-
323
- // Debug output to understand position issues
324
- if (process.env.SUNLINT_DEBUG) {
325
- console.log(
326
- `🔍 [S035] Violation at node kind: ${node.getKindName()}, text: "${node
327
- .getText()
328
- .substring(0, 50)}..."`
329
- );
330
- console.log(
331
- `🔍 [S035] Position: line ${lineAndColumn.line + 1}, column ${
332
- lineAndColumn.column + 1
333
- }`
334
- );
335
- }
336
-
337
- // Fix line number calculation - ts-morph may have offset issues
338
- // Use actual line calculation based on source file text
339
- const sourceText = sourceFile.getFullText();
340
- const actualLine = this.calculateActualLine(sourceText, start);
341
-
342
- this.violations.push({
343
- ruleId: this.ruleId,
344
- ruleName: this.ruleName,
345
- severity: "warning",
346
- message: message,
347
- line: actualLine,
348
- column: lineAndColumn.column + 1,
349
- source: "symbol-based",
350
- });
351
-
352
- if (process.env.SUNLINT_DEBUG) {
353
- console.log(`🔍 [S035] Added violation: ${message}`);
354
- }
355
- }
356
-
357
- calculateActualLine(sourceText, position) {
358
- // Count newlines up to the position to get accurate line number
359
- let lineCount = 1;
360
- for (let i = 0; i < position; i++) {
361
- if (sourceText[i] === "\n") {
362
- lineCount++;
363
- }
364
- }
365
- return lineCount;
366
- }
367
-
368
- cleanup() {
369
- this.violations = [];
370
- }
371
- }
372
-
373
- module.exports = S035SymbolBasedAnalyzer;
@@ -1,92 +0,0 @@
1
- {
2
- "id": "S039",
3
- "name": "Do not pass Session Tokens via URL parameters",
4
- "category": "security",
5
- "description": "S039 - Detects when session tokens, authentication tokens, JWT tokens, or other sensitive authentication data are passed as URL parameters instead of secure headers or request body. URL parameters are logged in web server logs, browser history, and can be exposed in referrer headers.",
6
- "severity": "warning",
7
- "enabled": true,
8
- "semantic": {
9
- "enabled": true,
10
- "priority": "high",
11
- "fallback": "heuristic"
12
- },
13
- "patterns": {
14
- "include": ["**/*.js", "**/*.ts", "**/*.jsx", "**/*.tsx"],
15
- "exclude": [
16
- "**/*.test.js",
17
- "**/*.test.ts",
18
- "**/*.spec.js",
19
- "**/*.spec.ts",
20
- "**/node_modules/**",
21
- "**/dist/**",
22
- "**/build/**"
23
- ]
24
- },
25
- "analysis": {
26
- "approach": "symbol-based-primary",
27
- "fallback": "regex-based",
28
- "depth": 1,
29
- "timeout": 4000
30
- },
31
- "validation": {
32
- "sessionTokenParams": [
33
- "sessionId",
34
- "session_id",
35
- "session-id",
36
- "sessionToken",
37
- "session_token",
38
- "session-token",
39
- "authToken",
40
- "auth_token",
41
- "auth-token",
42
- "authorization",
43
- "bearer",
44
- "jwt",
45
- "jwtToken",
46
- "jwt_token",
47
- "jwt-token",
48
- "accessToken",
49
- "access_token",
50
- "access-token",
51
- "refreshToken",
52
- "refresh_token",
53
- "refresh-token",
54
- "apiKey",
55
- "api_key",
56
- "api-key",
57
- "csrfToken",
58
- "csrf_token",
59
- "csrf-token",
60
- "xsrfToken",
61
- "xsrf_token",
62
- "xsrf-token",
63
- "token",
64
- "apiToken",
65
- "api_token",
66
- "api-token",
67
- "sid",
68
- "sessionkey",
69
- "session_key",
70
- "session-key",
71
- "userToken",
72
- "user_token",
73
- "user-token",
74
- "authKey",
75
- "auth_key",
76
- "auth-key",
77
- "securityToken",
78
- "security_token",
79
- "security-token"
80
- ],
81
- "urlAccessPatterns": [
82
- "req.query",
83
- "req.params",
84
- "searchParams.get",
85
- "@Query",
86
- "@Param",
87
- "URLSearchParams",
88
- "location.search"
89
- ],
90
- "frameworks": ["express", "nestjs", "nextjs", "nuxtjs"]
91
- }
92
- }