@oculum/scanner 1.0.0

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 (281) hide show
  1. package/dist/formatters/cli-terminal.d.ts +27 -0
  2. package/dist/formatters/cli-terminal.d.ts.map +1 -0
  3. package/dist/formatters/cli-terminal.js +412 -0
  4. package/dist/formatters/cli-terminal.js.map +1 -0
  5. package/dist/formatters/github-comment.d.ts +41 -0
  6. package/dist/formatters/github-comment.d.ts.map +1 -0
  7. package/dist/formatters/github-comment.js +306 -0
  8. package/dist/formatters/github-comment.js.map +1 -0
  9. package/dist/formatters/grouping.d.ts +52 -0
  10. package/dist/formatters/grouping.d.ts.map +1 -0
  11. package/dist/formatters/grouping.js +152 -0
  12. package/dist/formatters/grouping.js.map +1 -0
  13. package/dist/formatters/index.d.ts +9 -0
  14. package/dist/formatters/index.d.ts.map +1 -0
  15. package/dist/formatters/index.js +35 -0
  16. package/dist/formatters/index.js.map +1 -0
  17. package/dist/formatters/vscode-diagnostic.d.ts +103 -0
  18. package/dist/formatters/vscode-diagnostic.d.ts.map +1 -0
  19. package/dist/formatters/vscode-diagnostic.js +151 -0
  20. package/dist/formatters/vscode-diagnostic.js.map +1 -0
  21. package/dist/index.d.ts +52 -0
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.js +648 -0
  24. package/dist/index.js.map +1 -0
  25. package/dist/layer1/comments.d.ts +8 -0
  26. package/dist/layer1/comments.d.ts.map +1 -0
  27. package/dist/layer1/comments.js +203 -0
  28. package/dist/layer1/comments.js.map +1 -0
  29. package/dist/layer1/config-audit.d.ts +8 -0
  30. package/dist/layer1/config-audit.d.ts.map +1 -0
  31. package/dist/layer1/config-audit.js +252 -0
  32. package/dist/layer1/config-audit.js.map +1 -0
  33. package/dist/layer1/entropy.d.ts +8 -0
  34. package/dist/layer1/entropy.d.ts.map +1 -0
  35. package/dist/layer1/entropy.js +500 -0
  36. package/dist/layer1/entropy.js.map +1 -0
  37. package/dist/layer1/file-flags.d.ts +7 -0
  38. package/dist/layer1/file-flags.d.ts.map +1 -0
  39. package/dist/layer1/file-flags.js +112 -0
  40. package/dist/layer1/file-flags.js.map +1 -0
  41. package/dist/layer1/index.d.ts +36 -0
  42. package/dist/layer1/index.d.ts.map +1 -0
  43. package/dist/layer1/index.js +132 -0
  44. package/dist/layer1/index.js.map +1 -0
  45. package/dist/layer1/patterns.d.ts +8 -0
  46. package/dist/layer1/patterns.d.ts.map +1 -0
  47. package/dist/layer1/patterns.js +482 -0
  48. package/dist/layer1/patterns.js.map +1 -0
  49. package/dist/layer1/urls.d.ts +8 -0
  50. package/dist/layer1/urls.d.ts.map +1 -0
  51. package/dist/layer1/urls.js +296 -0
  52. package/dist/layer1/urls.js.map +1 -0
  53. package/dist/layer1/weak-crypto.d.ts +7 -0
  54. package/dist/layer1/weak-crypto.d.ts.map +1 -0
  55. package/dist/layer1/weak-crypto.js +291 -0
  56. package/dist/layer1/weak-crypto.js.map +1 -0
  57. package/dist/layer2/ai-agent-tools.d.ts +19 -0
  58. package/dist/layer2/ai-agent-tools.d.ts.map +1 -0
  59. package/dist/layer2/ai-agent-tools.js +528 -0
  60. package/dist/layer2/ai-agent-tools.js.map +1 -0
  61. package/dist/layer2/ai-endpoint-protection.d.ts +36 -0
  62. package/dist/layer2/ai-endpoint-protection.d.ts.map +1 -0
  63. package/dist/layer2/ai-endpoint-protection.js +332 -0
  64. package/dist/layer2/ai-endpoint-protection.js.map +1 -0
  65. package/dist/layer2/ai-execution-sinks.d.ts +18 -0
  66. package/dist/layer2/ai-execution-sinks.d.ts.map +1 -0
  67. package/dist/layer2/ai-execution-sinks.js +496 -0
  68. package/dist/layer2/ai-execution-sinks.js.map +1 -0
  69. package/dist/layer2/ai-fingerprinting.d.ts +7 -0
  70. package/dist/layer2/ai-fingerprinting.d.ts.map +1 -0
  71. package/dist/layer2/ai-fingerprinting.js +654 -0
  72. package/dist/layer2/ai-fingerprinting.js.map +1 -0
  73. package/dist/layer2/ai-prompt-hygiene.d.ts +19 -0
  74. package/dist/layer2/ai-prompt-hygiene.d.ts.map +1 -0
  75. package/dist/layer2/ai-prompt-hygiene.js +356 -0
  76. package/dist/layer2/ai-prompt-hygiene.js.map +1 -0
  77. package/dist/layer2/ai-rag-safety.d.ts +21 -0
  78. package/dist/layer2/ai-rag-safety.d.ts.map +1 -0
  79. package/dist/layer2/ai-rag-safety.js +459 -0
  80. package/dist/layer2/ai-rag-safety.js.map +1 -0
  81. package/dist/layer2/ai-schema-validation.d.ts +25 -0
  82. package/dist/layer2/ai-schema-validation.d.ts.map +1 -0
  83. package/dist/layer2/ai-schema-validation.js +375 -0
  84. package/dist/layer2/ai-schema-validation.js.map +1 -0
  85. package/dist/layer2/auth-antipatterns.d.ts +20 -0
  86. package/dist/layer2/auth-antipatterns.d.ts.map +1 -0
  87. package/dist/layer2/auth-antipatterns.js +333 -0
  88. package/dist/layer2/auth-antipatterns.js.map +1 -0
  89. package/dist/layer2/byok-patterns.d.ts +12 -0
  90. package/dist/layer2/byok-patterns.d.ts.map +1 -0
  91. package/dist/layer2/byok-patterns.js +299 -0
  92. package/dist/layer2/byok-patterns.js.map +1 -0
  93. package/dist/layer2/dangerous-functions.d.ts +7 -0
  94. package/dist/layer2/dangerous-functions.d.ts.map +1 -0
  95. package/dist/layer2/dangerous-functions.js +1375 -0
  96. package/dist/layer2/dangerous-functions.js.map +1 -0
  97. package/dist/layer2/data-exposure.d.ts +16 -0
  98. package/dist/layer2/data-exposure.d.ts.map +1 -0
  99. package/dist/layer2/data-exposure.js +279 -0
  100. package/dist/layer2/data-exposure.js.map +1 -0
  101. package/dist/layer2/framework-checks.d.ts +7 -0
  102. package/dist/layer2/framework-checks.d.ts.map +1 -0
  103. package/dist/layer2/framework-checks.js +388 -0
  104. package/dist/layer2/framework-checks.js.map +1 -0
  105. package/dist/layer2/index.d.ts +58 -0
  106. package/dist/layer2/index.d.ts.map +1 -0
  107. package/dist/layer2/index.js +380 -0
  108. package/dist/layer2/index.js.map +1 -0
  109. package/dist/layer2/logic-gates.d.ts +7 -0
  110. package/dist/layer2/logic-gates.d.ts.map +1 -0
  111. package/dist/layer2/logic-gates.js +182 -0
  112. package/dist/layer2/logic-gates.js.map +1 -0
  113. package/dist/layer2/risky-imports.d.ts +7 -0
  114. package/dist/layer2/risky-imports.d.ts.map +1 -0
  115. package/dist/layer2/risky-imports.js +161 -0
  116. package/dist/layer2/risky-imports.js.map +1 -0
  117. package/dist/layer2/variables.d.ts +8 -0
  118. package/dist/layer2/variables.d.ts.map +1 -0
  119. package/dist/layer2/variables.js +152 -0
  120. package/dist/layer2/variables.js.map +1 -0
  121. package/dist/layer3/anthropic.d.ts +83 -0
  122. package/dist/layer3/anthropic.d.ts.map +1 -0
  123. package/dist/layer3/anthropic.js +1745 -0
  124. package/dist/layer3/anthropic.js.map +1 -0
  125. package/dist/layer3/index.d.ts +24 -0
  126. package/dist/layer3/index.d.ts.map +1 -0
  127. package/dist/layer3/index.js +119 -0
  128. package/dist/layer3/index.js.map +1 -0
  129. package/dist/layer3/openai.d.ts +25 -0
  130. package/dist/layer3/openai.d.ts.map +1 -0
  131. package/dist/layer3/openai.js +238 -0
  132. package/dist/layer3/openai.js.map +1 -0
  133. package/dist/layer3/package-check.d.ts +63 -0
  134. package/dist/layer3/package-check.d.ts.map +1 -0
  135. package/dist/layer3/package-check.js +508 -0
  136. package/dist/layer3/package-check.js.map +1 -0
  137. package/dist/modes/incremental.d.ts +66 -0
  138. package/dist/modes/incremental.d.ts.map +1 -0
  139. package/dist/modes/incremental.js +200 -0
  140. package/dist/modes/incremental.js.map +1 -0
  141. package/dist/tiers.d.ts +125 -0
  142. package/dist/tiers.d.ts.map +1 -0
  143. package/dist/tiers.js +234 -0
  144. package/dist/tiers.js.map +1 -0
  145. package/dist/types.d.ts +175 -0
  146. package/dist/types.d.ts.map +1 -0
  147. package/dist/types.js +50 -0
  148. package/dist/types.js.map +1 -0
  149. package/dist/utils/auth-helper-detector.d.ts +56 -0
  150. package/dist/utils/auth-helper-detector.d.ts.map +1 -0
  151. package/dist/utils/auth-helper-detector.js +360 -0
  152. package/dist/utils/auth-helper-detector.js.map +1 -0
  153. package/dist/utils/context-helpers.d.ts +96 -0
  154. package/dist/utils/context-helpers.d.ts.map +1 -0
  155. package/dist/utils/context-helpers.js +493 -0
  156. package/dist/utils/context-helpers.js.map +1 -0
  157. package/dist/utils/diff-detector.d.ts +53 -0
  158. package/dist/utils/diff-detector.d.ts.map +1 -0
  159. package/dist/utils/diff-detector.js +104 -0
  160. package/dist/utils/diff-detector.js.map +1 -0
  161. package/dist/utils/diff-parser.d.ts +80 -0
  162. package/dist/utils/diff-parser.d.ts.map +1 -0
  163. package/dist/utils/diff-parser.js +202 -0
  164. package/dist/utils/diff-parser.js.map +1 -0
  165. package/dist/utils/imported-auth-detector.d.ts +37 -0
  166. package/dist/utils/imported-auth-detector.d.ts.map +1 -0
  167. package/dist/utils/imported-auth-detector.js +251 -0
  168. package/dist/utils/imported-auth-detector.js.map +1 -0
  169. package/dist/utils/middleware-detector.d.ts +55 -0
  170. package/dist/utils/middleware-detector.d.ts.map +1 -0
  171. package/dist/utils/middleware-detector.js +260 -0
  172. package/dist/utils/middleware-detector.js.map +1 -0
  173. package/dist/utils/oauth-flow-detector.d.ts +41 -0
  174. package/dist/utils/oauth-flow-detector.d.ts.map +1 -0
  175. package/dist/utils/oauth-flow-detector.js +202 -0
  176. package/dist/utils/oauth-flow-detector.js.map +1 -0
  177. package/dist/utils/path-exclusions.d.ts +55 -0
  178. package/dist/utils/path-exclusions.d.ts.map +1 -0
  179. package/dist/utils/path-exclusions.js +222 -0
  180. package/dist/utils/path-exclusions.js.map +1 -0
  181. package/dist/utils/project-context-builder.d.ts +119 -0
  182. package/dist/utils/project-context-builder.d.ts.map +1 -0
  183. package/dist/utils/project-context-builder.js +534 -0
  184. package/dist/utils/project-context-builder.js.map +1 -0
  185. package/dist/utils/registry-clients.d.ts +93 -0
  186. package/dist/utils/registry-clients.d.ts.map +1 -0
  187. package/dist/utils/registry-clients.js +273 -0
  188. package/dist/utils/registry-clients.js.map +1 -0
  189. package/dist/utils/trpc-analyzer.d.ts +78 -0
  190. package/dist/utils/trpc-analyzer.d.ts.map +1 -0
  191. package/dist/utils/trpc-analyzer.js +297 -0
  192. package/dist/utils/trpc-analyzer.js.map +1 -0
  193. package/package.json +45 -0
  194. package/src/__tests__/benchmark/fixtures/false-positives.ts +227 -0
  195. package/src/__tests__/benchmark/fixtures/index.ts +68 -0
  196. package/src/__tests__/benchmark/fixtures/layer1/config-audit.ts +364 -0
  197. package/src/__tests__/benchmark/fixtures/layer1/hardcoded-secrets.ts +173 -0
  198. package/src/__tests__/benchmark/fixtures/layer1/high-entropy.ts +234 -0
  199. package/src/__tests__/benchmark/fixtures/layer1/index.ts +31 -0
  200. package/src/__tests__/benchmark/fixtures/layer1/sensitive-urls.ts +90 -0
  201. package/src/__tests__/benchmark/fixtures/layer1/weak-crypto.ts +197 -0
  202. package/src/__tests__/benchmark/fixtures/layer2/ai-agent-tools.ts +170 -0
  203. package/src/__tests__/benchmark/fixtures/layer2/ai-endpoint-protection.ts +418 -0
  204. package/src/__tests__/benchmark/fixtures/layer2/ai-execution-sinks.ts +189 -0
  205. package/src/__tests__/benchmark/fixtures/layer2/ai-fingerprinting.ts +316 -0
  206. package/src/__tests__/benchmark/fixtures/layer2/ai-prompt-hygiene.ts +178 -0
  207. package/src/__tests__/benchmark/fixtures/layer2/ai-rag-safety.ts +184 -0
  208. package/src/__tests__/benchmark/fixtures/layer2/ai-schema-validation.ts +434 -0
  209. package/src/__tests__/benchmark/fixtures/layer2/auth-antipatterns.ts +159 -0
  210. package/src/__tests__/benchmark/fixtures/layer2/byok-patterns.ts +112 -0
  211. package/src/__tests__/benchmark/fixtures/layer2/dangerous-functions.ts +246 -0
  212. package/src/__tests__/benchmark/fixtures/layer2/data-exposure.ts +168 -0
  213. package/src/__tests__/benchmark/fixtures/layer2/framework-checks.ts +346 -0
  214. package/src/__tests__/benchmark/fixtures/layer2/index.ts +67 -0
  215. package/src/__tests__/benchmark/fixtures/layer2/injection-vulnerabilities.ts +239 -0
  216. package/src/__tests__/benchmark/fixtures/layer2/logic-gates.ts +246 -0
  217. package/src/__tests__/benchmark/fixtures/layer2/risky-imports.ts +231 -0
  218. package/src/__tests__/benchmark/fixtures/layer2/variables.ts +167 -0
  219. package/src/__tests__/benchmark/index.ts +29 -0
  220. package/src/__tests__/benchmark/run-benchmark.ts +144 -0
  221. package/src/__tests__/benchmark/run-depth-validation.ts +206 -0
  222. package/src/__tests__/benchmark/run-real-world-test.ts +243 -0
  223. package/src/__tests__/benchmark/security-benchmark-script.ts +1737 -0
  224. package/src/__tests__/benchmark/tier-integration-script.ts +177 -0
  225. package/src/__tests__/benchmark/types.ts +144 -0
  226. package/src/__tests__/benchmark/utils/test-runner.ts +475 -0
  227. package/src/__tests__/regression/known-false-positives.test.ts +467 -0
  228. package/src/__tests__/snapshots/__snapshots__/scan-depth.test.ts.snap +178 -0
  229. package/src/__tests__/snapshots/scan-depth.test.ts +258 -0
  230. package/src/__tests__/validation/analyze-results.ts +542 -0
  231. package/src/__tests__/validation/extract-for-triage.ts +146 -0
  232. package/src/__tests__/validation/fp-deep-analysis.ts +327 -0
  233. package/src/__tests__/validation/run-validation.ts +364 -0
  234. package/src/__tests__/validation/triage-template.md +132 -0
  235. package/src/formatters/cli-terminal.ts +446 -0
  236. package/src/formatters/github-comment.ts +382 -0
  237. package/src/formatters/grouping.ts +190 -0
  238. package/src/formatters/index.ts +47 -0
  239. package/src/formatters/vscode-diagnostic.ts +243 -0
  240. package/src/index.ts +823 -0
  241. package/src/layer1/comments.ts +218 -0
  242. package/src/layer1/config-audit.ts +289 -0
  243. package/src/layer1/entropy.ts +583 -0
  244. package/src/layer1/file-flags.ts +127 -0
  245. package/src/layer1/index.ts +181 -0
  246. package/src/layer1/patterns.ts +516 -0
  247. package/src/layer1/urls.ts +334 -0
  248. package/src/layer1/weak-crypto.ts +328 -0
  249. package/src/layer2/ai-agent-tools.ts +601 -0
  250. package/src/layer2/ai-endpoint-protection.ts +387 -0
  251. package/src/layer2/ai-execution-sinks.ts +580 -0
  252. package/src/layer2/ai-fingerprinting.ts +758 -0
  253. package/src/layer2/ai-prompt-hygiene.ts +411 -0
  254. package/src/layer2/ai-rag-safety.ts +511 -0
  255. package/src/layer2/ai-schema-validation.ts +421 -0
  256. package/src/layer2/auth-antipatterns.ts +394 -0
  257. package/src/layer2/byok-patterns.ts +336 -0
  258. package/src/layer2/dangerous-functions.ts +1563 -0
  259. package/src/layer2/data-exposure.ts +315 -0
  260. package/src/layer2/framework-checks.ts +433 -0
  261. package/src/layer2/index.ts +473 -0
  262. package/src/layer2/logic-gates.ts +206 -0
  263. package/src/layer2/risky-imports.ts +186 -0
  264. package/src/layer2/variables.ts +166 -0
  265. package/src/layer3/anthropic.ts +2030 -0
  266. package/src/layer3/index.ts +130 -0
  267. package/src/layer3/package-check.ts +604 -0
  268. package/src/modes/incremental.ts +293 -0
  269. package/src/tiers.ts +318 -0
  270. package/src/types.ts +284 -0
  271. package/src/utils/auth-helper-detector.ts +443 -0
  272. package/src/utils/context-helpers.ts +535 -0
  273. package/src/utils/diff-detector.ts +135 -0
  274. package/src/utils/diff-parser.ts +272 -0
  275. package/src/utils/imported-auth-detector.ts +320 -0
  276. package/src/utils/middleware-detector.ts +333 -0
  277. package/src/utils/oauth-flow-detector.ts +246 -0
  278. package/src/utils/path-exclusions.ts +266 -0
  279. package/src/utils/project-context-builder.ts +707 -0
  280. package/src/utils/registry-clients.ts +351 -0
  281. package/src/utils/trpc-analyzer.ts +382 -0
