@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,227 @@
|
|
|
1
|
+
# S033 - Set SameSite attribute for Session Cookies
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Rule S033 enforces the presence of the `SameSite` attribute on session cookies to reduce Cross-Site Request Forgery (CSRF) risk. The SameSite attribute controls whether cookies are sent with cross-site requests, providing protection against CSRF attacks.
|
|
6
|
+
|
|
7
|
+
## Security Impact
|
|
8
|
+
|
|
9
|
+
**CSRF Protection**: The SameSite attribute helps prevent CSRF attacks by controlling when cookies are sent:
|
|
10
|
+
|
|
11
|
+
- `Strict`: Cookies are never sent with cross-site requests
|
|
12
|
+
- `Lax`: Cookies are sent with top-level navigation but not with sub-requests
|
|
13
|
+
- `None`: Cookies are sent with all cross-site requests (requires Secure flag)
|
|
14
|
+
|
|
15
|
+
**Recommended Values**: For session cookies, `strict` or `lax` are recommended for optimal security.
|
|
16
|
+
|
|
17
|
+
## Rule Details
|
|
18
|
+
|
|
19
|
+
### Detected Patterns
|
|
20
|
+
|
|
21
|
+
✅ **Secure Examples:**
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
// ✅ GOOD: SameSite with strict value
|
|
25
|
+
res.cookie("session", sessionId, {
|
|
26
|
+
secure: true,
|
|
27
|
+
httpOnly: true,
|
|
28
|
+
sameSite: "strict",
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// ✅ GOOD: SameSite with lax value
|
|
32
|
+
res.cookie("auth", token, {
|
|
33
|
+
secure: true,
|
|
34
|
+
httpOnly: true,
|
|
35
|
+
sameSite: "lax",
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// ✅ GOOD: Set-Cookie header with SameSite
|
|
39
|
+
res.setHeader(
|
|
40
|
+
"Set-Cookie",
|
|
41
|
+
"session=abc123; HttpOnly; Secure; SameSite=Strict"
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
// ✅ GOOD: Express session with SameSite
|
|
45
|
+
app.use(
|
|
46
|
+
session({
|
|
47
|
+
secret: "secret",
|
|
48
|
+
cookie: {
|
|
49
|
+
secure: true,
|
|
50
|
+
httpOnly: true,
|
|
51
|
+
sameSite: "strict",
|
|
52
|
+
},
|
|
53
|
+
})
|
|
54
|
+
);
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
❌ **Violation Examples:**
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
// ❌ BAD: Missing SameSite attribute
|
|
61
|
+
res.cookie("session", sessionId, {
|
|
62
|
+
secure: true,
|
|
63
|
+
httpOnly: true,
|
|
64
|
+
// Missing: sameSite: "strict"
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// ❌ BAD: No options object
|
|
68
|
+
res.cookie("auth", token);
|
|
69
|
+
|
|
70
|
+
// ❌ BAD: Set-Cookie header without SameSite
|
|
71
|
+
res.setHeader("Set-Cookie", "session=abc123; HttpOnly; Secure");
|
|
72
|
+
|
|
73
|
+
// ❌ BAD: Express session missing SameSite
|
|
74
|
+
app.use(
|
|
75
|
+
session({
|
|
76
|
+
secret: "secret",
|
|
77
|
+
cookie: {
|
|
78
|
+
secure: true,
|
|
79
|
+
httpOnly: true,
|
|
80
|
+
// Missing: sameSite: 'strict'
|
|
81
|
+
},
|
|
82
|
+
})
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
// ❌ BAD: Session middleware without cookie config
|
|
86
|
+
app.use(
|
|
87
|
+
session({
|
|
88
|
+
secret: "secret",
|
|
89
|
+
// Missing: cookie configuration with sameSite
|
|
90
|
+
})
|
|
91
|
+
);
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Supported Cookie Methods
|
|
95
|
+
|
|
96
|
+
The rule detects SameSite violations in:
|
|
97
|
+
|
|
98
|
+
- `res.cookie()` - Express cookie method
|
|
99
|
+
- `res.setCookie()` - Generic cookie setting
|
|
100
|
+
- `res.setHeader("Set-Cookie", ...)` - Raw Set-Cookie headers
|
|
101
|
+
- `session()` - Express-session middleware
|
|
102
|
+
- Other cookie-related methods: `set`, `append`, `writeHead`
|
|
103
|
+
|
|
104
|
+
### Session Cookie Detection
|
|
105
|
+
|
|
106
|
+
The rule identifies session cookies by names containing:
|
|
107
|
+
|
|
108
|
+
- `session`, `sessionid`, `sessid`
|
|
109
|
+
- `jsessionid`, `phpsessid`, `asp.net_sessionid`
|
|
110
|
+
- `connect.sid` (Express session default)
|
|
111
|
+
- `auth`, `token`, `jwt`
|
|
112
|
+
- `csrf`, `refresh`
|
|
113
|
+
|
|
114
|
+
### SameSite Values
|
|
115
|
+
|
|
116
|
+
**Acceptable Values:**
|
|
117
|
+
|
|
118
|
+
- `"strict"` - Strictest protection, cookies never sent cross-site
|
|
119
|
+
- `"lax"` - Moderate protection, cookies sent with top-level navigation
|
|
120
|
+
- `"none"` - No protection, cookies sent with all requests (requires Secure)
|
|
121
|
+
|
|
122
|
+
**Recommended Values for Session Cookies:**
|
|
123
|
+
|
|
124
|
+
- `"strict"` - For maximum security
|
|
125
|
+
- `"lax"` - For better UX while maintaining security
|
|
126
|
+
|
|
127
|
+
## Analysis Approach
|
|
128
|
+
|
|
129
|
+
### Symbol-based Analysis (Primary)
|
|
130
|
+
|
|
131
|
+
- Uses TypeScript AST parsing via ts-morph
|
|
132
|
+
- Provides semantic understanding of code structure
|
|
133
|
+
- Handles complex patterns like object references and spread syntax
|
|
134
|
+
- Analyzes method calls, object literals, and configuration references
|
|
135
|
+
|
|
136
|
+
### Regex-based Analysis (Fallback)
|
|
137
|
+
|
|
138
|
+
- Pattern-matching approach for various cookie patterns
|
|
139
|
+
- Handles Set-Cookie headers and express-session middleware
|
|
140
|
+
- Fallback when semantic analysis is unavailable
|
|
141
|
+
- Covers edge cases that might be missed by AST analysis
|
|
142
|
+
|
|
143
|
+
## Implementation Details
|
|
144
|
+
|
|
145
|
+
### Class-based Configuration Support
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
// ✅ Detects missing SameSite in class properties
|
|
149
|
+
class SessionManager {
|
|
150
|
+
private cookieConfig = {
|
|
151
|
+
secure: true,
|
|
152
|
+
httpOnly: true,
|
|
153
|
+
// Missing: sameSite: "strict"
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
setSession(res: Response, token: string) {
|
|
157
|
+
res.cookie("session", token, this.cookieConfig); // ❌ Violation
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Spread Syntax Support
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
// ✅ Detects missing SameSite in spread configurations
|
|
166
|
+
res.cookie("auth", token, {
|
|
167
|
+
...baseCookieConfig, // If baseCookieConfig lacks sameSite
|
|
168
|
+
path: "/api",
|
|
169
|
+
// Still missing sameSite
|
|
170
|
+
}); // ❌ Violation
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Multiple Cookie Headers
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
// ✅ Detects individual cookies missing SameSite
|
|
177
|
+
res.setHeader("Set-Cookie", [
|
|
178
|
+
"auth=token1; Secure; HttpOnly", // ❌ Missing SameSite
|
|
179
|
+
"session=token2; HttpOnly; SameSite=Strict", // ✅ Has SameSite
|
|
180
|
+
"csrf=token3; Secure", // ❌ Missing SameSite
|
|
181
|
+
]);
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Configuration
|
|
185
|
+
|
|
186
|
+
The rule supports various configuration options in `config.json`:
|
|
187
|
+
|
|
188
|
+
- **Session Indicators**: Patterns that identify session cookies
|
|
189
|
+
- **Acceptable Values**: Valid SameSite attribute values
|
|
190
|
+
- **Cookie Methods**: Methods that accept cookie configurations
|
|
191
|
+
- **Analysis Depth**: How deep to analyze nested configurations
|
|
192
|
+
|
|
193
|
+
## Integration
|
|
194
|
+
|
|
195
|
+
This rule integrates with:
|
|
196
|
+
|
|
197
|
+
- **SunLint Engine**: Main analysis framework
|
|
198
|
+
- **TypeScript Semantic Engine**: For AST-based analysis
|
|
199
|
+
- **ts-morph**: TypeScript compiler API wrapper
|
|
200
|
+
- **Heuristic Engine**: Fallback analysis approach
|
|
201
|
+
|
|
202
|
+
## Performance
|
|
203
|
+
|
|
204
|
+
- **Analysis Timeout**: 5 seconds per file
|
|
205
|
+
- **Memory Optimized**: Uses semantic engine caching
|
|
206
|
+
- **Selective Analysis**: Only analyzes session-related cookies
|
|
207
|
+
- **Duplicate Detection**: Removes duplicate violations
|
|
208
|
+
|
|
209
|
+
## Related Rules
|
|
210
|
+
|
|
211
|
+
- **S031**: Secure flag for session cookies
|
|
212
|
+
- **S032**: HttpOnly attribute for session cookies
|
|
213
|
+
- **S029**: CSRF protection mechanisms
|
|
214
|
+
|
|
215
|
+
## Best Practices
|
|
216
|
+
|
|
217
|
+
1. **Always set SameSite**: Use `strict` or `lax` for session cookies
|
|
218
|
+
2. **Combine with other flags**: Use with `secure` and `httpOnly`
|
|
219
|
+
3. **Consider UX impact**: `strict` may break some legitimate cross-site flows
|
|
220
|
+
4. **Test thoroughly**: Verify application functionality after enabling
|
|
221
|
+
5. **Update legacy code**: Retrofit existing cookie implementations
|
|
222
|
+
|
|
223
|
+
## References
|
|
224
|
+
|
|
225
|
+
- [MDN SameSite Cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite)
|
|
226
|
+
- [OWASP Session Management](https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/06-Session_Management_Testing/)
|
|
227
|
+
- [RFC 6265bis SameSite](https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis)
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* S033 Main Analyzer - Set SameSite attribute for Session Cookies
|
|
3
|
+
* Primary: Symbol-based analysis (when available)
|
|
4
|
+
* Fallback: Regex-based for all other cases
|
|
5
|
+
* Command: node cli.js --rule=S033 --input=examples/rule-test-fixtures/rules/S033_samesite_session_cookies --engine=heuristic
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const S033SymbolBasedAnalyzer = require("./symbol-based-analyzer.js");
|
|
9
|
+
const S033RegexBasedAnalyzer = require("./regex-based-analyzer.js");
|
|
10
|
+
|
|
11
|
+
class S033Analyzer {
|
|
12
|
+
constructor(options = {}) {
|
|
13
|
+
if (process.env.SUNLINT_DEBUG) {
|
|
14
|
+
console.log(`🔧 [S033] Constructor called with options:`, !!options);
|
|
15
|
+
console.log(
|
|
16
|
+
`🔧 [S033] Options type:`,
|
|
17
|
+
typeof options,
|
|
18
|
+
Object.keys(options || {})
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
this.ruleId = "S033";
|
|
23
|
+
this.ruleName = "Set SameSite attribute for Session Cookies";
|
|
24
|
+
this.description =
|
|
25
|
+
"Set SameSite attribute for Session Cookies to reduce CSRF risk. This prevents the browser from sending cookies along with cross-site requests, mitigating CSRF attacks.";
|
|
26
|
+
this.semanticEngine = options.semanticEngine || null;
|
|
27
|
+
this.verbose = options.verbose || false;
|
|
28
|
+
|
|
29
|
+
// Configuration
|
|
30
|
+
this.config = {
|
|
31
|
+
useSymbolBased: true, // Primary approach
|
|
32
|
+
fallbackToRegex: true, // Secondary approach
|
|
33
|
+
regexBasedOnly: false, // Can be set to true for pure mode
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// Initialize analyzers
|
|
37
|
+
try {
|
|
38
|
+
this.symbolAnalyzer = new S033SymbolBasedAnalyzer(this.semanticEngine);
|
|
39
|
+
if (process.env.SUNLINT_DEBUG) {
|
|
40
|
+
console.log(`🔧 [S033] Symbol analyzer created successfully`);
|
|
41
|
+
}
|
|
42
|
+
} catch (error) {
|
|
43
|
+
console.error(`🔧 [S033] Error creating symbol analyzer:`, error);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
this.regexAnalyzer = new S033RegexBasedAnalyzer(this.semanticEngine);
|
|
48
|
+
if (process.env.SUNLINT_DEBUG) {
|
|
49
|
+
console.log(`🔧 [S033] Regex analyzer created successfully`);
|
|
50
|
+
}
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.error(`🔧 [S033] Error creating regex analyzer:`, error);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Initialize analyzer with semantic engine
|
|
58
|
+
*/
|
|
59
|
+
async initialize(semanticEngine) {
|
|
60
|
+
this.semanticEngine = semanticEngine;
|
|
61
|
+
|
|
62
|
+
if (process.env.SUNLINT_DEBUG) {
|
|
63
|
+
console.log(`🔧 [S033] Main analyzer initializing...`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Initialize both analyzers
|
|
67
|
+
if (this.symbolAnalyzer) {
|
|
68
|
+
await this.symbolAnalyzer.initialize?.(semanticEngine);
|
|
69
|
+
}
|
|
70
|
+
if (this.regexAnalyzer) {
|
|
71
|
+
await this.regexAnalyzer.initialize?.(semanticEngine);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Clean up if needed
|
|
75
|
+
if (this.regexAnalyzer) {
|
|
76
|
+
this.regexAnalyzer.cleanup?.();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (process.env.SUNLINT_DEBUG) {
|
|
80
|
+
console.log(`🔧 [S033] Main analyzer initialized successfully`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Single file analysis method for testing
|
|
86
|
+
*/
|
|
87
|
+
analyzeSingle(filePath, options = {}) {
|
|
88
|
+
if (process.env.SUNLINT_DEBUG) {
|
|
89
|
+
console.log(`� [S033] analyzeSingle() called for: ${filePath}`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Return result using same format as analyze method
|
|
93
|
+
return this.analyze([filePath], "typescript", options);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async analyze(files, language, options = {}) {
|
|
97
|
+
if (process.env.SUNLINT_DEBUG) {
|
|
98
|
+
console.log(
|
|
99
|
+
`🔧 [S033] analyze() method called with ${files.length} files, language: ${language}`
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const violations = [];
|
|
104
|
+
|
|
105
|
+
for (const filePath of files) {
|
|
106
|
+
try {
|
|
107
|
+
if (process.env.SUNLINT_DEBUG) {
|
|
108
|
+
console.log(`🔧 [S033] Processing file: ${filePath}`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const fileViolations = await this.analyzeFile(filePath, options);
|
|
112
|
+
violations.push(...fileViolations);
|
|
113
|
+
|
|
114
|
+
if (process.env.SUNLINT_DEBUG) {
|
|
115
|
+
console.log(
|
|
116
|
+
`🔧 [S033] File ${filePath}: Found ${fileViolations.length} violations`
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
} catch (error) {
|
|
120
|
+
console.warn(
|
|
121
|
+
`⚠ [S033] Analysis failed for ${filePath}:`,
|
|
122
|
+
error.message
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (process.env.SUNLINT_DEBUG) {
|
|
128
|
+
console.log(`🔧 [S033] Total violations found: ${violations.length}`);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return violations;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async analyzeFile(filePath, options = {}) {
|
|
135
|
+
if (process.env.SUNLINT_DEBUG) {
|
|
136
|
+
console.log(`🔍 [S033] analyzeFile() called for: ${filePath}`);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Create a Map to track unique violations and prevent duplicates
|
|
140
|
+
const violationMap = new Map();
|
|
141
|
+
|
|
142
|
+
// 1. Try Symbol-based analysis first (primary)
|
|
143
|
+
if (
|
|
144
|
+
this.config.useSymbolBased &&
|
|
145
|
+
this.semanticEngine?.project &&
|
|
146
|
+
this.semanticEngine?.initialized
|
|
147
|
+
) {
|
|
148
|
+
try {
|
|
149
|
+
if (process.env.SUNLINT_DEBUG) {
|
|
150
|
+
console.log(`🔧 [S033] Trying symbol-based analysis...`);
|
|
151
|
+
}
|
|
152
|
+
const sourceFile = this.semanticEngine.project.getSourceFile(filePath);
|
|
153
|
+
if (sourceFile) {
|
|
154
|
+
if (process.env.SUNLINT_DEBUG) {
|
|
155
|
+
console.log(`🔧 [S033] Source file found, analyzing...`);
|
|
156
|
+
}
|
|
157
|
+
const symbolViolations = await this.symbolAnalyzer.analyze(
|
|
158
|
+
sourceFile,
|
|
159
|
+
filePath
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
// Add to violation map with deduplication
|
|
163
|
+
symbolViolations.forEach((violation) => {
|
|
164
|
+
const key = `${violation.line}:${violation.column}:${violation.message}`;
|
|
165
|
+
if (!violationMap.has(key)) {
|
|
166
|
+
violationMap.set(key, violation);
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
if (process.env.SUNLINT_DEBUG) {
|
|
171
|
+
console.log(
|
|
172
|
+
`🔧 [S033] Symbol analysis completed: ${symbolViolations.length} violations`
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
} else {
|
|
176
|
+
if (process.env.SUNLINT_DEBUG) {
|
|
177
|
+
console.log(`🔧 [S033] Source file not found, falling back...`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
} catch (error) {
|
|
181
|
+
console.warn(`⚠ [S033] Symbol analysis failed:`, error.message);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// 2. Try Regex-based analysis (fallback or additional)
|
|
186
|
+
if (this.config.fallbackToRegex || this.config.regexBasedOnly) {
|
|
187
|
+
try {
|
|
188
|
+
if (process.env.SUNLINT_DEBUG) {
|
|
189
|
+
console.log(`🔧 [S033] Trying regex-based analysis...`);
|
|
190
|
+
}
|
|
191
|
+
const regexViolations = await this.regexAnalyzer.analyze(filePath);
|
|
192
|
+
|
|
193
|
+
// Add to violation map with deduplication
|
|
194
|
+
regexViolations.forEach((violation) => {
|
|
195
|
+
const key = `${violation.line}:${violation.column}:${violation.message}`;
|
|
196
|
+
if (!violationMap.has(key)) {
|
|
197
|
+
violationMap.set(key, violation);
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
if (process.env.SUNLINT_DEBUG) {
|
|
202
|
+
console.log(
|
|
203
|
+
`🔧 [S033] Regex analysis completed: ${regexViolations.length} violations`
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
} catch (error) {
|
|
207
|
+
console.warn(`⚠ [S033] Regex analysis failed:`, error.message);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Convert Map values to array and add filePath to each violation
|
|
212
|
+
const finalViolations = Array.from(violationMap.values()).map(
|
|
213
|
+
(violation) => ({
|
|
214
|
+
...violation,
|
|
215
|
+
filePath: filePath,
|
|
216
|
+
file: filePath, // Also add 'file' for compatibility
|
|
217
|
+
})
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
if (process.env.SUNLINT_DEBUG) {
|
|
221
|
+
console.log(
|
|
222
|
+
`🔧 [S033] File analysis completed: ${finalViolations.length} unique violations`
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return finalViolations;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Clean up resources
|
|
231
|
+
*/
|
|
232
|
+
cleanup() {
|
|
233
|
+
if (this.symbolAnalyzer?.cleanup) {
|
|
234
|
+
this.symbolAnalyzer.cleanup();
|
|
235
|
+
}
|
|
236
|
+
if (this.regexAnalyzer?.cleanup) {
|
|
237
|
+
this.regexAnalyzer.cleanup();
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
module.exports = S033Analyzer;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "S033",
|
|
3
|
+
"name": "Set SameSite attribute for Session Cookies",
|
|
4
|
+
"category": "security",
|
|
5
|
+
"description": "S033 - Set SameSite attribute for Session Cookies to reduce CSRF risk. This prevents the browser from sending cookies along with cross-site requests, mitigating CSRF attacks.",
|
|
6
|
+
"severity": "error",
|
|
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": 2,
|
|
29
|
+
"timeout": 5000
|
|
30
|
+
},
|
|
31
|
+
"validation": {
|
|
32
|
+
"cookieMethods": [
|
|
33
|
+
"setCookie",
|
|
34
|
+
"cookie",
|
|
35
|
+
"set",
|
|
36
|
+
"append",
|
|
37
|
+
"session",
|
|
38
|
+
"setHeader",
|
|
39
|
+
"writeHead"
|
|
40
|
+
],
|
|
41
|
+
"cookieLibraries": [
|
|
42
|
+
"express",
|
|
43
|
+
"koa",
|
|
44
|
+
"fastify",
|
|
45
|
+
"hapi",
|
|
46
|
+
"next",
|
|
47
|
+
"nuxt",
|
|
48
|
+
"cookie",
|
|
49
|
+
"cookie-parser",
|
|
50
|
+
"express-session",
|
|
51
|
+
"connect-session",
|
|
52
|
+
"passport"
|
|
53
|
+
],
|
|
54
|
+
"sessionIndicators": [
|
|
55
|
+
"session",
|
|
56
|
+
"sessionid",
|
|
57
|
+
"sessid",
|
|
58
|
+
"jsessionid",
|
|
59
|
+
"phpsessid",
|
|
60
|
+
"asp.net_sessionid",
|
|
61
|
+
"connect.sid",
|
|
62
|
+
"auth",
|
|
63
|
+
"token",
|
|
64
|
+
"jwt",
|
|
65
|
+
"csrf",
|
|
66
|
+
"refresh"
|
|
67
|
+
],
|
|
68
|
+
"sameSitePatterns": [
|
|
69
|
+
"sameSite:\\s*['\"]strict['\"]",
|
|
70
|
+
"sameSite:\\s*['\"]lax['\"]",
|
|
71
|
+
"sameSite:\\s*['\"]none['\"]",
|
|
72
|
+
"sameSite:['\"]strict['\"]",
|
|
73
|
+
"sameSite:['\"]lax['\"]",
|
|
74
|
+
"sameSite:['\"]none['\"]",
|
|
75
|
+
"SameSite=Strict",
|
|
76
|
+
"SameSite=Lax",
|
|
77
|
+
"SameSite=None"
|
|
78
|
+
],
|
|
79
|
+
"insecurePatterns": [
|
|
80
|
+
"(?<!sameSite[\\s=:]+)(?<!SameSite=)Set-Cookie",
|
|
81
|
+
"res\\.cookie\\([^)]*\\)(?![^{]*sameSite)",
|
|
82
|
+
"document\\.cookie\\s*="
|
|
83
|
+
],
|
|
84
|
+
"acceptableValues": ["strict", "lax", "none"],
|
|
85
|
+
"recommendedValues": ["strict", "lax"]
|
|
86
|
+
}
|
|
87
|
+
}
|