@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.
Files changed (59) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +5 -3
  3. package/config/rules/enhanced-rules-registry.json +144 -33
  4. package/core/analysis-orchestrator.js +167 -42
  5. package/core/auto-performance-manager.js +243 -0
  6. package/core/cli-action-handler.js +9 -1
  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/heuristic-engine.js +182 -5
  18. package/package.json +2 -1
  19. package/rules/common/C048_no_bypass_architectural_layers/analyzer.js +180 -0
  20. package/rules/common/C048_no_bypass_architectural_layers/config.json +50 -0
  21. package/rules/common/C048_no_bypass_architectural_layers/symbol-based-analyzer.js +235 -0
  22. package/rules/common/C052_parsing_or_data_transformation/analyzer.js +180 -0
  23. package/rules/common/C052_parsing_or_data_transformation/config.json +50 -0
  24. package/rules/common/C052_parsing_or_data_transformation/symbol-based-analyzer.js +132 -0
  25. package/rules/index.js +2 -0
  26. package/rules/security/S017_use_parameterized_queries/README.md +128 -0
  27. package/rules/security/S017_use_parameterized_queries/analyzer.js +286 -0
  28. package/rules/security/S017_use_parameterized_queries/config.json +109 -0
  29. package/rules/security/S017_use_parameterized_queries/regex-based-analyzer.js +541 -0
  30. package/rules/security/S017_use_parameterized_queries/symbol-based-analyzer.js +777 -0
  31. package/rules/security/S031_secure_session_cookies/README.md +127 -0
  32. package/rules/security/S031_secure_session_cookies/analyzer.js +245 -0
  33. package/rules/security/S031_secure_session_cookies/config.json +86 -0
  34. package/rules/security/S031_secure_session_cookies/regex-based-analyzer.js +196 -0
  35. package/rules/security/S031_secure_session_cookies/symbol-based-analyzer.js +1084 -0
  36. package/rules/security/S032_httponly_session_cookies/FRAMEWORK_SUPPORT.md +209 -0
  37. package/rules/security/S032_httponly_session_cookies/README.md +184 -0
  38. package/rules/security/S032_httponly_session_cookies/analyzer.js +282 -0
  39. package/rules/security/S032_httponly_session_cookies/config.json +96 -0
  40. package/rules/security/S032_httponly_session_cookies/regex-based-analyzer.js +715 -0
  41. package/rules/security/S032_httponly_session_cookies/symbol-based-analyzer.js +1348 -0
  42. package/rules/security/S033_samesite_session_cookies/README.md +227 -0
  43. package/rules/security/S033_samesite_session_cookies/analyzer.js +242 -0
  44. package/rules/security/S033_samesite_session_cookies/config.json +87 -0
  45. package/rules/security/S033_samesite_session_cookies/regex-based-analyzer.js +703 -0
  46. package/rules/security/S033_samesite_session_cookies/symbol-based-analyzer.js +732 -0
  47. package/rules/security/S034_host_prefix_session_cookies/README.md +204 -0
  48. package/rules/security/S034_host_prefix_session_cookies/analyzer.js +290 -0
  49. package/rules/security/S034_host_prefix_session_cookies/config.json +62 -0
  50. package/rules/security/S034_host_prefix_session_cookies/regex-based-analyzer.js +478 -0
  51. package/rules/security/S034_host_prefix_session_cookies/symbol-based-analyzer.js +277 -0
  52. package/rules/security/S035_path_session_cookies/README.md +257 -0
  53. package/rules/security/S035_path_session_cookies/analyzer.js +316 -0
  54. package/rules/security/S035_path_session_cookies/config.json +99 -0
  55. package/rules/security/S035_path_session_cookies/regex-based-analyzer.js +724 -0
  56. package/rules/security/S035_path_session_cookies/symbol-based-analyzer.js +373 -0
  57. package/scripts/batch-processing-demo.js +334 -0
  58. package/scripts/performance-test.js +541 -0
  59. package/scripts/quick-performance-test.js +108 -0