@@ -0,0 +1,175 @@
1
+ /**
2
+ * Scanner Types and Interfaces
3
+ * Defines the core data structures for the security scanning engine
4
+ */
5
+ export type VulnerabilitySeverity = 'critical' | 'high' | 'medium' | 'low' | 'info';
6
+ export type VulnerabilityCategory = 'hardcoded_secret' | 'high_entropy_string' | 'sensitive_variable' | 'security_bypass' | 'dangerous_function' | 'sql_injection' | 'xss' | 'command_injection' | 'insecure_config' | 'missing_auth' | 'suspicious_package' | 'cors_misconfiguration' | 'root_container' | 'dangerous_file' | 'ai_pattern' | 'sensitive_url' | 'weak_crypto' | 'data_exposure' | 'ai_prompt_injection' | 'ai_unsafe_execution' | 'ai_overpermissive_tool' | 'ai_rag_exfiltration' | 'ai_endpoint_unprotected' | 'ai_schema_mismatch';
7
+ export type ValidationStatus = 'confirmed' | 'downgraded' | 'dismissed' | 'not_validated';
8
+ export interface Vulnerability {
9
+ id: string;
10
+ filePath: string;
11
+ lineNumber: number;
12
+ lineContent: string;
13
+ severity: VulnerabilitySeverity;
14
+ category: VulnerabilityCategory;
15
+ title: string;
16
+ description: string;
17
+ suggestedFix?: string;
18
+ confidence: 'high' | 'medium' | 'low';
19
+ layer: 1 | 2 | 3;
20
+ requiresAIValidation?: boolean;
21
+ validatedByAI?: boolean;
22
+ validationStatus?: ValidationStatus;
23
+ validationNotes?: string;
24
+ originalSeverity?: VulnerabilitySeverity;
25
+ }
26
+ export interface ScanFile {
27
+ path: string;
28
+ content: string;
29
+ language: string;
30
+ size: number;
31
+ }
32
+ export interface SeverityCounts {
33
+ critical: number;
34
+ high: number;
35
+ medium: number;
36
+ low: number;
37
+ info: number;
38
+ }
39
+ export type CategoryCounts = Partial<Record<VulnerabilityCategory, number>>;
40
+ export interface ScanResult {
41
+ repoName: string;
42
+ repoUrl: string;
43
+ branch: string;
44
+ filesScanned: number;
45
+ filesSkipped: number;
46
+ vulnerabilities: Vulnerability[];
47
+ severityCounts: SeverityCounts;
48
+ categoryCounts: CategoryCounts;
49
+ hasBlockingIssues: boolean;
50
+ scanDuration: number;
51
+ timestamp: string;
52
+ validationStats?: {
53
+ totalFindings: number;
54
+ validatedFindings: number;
55
+ confirmedFindings: number;
56
+ dismissedFindings: number;
57
+ downgradedFindings: number;
58
+ autoDismissedFindings: number;
59
+ estimatedInputTokens: number;
60
+ estimatedOutputTokens: number;
61
+ estimatedCost: number;
62
+ apiCalls: number;
63
+ cacheCreationTokens: number;
64
+ cacheReadTokens: number;
65
+ cacheHitRate: number;
66
+ };
67
+ }
68
+ export interface ScanProgress {
69
+ status: 'fetching' | 'scanning_layer1' | 'scanning_layer2' | 'scanning_layer3' | 'complete' | 'failed';
70
+ currentFile?: string;
71
+ filesProcessed: number;
72
+ totalFiles: number;
73
+ vulnerabilitiesFound: number;
74
+ }
75
+ export interface SecretPattern {
76
+ name: string;
77
+ pattern: RegExp;
78
+ severity: VulnerabilitySeverity;
79
+ description: string;
80
+ }
81
+ export interface ConfigRule {
82
+ name: string;
83
+ filePatterns: string[];
84
+ check: (content: string, filePath: string) => ConfigViolation[];
85
+ }
86
+ export interface ConfigViolation {
87
+ line: number;
88
+ lineContent: string;
89
+ message: string;
90
+ severity: VulnerabilitySeverity;
91
+ }
92
+ export interface SensitiveVariablePattern {
93
+ pattern: RegExp;
94
+ severity: VulnerabilitySeverity;
95
+ description: string;
96
+ }
97
+ export interface AIAnalysisRequest {
98
+ filePath: string;
99
+ content: string;
100
+ context: string;
101
+ }
102
+ export interface AIFinding {
103
+ lineNumber: number;
104
+ lineContent: string;
105
+ severity: VulnerabilitySeverity;
106
+ category: VulnerabilityCategory;
107
+ title: string;
108
+ description: string;
109
+ suggestedFix: string;
110
+ }
111
+ export declare const SCANNABLE_EXTENSIONS: string[];
112
+ export declare const SPECIAL_FILES: string[];
113
+ export declare const MAX_FILE_SIZE: number;
114
+ /**
115
+ * Scan mode determines the depth and cost of the scan
116
+ *
117
+ * - full: Complete scan with AI validation on all files (initial onboarding, deep audits)
118
+ * - incremental: Focused scan on changed files only (CI/CD, fast feedback)
119
+ */
120
+ export type ScanMode = 'full' | 'incremental';
121
+ /**
122
+ * Scan depth controls AI usage independent of full vs incremental mode
123
+ *
124
+ * - cheap: Layer 1 + Layer 2 only. No AI validation, no Layer 3.
125
+ * Only Tier A (core) findings are surfaced.
126
+ * Target: <5s for typical PR scans.
127
+ *
128
+ * - validated: Layer 1 + Layer 2 + AI validation on selected findings. No Layer 3.
129
+ * Tier A is surfaced directly, Tier B goes through AI validation.
130
+ * Target: <15s with <10 AI calls.
131
+ *
132
+ * - deep: Layer 1 + Layer 2 + AI validation + Layer 3 semantic analysis.
133
+ * Full analysis for initial onboarding or deep security audits.
134
+ * Target: Complete thoroughness, cost secondary.
135
+ *
136
+ * ## Workflow Profile Recommendations:
137
+ *
138
+ * | Workflow | Default Depth | Rationale |
139
+ * |----------------|---------------|--------------------------------------|
140
+ * | GitHub PR | cheap | Fast feedback, high-signal findings |
141
+ * | VS Code | validated | Interactive, balance depth + speed |
142
+ * | CLI (default) | cheap | Fast local scans |
143
+ * | CLI --deep | deep | Thorough analysis when requested |
144
+ * | Onboarding | deep | Full picture on first scan |
145
+ */
146
+ export type ScanDepth = 'cheap' | 'validated' | 'deep';
147
+ export interface ScanModeConfig {
148
+ /** The scan mode */
149
+ mode: ScanMode;
150
+ /** For incremental scans: paths of changed files */
151
+ changedFiles?: string[];
152
+ /** For incremental scans: base commit/branch to diff against */
153
+ baseBranch?: string;
154
+ /** Whether to skip AI validation entirely (for very fast scans) */
155
+ skipAIValidation?: boolean;
156
+ /** Whether to skip Layer 3 deep analysis (reduces cost) */
157
+ skipLayer3?: boolean;
158
+ /** Maximum files to send to AI validation (cost control) */
159
+ maxAIValidationFiles?: number;
160
+ /** Maximum files for Layer 3 analysis (cost control) */
161
+ maxLayer3Files?: number;
162
+ /** Scan depth mode (cheap/validated/deep) - controls AI usage */
163
+ scanDepth?: ScanDepth;
164
+ /** Whether to exclude test files from scanning (default: true) */
165
+ excludeTestFiles?: boolean;
166
+ /** Whether to exclude seed files from scanning (default: true) */
167
+ excludeSeedFiles?: boolean;
168
+ /** Custom file path patterns to exclude (glob format) */
169
+ customExclusions?: string[];
170
+ }
171
+ /**
172
+ * Default configurations for each scan mode
173
+ */
174
+ export declare const SCAN_MODE_DEFAULTS: Record<ScanMode, Partial<ScanModeConfig>>;
175
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ /**
3
+ * Scanner Types and Interfaces
4
+ * Defines the core data structures for the security scanning engine
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.SCAN_MODE_DEFAULTS = exports.MAX_FILE_SIZE = exports.SPECIAL_FILES = exports.SCANNABLE_EXTENSIONS = void 0;
8
+ // Supported file extensions for scanning
9
+ exports.SCANNABLE_EXTENSIONS = [
10
+ '.js', '.jsx', '.ts', '.tsx', '.mjs', '.cjs',
11
+ '.py', '.rb', '.php', '.go', '.java', '.cs',
12
+ '.env', '.yaml', '.yml', '.json', '.toml',
13
+ '.dockerfile', '.sh', '.bash',
14
+ ];
15
+ // Files to always scan regardless of extension
16
+ exports.SPECIAL_FILES = [
17
+ 'Dockerfile',
18
+ 'docker-compose.yml',
19
+ 'docker-compose.yaml',
20
+ '.env',
21
+ '.env.local',
22
+ '.env.production',
23
+ '.env.development',
24
+ 'package.json',
25
+ 'requirements.txt',
26
+ 'Gemfile',
27
+ 'go.mod',
28
+ ];
29
+ // Max file size to scan (50KB as per PRD)
30
+ exports.MAX_FILE_SIZE = 50 * 1024;
31
+ /**
32
+ * Default configurations for each scan mode
33
+ */
34
+ exports.SCAN_MODE_DEFAULTS = {
35
+ full: {
36
+ mode: 'full',
37
+ skipAIValidation: false,
38
+ skipLayer3: false,
39
+ maxAIValidationFiles: 50,
40
+ maxLayer3Files: 15,
41
+ },
42
+ incremental: {
43
+ mode: 'incremental',
44
+ skipAIValidation: false,
45
+ skipLayer3: true, // Skip expensive Layer 3 for incremental
46
+ maxAIValidationFiles: 20,
47
+ maxLayer3Files: 5,
48
+ },
49
+ };
50
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAkKH,yCAAyC;AAC5B,QAAA,oBAAoB,GAAG;IAClC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAC5C,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK;IAC3C,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO;IACzC,aAAa,EAAE,KAAK,EAAE,OAAO;CAC9B,CAAA;AAED,+CAA+C;AAClC,QAAA,aAAa,GAAG;IAC3B,YAAY;IACZ,oBAAoB;IACpB,qBAAqB;IACrB,MAAM;IACN,YAAY;IACZ,iBAAiB;IACjB,kBAAkB;IAClB,cAAc;IACd,kBAAkB;IAClB,SAAS;IACT,QAAQ;CACT,CAAA;AAED,0CAA0C;AAC7B,QAAA,aAAa,GAAG,EAAE,GAAG,IAAI,CAAA;AA4EtC;;GAEG;AACU,QAAA,kBAAkB,GAA8C;IAC3E,IAAI,EAAE;QACJ,IAAI,EAAE,MAAM;QACZ,gBAAgB,EAAE,KAAK;QACvB,UAAU,EAAE,KAAK;QACjB,oBAAoB,EAAE,EAAE;QACxB,cAAc,EAAE,EAAE;KACnB;IACD,WAAW,EAAE;QACX,IAAI,EAAE,aAAa;QACnB,gBAAgB,EAAE,KAAK;QACvB,UAAU,EAAE,IAAI,EAAE,yCAAyC;QAC3D,oBAAoB,EAAE,EAAE;QACxB,cAAc,EAAE,CAAC;KAClB;CACF,CAAA"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Auth Helper Detector
3
+ *
4
+ * Detects authentication helper functions that throw on missing auth,
5
+ * return non-null types, and provide security guarantees.
6
+ *
7
+ * When these helpers are called, subsequent code is GUARANTEED to have
8
+ * an authenticated user - no additional `if (!userId)` checks are needed.
9
+ */
10
+ import type { ScanFile } from '../types';
11
+ export interface AuthHelper {
12
+ /** Name of the helper function */
13
+ name: string;
14
+ /** File where it's defined (if detected) */
15
+ definedIn?: string;
16
+ /** Whether it throws on missing auth (vs returning null) */
17
+ throwsOnMissing: boolean;
18
+ /** Return type if detected (e.g., 'string', 'User', 'Promise<string>') */
19
+ returnType?: string;
20
+ /** Whether return type is non-null */
21
+ returnsNonNull: boolean;
22
+ /** Pattern that matches calls to this helper */
23
+ callPattern: RegExp;
24
+ }
25
+ export interface AuthHelperContext {
26
+ /** All detected auth helpers */
27
+ helpers: AuthHelper[];
28
+ /** Whether the project has any throwing auth helpers */
29
+ hasThrowingHelpers: boolean;
30
+ /** Summary for AI validation */
31
+ summary: string;
32
+ }
33
+ /**
34
+ * Detect auth helper functions in the codebase
35
+ */
36
+ export declare function detectAuthHelpers(files: ScanFile[]): AuthHelperContext;
37
+ /**
38
+ * Check if a code line uses a throwing auth helper
39
+ * Returns the helper if found, undefined otherwise
40
+ */
41
+ export declare function usesThrowingAuthHelper(lineContent: string, surroundingContent: string, helpers: AuthHelper[]): AuthHelper | undefined;
42
+ /**
43
+ * Check if a file has auth helper calls before a given line
44
+ * This indicates the code after the call is in authenticated context
45
+ */
46
+ export declare function hasAuthHelperCallBefore(content: string, lineNumber: number, helpers: AuthHelper[]): {
47
+ hasCall: boolean;
48
+ helper?: AuthHelper;
49
+ callLine?: number;
50
+ };
51
+ /**
52
+ * Check if code suggests user ID is already validated
53
+ * Detects patterns like: const userId = await getCurrentUserId()
54
+ */
55
+ export declare function isUserIdAlreadyValidated(content: string, lineNumber: number, helpers: AuthHelper[]): boolean;
56
+ //# sourceMappingURL=auth-helper-detector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-helper-detector.d.ts","sourceRoot":"","sources":["../../src/utils/auth-helper-detector.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAMxC,MAAM,WAAW,UAAU;IACzB,kCAAkC;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,4CAA4C;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,4DAA4D;IAC5D,eAAe,EAAE,OAAO,CAAA;IACxB,0EAA0E;IAC1E,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,sCAAsC;IACtC,cAAc,EAAE,OAAO,CAAA;IACvB,gDAAgD;IAChD,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,gCAAgC;IAChC,OAAO,EAAE,UAAU,EAAE,CAAA;IACrB,wDAAwD;IACxD,kBAAkB,EAAE,OAAO,CAAA;IAC3B,gCAAgC;IAChC,OAAO,EAAE,MAAM,CAAA;CAChB;AA6ED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,iBAAiB,CA6CtE;AAuFD;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,MAAM,EACnB,kBAAkB,EAAE,MAAM,EAC1B,OAAO,EAAE,UAAU,EAAE,GACpB,UAAU,GAAG,SAAS,CAYxB;AA+BD;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,UAAU,EAAE,GACpB;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,CAkE9D;AAoCD;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,UAAU,EAAE,GACpB,OAAO,CAuBT"}
@@ -0,0 +1,360 @@
1
+ "use strict";
2
+ /**
3
+ * Auth Helper Detector
4
+ *
5
+ * Detects authentication helper functions that throw on missing auth,
6
+ * return non-null types, and provide security guarantees.
7
+ *
8
+ * When these helpers are called, subsequent code is GUARANTEED to have
9
+ * an authenticated user - no additional `if (!userId)` checks are needed.
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.detectAuthHelpers = detectAuthHelpers;
13
+ exports.usesThrowingAuthHelper = usesThrowingAuthHelper;
14
+ exports.hasAuthHelperCallBefore = hasAuthHelperCallBefore;
15
+ exports.isUserIdAlreadyValidated = isUserIdAlreadyValidated;
16
+ // ============================================================================
17
+ // Well-Known Auth Helper Patterns
18
+ // ============================================================================
19
+ /**
20
+ * Common patterns for auth helpers that THROW on missing auth
21
+ * These are functions that guarantee authenticated context after call
22
+ */
23
+ const THROWING_AUTH_HELPER_PATTERNS = [
24
+ // Generic patterns
25
+ {
26
+ namePattern: /^(get|fetch|require|ensure)(Current)?(User|UserId|Auth|Session|Principal)(Id)?$/i,
27
+ callPattern: /\b(get|fetch|require|ensure)(Current)?(User|UserId|Auth|Session|Principal)(Id)?\s*\(/gi,
28
+ description: 'Auth helper that retrieves authenticated user',
29
+ },
30
+ // Clerk patterns
31
+ {
32
+ namePattern: /^auth$/,
33
+ callPattern: /\bauth\s*\(\s*\)/gi,
34
+ description: 'Clerk auth() helper',
35
+ },
36
+ {
37
+ namePattern: /^currentUser$/,
38
+ callPattern: /\bcurrentUser\s*\(\s*\)/gi,
39
+ description: 'Clerk currentUser() helper',
40
+ },
41
+ // NextAuth patterns
42
+ {
43
+ namePattern: /^getServerSession$/,
44
+ callPattern: /\bgetServerSession\s*\(/gi,
45
+ description: 'NextAuth getServerSession()',
46
+ },
47
+ {
48
+ namePattern: /^getSession$/,
49
+ callPattern: /\bgetSession\s*\(/gi,
50
+ description: 'Session helper',
51
+ },
52
+ // Supabase patterns
53
+ {
54
+ namePattern: /^getUser$/,
55
+ callPattern: /\bsupabase\.auth\.getUser\s*\(/gi,
56
+ description: 'Supabase getUser()',
57
+ },
58
+ ];
59
+ /**
60
+ * Patterns that indicate a function THROWS on missing auth
61
+ */
62
+ const THROWING_INDICATORS = [
63
+ /throw\s+new\s+(Error|UnauthorizedError|AuthError|HttpException)/i,
64
+ /throw\s+.*401/i,
65
+ /throw\s+.*unauthorized/i,
66
+ /throw\s+.*unauthenticated/i,
67
+ /return\s+.*401/i,
68
+ /NextResponse\.json\s*\([^)]*401/i,
69
+ /res\.status\s*\(\s*401\s*\)/i,
70
+ /redirect\s*\(\s*['"`].*login/i,
71
+ ];
72
+ /**
73
+ * Patterns that indicate NON-NULL return type
74
+ */
75
+ const NON_NULL_RETURN_PATTERNS = [
76
+ /:\s*Promise<string>/i, // : Promise<string>
77
+ /:\s*string(?!\s*\|)/i, // : string (not string | null)
78
+ /:\s*Promise<User>/i, // : Promise<User>
79
+ /:\s*User(?!\s*\|)/i, // : User (not User | null)
80
+ /:\s*Promise<\w+>(?!\s*\|)/i, // : Promise<SomeType>
81
+ /\w+!$/i, // Non-null assertion in return
82
+ ];
83
+ // ============================================================================
84
+ // Detection Functions
85
+ // ============================================================================
86
+ /**
87
+ * Detect auth helper functions in the codebase
88
+ */
89
+ function detectAuthHelpers(files) {
90
+ const helpers = [];
91
+ const detectedNames = new Set();
92
+ // First pass: find auth helper definitions
93
+ for (const file of files) {
94
+ // Skip non-code files
95
+ if (!/\.(ts|tsx|js|jsx)$/i.test(file.path))
96
+ continue;
97
+ // Look for function definitions that look like auth helpers
98
+ const functionMatches = findAuthHelperDefinitions(file.content, file.path);
99
+ for (const helper of functionMatches) {
100
+ if (!detectedNames.has(helper.name)) {
101
+ detectedNames.add(helper.name);
102
+ helpers.push(helper);
103
+ }
104
+ }
105
+ }
106
+ // Add well-known helpers if not already detected
107
+ for (const pattern of THROWING_AUTH_HELPER_PATTERNS) {
108
+ const nameMatch = pattern.namePattern.source.replace(/[\^\$\\b]/g, '');
109
+ if (!detectedNames.has(nameMatch)) {
110
+ // Check if this pattern is used in any file
111
+ const isUsed = files.some(f => pattern.callPattern.test(f.content));
112
+ if (isUsed) {
113
+ helpers.push({
114
+ name: nameMatch,
115
+ throwsOnMissing: true, // Assume throwing for well-known patterns
116
+ returnsNonNull: true,
117
+ callPattern: pattern.callPattern,
118
+ });
119
+ }
120
+ }
121
+ }
122
+ // Generate summary
123
+ const throwingHelpers = helpers.filter(h => h.throwsOnMissing);
124
+ const summary = generateAuthHelperSummary(helpers);
125
+ return {
126
+ helpers,
127
+ hasThrowingHelpers: throwingHelpers.length > 0,
128
+ summary,
129
+ };
130
+ }
131
+ /**
132
+ * Find auth helper function definitions in a file
133
+ */
134
+ function findAuthHelperDefinitions(content, filePath) {
135
+ const helpers = [];
136
+ const lines = content.split('\n');
137
+ // Patterns for function definitions
138
+ const funcDefPatterns = [
139
+ // async function getCurrentUserId(): Promise<string> { ... throw
140
+ /(?:export\s+)?(?:async\s+)?function\s+(\w+)\s*\([^)]*\)\s*(?::\s*([^{]+))?\s*\{/gi,
141
+ // const getCurrentUserId = async (): Promise<string> => { ... throw
142
+ /(?:export\s+)?const\s+(\w+)\s*=\s*(?:async\s*)?\([^)]*\)\s*(?::\s*([^=]+))?\s*=>/gi,
143
+ ];
144
+ for (let i = 0; i < lines.length; i++) {
145
+ const line = lines[i];
146
+ for (const pattern of funcDefPatterns) {
147
+ pattern.lastIndex = 0;
148
+ const match = pattern.exec(line);
149
+ if (!match)
150
+ continue;
151
+ const funcName = match[1];
152
+ const returnType = match[2]?.trim();
153
+ // Check if this looks like an auth helper by name
154
+ const isAuthHelperName = THROWING_AUTH_HELPER_PATTERNS.some(p => p.namePattern.test(funcName));
155
+ if (!isAuthHelperName)
156
+ continue;
157
+ // Look ahead for throwing patterns and return type
158
+ const functionBody = extractFunctionBody(lines, i);
159
+ const throwsOnMissing = THROWING_INDICATORS.some(p => p.test(functionBody));
160
+ const returnsNonNull = returnType
161
+ ? NON_NULL_RETURN_PATTERNS.some(p => p.test(`: ${returnType}`))
162
+ : false;
163
+ // Create call pattern for this helper
164
+ const callPattern = new RegExp(`\\b${escapeRegex(funcName)}\\s*\\(`, 'gi');
165
+ helpers.push({
166
+ name: funcName,
167
+ definedIn: filePath,
168
+ throwsOnMissing,
169
+ returnType,
170
+ returnsNonNull: returnsNonNull || throwsOnMissing, // If it throws, the return is non-null
171
+ callPattern,
172
+ });
173
+ }
174
+ }
175
+ return helpers;
176
+ }
177
+ /**
178
+ * Extract function body for analysis (up to closing brace)
179
+ */
180
+ function extractFunctionBody(lines, startLine, maxLines = 50) {
181
+ let braceCount = 0;
182
+ let started = false;
183
+ const bodyLines = [];
184
+ for (let i = startLine; i < Math.min(lines.length, startLine + maxLines); i++) {
185
+ const line = lines[i];
186
+ bodyLines.push(line);
187
+ for (const char of line) {
188
+ if (char === '{') {
189
+ braceCount++;
190
+ started = true;
191
+ }
192
+ else if (char === '}') {
193
+ braceCount--;
194
+ if (started && braceCount === 0) {
195
+ return bodyLines.join('\n');
196
+ }
197
+ }
198
+ }
199
+ }
200
+ return bodyLines.join('\n');
201
+ }
202
+ /**
203
+ * Check if a code line uses a throwing auth helper
204
+ * Returns the helper if found, undefined otherwise
205
+ */
206
+ function usesThrowingAuthHelper(lineContent, surroundingContent, helpers) {
207
+ // Check if any throwing helper is called in the surrounding context
208
+ const throwingHelpers = helpers.filter(h => h.throwsOnMissing);
209
+ for (const helper of throwingHelpers) {
210
+ helper.callPattern.lastIndex = 0;
211
+ if (helper.callPattern.test(surroundingContent)) {
212
+ return helper;
213
+ }
214
+ }
215
+ return undefined;
216
+ }
217
+ /**
218
+ * Well-known auth helper call patterns - used as fallback when no helpers are detected
219
+ * These patterns are common across frameworks and should be recognized even without project analysis
220
+ */
221
+ const WELL_KNOWN_AUTH_CALL_PATTERNS = [
222
+ // Generic throwing auth patterns
223
+ { pattern: /\bgetCurrentUserId\s*\(/i, name: 'getCurrentUserId' },
224
+ { pattern: /\bgetCurrentUser\s*\(/i, name: 'getCurrentUser' },
225
+ { pattern: /\brequireAuth\s*\(/i, name: 'requireAuth' },
226
+ { pattern: /\brequireUser\s*\(/i, name: 'requireUser' },
227
+ { pattern: /\bensureAuth\s*\(/i, name: 'ensureAuth' },
228
+ { pattern: /\bensureAuthenticated\s*\(/i, name: 'ensureAuthenticated' },
229
+ { pattern: /\bverifyAuth\s*\(/i, name: 'verifyAuth' },
230
+ { pattern: /\bcheckAuth\s*\(/i, name: 'checkAuth' },
231
+ { pattern: /\bvalidateAuth\s*\(/i, name: 'validateAuth' },
232
+ { pattern: /\bassertAuth\s*\(/i, name: 'assertAuth' },
233
+ { pattern: /\bgetAuth\s*\(/i, name: 'getAuth' },
234
+ { pattern: /\bfetchCurrentUser\s*\(/i, name: 'fetchCurrentUser' },
235
+ // Clerk
236
+ { pattern: /\bauth\s*\(\s*\)\.protect\s*\(/i, name: 'auth().protect' },
237
+ { pattern: /\bcurrentUser\s*\(\s*\)/i, name: 'currentUser' },
238
+ // NextAuth
239
+ { pattern: /\bgetServerSession\s*\(/i, name: 'getServerSession' },
240
+ // Supabase
241
+ { pattern: /\bsupabase\.auth\.getUser\s*\(/i, name: 'supabase.auth.getUser' },
242
+ // Destructuring pattern
243
+ { pattern: /const\s+\{\s*user\s*\}\s*=\s*await\s+auth/i, name: 'destructured auth' },
244
+ ];
245
+ /**
246
+ * Check if a file has auth helper calls before a given line
247
+ * This indicates the code after the call is in authenticated context
248
+ */
249
+ function hasAuthHelperCallBefore(content, lineNumber, helpers) {
250
+ const lines = content.split('\n');
251
+ const throwingHelpers = helpers.filter(h => h.throwsOnMissing);
252
+ // Increase search window from 30 to 100 lines for better coverage
253
+ const searchStart = Math.max(0, lineNumber - 100);
254
+ // Look backwards from the current line
255
+ for (let i = lineNumber - 1; i >= searchStart; i--) {
256
+ const line = lines[i];
257
+ // Check detected helpers first
258
+ for (const helper of throwingHelpers) {
259
+ helper.callPattern.lastIndex = 0;
260
+ if (helper.callPattern.test(line)) {
261
+ return { hasCall: true, helper, callLine: i + 1 };
262
+ }
263
+ }
264
+ // Stop at function boundaries (for module-level helpers, we still check inside function)
265
+ if (/\b(function|async function|=>|export\s+default)\b/.test(line) && /\{/.test(line)) {
266
+ // If this is the route handler definition line, continue checking inside it
267
+ if (i !== lineNumber - 1) {
268
+ break;
269
+ }
270
+ }
271
+ }
272
+ // FORWARD SEARCH: Always search forward into the function body
273
+ // This catches auth helpers called INSIDE the route handler, not just before it
274
+ // Example: export async function GET() { const userId = await getCurrentUserId(); ... }
275
+ const endLine = Math.min(lines.length, lineNumber + 30);
276
+ for (let i = lineNumber; i < endLine; i++) {
277
+ const line = lines[i];
278
+ // Check detected helpers
279
+ for (const helper of throwingHelpers) {
280
+ helper.callPattern.lastIndex = 0;
281
+ if (helper.callPattern.test(line)) {
282
+ return { hasCall: true, helper, callLine: i + 1 };
283
+ }
284
+ }
285
+ // Also check well-known patterns (fallback for single-file scans)
286
+ for (const known of WELL_KNOWN_AUTH_CALL_PATTERNS) {
287
+ if (known.pattern.test(line)) {
288
+ return {
289
+ hasCall: true,
290
+ helper: {
291
+ name: known.name,
292
+ throwsOnMissing: true,
293
+ returnsNonNull: true,
294
+ callPattern: known.pattern,
295
+ },
296
+ callLine: i + 1
297
+ };
298
+ }
299
+ }
300
+ // Stop at another function boundary (but not the current line)
301
+ if (i > lineNumber && /\bexport\s+(async\s+)?function\s+(GET|POST|PUT|DELETE|PATCH)\s*\(/i.test(line)) {
302
+ break;
303
+ }
304
+ }
305
+ return { hasCall: false };
306
+ }
307
+ /**
308
+ * Generate summary for AI validation
309
+ */
310
+ function generateAuthHelperSummary(helpers) {
311
+ if (helpers.length === 0) {
312
+ return 'No auth helper functions detected.';
313
+ }
314
+ const throwing = helpers.filter(h => h.throwsOnMissing);
315
+ const lines = [];
316
+ lines.push('### Auth Helper Functions');
317
+ lines.push('');
318
+ if (throwing.length > 0) {
319
+ lines.push('**Throwing auth helpers** (guarantee authenticated context when called):');
320
+ for (const h of throwing) {
321
+ const location = h.definedIn ? ` (defined in ${h.definedIn})` : '';
322
+ lines.push(`- \`${h.name}()\`${location}`);
323
+ }
324
+ lines.push('');
325
+ lines.push('When these helpers are called at the start of a function, subsequent code is GUARANTEED to have an authenticated user. Do NOT flag "missing auth" or suggest `if (!userId)` checks after these calls.');
326
+ }
327
+ return lines.join('\n');
328
+ }
329
+ /**
330
+ * Escape special regex characters
331
+ */
332
+ function escapeRegex(str) {
333
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
334
+ }
335
+ /**
336
+ * Check if code suggests user ID is already validated
337
+ * Detects patterns like: const userId = await getCurrentUserId()
338
+ */
339
+ function isUserIdAlreadyValidated(content, lineNumber, helpers) {
340
+ const lines = content.split('\n');
341
+ const contextStart = Math.max(0, lineNumber - 20);
342
+ const context = lines.slice(contextStart, lineNumber).join('\n');
343
+ // Check for throwing helper calls
344
+ const throwingHelpers = helpers.filter(h => h.throwsOnMissing);
345
+ for (const helper of throwingHelpers) {
346
+ helper.callPattern.lastIndex = 0;
347
+ if (helper.callPattern.test(context)) {
348
+ return true;
349
+ }
350
+ }
351
+ // Check for common validation patterns
352
+ const validationPatterns = [
353
+ /const\s+(?:userId|user_id|currentUserId)\s*=\s*await/i,
354
+ /if\s*\(\s*!(?:userId|user_id|user|session)\s*\)/i, // Already has the check
355
+ /(?:userId|user_id)\s*\|\|\s*throw/i,
356
+ /auth\(\)\.protect\(\)/i, // Clerk protect
357
+ ];
358
+ return validationPatterns.some(p => p.test(context));
359
+ }
360
+ //# sourceMappingURL=auth-helper-detector.js.map