@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,382 @@
1
+ /**
2
+ * tRPC Router Analyzer
3
+ * Analyzes tRPC router definitions to understand which procedures are protected
4
+ * by middleware (e.g., adminProcedure, protectedProcedure).
5
+ *
6
+ * This helps prevent false positives when frontend code calls protected tRPC
7
+ * procedures - the backend already handles authorization.
8
+ */
9
+
10
+ import type { ScanFile } from '../types'
11
+
12
+ export interface TRPCMiddleware {
13
+ /** Name of the middleware/procedure base */
14
+ name: string
15
+ /** Type of protection */
16
+ type: 'admin' | 'authenticated' | 'public' | 'unknown'
17
+ /** File where defined */
18
+ definedIn?: string
19
+ /** Line number where defined */
20
+ lineNumber?: number
21
+ }
22
+
23
+ export interface TRPCProcedure {
24
+ /** Procedure name (e.g., 'getUsers', 'createPost') */
25
+ name: string
26
+ /** Router this procedure belongs to */
27
+ router: string
28
+ /** Whether this procedure has auth middleware */
29
+ hasAuthMiddleware: boolean
30
+ /** Type of middleware protection */
31
+ middlewareType?: 'admin' | 'authenticated' | 'public'
32
+ /** File where defined */
33
+ definedIn?: string
34
+ }
35
+
36
+ export interface TRPCRouter {
37
+ /** Router name (e.g., 'admin', 'user', 'post') */
38
+ name: string
39
+ /** File where router is defined */
40
+ file: string
41
+ /** Procedures in this router */
42
+ procedures: string[]
43
+ /** Default middleware for this router (if any) */
44
+ defaultMiddleware?: string
45
+ /** Whether all procedures in this router are protected */
46
+ isProtected: boolean
47
+ }
48
+
49
+ export interface TRPCRouterContext {
50
+ /** All detected routers */
51
+ routers: Map<string, TRPCRouter>
52
+ /** All detected procedures with their protection status */
53
+ procedures: Map<string, TRPCProcedure>
54
+ /** All detected middleware definitions */
55
+ middlewares: Map<string, TRPCMiddleware>
56
+ /** Whether tRPC is detected in the project */
57
+ hasTRPC: boolean
58
+ }
59
+
60
+ /**
61
+ * Patterns that indicate admin-level middleware
62
+ */
63
+ const ADMIN_MIDDLEWARE_PATTERNS = [
64
+ /adminProcedure/i,
65
+ /adminMiddleware/i,
66
+ /requireAdmin/i,
67
+ /isAdmin\s*:\s*true/i,
68
+ /role\s*===?\s*['"]admin['"]/i,
69
+ /\.admin\s*\(/i,
70
+ /adminRouter/i,
71
+ /superAdminProcedure/i,
72
+ ]
73
+
74
+ /**
75
+ * Patterns that indicate authenticated middleware
76
+ */
77
+ const AUTH_MIDDLEWARE_PATTERNS = [
78
+ /protectedProcedure/i,
79
+ /authenticatedProcedure/i,
80
+ /requireAuth/i,
81
+ /isAuthenticated/i,
82
+ /privateProcedure/i,
83
+ /authedProcedure/i,
84
+ /userProcedure/i,
85
+ /loggedInProcedure/i,
86
+ ]
87
+
88
+ /**
89
+ * Patterns that indicate public/unprotected procedures
90
+ */
91
+ const PUBLIC_MIDDLEWARE_PATTERNS = [
92
+ /publicProcedure/i,
93
+ /guestProcedure/i,
94
+ /unauthenticatedProcedure/i,
95
+ /openProcedure/i,
96
+ ]
97
+
98
+ /**
99
+ * Detect tRPC usage in files
100
+ */
101
+ function detectTRPCUsage(content: string): boolean {
102
+ const trpcPatterns = [
103
+ /@trpc\/server/i,
104
+ /@trpc\/client/i,
105
+ /@trpc\/react-query/i,
106
+ /@trpc\/next/i,
107
+ /createTRPCRouter/i,
108
+ /initTRPC/i,
109
+ /trpc\.router/i,
110
+ /t\.router\s*\(/i,
111
+ /t\.procedure/i,
112
+ ]
113
+ return trpcPatterns.some(p => p.test(content))
114
+ }
115
+
116
+ /**
117
+ * Extract middleware definitions from a file
118
+ */
119
+ function extractMiddlewares(content: string, filePath: string): TRPCMiddleware[] {
120
+ const middlewares: TRPCMiddleware[] = []
121
+ const lines = content.split('\n')
122
+
123
+ // Look for procedure definitions with middleware
124
+ const procedureDefPatterns = [
125
+ /export\s+const\s+(\w+Procedure)\s*=\s*t\.procedure\.use/i,
126
+ /const\s+(\w+Procedure)\s*=\s*t\.procedure\.use/i,
127
+ /(\w+Procedure)\s*=\s*publicProcedure\.use/i,
128
+ /export\s+const\s+(\w+Procedure)\s*=/i,
129
+ ]
130
+
131
+ for (let i = 0; i < lines.length; i++) {
132
+ const line = lines[i]
133
+
134
+ for (const pattern of procedureDefPatterns) {
135
+ const match = line.match(pattern)
136
+ if (match) {
137
+ const name = match[1]
138
+ let type: 'admin' | 'authenticated' | 'public' | 'unknown' = 'unknown'
139
+
140
+ // Determine type based on name and context
141
+ if (ADMIN_MIDDLEWARE_PATTERNS.some(p => p.test(name) || p.test(line))) {
142
+ type = 'admin'
143
+ } else if (AUTH_MIDDLEWARE_PATTERNS.some(p => p.test(name) || p.test(line))) {
144
+ type = 'authenticated'
145
+ } else if (PUBLIC_MIDDLEWARE_PATTERNS.some(p => p.test(name) || p.test(line))) {
146
+ type = 'public'
147
+ }
148
+
149
+ middlewares.push({
150
+ name,
151
+ type,
152
+ definedIn: filePath,
153
+ lineNumber: i + 1,
154
+ })
155
+ }
156
+ }
157
+ }
158
+
159
+ return middlewares
160
+ }
161
+
162
+ /**
163
+ * Extract router definitions from a file
164
+ */
165
+ function extractRouters(content: string, filePath: string, middlewares: Map<string, TRPCMiddleware>): TRPCRouter[] {
166
+ const routers: TRPCRouter[] = []
167
+ const lines = content.split('\n')
168
+
169
+ // Pattern to find router definitions
170
+ const routerDefPatterns = [
171
+ /export\s+const\s+(\w+Router)\s*=\s*(?:createTRPCRouter|t\.router)\s*\(\s*\{/i,
172
+ /const\s+(\w+Router)\s*=\s*(?:createTRPCRouter|t\.router)\s*\(\s*\{/i,
173
+ /(\w+):\s*(?:createTRPCRouter|t\.router)\s*\(\s*\{/i,
174
+ ]
175
+
176
+ for (let i = 0; i < lines.length; i++) {
177
+ const line = lines[i]
178
+
179
+ for (const pattern of routerDefPatterns) {
180
+ const match = line.match(pattern)
181
+ if (match) {
182
+ const name = match[1].replace(/Router$/i, '').toLowerCase()
183
+
184
+ // Look ahead to find procedures in this router
185
+ const procedures: string[] = []
186
+ let isProtected = false
187
+ let defaultMiddleware: string | undefined
188
+
189
+ // Check if router name suggests admin protection
190
+ if (/admin/i.test(name)) {
191
+ isProtected = true
192
+ defaultMiddleware = 'adminProcedure'
193
+ }
194
+
195
+ // Scan the router body for procedures
196
+ let braceDepth = 0
197
+ let inRouter = false
198
+ for (let j = i; j < Math.min(i + 100, lines.length); j++) {
199
+ const routerLine = lines[j]
200
+
201
+ if (routerLine.includes('{')) {
202
+ braceDepth += (routerLine.match(/\{/g) || []).length
203
+ inRouter = true
204
+ }
205
+ if (routerLine.includes('}')) {
206
+ braceDepth -= (routerLine.match(/\}/g) || []).length
207
+ }
208
+
209
+ if (inRouter && braceDepth === 0) break
210
+
211
+ // Look for procedure definitions
212
+ const procMatch = routerLine.match(/(\w+)\s*:\s*(\w+Procedure)\./i)
213
+ if (procMatch) {
214
+ procedures.push(procMatch[1])
215
+ const middlewareName = procMatch[2]
216
+ const middleware = middlewares.get(middlewareName)
217
+ if (middleware && (middleware.type === 'admin' || middleware.type === 'authenticated')) {
218
+ isProtected = true
219
+ }
220
+ }
221
+
222
+ // Check for admin/protected procedure usage
223
+ if (ADMIN_MIDDLEWARE_PATTERNS.some(p => p.test(routerLine))) {
224
+ isProtected = true
225
+ } else if (AUTH_MIDDLEWARE_PATTERNS.some(p => p.test(routerLine))) {
226
+ isProtected = true
227
+ }
228
+ }
229
+
230
+ routers.push({
231
+ name,
232
+ file: filePath,
233
+ procedures,
234
+ defaultMiddleware,
235
+ isProtected,
236
+ })
237
+ }
238
+ }
239
+ }
240
+
241
+ return routers
242
+ }
243
+
244
+ /**
245
+ * Analyze tRPC routers across all files
246
+ */
247
+ export function analyzeTRPCRouters(files: ScanFile[]): TRPCRouterContext {
248
+ const context: TRPCRouterContext = {
249
+ routers: new Map(),
250
+ procedures: new Map(),
251
+ middlewares: new Map(),
252
+ hasTRPC: false,
253
+ }
254
+
255
+ // First pass: detect if tRPC is used
256
+ for (const file of files) {
257
+ if (detectTRPCUsage(file.content)) {
258
+ context.hasTRPC = true
259
+ break
260
+ }
261
+ }
262
+
263
+ if (!context.hasTRPC) {
264
+ return context
265
+ }
266
+
267
+ // Second pass: extract middlewares
268
+ for (const file of files) {
269
+ const middlewares = extractMiddlewares(file.content, file.path)
270
+ for (const middleware of middlewares) {
271
+ context.middlewares.set(middleware.name, middleware)
272
+ }
273
+ }
274
+
275
+ // Third pass: extract routers
276
+ for (const file of files) {
277
+ const routers = extractRouters(file.content, file.path, context.middlewares)
278
+ for (const router of routers) {
279
+ context.routers.set(router.name, router)
280
+
281
+ // Add procedures to the procedures map
282
+ for (const procName of router.procedures) {
283
+ context.procedures.set(`${router.name}.${procName}`, {
284
+ name: procName,
285
+ router: router.name,
286
+ hasAuthMiddleware: router.isProtected,
287
+ middlewareType: router.isProtected ? 'authenticated' : 'public',
288
+ definedIn: router.file,
289
+ })
290
+ }
291
+ }
292
+ }
293
+
294
+ return context
295
+ }
296
+
297
+ /**
298
+ * Check if a tRPC procedure is protected by middleware
299
+ */
300
+ export function isProcedureProtected(
301
+ context: TRPCRouterContext,
302
+ routerName: string,
303
+ procedureName?: string
304
+ ): boolean {
305
+ // Check if the router itself is protected
306
+ const router = context.routers.get(routerName.toLowerCase())
307
+ if (router?.isProtected) {
308
+ return true
309
+ }
310
+
311
+ // Check specific procedure if provided
312
+ if (procedureName) {
313
+ const procedure = context.procedures.get(`${routerName.toLowerCase()}.${procedureName}`)
314
+ if (procedure?.hasAuthMiddleware) {
315
+ return true
316
+ }
317
+ }
318
+
319
+ // Check if router name suggests admin protection
320
+ if (/admin/i.test(routerName)) {
321
+ return true
322
+ }
323
+
324
+ return false
325
+ }
326
+
327
+ /**
328
+ * Parse a tRPC client call to extract router and procedure names
329
+ * e.g., trpc.admin.getUsers.useQuery() -> { router: 'admin', procedure: 'getUsers' }
330
+ */
331
+ export function parseTRPCCall(lineContent: string): { router: string; procedure: string } | null {
332
+ // Pattern: trpc.routerName.procedureName
333
+ const patterns = [
334
+ /trpc\.(\w+)\.(\w+)\./i,
335
+ /api\.(\w+)\.(\w+)\./i,
336
+ /client\.(\w+)\.(\w+)\./i,
337
+ ]
338
+
339
+ for (const pattern of patterns) {
340
+ const match = lineContent.match(pattern)
341
+ if (match) {
342
+ return {
343
+ router: match[1],
344
+ procedure: match[2],
345
+ }
346
+ }
347
+ }
348
+
349
+ return null
350
+ }
351
+
352
+ /**
353
+ * Check if a line contains a tRPC client call
354
+ */
355
+ export function isTRPCClientCall(lineContent: string): boolean {
356
+ const patterns = [
357
+ /trpc\.\w+\.\w+\.(useQuery|useMutation|useInfiniteQuery)/i,
358
+ /api\.\w+\.\w+\.(useQuery|useMutation|useInfiniteQuery)/i,
359
+ /trpc\.\w+\.\w+\.query\(/i,
360
+ /trpc\.\w+\.\w+\.mutate\(/i,
361
+ ]
362
+ return patterns.some(p => p.test(lineContent))
363
+ }
364
+
365
+ /**
366
+ * Get a summary of tRPC context for logging
367
+ */
368
+ export function getTRPCSummary(context: TRPCRouterContext): string {
369
+ if (!context.hasTRPC) {
370
+ return 'No tRPC detected'
371
+ }
372
+
373
+ const parts: string[] = []
374
+ parts.push(`tRPC detected: ${context.routers.size} routers, ${context.procedures.size} procedures`)
375
+
376
+ const protectedRouters = Array.from(context.routers.values()).filter(r => r.isProtected)
377
+ if (protectedRouters.length > 0) {
378
+ parts.push(`Protected routers: ${protectedRouters.map(r => r.name).join(', ')}`)
379
+ }
380
+
381
+ return parts.join('; ')
382
+ }