@sun-asterisk/sunlint 1.3.2 → 1.3.4

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 (60) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/README.md +5 -3
  3. package/config/rules/enhanced-rules-registry.json +144 -33
  4. package/core/analysis-orchestrator.js +173 -42
  5. package/core/auto-performance-manager.js +243 -0
  6. package/core/cli-action-handler.js +24 -2
  7. package/core/cli-program.js +19 -5
  8. package/core/constants/defaults.js +56 -0
  9. package/core/performance-optimizer.js +271 -0
  10. package/docs/FILE_LIMITS_COMPLETION_REPORT.md +151 -0
  11. package/docs/FILE_LIMITS_EXPLANATION.md +190 -0
  12. package/docs/PERFORMANCE.md +311 -0
  13. package/docs/PERFORMANCE_MIGRATION_GUIDE.md +368 -0
  14. package/docs/PERFORMANCE_OPTIMIZATION_PLAN.md +255 -0
  15. package/docs/QUICK_FILE_LIMITS.md +64 -0
  16. package/docs/SIMPLIFIED_USAGE_GUIDE.md +208 -0
  17. package/engines/engine-factory.js +7 -0
  18. package/engines/heuristic-engine.js +182 -5
  19. package/package.json +2 -1
  20. package/rules/common/C048_no_bypass_architectural_layers/analyzer.js +180 -0
  21. package/rules/common/C048_no_bypass_architectural_layers/config.json +50 -0
  22. package/rules/common/C048_no_bypass_architectural_layers/symbol-based-analyzer.js +235 -0
  23. package/rules/common/C052_parsing_or_data_transformation/analyzer.js +180 -0
  24. package/rules/common/C052_parsing_or_data_transformation/config.json +50 -0
  25. package/rules/common/C052_parsing_or_data_transformation/symbol-based-analyzer.js +132 -0
  26. package/rules/index.js +2 -0
  27. package/rules/security/S017_use_parameterized_queries/README.md +128 -0
  28. package/rules/security/S017_use_parameterized_queries/analyzer.js +286 -0
  29. package/rules/security/S017_use_parameterized_queries/config.json +109 -0
  30. package/rules/security/S017_use_parameterized_queries/regex-based-analyzer.js +541 -0
  31. package/rules/security/S017_use_parameterized_queries/symbol-based-analyzer.js +777 -0
  32. package/rules/security/S031_secure_session_cookies/README.md +127 -0
  33. package/rules/security/S031_secure_session_cookies/analyzer.js +245 -0
  34. package/rules/security/S031_secure_session_cookies/config.json +86 -0
  35. package/rules/security/S031_secure_session_cookies/regex-based-analyzer.js +196 -0
  36. package/rules/security/S031_secure_session_cookies/symbol-based-analyzer.js +1084 -0
  37. package/rules/security/S032_httponly_session_cookies/FRAMEWORK_SUPPORT.md +209 -0
  38. package/rules/security/S032_httponly_session_cookies/README.md +184 -0
  39. package/rules/security/S032_httponly_session_cookies/analyzer.js +282 -0
  40. package/rules/security/S032_httponly_session_cookies/config.json +96 -0
  41. package/rules/security/S032_httponly_session_cookies/regex-based-analyzer.js +715 -0
  42. package/rules/security/S032_httponly_session_cookies/symbol-based-analyzer.js +1348 -0
  43. package/rules/security/S033_samesite_session_cookies/README.md +227 -0
  44. package/rules/security/S033_samesite_session_cookies/analyzer.js +242 -0
  45. package/rules/security/S033_samesite_session_cookies/config.json +87 -0
  46. package/rules/security/S033_samesite_session_cookies/regex-based-analyzer.js +703 -0
  47. package/rules/security/S033_samesite_session_cookies/symbol-based-analyzer.js +732 -0
  48. package/rules/security/S034_host_prefix_session_cookies/README.md +204 -0
  49. package/rules/security/S034_host_prefix_session_cookies/analyzer.js +290 -0
  50. package/rules/security/S034_host_prefix_session_cookies/config.json +62 -0
  51. package/rules/security/S034_host_prefix_session_cookies/regex-based-analyzer.js +478 -0
  52. package/rules/security/S034_host_prefix_session_cookies/symbol-based-analyzer.js +277 -0
  53. package/rules/security/S035_path_session_cookies/README.md +257 -0
  54. package/rules/security/S035_path_session_cookies/analyzer.js +316 -0
  55. package/rules/security/S035_path_session_cookies/config.json +99 -0
  56. package/rules/security/S035_path_session_cookies/regex-based-analyzer.js +724 -0
  57. package/rules/security/S035_path_session_cookies/symbol-based-analyzer.js +373 -0
  58. package/scripts/batch-processing-demo.js +334 -0
  59. package/scripts/performance-test.js +541 -0
  60. package/scripts/quick-performance-test.js +108 -0