@@ -0,0 +1,703 @@
1
+ /**
2
+ * S033 Regex-Based Analyzer - Set SameSite attribute for Session Cookies
3
+ * Fallback analysis using regex patterns
4
+ */
5
+
6
+ const fs = require("fs");
7
+
8
+ class S033RegexBasedAnalyzer {
9
+ constructor(semanticEngine = null) {
10
+ this.semanticEngine = semanticEngine;
11
+ this.ruleId = "S033";
12
+ this.category = "security";
13
+
14
+ // Session cookie indicators (enhanced with framework patterns)
15
+ this.sessionIndicators = [
16
+ "session",
17
+ "sessionid",
18
+ "sessid",
19
+ "jsessionid",
20
+ "phpsessid",
21
+ "asp.net_sessionid",
22
+ "connect.sid",
23
+ "auth",
24
+ "token",
25
+ "jwt",
26
+ "csrf",
27
+ "refresh",
28
+ "next-auth",
29
+ "sessiontoken",
30
+ "csrftoken",
31
+ "user_session",
32
+ "api_session",
33
+ "login_session",
34
+ "auth_token",
35
+ "csrf_token",
36
+ "refresh_token",
37
+ "admin_session",
38
+ "app_auth",
39
+ "edge_session",
40
+ "server_session",
41
+ "custom-session",
42
+ ];
43
+
44
+ // Cookie method patterns (enhanced with framework support)
45
+ this.cookieMethodPatterns = [
46
+ // Express.js res.cookie patterns
47
+ /res\.cookie\s*\(\s*(['"`])(session|sessionid|sessid|auth|token|jwt|csrf|refresh|connect\.sid)[^)]*\)/gi,
48
+ // Next.js cookies.set patterns
49
+ /(?:response\.)?cookies\.set\s*\(\s*(['"`])(session|sessionid|sessid|auth|token|jwt|csrf|refresh)[^)]*\)/gi,
50
+ // Nuxt.js useCookie patterns
51
+ /useCookie\s*\(\s*(['"`])(session|sessionid|sessid|auth|token|jwt|csrf|refresh)[^)]*\)/gi,
52
+ // Nuxt.js setCookie patterns
53
+ /setCookie\s*\(\s*[^,]+,\s*(['"`])(session|sessionid|sessid|auth|token|jwt|csrf|refresh)[^)]*\)/gi,
54
+ // Set-Cookie header patterns
55
+ /res\.setHeader\s*\(\s*['"`]set-cookie['"`]\s*,\s*([^)]+)\)/gi,
56
+ // Express session patterns
57
+ /session\s*\(\s*\{[^}]*\}/gi,
58
+ ];
59
+
60
+ // SameSite validation patterns (enhanced with ternary support)
61
+ this.sameSitePatterns = [
62
+ /sameSite\s*:\s*['"`](strict|lax|none)['"`]/gi,
63
+ /sameSite\s*:\s*(strict|lax|none)/gi,
64
+ /SameSite=(Strict|Lax|None)/gi,
65
+ /sameSite\s*:\s*.*\?\s*['"`](strict|lax|none)['"`]\s*:\s*['"`](strict|lax|none)['"`]/gi, // Ternary with quotes
66
+ /sameSite\s*:\s*.*\?\s*(strict|lax|none)\s*:\s*(strict|lax|none)/gi, // Ternary without quotes
67
+ ];
68
+ }
69
+
70
+ async analyze(filePath) {
71
+ if (this.verbose) {
72
+ console.log(`🔍 [${this.ruleId}] Regex-based analysis for: ${filePath}`);
73
+ }
74
+
75
+ try {
76
+ const content = fs.readFileSync(filePath, "utf8");
77
+ return this.analyzeContent(content, filePath);
78
+ } catch (error) {
79
+ if (this.verbose) {
80
+ console.log(
81
+ `🔍 [${this.ruleId}] Regex: Error reading file:`,
82
+ error.message
83
+ );
84
+ }
85
+ return [];
86
+ }
87
+ }
88
+
89
+ analyzeContent(content, filePath) {
90
+ const violations = [];
91
+ const lines = content.split("\n");
92
+
93
+ // Remove comments to avoid false positives
94
+ const cleanContent = this.removeComments(content);
95
+
96
+ try {
97
+ // Pattern 1: res.cookie() calls (Express.js)
98
+ violations.push(
99
+ ...this.analyzeCookieCalls(cleanContent, lines, filePath)
100
+ );
101
+
102
+ // Pattern 2: Set-Cookie headers
103
+ violations.push(
104
+ ...this.analyzeSetCookieHeaders(cleanContent, lines, filePath)
105
+ );
106
+
107
+ // Pattern 3: Express session middleware
108
+ violations.push(
109
+ ...this.analyzeSessionMiddleware(cleanContent, lines, filePath)
110
+ );
111
+
112
+ // Pattern 4: Next.js cookies.set() calls
113
+ violations.push(
114
+ ...this.analyzeNextJSCookies(cleanContent, lines, filePath)
115
+ );
116
+
117
+ // Pattern 5: Nuxt.js useCookie() calls
118
+ violations.push(
119
+ ...this.analyzeNuxtJSUseCookie(cleanContent, lines, filePath)
120
+ );
121
+
122
+ // Pattern 6: Nuxt.js setCookie() calls
123
+ violations.push(
124
+ ...this.analyzeNuxtJSSetCookie(cleanContent, lines, filePath)
125
+ );
126
+
127
+ // Pattern 7: NextAuth configuration
128
+ violations.push(
129
+ ...this.analyzeNextAuthConfig(cleanContent, lines, filePath)
130
+ );
131
+ } catch (error) {
132
+ if (this.verbose) {
133
+ console.log(
134
+ `🔍 [${this.ruleId}] Regex: Error in analysis:`,
135
+ error.message
136
+ );
137
+ }
138
+ }
139
+
140
+ return violations;
141
+ }
142
+
143
+ analyzeCookieCalls(content, lines, filePath) {
144
+ const violations = [];
145
+
146
+ // Pattern for res.cookie calls
147
+ const cookieCallPattern =
148
+ /res\.cookie\s*\(\s*(['"`])([^'"`]+)\1\s*,\s*[^,]+(?:\s*,\s*(\{[^}]*\}))?\s*\)/gi;
149
+ let match;
150
+
151
+ while ((match = cookieCallPattern.exec(content)) !== null) {
152
+ const fullMatch = match[0];
153
+ const cookieName = match[2];
154
+ const configObject = match[3] || "";
155
+
156
+ if (this.verbose) {
157
+ console.log(
158
+ `🔍 [${
159
+ this.ruleId
160
+ }] Regex: Pattern match - cookieName: "${cookieName}", config: "${configObject.substring(
161
+ 0,
162
+ 50
163
+ )}..."`
164
+ );
165
+ }
166
+
167
+ // Only check session cookies
168
+ if (!this.isSessionCookie(cookieName)) {
169
+ continue;
170
+ }
171
+
172
+ // Check if config object has SameSite
173
+ if (!configObject || !this.hasSameSiteInText(configObject)) {
174
+ // Skip if this looks like a config reference
175
+ if (this.isConfigReference(configObject)) {
176
+ if (this.verbose) {
177
+ console.log(
178
+ `🔍 [${
179
+ this.ruleId
180
+ }] Regex: Skipping config reference: ${configObject.substring(
181
+ 0,
182
+ 50
183
+ )}...`
184
+ );
185
+ }
186
+ continue;
187
+ }
188
+
189
+ const lineNumber = this.getLineNumber(content, match.index);
190
+ violations.push({
191
+ rule: this.ruleId,
192
+ source: filePath,
193
+ category: this.category,
194
+ line: lineNumber,
195
+ column: 1,
196
+ message: `Insecure session cookie: Session cookie "${cookieName}" missing SameSite attribute`,
197
+ severity: "error",
198
+ });
199
+ }
200
+ }
201
+
202
+ return violations;
203
+ }
204
+
205
+ analyzeSetCookieHeaders(content, lines, filePath) {
206
+ const violations = [];
207
+
208
+ // Pattern for setHeader with Set-Cookie
209
+ const setHeaderPattern =
210
+ /res\.setHeader\s*\(\s*['"`]set-cookie['"`]\s*,\s*([^)]+)\)/gi;
211
+ let match;
212
+
213
+ while ((match = setHeaderPattern.exec(content)) !== null) {
214
+ const cookieValue = match[1];
215
+
216
+ if (this.verbose) {
217
+ console.log(
218
+ `🔍 [${
219
+ this.ruleId
220
+ }] Regex: Checking Set-Cookie header pattern: ${match[0].substring(
221
+ 0,
222
+ 100
223
+ )}...`
224
+ );
225
+ }
226
+
227
+ // Handle array of cookies
228
+ if (cookieValue.includes("[") && cookieValue.includes("]")) {
229
+ const cookieStrings = this.extractCookieStrings(cookieValue);
230
+ for (const cookieString of cookieStrings) {
231
+ const cookieName = this.extractCookieNameFromString(cookieString);
232
+
233
+ if (this.verbose) {
234
+ console.log(
235
+ `🔍 [${
236
+ this.ruleId
237
+ }] Regex: Checking Set-Cookie string: "${cookieString.substring(
238
+ 0,
239
+ 50
240
+ )}..." - name: "${cookieName}"`
241
+ );
242
+ }
243
+
244
+ if (
245
+ this.isSessionCookie(cookieName) &&
246
+ !this.hasSameSiteAttribute(cookieString)
247
+ ) {
248
+ const lineNumber = this.getLineNumber(content, match.index);
249
+ violations.push({
250
+ rule: this.ruleId,
251
+ source: filePath,
252
+ category: this.category,
253
+ line: lineNumber,
254
+ column: 1,
255
+ message: `Insecure session cookie: Session cookie "${cookieName}" in Set-Cookie header missing SameSite attribute`,
256
+ severity: "error",
257
+ });
258
+ }
259
+ }
260
+ } else {
261
+ // Single cookie
262
+ const cookieName = this.extractCookieNameFromString(cookieValue);
263
+ if (
264
+ this.isSessionCookie(cookieName) &&
265
+ !this.hasSameSiteAttribute(cookieValue)
266
+ ) {
267
+ const lineNumber = this.getLineNumber(content, match.index);
268
+ violations.push({
269
+ rule: this.ruleId,
270
+ source: filePath,
271
+ category: this.category,
272
+ line: lineNumber,
273
+ column: 1,
274
+ message: `Insecure session cookie: Session cookie "${cookieName}" in Set-Cookie header missing SameSite attribute`,
275
+ severity: "error",
276
+ });
277
+ }
278
+ }
279
+ }
280
+
281
+ return violations;
282
+ }
283
+
284
+ analyzeSessionMiddleware(content, lines, filePath) {
285
+ const violations = [];
286
+
287
+ // Pattern for express-session middleware
288
+ const sessionPattern =
289
+ /session\s*\(\s*\{([^}]*(?:\{[^}]*\}[^}]*)*)\}\s*\)/gi;
290
+ let match;
291
+
292
+ while ((match = sessionPattern.exec(content)) !== null) {
293
+ const sessionConfig = match[1];
294
+
295
+ if (this.verbose) {
296
+ console.log(
297
+ `🔍 [${
298
+ this.ruleId
299
+ }] Regex: Checking session middleware: ${sessionConfig.substring(
300
+ 0,
301
+ 100
302
+ )}...`
303
+ );
304
+ }
305
+
306
+ // Check if cookie config has SameSite
307
+ const cookieConfigMatch = sessionConfig.match(/cookie\s*:\s*\{([^}]*)\}/);
308
+ if (cookieConfigMatch) {
309
+ const cookieConfig = cookieConfigMatch[1];
310
+ if (!this.hasSameSiteInText(cookieConfig)) {
311
+ const lineNumber = this.getLineNumber(content, match.index);
312
+ violations.push({
313
+ rule: this.ruleId,
314
+ source: filePath,
315
+ category: this.category,
316
+ line: lineNumber,
317
+ column: 1,
318
+ message: `Insecure session cookie: Session middleware missing SameSite attribute in cookie configuration`,
319
+ severity: "error",
320
+ });
321
+ }
322
+ } else {
323
+ // No cookie config at all
324
+ const lineNumber = this.getLineNumber(content, match.index);
325
+ violations.push({
326
+ rule: this.ruleId,
327
+ source: filePath,
328
+ category: this.category,
329
+ line: lineNumber,
330
+ column: 1,
331
+ message: `Insecure session cookie: Session middleware missing cookie configuration with SameSite attribute`,
332
+ severity: "error",
333
+ });
334
+ }
335
+ }
336
+
337
+ return violations;
338
+ }
339
+
340
+ extractCookieStrings(cookieValue) {
341
+ const strings = [];
342
+ const matches = cookieValue.match(/['"`][^'"`]*['"`]/g);
343
+ if (matches) {
344
+ strings.push(...matches.map((s) => s.slice(1, -1))); // Remove quotes
345
+ }
346
+ return strings;
347
+ }
348
+
349
+ extractCookieNameFromString(cookieString) {
350
+ const match = cookieString.match(/^[^=]+/);
351
+ return match ? match[0].trim() : "unknown";
352
+ }
353
+
354
+ hasSameSiteInText(text) {
355
+ return this.sameSitePatterns.some((pattern) => {
356
+ pattern.lastIndex = 0; // Reset regex state
357
+ return pattern.test(text);
358
+ });
359
+ }
360
+
361
+ hasSameSiteAttribute(cookieValue) {
362
+ const sameSitePattern = /SameSite=(Strict|Lax|None)/i;
363
+ return sameSitePattern.test(cookieValue);
364
+ }
365
+
366
+ isSessionCookie(cookieName) {
367
+ if (!cookieName || cookieName === "null" || cookieName === "undefined") {
368
+ return false;
369
+ }
370
+
371
+ const name = cookieName.toLowerCase();
372
+ return this.sessionIndicators.some((indicator) =>
373
+ name.includes(indicator.toLowerCase())
374
+ );
375
+ }
376
+
377
+ isConfigReference(configText) {
378
+ if (!configText) return false;
379
+
380
+ // Check for common config reference patterns
381
+ const configRefPatterns = [
382
+ /\.\.\./, // Spread operator
383
+ /\w+Config/, // Variable ending with Config
384
+ /this\.\w+/, // this.property
385
+ /^\s*\w+\s*$/, // Simple variable reference
386
+ ];
387
+
388
+ return configRefPatterns.some((pattern) => pattern.test(configText));
389
+ }
390
+
391
+ removeComments(content) {
392
+ // Remove single-line comments
393
+ content = content.replace(/\/\/.*$/gm, "");
394
+ // Remove multi-line comments
395
+ content = content.replace(/\/\*[\s\S]*?\*\//g, "");
396
+ return content;
397
+ }
398
+
399
+ getLineNumber(content, index) {
400
+ return content.substring(0, index).split("\n").length;
401
+ }
402
+
403
+ analyzeNextJSCookies(content, lines, filePath) {
404
+ const violations = [];
405
+
406
+ // Pattern 1: Next.js response.cookies.set() calls
407
+ const nextJSResponsePattern =
408
+ /(?:response\.)?cookies\.set\s*\(\s*(['"`])([^'"`]+)\1\s*,\s*[^,]+(?:\s*,\s*(\{[^}]*\}))?\s*\)/gi;
409
+ let match;
410
+
411
+ while ((match = nextJSResponsePattern.exec(content)) !== null) {
412
+ const cookieName = match[2];
413
+ const configObject = match[3] || "";
414
+
415
+ if (this.verbose) {
416
+ console.log(
417
+ `🔍 [${
418
+ this.ruleId
419
+ }] Regex: Next.js Response Pattern match - cookieName: "${cookieName}", config: "${configObject.substring(
420
+ 0,
421
+ 50
422
+ )}..."`
423
+ );
424
+ }
425
+
426
+ // Only check session cookies
427
+ if (!this.isSessionCookie(cookieName)) {
428
+ continue;
429
+ }
430
+
431
+ // Check if config object has SameSite
432
+ if (!configObject || !this.hasSameSiteInText(configObject)) {
433
+ const lineNumber = this.getLineNumber(content, match.index);
434
+ violations.push({
435
+ rule: this.ruleId,
436
+ source: filePath,
437
+ category: this.category,
438
+ line: lineNumber,
439
+ column: 1,
440
+ message: `Insecure session cookie: Session cookie "${cookieName}" (Next.js) missing SameSite attribute`,
441
+ severity: "error",
442
+ });
443
+ }
444
+ }
445
+
446
+ // Pattern 2: Next.js cookies().set() calls (from next/headers)
447
+ const nextJSCookiesPattern =
448
+ /cookies\(\)\.set\s*\(\s*(['"`])([^'"`]+)\1\s*,\s*[^,]+(?:\s*,\s*(\{[^}]*\}))?\s*\)/gi;
449
+
450
+ while ((match = nextJSCookiesPattern.exec(content)) !== null) {
451
+ const cookieName = match[2];
452
+ const configObject = match[3] || "";
453
+
454
+ if (this.verbose) {
455
+ console.log(
456
+ `🔍 [${
457
+ this.ruleId
458
+ }] Regex: Next.js cookies() Pattern match - cookieName: "${cookieName}", config: "${configObject.substring(
459
+ 0,
460
+ 50
461
+ )}..."`
462
+ );
463
+ }
464
+
465
+ // Only check session cookies
466
+ if (!this.isSessionCookie(cookieName)) {
467
+ continue;
468
+ }
469
+
470
+ // Check if config object has SameSite
471
+ if (!configObject || !this.hasSameSiteInText(configObject)) {
472
+ const lineNumber = this.getLineNumber(content, match.index);
473
+ violations.push({
474
+ rule: this.ruleId,
475
+ source: filePath,
476
+ category: this.category,
477
+ line: lineNumber,
478
+ column: 1,
479
+ message: `Insecure session cookie: Session cookie "${cookieName}" (Next.js) missing SameSite attribute`,
480
+ severity: "error",
481
+ });
482
+ }
483
+ }
484
+
485
+ // Pattern 3: cookieStore.set() calls
486
+ const cookieStorePattern =
487
+ /cookieStore\.set\s*\(\s*(['"`])([^'"`]+)\1\s*,\s*[^,]+(?:\s*,\s*(\{[^}]*\}))?\s*\)/gi;
488
+
489
+ while ((match = cookieStorePattern.exec(content)) !== null) {
490
+ const cookieName = match[2];
491
+ const configObject = match[3] || "";
492
+
493
+ if (this.verbose) {
494
+ console.log(
495
+ `🔍 [${
496
+ this.ruleId
497
+ }] Regex: cookieStore Pattern match - cookieName: "${cookieName}", config: "${configObject.substring(
498
+ 0,
499
+ 50
500
+ )}..."`
501
+ );
502
+ }
503
+
504
+ // Only check session cookies
505
+ if (!this.isSessionCookie(cookieName)) {
506
+ continue;
507
+ }
508
+
509
+ // Check if config object has SameSite
510
+ if (!configObject || !this.hasSameSiteInText(configObject)) {
511
+ const lineNumber = this.getLineNumber(content, match.index);
512
+ violations.push({
513
+ rule: this.ruleId,
514
+ source: filePath,
515
+ category: this.category,
516
+ line: lineNumber,
517
+ column: 1,
518
+ message: `Insecure session cookie: Session cookie "${cookieName}" (Next.js) missing SameSite attribute`,
519
+ severity: "error",
520
+ });
521
+ }
522
+ }
523
+
524
+ return violations;
525
+ }
526
+
527
+ analyzeNuxtJSUseCookie(content, lines, filePath) {
528
+ const violations = [];
529
+
530
+ // Pattern for Nuxt.js useCookie() calls
531
+ const nuxtUseCookiePattern =
532
+ /useCookie\s*\(\s*(['"`])([^'"`]+)\1(?:\s*,\s*(\{[^}]*\}))?\s*\)/gi;
533
+ let match;
534
+
535
+ while ((match = nuxtUseCookiePattern.exec(content)) !== null) {
536
+ const cookieName = match[2];
537
+ const configObject = match[3] || "";
538
+
539
+ if (this.verbose) {
540
+ console.log(
541
+ `🔍 [${
542
+ this.ruleId
543
+ }] Regex: Nuxt.js useCookie Pattern match - cookieName: "${cookieName}", config: "${configObject.substring(
544
+ 0,
545
+ 50
546
+ )}..."`
547
+ );
548
+ }
549
+
550
+ // Only check session cookies
551
+ if (!this.isSessionCookie(cookieName)) {
552
+ continue;
553
+ }
554
+
555
+ // Check if config object has SameSite
556
+ if (!configObject || !this.hasSameSiteInText(configObject)) {
557
+ const lineNumber = this.getLineNumber(content, match.index);
558
+ violations.push({
559
+ rule: this.ruleId,
560
+ source: filePath,
561
+ category: this.category,
562
+ line: lineNumber,
563
+ column: 1,
564
+ message: `Insecure session cookie: Session cookie "${cookieName}" (Nuxt.js) missing SameSite attribute`,
565
+ severity: "error",
566
+ });
567
+ }
568
+ }
569
+
570
+ return violations;
571
+ }
572
+
573
+ analyzeNuxtJSSetCookie(content, lines, filePath) {
574
+ const violations = [];
575
+
576
+ // Pattern for Nuxt.js setCookie(event, name, value, options) calls
577
+ const nuxtSetCookiePattern =
578
+ /setCookie\s*\(\s*[^,]+,\s*(['"`])([^'"`]+)\1\s*,\s*[^,]+(?:\s*,\s*(\{[^}]*\}))?\s*\)/gi;
579
+ let match;
580
+
581
+ while ((match = nuxtSetCookiePattern.exec(content)) !== null) {
582
+ const cookieName = match[2];
583
+ const configObject = match[3] || "";
584
+
585
+ if (this.verbose) {
586
+ console.log(
587
+ `🔍 [${
588
+ this.ruleId
589
+ }] Regex: Nuxt.js setCookie Pattern match - cookieName: "${cookieName}", config: "${configObject.substring(
590
+ 0,
591
+ 50
592
+ )}..."`
593
+ );
594
+ }
595
+
596
+ // Only check session cookies
597
+ if (!this.isSessionCookie(cookieName)) {
598
+ continue;
599
+ }
600
+
601
+ // Check if config object has SameSite
602
+ if (!configObject || !this.hasSameSiteInText(configObject)) {
603
+ const lineNumber = this.getLineNumber(content, match.index);
604
+ violations.push({
605
+ rule: this.ruleId,
606
+ source: filePath,
607
+ category: this.category,
608
+ line: lineNumber,
609
+ column: 1,
610
+ message: `Insecure session cookie: Session cookie "${cookieName}" (Nuxt.js) missing SameSite attribute`,
611
+ severity: "error",
612
+ });
613
+ }
614
+ }
615
+
616
+ return violations;
617
+ }
618
+
619
+ analyzeNextAuthConfig(content, lines, filePath) {
620
+ const violations = [];
621
+
622
+ // Simple approach: find NextAuth cookie configurations directly
623
+ // Pattern 1: sessionToken configurations
624
+ const sessionTokenPattern =
625
+ /sessionToken\s*:\s*\{[^}]*options\s*:\s*\{([^}]*)\}/gi;
626
+ let match;
627
+
628
+ while ((match = sessionTokenPattern.exec(content)) !== null) {
629
+ const cookieOptions = match[1];
630
+
631
+ if (this.verbose) {
632
+ console.log(
633
+ `🔍 [${
634
+ this.ruleId
635
+ }] Regex: sessionToken Pattern match - options: "${cookieOptions.substring(
636
+ 0,
637
+ 50
638
+ )}..."`
639
+ );
640
+ }
641
+
642
+ if (!this.hasSameSiteInText(cookieOptions)) {
643
+ const lineNumber = this.getLineNumber(content, match.index);
644
+ violations.push({
645
+ rule: this.ruleId,
646
+ source: filePath,
647
+ category: this.category,
648
+ line: lineNumber,
649
+ column: 1,
650
+ message: `Insecure session cookie: NextAuth "sessionToken" cookie missing SameSite attribute`,
651
+ severity: "error",
652
+ });
653
+ }
654
+ }
655
+
656
+ // Pattern 2: csrfToken configurations
657
+ const csrfTokenPattern =
658
+ /csrfToken\s*:\s*\{[^}]*options\s*:\s*\{([^}]*)\}/gi;
659
+
660
+ while ((match = csrfTokenPattern.exec(content)) !== null) {
661
+ const cookieOptions = match[1];
662
+
663
+ if (this.verbose) {
664
+ console.log(
665
+ `🔍 [${
666
+ this.ruleId
667
+ }] Regex: csrfToken Pattern match - options: "${cookieOptions.substring(
668
+ 0,
669
+ 50
670
+ )}..."`
671
+ );
672
+ }
673
+
674
+ if (!this.hasSameSiteInText(cookieOptions)) {
675
+ const lineNumber = this.getLineNumber(content, match.index);
676
+ violations.push({
677
+ rule: this.ruleId,
678
+ source: filePath,
679
+ category: this.category,
680
+ line: lineNumber,
681
+ column: 1,
682
+ message: `Insecure session cookie: NextAuth "csrfToken" cookie missing SameSite attribute`,
683
+ severity: "error",
684
+ });
685
+ }
686
+ }
687
+
688
+ return violations;
689
+ }
690
+
691
+ // This helper method is no longer needed with the simplified approach
692
+ extractNextAuthCookieViolations(
693
+ configContent,
694
+ fullContent,
695
+ baseIndex,
696
+ filePath
697
+ ) {
698
+ // Deprecated - using direct pattern matching instead
699
+ return [];
700
+ }
701
+ }
702
+
703
+ module.exports = S033RegexBasedAnalyzer;