@sun-asterisk/sunlint 1.3.2 → 1.3.3
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/CHANGELOG.md +38 -0
- package/README.md +5 -3
- package/config/rules/enhanced-rules-registry.json +144 -33
- package/core/analysis-orchestrator.js +167 -42
- package/core/auto-performance-manager.js +243 -0
- package/core/cli-action-handler.js +9 -1
- package/core/cli-program.js +19 -5
- package/core/constants/defaults.js +56 -0
- package/core/performance-optimizer.js +271 -0
- package/docs/FILE_LIMITS_COMPLETION_REPORT.md +151 -0
- package/docs/FILE_LIMITS_EXPLANATION.md +190 -0
- package/docs/PERFORMANCE.md +311 -0
- package/docs/PERFORMANCE_MIGRATION_GUIDE.md +368 -0
- package/docs/PERFORMANCE_OPTIMIZATION_PLAN.md +255 -0
- package/docs/QUICK_FILE_LIMITS.md +64 -0
- package/docs/SIMPLIFIED_USAGE_GUIDE.md +208 -0
- package/engines/heuristic-engine.js +182 -5
- package/package.json +2 -1
- package/rules/common/C048_no_bypass_architectural_layers/analyzer.js +180 -0
- package/rules/common/C048_no_bypass_architectural_layers/config.json +50 -0
- package/rules/common/C048_no_bypass_architectural_layers/symbol-based-analyzer.js +235 -0
- package/rules/common/C052_parsing_or_data_transformation/analyzer.js +180 -0
- package/rules/common/C052_parsing_or_data_transformation/config.json +50 -0
- package/rules/common/C052_parsing_or_data_transformation/symbol-based-analyzer.js +132 -0
- package/rules/index.js +2 -0
- package/rules/security/S017_use_parameterized_queries/README.md +128 -0
- package/rules/security/S017_use_parameterized_queries/analyzer.js +286 -0
- package/rules/security/S017_use_parameterized_queries/config.json +109 -0
- package/rules/security/S017_use_parameterized_queries/regex-based-analyzer.js +541 -0
- package/rules/security/S017_use_parameterized_queries/symbol-based-analyzer.js +777 -0
- package/rules/security/S031_secure_session_cookies/README.md +127 -0
- package/rules/security/S031_secure_session_cookies/analyzer.js +245 -0
- package/rules/security/S031_secure_session_cookies/config.json +86 -0
- package/rules/security/S031_secure_session_cookies/regex-based-analyzer.js +196 -0
- package/rules/security/S031_secure_session_cookies/symbol-based-analyzer.js +1084 -0
- package/rules/security/S032_httponly_session_cookies/FRAMEWORK_SUPPORT.md +209 -0
- package/rules/security/S032_httponly_session_cookies/README.md +184 -0
- package/rules/security/S032_httponly_session_cookies/analyzer.js +282 -0
- package/rules/security/S032_httponly_session_cookies/config.json +96 -0
- package/rules/security/S032_httponly_session_cookies/regex-based-analyzer.js +715 -0
- package/rules/security/S032_httponly_session_cookies/symbol-based-analyzer.js +1348 -0
- package/rules/security/S033_samesite_session_cookies/README.md +227 -0
- package/rules/security/S033_samesite_session_cookies/analyzer.js +242 -0
- package/rules/security/S033_samesite_session_cookies/config.json +87 -0
- package/rules/security/S033_samesite_session_cookies/regex-based-analyzer.js +703 -0
- package/rules/security/S033_samesite_session_cookies/symbol-based-analyzer.js +732 -0
- package/rules/security/S034_host_prefix_session_cookies/README.md +204 -0
- package/rules/security/S034_host_prefix_session_cookies/analyzer.js +290 -0
- package/rules/security/S034_host_prefix_session_cookies/config.json +62 -0
- package/rules/security/S034_host_prefix_session_cookies/regex-based-analyzer.js +478 -0
- package/rules/security/S034_host_prefix_session_cookies/symbol-based-analyzer.js +277 -0
- package/rules/security/S035_path_session_cookies/README.md +257 -0
- package/rules/security/S035_path_session_cookies/analyzer.js +316 -0
- package/rules/security/S035_path_session_cookies/config.json +99 -0
- package/rules/security/S035_path_session_cookies/regex-based-analyzer.js +724 -0
- package/rules/security/S035_path_session_cookies/symbol-based-analyzer.js +373 -0
- package/scripts/batch-processing-demo.js +334 -0
- package/scripts/performance-test.js +541 -0
- 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.
|