@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,333 @@
1
+ "use strict";
2
+ /**
3
+ * Layer 2: Authentication Anti-Pattern Detection
4
+ * Identifies weak or missing authentication/authorization patterns
5
+ *
6
+ * Key improvements:
7
+ * - Respects global middleware protection (caps severity at info)
8
+ * - Detects throwing auth helpers and suppresses redundant null checks
9
+ * - Properly classifies public endpoints
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.detectAuthAntipatterns = detectAuthAntipatterns;
13
+ const middleware_detector_1 = require("../utils/middleware-detector");
14
+ const auth_helper_detector_1 = require("../utils/auth-helper-detector");
15
+ const AUTH_ANTIPATTERNS = [
16
+ // Missing auth checks
17
+ {
18
+ name: 'Unprotected API route',
19
+ pattern: /export\s+(async\s+)?function\s+(GET|POST|PUT|DELETE|PATCH)\s*\(/gi,
20
+ severity: 'medium',
21
+ description: 'API route handler may lack authentication - verify auth is checked',
22
+ suggestedFix: 'Add authentication middleware or check session at the start of the handler',
23
+ },
24
+ {
25
+ name: 'Express route without auth middleware',
26
+ pattern: /\.(get|post|put|delete|patch)\s*\(\s*['"][^'"]+['"]\s*,\s*(async\s*)?\(\s*(req|request)/gi,
27
+ severity: 'medium',
28
+ description: 'Express route may lack authentication middleware',
29
+ suggestedFix: 'Add authentication middleware before the route handler',
30
+ },
31
+ // Weak authentication patterns
32
+ {
33
+ name: 'Hardcoded credentials check',
34
+ pattern: /if\s*\(\s*(username|user|email)\s*===?\s*['"][^'"]+['"]\s*&&\s*(password|pass|pwd)\s*===?\s*['"][^'"]+['"]/gi,
35
+ severity: 'critical',
36
+ description: 'Hardcoded credentials in authentication logic',
37
+ suggestedFix: 'Use a proper user database with hashed passwords',
38
+ },
39
+ {
40
+ name: 'Password in plain text comparison',
41
+ pattern: /password\s*===?\s*user\.password|user\.password\s*===?\s*password/gi,
42
+ severity: 'high',
43
+ description: 'Plain text password comparison detected',
44
+ suggestedFix: 'Use bcrypt.compare() or similar for password verification',
45
+ },
46
+ {
47
+ name: 'JWT without verification',
48
+ pattern: /jwt\.decode\s*\([^)]+\)(?!.*verify)/gi,
49
+ severity: 'high',
50
+ description: 'JWT decoded without signature verification',
51
+ suggestedFix: 'Use jwt.verify() instead of jwt.decode() for authentication',
52
+ },
53
+ {
54
+ name: 'Weak JWT secret',
55
+ pattern: /jwt\.sign\s*\([^)]+,\s*['"][^'"]{1,20}['"]/gi,
56
+ severity: 'high',
57
+ description: 'JWT signed with a short/weak secret',
58
+ suggestedFix: 'Use a strong, random secret of at least 256 bits from environment variables',
59
+ },
60
+ // Session issues
61
+ {
62
+ name: 'Session without secure flag',
63
+ pattern: /session\s*\(\s*\{[^}]*(?!secure\s*:\s*true)[^}]*\}/gi,
64
+ severity: 'medium',
65
+ description: 'Session configuration may lack secure flag',
66
+ suggestedFix: 'Set secure: true for cookies in production',
67
+ },
68
+ {
69
+ name: 'Cookie without httpOnly',
70
+ pattern: /cookie\s*\(\s*['"][^'"]+['"]\s*,[^)]*(?!httpOnly)[^)]*\)|setCookie\s*\([^)]*(?!httpOnly)/gi,
71
+ severity: 'medium',
72
+ description: 'Cookie set without httpOnly flag',
73
+ suggestedFix: 'Add httpOnly: true to prevent XSS access to cookies',
74
+ },
75
+ // Authorization issues
76
+ // NOTE: We intentionally do NOT flag "if (!user)" or "if (!userId)" patterns as issues
77
+ // when throwing auth helpers are in use. Those helpers guarantee the user exists.
78
+ // This pattern is now much more targeted.
79
+ {
80
+ name: 'Missing role check for admin operation',
81
+ pattern: /\b(admin|superuser|moderator|owner)\b.*(?:delete|remove|update|modify|grant|revoke)/gi,
82
+ severity: 'low',
83
+ description: 'Potentially privileged operation - verify role/permission checks are in place',
84
+ suggestedFix: 'Add role-based access control for sensitive operations',
85
+ },
86
+ {
87
+ name: 'Client-side only auth check',
88
+ pattern: /if\s*\(\s*!?\s*(isAuthenticated|isLoggedIn|user)\s*\)\s*\{?\s*(router\.push|navigate|redirect|window\.location)/gi,
89
+ severity: 'medium',
90
+ description: 'Client-side only authentication redirect detected',
91
+ suggestedFix: 'Implement server-side authentication checks as well',
92
+ },
93
+ // Insecure token handling
94
+ {
95
+ name: 'Token in URL',
96
+ pattern: /\?.*token=|&token=|\?.*api_key=|&api_key=/gi,
97
+ severity: 'high',
98
+ description: 'Sensitive token passed in URL query parameter',
99
+ suggestedFix: 'Pass tokens in Authorization header or request body',
100
+ },
101
+ {
102
+ name: 'Token in localStorage',
103
+ pattern: /localStorage\.(setItem|getItem)\s*\(\s*['"](token|jwt|auth|session|apiKey)/gi,
104
+ severity: 'medium',
105
+ description: 'Sensitive token stored in localStorage (XSS vulnerable)',
106
+ suggestedFix: 'Use httpOnly cookies for token storage',
107
+ },
108
+ // OAuth/Social auth issues
109
+ {
110
+ name: 'OAuth state parameter missing',
111
+ pattern: /oauth|authorize\?.*(?!state=)/gi,
112
+ severity: 'medium',
113
+ description: 'OAuth flow may lack state parameter for CSRF protection',
114
+ suggestedFix: 'Include a random state parameter in OAuth requests',
115
+ },
116
+ // Password handling issues
117
+ {
118
+ name: 'Password logged',
119
+ pattern: /console\.(log|info|debug|warn|error)\s*\([^)]*password/gi,
120
+ severity: 'critical',
121
+ description: 'Password may be logged to console',
122
+ suggestedFix: 'Never log passwords or sensitive credentials',
123
+ },
124
+ {
125
+ name: 'Password in error message',
126
+ pattern: /throw\s+new\s+Error\s*\([^)]*password|Error\s*\([^)]*password/gi,
127
+ severity: 'high',
128
+ description: 'Password may be included in error message',
129
+ suggestedFix: 'Never include passwords in error messages',
130
+ },
131
+ // Rate limiting
132
+ {
133
+ name: 'Login without rate limiting',
134
+ pattern: /\/(login|signin|auth|authenticate)\s*['"],\s*(async\s*)?\(/gi,
135
+ severity: 'medium',
136
+ description: 'Login endpoint may lack rate limiting',
137
+ suggestedFix: 'Add rate limiting to prevent brute force attacks',
138
+ },
139
+ // 2FA bypass
140
+ {
141
+ name: 'Potential 2FA bypass',
142
+ pattern: /skip2fa|bypass2fa|disable2fa|twoFactor\s*[=:]\s*false/gi,
143
+ severity: 'high',
144
+ description: 'Two-factor authentication bypass detected',
145
+ suggestedFix: 'Remove 2FA bypass options in production',
146
+ },
147
+ ];
148
+ // Check if line is a comment
149
+ function isComment(line) {
150
+ const trimmed = line.trim();
151
+ return (trimmed.startsWith('//') ||
152
+ trimmed.startsWith('#') ||
153
+ trimmed.startsWith('*') ||
154
+ trimmed.startsWith('/*'));
155
+ }
156
+ // Check if file is likely an auth-related file
157
+ function isAuthRelatedFile(filePath) {
158
+ const authKeywords = ['auth', 'login', 'session', 'user', 'account', 'credential', 'password', 'token', 'jwt', 'oauth'];
159
+ const lowerPath = filePath.toLowerCase();
160
+ return authKeywords.some(keyword => lowerPath.includes(keyword));
161
+ }
162
+ // Check if endpoint is a known public endpoint (health checks, webhooks, cron)
163
+ function isKnownPublicEndpoint(lineContent, filePath) {
164
+ const PUBLIC_ENDPOINTS = [
165
+ // Health checks
166
+ /\/health\b/i,
167
+ /\/healthz\b/i,
168
+ /\/ready\b/i,
169
+ /\/live\b/i,
170
+ /\/ping\b/i,
171
+ /\/status\b/i,
172
+ // Webhooks (receive external calls)
173
+ /\/webhook\b/i,
174
+ /\/webhooks\//i,
175
+ /\/callback\b/i,
176
+ // Cron/scheduled tasks
177
+ /\/cron\//i,
178
+ /\/scheduled\//i,
179
+ /\/tasks\//i,
180
+ // Public APIs
181
+ /\/public\//i,
182
+ /\bGET\b.*\/api\/\w+\/\[id\]/i, // Public resource reads with ID param
183
+ ];
184
+ return PUBLIC_ENDPOINTS.some(pattern => pattern.test(lineContent) || pattern.test(filePath));
185
+ }
186
+ // Check if there are auth indicators in nearby lines (within 15 lines)
187
+ function hasAuthCheckNearby(lines, lineIndex) {
188
+ const startLine = Math.max(0, lineIndex);
189
+ const endLine = Math.min(lines.length, lineIndex + 15);
190
+ const searchWindow = lines.slice(startLine, endLine);
191
+ const authIndicators = [
192
+ /authorization/i,
193
+ /bearer\s+token/i,
194
+ /req\.user/,
195
+ /request\.user/,
196
+ /isAuthenticated/,
197
+ /requireAuth/,
198
+ /verifyToken/,
199
+ /checkPermission/,
200
+ /getServerSession/,
201
+ /auth\(\)/,
202
+ /middleware.*auth/i,
203
+ /session\s*\.\s*user/,
204
+ // Internal secret checks (network-level auth)
205
+ /internal.?secret/i,
206
+ /INTERNAL_SECRET/,
207
+ /x-internal-secret/i,
208
+ /admin.?secret/i,
209
+ /service.?token/i,
210
+ // BYOK patterns - user provides their own API key (implicit auth)
211
+ /userApiKey|user_api_key|clientApiKey/i,
212
+ /req\.body\.(?:apiKey|api_key|openaiKey|anthropicKey)/i,
213
+ /headers\[['"`]x-(?:openai|api|anthropic)-key['"`]\]/i,
214
+ ];
215
+ return searchWindow.some(line => authIndicators.some(pattern => pattern.test(line)));
216
+ }
217
+ function detectAuthAntipatterns(content, filePath, options = {}) {
218
+ const { middlewareConfig, authHelpers, fileAuthImports } = options;
219
+ const vulnerabilities = [];
220
+ const lines = content.split('\n');
221
+ const isAuthFile = isAuthRelatedFile(filePath);
222
+ // Check if this route is protected by global middleware
223
+ const routePath = (0, middleware_detector_1.getRoutePathFromFile)(filePath);
224
+ const middlewareProtection = routePath && middlewareConfig
225
+ ? (0, middleware_detector_1.isRouteProtectedByMiddleware)(routePath, middlewareConfig)
226
+ : { isProtected: false, reason: '' };
227
+ // Check if file uses imported auth middleware/helpers
228
+ const importedAuthProtection = fileAuthImports?.get(filePath);
229
+ const usesImportedAuth = importedAuthProtection?.usesImportedAuth ?? false;
230
+ // Check if file uses throwing auth helpers
231
+ const helpersList = authHelpers?.helpers || [];
232
+ lines.forEach((line, index) => {
233
+ // Skip comment lines
234
+ if (isComment(line))
235
+ return;
236
+ for (const pattern of AUTH_ANTIPATTERNS) {
237
+ const regex = new RegExp(pattern.pattern.source, pattern.pattern.flags);
238
+ if (regex.test(line)) {
239
+ // Special handling for unprotected route patterns
240
+ if (pattern.name === 'Unprotected API route' ||
241
+ pattern.name === 'Express route without auth middleware') {
242
+ // PRIORITY 0: Check if this is actually a route file
243
+ // In Next.js, routes must be in `route.ts/js` files. Files like `handlers.ts`,
244
+ // `safe-handlers.ts`, `utils.ts` etc. are NOT actual API routes even if they
245
+ // export GET/POST functions.
246
+ const isActualRouteFile = /\/(route|page)\.(ts|js|tsx|jsx)$/i.test(filePath) ||
247
+ /\/(api|routes?)\/.*\/(index|route)\.(ts|js)$/i.test(filePath) ||
248
+ // Express/Koa routes typically have 'routes' or 'router' in path
249
+ /\/(routes?|router|controllers?)\.[tj]s$/i.test(filePath);
250
+ // Files explicitly named as handlers, helpers, utils are not routes
251
+ const isUtilityFile = /(handler|helper|util|mock|test|fixture|safe|example)/i.test(filePath);
252
+ if (!isActualRouteFile || isUtilityFile) {
253
+ // Not an actual route file - skip this finding
254
+ break;
255
+ }
256
+ // PRIORITY 1: Check if route is protected by global middleware
257
+ // This is the STRONGEST signal - if middleware protects the route, suppress entirely
258
+ if (middlewareProtection.isProtected) {
259
+ // Route is authenticated by middleware - no finding needed
260
+ break; // Skip this pattern, route is protected
261
+ }
262
+ // PRIORITY 1.5: Check if file imports and uses auth middleware
263
+ // e.g., import { authMiddleware } from '@/lib/auth' + wraps handlers
264
+ if (usesImportedAuth) {
265
+ // File imports auth middleware and uses it - no finding needed
266
+ break; // Skip this pattern, route is protected via imported auth
267
+ }
268
+ // PRIORITY 2: Check if file uses throwing auth helpers
269
+ // If getCurrentUserId() or similar is called, the route is authenticated
270
+ const authHelperCall = (0, auth_helper_detector_1.hasAuthHelperCallBefore)(content, index, helpersList);
271
+ if (authHelperCall.hasCall && authHelperCall.helper) {
272
+ // Route uses a throwing auth helper - no finding needed
273
+ // The auth helper guarantees authenticated context
274
+ break; // Skip this pattern, route is protected
275
+ }
276
+ // PRIORITY 3: Check if this is a known public endpoint
277
+ if (isKnownPublicEndpoint(line, filePath)) {
278
+ vulnerabilities.push({
279
+ id: `auth-antipattern-${filePath}-${index + 1}-${pattern.name}`,
280
+ filePath,
281
+ lineNumber: index + 1,
282
+ lineContent: line.trim(),
283
+ severity: 'info',
284
+ category: 'missing_auth',
285
+ title: pattern.name + ' (public endpoint)',
286
+ description: 'This appears to be a public endpoint (health check, webhook, cron, etc.). Verify this is intentionally public and consider rate limiting if needed.',
287
+ suggestedFix: 'If this is a webhook or cron endpoint, ensure it has appropriate authentication (API keys, signatures, etc.). Health checks typically do not need auth.',
288
+ confidence: 'low',
289
+ layer: 2,
290
+ });
291
+ break; // Only report once per line
292
+ }
293
+ // PRIORITY 4: Check if auth check exists nearby (inline check)
294
+ if (hasAuthCheckNearby(lines, index)) {
295
+ vulnerabilities.push({
296
+ id: `auth-antipattern-${filePath}-${index + 1}-${pattern.name}`,
297
+ filePath,
298
+ lineNumber: index + 1,
299
+ lineContent: line.trim(),
300
+ severity: 'low',
301
+ category: 'missing_auth',
302
+ title: pattern.name,
303
+ description: pattern.description + ' (auth check detected in nearby lines)',
304
+ suggestedFix: pattern.suggestedFix,
305
+ confidence: 'low',
306
+ layer: 2,
307
+ });
308
+ break; // Only report once per line
309
+ }
310
+ }
311
+ // Standard handling for all patterns (or fallback for route patterns)
312
+ // Boost confidence for auth-related files
313
+ const confidence = isAuthFile ? 'high' : 'medium';
314
+ vulnerabilities.push({
315
+ id: `auth-antipattern-${filePath}-${index + 1}-${pattern.name}`,
316
+ filePath,
317
+ lineNumber: index + 1,
318
+ lineContent: line.trim(),
319
+ severity: pattern.severity,
320
+ category: 'missing_auth',
321
+ title: pattern.name,
322
+ description: pattern.description,
323
+ suggestedFix: pattern.suggestedFix,
324
+ confidence,
325
+ layer: 2,
326
+ });
327
+ break; // Only report once per line
328
+ }
329
+ }
330
+ });
331
+ return vulnerabilities;
332
+ }
333
+ //# sourceMappingURL=auth-antipatterns.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-antipatterns.js","sourceRoot":"","sources":["../../src/layer2/auth-antipatterns.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Layer 2: BYOK (Bring Your Own Key) Pattern Detection
3
+ * Distinguishes per-user BYOK flows from central/shared key storage
4
+ * BYOK is often a feature, not a vulnerability - severity depends on context
5
+ */
6
+ import type { Vulnerability } from '../types';
7
+ import type { MiddlewareAuthConfig } from '../utils/middleware-detector';
8
+ /**
9
+ * Detect BYOK patterns and classify their risk level
10
+ */
11
+ export declare function detectBYOKPatterns(content: string, filePath: string, middlewareConfig?: MiddlewareAuthConfig): Vulnerability[];
12
+ //# sourceMappingURL=byok-patterns.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"byok-patterns.d.ts","sourceRoot":"","sources":["../../src/layer2/byok-patterns.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAyB,MAAM,UAAU,CAAA;AACpE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAA;AAkNxE;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,gBAAgB,CAAC,EAAE,oBAAoB,GACtC,aAAa,EAAE,CA+GjB"}
@@ -0,0 +1,299 @@
1
+ "use strict";
2
+ /**
3
+ * Layer 2: BYOK (Bring Your Own Key) Pattern Detection
4
+ * Distinguishes per-user BYOK flows from central/shared key storage
5
+ * BYOK is often a feature, not a vulnerability - severity depends on context
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.detectBYOKPatterns = detectBYOKPatterns;
9
+ const context_helpers_1 = require("../utils/context-helpers");
10
+ const middleware_detector_1 = require("../utils/middleware-detector");
11
+ /**
12
+ * Check if line contains example/placeholder API key patterns
13
+ */
14
+ function isExampleCredential(line) {
15
+ const examplePatterns = [
16
+ /your[-_]?api[-_]?key/i,
17
+ /example[-_]?api[-_]?key/i,
18
+ /sample[-_]?key/i,
19
+ /replace[-_]?with/i,
20
+ /placeholder/i,
21
+ /your[-_]?secret/i,
22
+ /example[-_]?secret/i,
23
+ /<.*api.*key.*>/i, // <YOUR_API_KEY>
24
+ /\[.*api.*key.*\]/i, // [API_KEY]
25
+ /xxx+/i,
26
+ /demo[-_]?key/i,
27
+ /test[-_]?key/i,
28
+ /fake[-_]?key/i,
29
+ /mock[-_]?key/i,
30
+ ];
31
+ return examplePatterns.some(p => p.test(line));
32
+ }
33
+ const BYOK_PATTERNS = [
34
+ // OpenAI API key patterns
35
+ {
36
+ name: 'User-provided OpenAI API key',
37
+ pattern: /(?:openai|api).*key.*(?:req\.|body\.|params\.|input\.|user)|(?:req\.|body\.|params\.).*(?:openai|api).*key/gi,
38
+ keyType: 'openai',
39
+ description: 'User-provided OpenAI API key detected',
40
+ },
41
+ {
42
+ name: 'OpenAI key from request',
43
+ pattern: /new\s+OpenAI\s*\(\s*\{[^}]*apiKey\s*:\s*(?:req|body|params|input)/gi,
44
+ keyType: 'openai',
45
+ description: 'OpenAI client initialized with user-provided key',
46
+ },
47
+ // Anthropic API key patterns
48
+ {
49
+ name: 'User-provided Anthropic API key',
50
+ pattern: /(?:anthropic|claude).*key.*(?:req\.|body\.|params\.|input\.|user)|(?:req\.|body\.|params\.).*(?:anthropic|claude).*key/gi,
51
+ keyType: 'anthropic',
52
+ description: 'User-provided Anthropic API key detected',
53
+ },
54
+ {
55
+ name: 'Anthropic key from request',
56
+ pattern: /new\s+Anthropic\s*\(\s*\{[^}]*apiKey\s*:\s*(?:req|body|params|input)/gi,
57
+ keyType: 'anthropic',
58
+ description: 'Anthropic client initialized with user-provided key',
59
+ },
60
+ // Generic AI key patterns
61
+ {
62
+ name: 'User-provided AI API key',
63
+ pattern: /(?:ai|llm|model).*(?:api)?.*key.*(?:req\.|body\.|params\.|input\.)/gi,
64
+ keyType: 'generic_ai',
65
+ description: 'User-provided AI API key detected',
66
+ },
67
+ // Generic key patterns (more cautious)
68
+ {
69
+ name: 'API key from user input',
70
+ pattern: /apiKey\s*[=:]\s*(?:req|body|params|input)\./gi,
71
+ keyType: 'generic',
72
+ description: 'API key received from user input',
73
+ },
74
+ ];
75
+ /**
76
+ * Check if file has explicit authentication checks
77
+ */
78
+ function hasExplicitAuthCheck(content) {
79
+ const authPatterns = [
80
+ // Common auth check functions
81
+ /getCurrentUser(Id)?\s*\(/i,
82
+ /getSession\s*\(/i,
83
+ /getServerSession\s*\(/i,
84
+ /auth\s*\(\s*\)/i,
85
+ /requireAuth\s*\(/i,
86
+ /verifyToken\s*\(/i,
87
+ /isAuthenticated/i,
88
+ /checkAuth\s*\(/i,
89
+ /withAuth\s*\(/i,
90
+ // Request user checks
91
+ /req\.user|request\.user/i,
92
+ /context\.user/i,
93
+ // Supabase auth
94
+ /supabase\.auth\.getUser/i,
95
+ /supabase\.auth\.getSession/i,
96
+ // NextAuth
97
+ /getServerSession\s*\(/i,
98
+ /useSession\s*\(/i,
99
+ // Clerk
100
+ /currentUser\s*\(/i,
101
+ /auth\(\)\.protect/i,
102
+ ];
103
+ return authPatterns.some(p => p.test(content));
104
+ }
105
+ /**
106
+ * Check if key appears to be stored to database
107
+ */
108
+ function isKeyStoredToDatabase(content, lineNumber) {
109
+ const lines = content.split('\n');
110
+ const startLine = Math.max(0, lineNumber - 5);
111
+ const endLine = Math.min(lines.length, lineNumber + 15);
112
+ const context = lines.slice(startLine, endLine).join('\n');
113
+ // Patterns indicating database storage operations
114
+ // NOTE: We use specific patterns to avoid false positives with API calls like
115
+ // openai.chat.completions.create() or anthropic.messages.create()
116
+ const storagePatterns = [
117
+ /\.insert\s*\(/i,
118
+ /\.update\s*\(/i,
119
+ /\.upsert\s*\(/i,
120
+ /\.save\s*\(/i,
121
+ /INSERT\s+INTO/i,
122
+ /UPDATE\s+.*SET/i,
123
+ // ORM-specific patterns - must have table/model context
124
+ /prisma\.\w+\.create\s*\(/i,
125
+ /supabase\.from\(.*\)\.(?:insert|update|upsert)/i,
126
+ /db\.\w+\.create\s*\(/i,
127
+ /knex\(.*\)\.insert/i,
128
+ /\.collection\(.*\)\.(?:insertOne|updateOne|save)/i, // MongoDB
129
+ /sequelize\..*\.create\s*\(/i,
130
+ // localStorage/sessionStorage (client-side storage)
131
+ /localStorage\.setItem/i,
132
+ /sessionStorage\.setItem/i,
133
+ ];
134
+ // Exclude AI SDK patterns that use .create() for API calls, not storage
135
+ const aiApiPatterns = [
136
+ /openai\..*\.create/i,
137
+ /anthropic\..*\.create/i,
138
+ /\.chat\.completions\.create/i,
139
+ /\.messages\.create/i,
140
+ /\.completions\.create/i,
141
+ /\.embeddings\.create/i,
142
+ ];
143
+ const hasStoragePattern = storagePatterns.some(p => p.test(context));
144
+ const hasAiApiPattern = aiApiPatterns.some(p => p.test(context));
145
+ // Only consider it storage if we have storage patterns but NOT AI API patterns
146
+ // If both are present, check if storage patterns are separate from AI patterns
147
+ if (hasStoragePattern && hasAiApiPattern) {
148
+ // Check if there's a clear database storage operation separate from AI calls
149
+ // Look for keywords like 'user', 'key', 'credential' near storage patterns
150
+ return /(?:insert|update|upsert|save).*(?:apiKey|api_key|key|credential)/i.test(context) ||
151
+ /(?:apiKey|api_key|key|credential).*(?:insert|update|upsert|save)/i.test(context);
152
+ }
153
+ return hasStoragePattern;
154
+ }
155
+ /**
156
+ * Check if key is only used transiently (same request, not stored)
157
+ */
158
+ function isTransientKeyUsage(content, lineNumber) {
159
+ const lines = content.split('\n');
160
+ const startLine = Math.max(0, lineNumber - 3);
161
+ const endLine = Math.min(lines.length, lineNumber + 20);
162
+ const context = lines.slice(startLine, endLine).join('\n');
163
+ // Transient usage patterns - key used immediately then discarded
164
+ const transientPatterns = [
165
+ /new\s+(?:OpenAI|Anthropic)\s*\(/i, // Immediate client creation
166
+ /\.create\s*\(\s*\{[^}]*messages/i, // Immediate API call
167
+ /\.completions\.create/i,
168
+ /\.messages\.create/i,
169
+ /await\s+(?:openai|anthropic|client)\./i,
170
+ ];
171
+ // If we see API calls but no storage, it's transient
172
+ const hasApiCalls = transientPatterns.some(p => p.test(context));
173
+ const hasStorage = isKeyStoredToDatabase(content, lineNumber);
174
+ return hasApiCalls && !hasStorage;
175
+ }
176
+ /**
177
+ * Check if key appears to be logged (bad practice)
178
+ */
179
+ function isKeyLogged(content, lineNumber) {
180
+ const lines = content.split('\n');
181
+ const startLine = Math.max(0, lineNumber - 3);
182
+ const endLine = Math.min(lines.length, lineNumber + 10);
183
+ const context = lines.slice(startLine, endLine).join('\n');
184
+ const loggingPatterns = [
185
+ /console\.(log|info|debug|warn|error)\s*\([^)]*(?:apiKey|api_key|key)/i,
186
+ /logger\.(log|info|debug|warn|error)\s*\([^)]*(?:apiKey|api_key|key)/i,
187
+ /\.(log|info|debug|warn|error)\s*\(.*(?:apiKey|api_key|key)/i,
188
+ ];
189
+ return loggingPatterns.some(p => p.test(context));
190
+ }
191
+ /**
192
+ * Detect BYOK patterns and classify their risk level
193
+ */
194
+ function detectBYOKPatterns(content, filePath, middlewareConfig) {
195
+ const vulnerabilities = [];
196
+ const lines = content.split('\n');
197
+ const isTestFile = (0, context_helpers_1.isTestOrMockFile)(filePath);
198
+ // Skip example/demo files entirely - they contain placeholder credentials by design
199
+ if ((0, context_helpers_1.isExampleFile)(filePath)) {
200
+ return vulnerabilities;
201
+ }
202
+ // Check route protection
203
+ const routePath = (0, middleware_detector_1.getRoutePathFromFile)(filePath);
204
+ const middlewareProtection = routePath && middlewareConfig
205
+ ? (0, middleware_detector_1.isRouteProtectedByMiddleware)(routePath, middlewareConfig)
206
+ : { isProtected: false, reason: '' };
207
+ // Check for user scoping patterns in the file
208
+ const userScoping = (0, middleware_detector_1.detectUserScopingPatterns)(content);
209
+ // Check for explicit auth in file content (more reliable than middleware detection)
210
+ const hasExplicitAuth = hasExplicitAuthCheck(content);
211
+ lines.forEach((line, index) => {
212
+ if ((0, context_helpers_1.isComment)(line))
213
+ return;
214
+ // Skip lines with example/placeholder credentials
215
+ if (isExampleCredential(line) || (0, context_helpers_1.isPlaceholderValue)('', line)) {
216
+ return;
217
+ }
218
+ for (const pattern of BYOK_PATTERNS) {
219
+ const regex = new RegExp(pattern.pattern.source, pattern.pattern.flags);
220
+ if (regex.test(line)) {
221
+ // Determine context
222
+ const isAuthenticated = middlewareProtection.isProtected || hasExplicitAuth;
223
+ const isUserScoped = userScoping.hasUserScoping;
224
+ const isStoredCentrally = isKeyStoredToDatabase(content, index);
225
+ const isTransient = isTransientKeyUsage(content, index);
226
+ // Determine severity based on context
227
+ let severity;
228
+ let description;
229
+ let suggestedFix;
230
+ if (isAuthenticated && isTransient) {
231
+ // Authenticated and transient - this is the IDEAL BYOK pattern
232
+ // Check if key is being logged (bad practice)
233
+ const keyLogged = isKeyLogged(content, index);
234
+ if (keyLogged) {
235
+ severity = 'low';
236
+ description = `BYOK feature detected: ${pattern.description}. Keys are used transiently (good!) but appear to be logged (avoid logging API keys, even in debug).`;
237
+ suggestedFix = 'Remove logging of API keys. Best practices: (1) Validate API key format, (2) Add per-user rate limiting.';
238
+ }
239
+ else {
240
+ // IDEAL PATTERN: Authenticated + transient + no logging = no issue
241
+ // Skip emitting a finding entirely for the ideal case
242
+ continue;
243
+ }
244
+ }
245
+ else if (!isAuthenticated && isTransient) {
246
+ // Unauthenticated but transient - still lower risk than storage
247
+ severity = 'low';
248
+ description = `BYOK feature detected: ${pattern.description}. Keys are used transiently (not stored). Consider adding authentication or rate limiting.`;
249
+ suggestedFix = 'Consider adding authentication. If intentionally public: add rate limiting, key format validation, and usage tracking.';
250
+ }
251
+ else if (!isAuthenticated && isStoredCentrally) {
252
+ // Unauthenticated AND storing keys - this is the real risk
253
+ severity = 'medium';
254
+ description = `${pattern.description}. This endpoint appears to lack authentication AND stores keys. This could allow unauthorized key storage.`;
255
+ suggestedFix = 'Add authentication. Ensure stored keys are scoped by user_id with proper access controls.';
256
+ }
257
+ else if (isStoredCentrally && !isUserScoped) {
258
+ // Authenticated but keys stored without user scoping - medium risk
259
+ severity = 'medium';
260
+ description = `${pattern.description}. Keys appear to be stored centrally without user-scoping, which could lead to cross-tenant key access.`;
261
+ suggestedFix = 'Ensure stored keys are scoped by user_id. Add proper access controls to prevent users from accessing other users\' keys.';
262
+ }
263
+ else if (isAuthenticated && isUserScoped) {
264
+ // Authenticated and user-scoped with storage - generally okay
265
+ severity = 'info';
266
+ description = `${pattern.description}. Route is authenticated and operations appear user-scoped. If keys are stored, consider encryption at rest.`;
267
+ suggestedFix = 'If storing keys: consider encryption at rest. Add rate limiting to prevent cost abuse.';
268
+ }
269
+ else {
270
+ // Authenticated but unclear scoping - needs review, but low priority
271
+ severity = 'info';
272
+ description = `${pattern.description}. Route is authenticated. This appears to be a BYOK feature.`;
273
+ suggestedFix = 'Verify user-scoping for stored keys. Add rate limiting for cost control.';
274
+ }
275
+ // Downgrade test files
276
+ if (isTestFile) {
277
+ severity = 'info';
278
+ description = `${description} (in test file)`;
279
+ }
280
+ vulnerabilities.push({
281
+ id: `byok-${filePath}-${index + 1}-${pattern.name}`,
282
+ filePath,
283
+ lineNumber: index + 1,
284
+ lineContent: line.trim(),
285
+ severity,
286
+ category: 'ai_pattern',
287
+ title: `BYOK: ${pattern.name}`,
288
+ description,
289
+ suggestedFix,
290
+ confidence: isTestFile ? 'low' : 'medium',
291
+ layer: 2,
292
+ });
293
+ break; // One finding per line
294
+ }
295
+ }
296
+ });
297
+ return vulnerabilities;
298
+ }
299
+ //# sourceMappingURL=byok-patterns.js.map