@@ -0,0 +1,277 @@
1
+ /**
2
+ * S034 Symbol-based Analyzer - Use __Host- prefix for Session Cookies
3
+ * Detects session cookies without __Host- prefix using TypeScript AST analysis
4
+ */
5
+
6
+ const { SyntaxKind } = require("typescript");
7
+
8
+ class S034SymbolBasedAnalyzer {
9
+ constructor(semanticEngine) {
10
+ this.semanticEngine = semanticEngine;
11
+ this.ruleId = "S034";
12
+ this.ruleName = "Use __Host- prefix for Session Cookies";
13
+
14
+ // Session cookie name patterns
15
+ this.sessionCookiePatterns = [
16
+ /^session$/i,
17
+ /^sessionid$/i,
18
+ /^session_id$/i,
19
+ /^sid$/i,
20
+ /^connect\.sid$/i,
21
+ /^auth$/i,
22
+ /^auth_token$/i,
23
+ /^authentication$/i,
24
+ /^jwt$/i,
25
+ /^token$/i,
26
+ /^csrf$/i,
27
+ /^csrf_token$/i,
28
+ /^xsrf$/i,
29
+ /^login$/i,
30
+ /^user$/i,
31
+ /^userid$/i,
32
+ /^user_id$/i,
33
+ ];
34
+
35
+ this.violations = [];
36
+ }
37
+
38
+ async initialize(semanticEngine) {
39
+ this.semanticEngine = semanticEngine;
40
+ }
41
+
42
+ async analyze(sourceFile, filePath) {
43
+ if (process.env.SUNLINT_DEBUG) {
44
+ console.log(`🔍 [S034] Symbol analysis starting for: ${filePath}`);
45
+ }
46
+
47
+ this.violations = [];
48
+
49
+ try {
50
+ // Visit all nodes in the source file
51
+ this.visitNode(sourceFile);
52
+ } catch (error) {
53
+ console.warn(`⚠ [S034] Symbol analysis error:`, error.message);
54
+ }
55
+
56
+ if (process.env.SUNLINT_DEBUG) {
57
+ console.log(
58
+ `🔍 [S034] Symbol analysis completed: ${this.violations.length} violations`
59
+ );
60
+ }
61
+
62
+ return this.violations;
63
+ }
64
+
65
+ visitNode(node) {
66
+ // Check for res.cookie() calls
67
+ if (this.isCallExpression(node)) {
68
+ this.checkCookieCall(node);
69
+ this.checkSetHeaderCall(node);
70
+ }
71
+
72
+ // Check for session middleware configuration
73
+ if (this.isSessionMiddleware(node)) {
74
+ this.checkSessionMiddleware(node);
75
+ }
76
+
77
+ // Recursively visit child nodes
78
+ node.forEachChild((child) => this.visitNode(child));
79
+ }
80
+
81
+ isCallExpression(node) {
82
+ return node.getKind() === SyntaxKind.CallExpression;
83
+ }
84
+
85
+ checkCookieCall(node) {
86
+ const expression = node.getExpression();
87
+
88
+ // Check if it's res.cookie() call
89
+ if (expression.getKind() === SyntaxKind.PropertyAccessExpression) {
90
+ const propertyAccess = expression;
91
+ const property = propertyAccess.getName();
92
+
93
+ if (property === "cookie") {
94
+ const args = node.getArguments();
95
+ if (args.length >= 1) {
96
+ const cookieName = this.extractStringValue(args[0]);
97
+
98
+ if (
99
+ cookieName &&
100
+ this.isSessionCookie(cookieName) &&
101
+ !this.hasHostPrefix(cookieName)
102
+ ) {
103
+ this.addViolation(
104
+ node,
105
+ `Session cookie "${cookieName}" should use __Host- prefix to prevent subdomain sharing`
106
+ );
107
+ }
108
+ }
109
+ }
110
+ }
111
+ }
112
+
113
+ checkSetHeaderCall(node) {
114
+ const expression = node.getExpression();
115
+
116
+ if (expression.getKind() === SyntaxKind.PropertyAccessExpression) {
117
+ const propertyAccess = expression;
118
+ const property = propertyAccess.getName();
119
+
120
+ if (property === "setHeader") {
121
+ const args = node.getArguments();
122
+ if (args.length >= 2) {
123
+ const headerName = this.extractStringValue(args[0]);
124
+
125
+ if (headerName && headerName.toLowerCase() === "set-cookie") {
126
+ const headerValue = this.extractStringValue(args[1]);
127
+ if (headerValue) {
128
+ this.checkSetCookieHeader(node, headerValue);
129
+ } else if (
130
+ args[1].getKind() === SyntaxKind.ArrayLiteralExpression
131
+ ) {
132
+ // Handle array of Set-Cookie headers
133
+ const arrayElements = args[1].getElements();
134
+ arrayElements.forEach((element) => {
135
+ const cookieString = this.extractStringValue(element);
136
+ if (cookieString) {
137
+ this.checkSetCookieHeader(node, cookieString);
138
+ }
139
+ });
140
+ }
141
+ }
142
+ }
143
+ }
144
+ }
145
+ }
146
+
147
+ checkSetCookieHeader(node, cookieString) {
148
+ // Extract cookie name from Set-Cookie header
149
+ const cookieMatch = cookieString.match(/^([^=]+)=/);
150
+ if (cookieMatch) {
151
+ const cookieName = cookieMatch[1].trim();
152
+
153
+ if (this.isSessionCookie(cookieName) && !this.hasHostPrefix(cookieName)) {
154
+ this.addViolation(
155
+ node,
156
+ `Session cookie "${cookieName}" in Set-Cookie header should use __Host- prefix`
157
+ );
158
+ }
159
+ }
160
+ }
161
+
162
+ checkSessionMiddleware(node) {
163
+ // Check for session() middleware configuration
164
+ const args = node.getArguments();
165
+ if (
166
+ args.length >= 1 &&
167
+ args[0].getKind() === SyntaxKind.ObjectLiteralExpression
168
+ ) {
169
+ const config = args[0];
170
+ const nameProperty = this.findProperty(config, "name");
171
+
172
+ if (nameProperty) {
173
+ const cookieName = this.extractStringValue(
174
+ nameProperty.getInitializer()
175
+ );
176
+ if (
177
+ cookieName &&
178
+ this.isSessionCookie(cookieName) &&
179
+ !this.hasHostPrefix(cookieName)
180
+ ) {
181
+ this.addViolation(
182
+ node,
183
+ `Session middleware cookie name "${cookieName}" should use __Host- prefix`
184
+ );
185
+ }
186
+ }
187
+ }
188
+ }
189
+
190
+ isSessionMiddleware(node) {
191
+ if (node.getKind() !== SyntaxKind.CallExpression) {
192
+ return false;
193
+ }
194
+
195
+ const expression = node.getExpression();
196
+ const expressionText = expression.getText();
197
+
198
+ return expressionText === "session" || expressionText.includes("session");
199
+ }
200
+
201
+ isSessionCookie(cookieName) {
202
+ return this.sessionCookiePatterns.some((pattern) =>
203
+ pattern.test(cookieName)
204
+ );
205
+ }
206
+
207
+ hasHostPrefix(cookieName) {
208
+ return cookieName.startsWith("__Host-");
209
+ }
210
+
211
+ extractStringValue(node) {
212
+ if (!node) return null;
213
+
214
+ const kind = node.getKind();
215
+
216
+ if (kind === SyntaxKind.StringLiteral) {
217
+ return node.getLiteralValue();
218
+ }
219
+
220
+ if (kind === SyntaxKind.NoSubstitutionTemplateLiteral) {
221
+ return node.getLiteralValue();
222
+ }
223
+
224
+ return null;
225
+ }
226
+
227
+ findProperty(objectLiteral, propertyName) {
228
+ const properties = objectLiteral.getProperties();
229
+
230
+ for (const property of properties) {
231
+ if (property.getKind() === SyntaxKind.PropertyAssignment) {
232
+ const name = property.getName();
233
+ if (name === propertyName) {
234
+ return property;
235
+ }
236
+ }
237
+ }
238
+
239
+ return null;
240
+ }
241
+
242
+ addViolation(node, message) {
243
+ const start = node.getStart();
244
+ const sourceFile = node.getSourceFile();
245
+ const lineAndColumn = sourceFile.getLineAndColumnAtPos(start);
246
+
247
+ // Debug output to understand position issues
248
+ if (process.env.SUNLINT_DEBUG) {
249
+ console.log(
250
+ `🔍 [S034] Violation at node kind: ${node.getKindName()}, text: "${node
251
+ .getText()
252
+ .substring(0, 50)}..."`
253
+ );
254
+ console.log(
255
+ `🔍 [S034] Position: line ${lineAndColumn.line + 1}, column ${
256
+ lineAndColumn.column + 1
257
+ }`
258
+ );
259
+ }
260
+
261
+ this.violations.push({
262
+ ruleId: this.ruleId,
263
+ ruleName: this.ruleName,
264
+ severity: "warning",
265
+ message: message,
266
+ line: lineAndColumn.line + 1,
267
+ column: lineAndColumn.column + 1,
268
+ source: "symbol-based",
269
+ });
270
+ }
271
+
272
+ cleanup() {
273
+ this.violations = [];
274
+ }
275
+ }
276
+
277
+ module.exports = S034SymbolBasedAnalyzer;
@@ -0,0 +1,257 @@
1
+ # S035 - Set Path attribute for Session Cookies
2
+
3
+ ## Overview
4
+
5
+ This rule enforces the use of the `Path` attribute for session cookies to limit their access scope and reduce the attack surface.
6
+
7
+ ## Description
8
+
9
+ The `Path` attribute restricts where cookies can be sent by specifying the URL path for which the cookie is valid. This security measure helps prevent:
10
+
11
+ - **Unintended cookie exposure** to different parts of the application
12
+ - **Cross-path cookie attacks** where malicious code in one path accesses cookies from another
13
+ - **Privilege escalation** through cookie access from less secure paths
14
+
15
+ ## Rule Details
16
+
17
+ **Rule ID**: S035
18
+ **Category**: Security
19
+ **Severity**: Warning
20
+ **Type**: Hybrid Analysis (Symbol-based + Regex-based)
21
+
22
+ ## Examples
23
+
24
+ ### ❌ Violations
25
+
26
+ ```typescript
27
+ // Express.js - Missing Path attribute
28
+ res.cookie("sessionid", sessionId, {
29
+ secure: true,
30
+ httpOnly: true,
31
+ sameSite: "strict",
32
+ // Missing: path attribute
33
+ });
34
+
35
+ // NestJS - Authentication cookie without Path attribute
36
+ @Post('login')
37
+ login(@Res() response: Response) {
38
+ response.cookie('auth_token', value, {
39
+ secure: true,
40
+ httpOnly: true,
41
+ // Missing: path attribute
42
+ });
43
+ }
44
+
45
+ // Next.js - Session cookie missing Path attribute
46
+ export async function POST() {
47
+ const response = NextResponse.json({ success: true });
48
+ response.cookies.set('sessionid', token, {
49
+ secure: true,
50
+ httpOnly: true,
51
+ // Missing: path attribute
52
+ });
53
+ return response;
54
+ }
55
+
56
+ // NextAuth.js - Session token without Path attribute
57
+ export default NextAuth({
58
+ cookies: {
59
+ sessionToken: {
60
+ name: 'next-auth.session-token',
61
+ options: {
62
+ secure: true,
63
+ httpOnly: true,
64
+ // Missing: path attribute
65
+ }
66
+ }
67
+ }
68
+ });
69
+
70
+ // Using root path (not recommended)
71
+ res.cookie("auth", authToken, {
72
+ secure: true,
73
+ httpOnly: true,
74
+ path: "/", // Too broad - exposes cookie to entire domain
75
+ sameSite: "strict",
76
+ });
77
+ ```
78
+
79
+ ### ✅ Correct Usage
80
+
81
+ ```typescript
82
+ // Express.js - Specific path for admin area
83
+ res.cookie("admin_session", sessionId, {
84
+ secure: true,
85
+ httpOnly: true,
86
+ path: "/admin",
87
+ sameSite: "strict",
88
+ });
89
+
90
+ // NestJS - API-specific cookie with path
91
+ @Post('login')
92
+ login(@Res() response: Response) {
93
+ response.cookie('auth_token', value, {
94
+ secure: true,
95
+ httpOnly: true,
96
+ path: '/api',
97
+ sameSite: 'strict',
98
+ });
99
+ }
100
+
101
+ // Next.js - Session cookie with specific path
102
+ export async function POST() {
103
+ const response = NextResponse.json({ success: true });
104
+ response.cookies.set('sessionid', token, {
105
+ secure: true,
106
+ httpOnly: true,
107
+ path: '/app',
108
+ sameSite: 'strict',
109
+ });
110
+ return response;
111
+ }
112
+
113
+ // NextAuth.js - Session token with path
114
+ export default NextAuth({
115
+ cookies: {
116
+ sessionToken: {
117
+ name: 'next-auth.session-token',
118
+ options: {
119
+ secure: true,
120
+ httpOnly: true,
121
+ path: '/auth',
122
+ sameSite: 'lax',
123
+ }
124
+ }
125
+ }
126
+ });
127
+ ```
128
+
129
+ ## Configuration
130
+
131
+ ### Detected Patterns
132
+
133
+ This rule detects session cookies without Path attribute in multiple frameworks:
134
+
135
+ ### Express.js
136
+
137
+ - `res.cookie()` calls with session cookie names without Path attribute
138
+ - `res.setHeader()` with `Set-Cookie` headers missing Path attribute
139
+ - Session middleware configuration without Path attribute
140
+ - Array of Set-Cookie headers with session cookies missing Path attribute
141
+
142
+ ### NestJS
143
+
144
+ - `@Res()` decorator response cookie methods
145
+ - `@Cookies()` decorator usage with session cookies
146
+ - Response object cookie setting methods
147
+ - NestJS session middleware configuration
148
+
149
+ ### Next.js
150
+
151
+ - `response.cookies.set()` method calls
152
+ - `cookies().set()` from next/headers
153
+ - NextAuth.js session and CSRF token configuration
154
+ - API route response cookie setting
155
+
156
+ ### NextAuth.js
157
+
158
+ - Session token configuration without Path attribute
159
+ - CSRF token configuration missing Path attribute
160
+ - Cookie configuration in NextAuth providers
161
+
162
+ **Session Cookie Names:**
163
+
164
+ - `session`, `sessionid`, `session_id`
165
+ - `sid`, `connect.sid`
166
+ - `auth`, `auth_token`, `authentication`
167
+ - `jwt`, `token`, `csrf`, `csrf_token`, `xsrf`
168
+ - `user`, `userid`, `user_id`, `login`
169
+ - `sessionToken`, `csrfToken` (NextAuth specific)
170
+ - `next-auth.session-token`, `next-auth.csrf-token`
171
+
172
+ **Cookie Methods:**
173
+
174
+ - Express.js: `res.cookie()`, `res.setHeader()`, session middleware
175
+ - NestJS: `@Res()` response methods, `@Cookies()` decorators
176
+ - Next.js: `response.cookies.set()`, `cookies().set()`
177
+ - NextAuth.js: sessionToken/csrfToken configuration
178
+
179
+ ### Recommended Path Values
180
+
181
+ **Specific Paths** (Recommended):
182
+
183
+ - `/app` - Application-specific
184
+ - `/admin` - Administrative areas
185
+ - `/api` - API endpoints
186
+ - `/auth` - Authentication flows
187
+ - `/user` - User-specific areas
188
+
189
+ **Root Path** (`/`):
190
+
191
+ - Acceptable but triggers warning
192
+ - Should be avoided for security
193
+
194
+ ## Security Benefits
195
+
196
+ 1. **Scope Limitation**: Restricts cookie access to specific application areas
197
+ 2. **Attack Surface Reduction**: Prevents cookie exposure to unintended paths
198
+ 3. **Privilege Separation**: Isolates cookies between different functional areas
199
+ 4. **Defense in Depth**: Adds another layer of security control
200
+
201
+ ## Analysis Approach
202
+
203
+ ### Symbol-based Analysis (Primary)
204
+
205
+ - Uses TypeScript AST for semantic analysis
206
+ - Detects cookie configurations in object literals
207
+ - Analyzes session middleware setups
208
+ - Provides precise line/column positions
209
+
210
+ ### Regex-based Analysis (Fallback)
211
+
212
+ - Pattern-based detection for complex cases
213
+ - Handles Set-Cookie headers and arrays
214
+ - Covers edge cases missed by AST analysis
215
+ - Maintains line number accuracy
216
+
217
+ ## Best Practices
218
+
219
+ 1. **Use specific paths** instead of root path `/`
220
+ 2. **Match paths to functionality** (e.g., `/admin` for admin cookies)
221
+ 3. **Avoid overly broad paths** that expose cookies unnecessarily
222
+ 4. **Consider path hierarchy** for nested application structures
223
+ 5. **Document path conventions** in your application
224
+
225
+ ## Testing
226
+
227
+ Run the rule on test fixtures:
228
+
229
+ ```bash
230
+ # Test violations (Express.js)
231
+ node cli.js --rule=S035 --input=examples/rule-test-fixtures/rules/S035_path_session_cookies/violations --engine=heuristic
232
+
233
+ # Test clean examples
234
+ node cli.js --rule=S035 --input=examples/rule-test-fixtures/rules/S035_path_session_cookies/clean --engine=heuristic
235
+
236
+ # Framework-specific testing
237
+ # Test with ESLint engine (fast)
238
+ node cli.js --rule=S035 --input=path/to/your/files
239
+
240
+ # Test with heuristic engine (comprehensive)
241
+ node cli.js --rule=S035 --input=path/to/your/files --engine=heuristic
242
+ ```
243
+
244
+ ## Framework Compatibility
245
+
246
+ - **Node.js**: All versions
247
+ - **Frameworks**: Express.js, NestJS, Next.js, NextAuth.js
248
+ - **Languages**: JavaScript, TypeScript
249
+ - **Analysis Engines**: ESLint (fast), Heuristic (comprehensive)
250
+
251
+ ## Related Rules
252
+
253
+ - **S032**: Set Secure flag for Session Cookies
254
+ - **S033**: Set SameSite attribute for Session Cookies
255
+ - **S034**: Use \_\_Host- prefix for Session Cookies
256
+
257
+ Together, these rules provide comprehensive session cookie security.