@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,516 @@
1
+ /**
2
+ * Layer 1: Known Pattern Matching
3
+ * Curated library of high-fidelity regex patterns for detecting secrets
4
+ */
5
+
6
+ import type { SecretPattern, Vulnerability } from '../types'
7
+ import {
8
+ isServerOnlyFile,
9
+ isExampleFile,
10
+ isEnvVarReference,
11
+ isNextPublicEnvVar,
12
+ isComment,
13
+ isPlaceholderValue,
14
+ isTestOrMockFile,
15
+ isScannerOrFixtureFile,
16
+ isBYOKContext,
17
+ getServiceRoleKeyContext,
18
+ } from '../utils/context-helpers'
19
+
20
+ // Check if file is documentation/README
21
+ function isDocumentationFile(filePath: string): boolean {
22
+ const docPatterns = [
23
+ /README/i,
24
+ /CHANGELOG/i,
25
+ /CONTRIBUTING/i,
26
+ /LICENSE/i,
27
+ /CODE_OF_CONDUCT/i,
28
+ /SECURITY/i,
29
+ /AUTHORS/i,
30
+ /HISTORY/i,
31
+ /\.md$/i,
32
+ /\.mdx$/i,
33
+ /\.rst$/i, // reStructuredText
34
+ /\.adoc$/i, // AsciiDoc
35
+ /\.txt$/i, // Plain text docs
36
+ /\/docs\//i,
37
+ /\/documentation\//i,
38
+ /\/wiki\//i,
39
+ /\/guides?\//i,
40
+ /\/tutorials?\//i,
41
+ /\/examples?\//i, // Example directories often have sample configs
42
+ ]
43
+ return docPatterns.some(p => p.test(filePath))
44
+ }
45
+
46
+ // Check if line contains example/sample placeholder values (not real secrets)
47
+ function isExamplePlaceholder(line: string, value: string): boolean {
48
+ const placeholderPatterns = [
49
+ // Common placeholder indicators in the line context
50
+ /example/i,
51
+ /sample/i,
52
+ /demo/i,
53
+ /placeholder/i,
54
+ /your[_-]?api[_-]?key/i,
55
+ /your[_-]?secret/i,
56
+ /replace[_-]?with/i,
57
+ /insert[_-]?here/i,
58
+ /xxx+/i,
59
+ /yyy+/i,
60
+ /todo/i,
61
+ /fixme/i,
62
+ ]
63
+
64
+ // Check if the value itself looks like a placeholder
65
+ const valuePlaceholderPatterns = [
66
+ /^your[_-]/i,
67
+ /^my[_-]/i,
68
+ /^test[_-]/i,
69
+ /^sample[_-]/i,
70
+ /^example[_-]/i,
71
+ /^demo[_-]/i,
72
+ /^placeholder/i,
73
+ /^xxx+$/i,
74
+ /^yyy+$/i,
75
+ /^\*+$/, // Just asterisks
76
+ /^\.{3,}$/, // Just dots
77
+ /^<.*>$/, // Angle bracket placeholders like <YOUR_KEY>
78
+ /^\[.*\]$/, // Square bracket placeholders like [YOUR_KEY]
79
+ /^\{.*\}$/, // Curly bracket placeholders like {YOUR_KEY}
80
+ ]
81
+
82
+ return placeholderPatterns.some(p => p.test(line)) ||
83
+ valuePlaceholderPatterns.some(p => p.test(value))
84
+ }
85
+
86
+ // Check if the variable name indicates test/mock data
87
+ function hasTestVariableName(line: string): boolean {
88
+ const varNamePatterns = [
89
+ // JS/TS variable declarations: const TEST_API_KEY = "..."
90
+ /(?:const|let|var|export\s+const|export\s+let)\s+([A-Z_][A-Z0-9_]*)\s*=/i,
91
+ // Object property shorthand or assignment: TEST_KEY: "..." or "testKey": "..."
92
+ /([A-Z_][A-Z0-9_]*)\s*:\s*['"`]/,
93
+ // JSON-style keys: "test_key": or 'testKey':
94
+ /['"]([a-zA-Z_][a-zA-Z0-9_]*)['"]\s*:/,
95
+ ]
96
+
97
+ // Keywords that indicate test/mock data when in variable names
98
+ const testKeywords = /^(TEST|MOCK|EXAMPLE|DUMMY|FAKE|SAMPLE|PLACEHOLDER|DEMO)[_A-Z0-9]*$/i
99
+ const testSuffixes = /_?(TEST|MOCK|EXAMPLE|DUMMY|FAKE|SAMPLE)$/i
100
+
101
+ for (const pattern of varNamePatterns) {
102
+ const match = line.match(pattern)
103
+ if (match && match[1]) {
104
+ const varName = match[1]
105
+ if (testKeywords.test(varName) || testSuffixes.test(varName)) {
106
+ return true
107
+ }
108
+ }
109
+ }
110
+ return false
111
+ }
112
+
113
+ // Comprehensive list of secret patterns with specific prefixes
114
+ export const SECRET_PATTERNS: SecretPattern[] = [
115
+ // API Keys with known prefixes
116
+ {
117
+ name: 'OpenAI API Key',
118
+ pattern: /sk-[a-zA-Z0-9]{20,}/g,
119
+ severity: 'critical',
120
+ description: 'OpenAI API key detected',
121
+ },
122
+ {
123
+ name: 'OpenAI Project API Key',
124
+ pattern: /sk-proj-[a-zA-Z0-9]{48,}/g,
125
+ severity: 'critical',
126
+ description: 'OpenAI project API key detected (new format)',
127
+ },
128
+ {
129
+ name: 'Anthropic API Key',
130
+ pattern: /sk-ant-[a-zA-Z0-9-]{20,}/g,
131
+ severity: 'critical',
132
+ description: 'Anthropic API key detected',
133
+ },
134
+ {
135
+ name: 'Anthropic API Key (Full)',
136
+ pattern: /sk-ant-api03-[a-zA-Z0-9_-]{90,}/g,
137
+ severity: 'critical',
138
+ description: 'Anthropic API key detected (full format)',
139
+ },
140
+ {
141
+ name: 'GitHub Token',
142
+ pattern: /ghp_[a-zA-Z0-9]{36,}/g,
143
+ severity: 'critical',
144
+ description: 'GitHub personal access token detected',
145
+ },
146
+ {
147
+ name: 'GitHub OAuth Token',
148
+ pattern: /gho_[a-zA-Z0-9]{36,}/g,
149
+ severity: 'critical',
150
+ description: 'GitHub OAuth token detected',
151
+ },
152
+ {
153
+ name: 'GitHub App Token',
154
+ pattern: /ghu_[a-zA-Z0-9]{36,}/g,
155
+ severity: 'critical',
156
+ description: 'GitHub App user token detected',
157
+ },
158
+ {
159
+ name: 'GitHub Refresh Token',
160
+ pattern: /ghr_[a-zA-Z0-9]{36,}/g,
161
+ severity: 'critical',
162
+ description: 'GitHub refresh token detected',
163
+ },
164
+ {
165
+ name: 'Stripe Secret Key',
166
+ pattern: /sk_live_[a-zA-Z0-9]{24,}/g,
167
+ severity: 'critical',
168
+ description: 'Stripe live secret key detected',
169
+ },
170
+ {
171
+ name: 'Stripe Test Key',
172
+ pattern: /sk_test_[a-zA-Z0-9]{24,}/g,
173
+ severity: 'medium',
174
+ description: 'Stripe test secret key detected',
175
+ },
176
+ {
177
+ name: 'Stripe Publishable Key',
178
+ pattern: /pk_(live|test)_[a-zA-Z0-9]{24,}/g,
179
+ severity: 'low',
180
+ description: 'Stripe publishable key detected (usually safe but verify)',
181
+ },
182
+ {
183
+ name: 'Square Access Token',
184
+ pattern: /sq0csp-[a-zA-Z0-9-_]{43}/g,
185
+ severity: 'critical',
186
+ description: 'Square access token detected',
187
+ },
188
+ {
189
+ name: 'AWS Access Key ID',
190
+ pattern: /AKIA[0-9A-Z]{16}/g,
191
+ severity: 'critical',
192
+ description: 'AWS Access Key ID detected',
193
+ },
194
+ {
195
+ name: 'AWS Secret Access Key',
196
+ // AWS secret keys are exactly 40 chars, base64-like, and typically appear near AWS context
197
+ // The pattern requires it to be in an AWS-related context (variable name, nearby AKIA, etc.)
198
+ pattern: /(?:aws[_-]?secret[_-]?(?:access[_-]?)?key|secret[_-]?access[_-]?key|AWS_SECRET)\s*[:=]\s*['"`]?([a-zA-Z0-9/+=]{40})['"`]?/gi,
199
+ severity: 'critical',
200
+ description: 'AWS Secret Access Key detected',
201
+ },
202
+ {
203
+ name: 'Google API Key',
204
+ pattern: /AIza[0-9A-Za-z-_]{35}/g,
205
+ severity: 'high',
206
+ description: 'Google API key detected',
207
+ },
208
+ {
209
+ name: 'Slack Token',
210
+ pattern: /xox[baprs]-[0-9a-zA-Z]{10,}/g,
211
+ severity: 'critical',
212
+ description: 'Slack token detected',
213
+ },
214
+ {
215
+ name: 'Slack Webhook',
216
+ pattern: /https:\/\/hooks\.slack\.com\/services\/T[a-zA-Z0-9_]+\/B[a-zA-Z0-9_]+\/[a-zA-Z0-9_]+/g,
217
+ severity: 'high',
218
+ description: 'Slack webhook URL detected',
219
+ },
220
+ {
221
+ name: 'Discord Webhook',
222
+ pattern: /https:\/\/discord(?:app)?\.com\/api\/webhooks\/[0-9]+\/[a-zA-Z0-9_-]+/g,
223
+ severity: 'high',
224
+ description: 'Discord webhook URL detected',
225
+ },
226
+ {
227
+ name: 'Twilio API Key',
228
+ pattern: /SK[a-f0-9]{32}/g,
229
+ severity: 'critical',
230
+ description: 'Twilio API key detected',
231
+ },
232
+ {
233
+ name: 'SendGrid API Key',
234
+ pattern: /SG\.[a-zA-Z0-9_-]{22}\.[a-zA-Z0-9_-]{43}/g,
235
+ severity: 'critical',
236
+ description: 'SendGrid API key detected',
237
+ },
238
+ {
239
+ name: 'Mailgun API Key',
240
+ pattern: /key-[a-zA-Z0-9]{32}/g,
241
+ severity: 'critical',
242
+ description: 'Mailgun API key detected',
243
+ },
244
+ {
245
+ name: 'Firebase API Key',
246
+ pattern: /AIza[0-9A-Za-z-_]{35}/g,
247
+ severity: 'high',
248
+ description: 'Firebase API key detected',
249
+ },
250
+ {
251
+ name: 'Supabase Anon Key',
252
+ pattern: /eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9\.[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+/g,
253
+ severity: 'low',
254
+ description: 'Supabase anon key detected (usually safe for client-side)',
255
+ },
256
+ {
257
+ name: 'Supabase Service Role Key',
258
+ pattern: /eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9\.[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+/g,
259
+ severity: 'critical',
260
+ description: 'JWT token detected - verify if this is a service role key',
261
+ },
262
+ {
263
+ name: 'Heroku API Key',
264
+ pattern: /[h|H][e|E][r|R][o|O][k|K][u|U].*[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}/gi,
265
+ severity: 'critical',
266
+ description: 'Heroku API key detected',
267
+ },
268
+ {
269
+ name: 'NPM Token',
270
+ pattern: /npm_[a-zA-Z0-9]{36}/g,
271
+ severity: 'critical',
272
+ description: 'NPM access token detected',
273
+ },
274
+ {
275
+ name: 'PyPI Token',
276
+ pattern: /pypi-[a-zA-Z0-9]{32,}/g,
277
+ severity: 'critical',
278
+ description: 'PyPI API token detected',
279
+ },
280
+ {
281
+ name: 'Private Key',
282
+ pattern: /-----BEGIN (RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/g,
283
+ severity: 'critical',
284
+ description: 'Private key detected',
285
+ },
286
+ {
287
+ name: 'Generic Password in URL',
288
+ pattern: /[a-zA-Z]{3,10}:\/\/[^:]+:[^@]+@/g,
289
+ severity: 'high',
290
+ description: 'Password in URL detected',
291
+ },
292
+ {
293
+ name: 'Database Connection String',
294
+ pattern: /(mongodb|postgres|mysql|redis|amqp):\/\/[^\s"']+/gi,
295
+ severity: 'high',
296
+ description: 'Database connection string detected - may contain credentials',
297
+ },
298
+ // Additional patterns from checklist
299
+ {
300
+ name: 'Hardcoded JWT Token',
301
+ pattern: /eyJ[a-zA-Z0-9_-]{10,}\.eyJ[a-zA-Z0-9_-]{10,}\.[a-zA-Z0-9_-]{10,}/g,
302
+ severity: 'high',
303
+ description: 'Hardcoded JWT token detected',
304
+ },
305
+ {
306
+ name: 'PostgreSQL Connection String',
307
+ pattern: /postgres:\/\/[^:]+:[^@]+@[^\s"']+/gi,
308
+ severity: 'critical',
309
+ description: 'PostgreSQL connection string with credentials detected',
310
+ },
311
+ {
312
+ name: 'MongoDB Connection String',
313
+ pattern: /mongodb(\+srv)?:\/\/[^:]+:[^@]+@[^\s"']+/gi,
314
+ severity: 'critical',
315
+ description: 'MongoDB connection string with credentials detected',
316
+ },
317
+ {
318
+ name: 'MySQL Connection String',
319
+ pattern: /mysql:\/\/[^:]+:[^@]+@[^\s"']+/gi,
320
+ severity: 'critical',
321
+ description: 'MySQL connection string with credentials detected',
322
+ },
323
+ {
324
+ name: 'Generic API Key Assignment',
325
+ pattern: /['"']?api[_-]?key['"']?\s*[:=]\s*['"][a-zA-Z0-9_-]{20,}['"]/gi,
326
+ severity: 'medium',
327
+ description: 'Possible API key assignment detected - requires validation',
328
+ },
329
+ {
330
+ name: 'Generic Secret Key Assignment',
331
+ pattern: /['"']?secret[_-]?key['"']?\s*[:=]\s*['"][a-zA-Z0-9_-]{20,}['"]/gi,
332
+ severity: 'medium',
333
+ description: 'Possible secret key assignment detected - requires validation',
334
+ },
335
+ {
336
+ name: 'Vercel Token',
337
+ pattern: /vercel_[a-zA-Z0-9]{24,}/gi,
338
+ severity: 'critical',
339
+ description: 'Vercel API token detected',
340
+ },
341
+ {
342
+ name: 'Netlify Token',
343
+ pattern: /nfp_[a-zA-Z0-9]{40,}/gi,
344
+ severity: 'critical',
345
+ description: 'Netlify personal access token detected',
346
+ },
347
+ {
348
+ name: 'Cloudflare API Token',
349
+ pattern: /[a-zA-Z0-9_-]{40}(?=.*cloudflare)/gi,
350
+ severity: 'high',
351
+ description: 'Potential Cloudflare API token detected',
352
+ },
353
+ ]
354
+
355
+ export function detectKnownPatterns(
356
+ content: string,
357
+ filePath: string
358
+ ): Vulnerability[] {
359
+ const vulnerabilities: Vulnerability[] = []
360
+ const lines = content.split('\n')
361
+
362
+ // Skip example files entirely
363
+ if (isExampleFile(filePath)) {
364
+ return vulnerabilities
365
+ }
366
+
367
+ // Skip scanner/fixture files to avoid self-detection
368
+ if (isScannerOrFixtureFile(filePath)) {
369
+ return vulnerabilities
370
+ }
371
+
372
+ // Skip documentation/README files
373
+ if (isDocumentationFile(filePath)) {
374
+ return vulnerabilities
375
+ }
376
+
377
+ // Check context for file-level decisions
378
+ const isServerFile = isServerOnlyFile(filePath)
379
+ const isTestFile = isTestOrMockFile(filePath)
380
+
381
+ for (const secretPattern of SECRET_PATTERNS) {
382
+ lines.forEach((line, index) => {
383
+ // Skip comments
384
+ if (isComment(line)) return
385
+
386
+ // Reset regex state
387
+ const regex = new RegExp(secretPattern.pattern.source, secretPattern.pattern.flags)
388
+ let match
389
+
390
+ while ((match = regex.exec(line)) !== null) {
391
+ const value = match[0]
392
+
393
+ // Skip placeholder values
394
+ if (isPlaceholderValue(value, line)) {
395
+ continue
396
+ }
397
+
398
+ // Skip obvious example/sample values
399
+ if (/example|sample|demo|test|dummy|fake|mock/i.test(value)) {
400
+ continue
401
+ }
402
+
403
+ // Skip values that look like format descriptions
404
+ if (/^[a-z]+_[a-z]+_[a-z]+$/i.test(value) || /^your[_-]/i.test(value)) {
405
+ continue
406
+ }
407
+
408
+ // Skip example/placeholder values (more comprehensive check)
409
+ if (isExamplePlaceholder(line, value)) {
410
+ continue
411
+ }
412
+
413
+ // Skip secrets in variables with test/mock names (e.g., TEST_API_KEY, MOCK_SECRET)
414
+ if (hasTestVariableName(line)) {
415
+ continue
416
+ }
417
+
418
+ // Check for BYOK (Bring Your Own Key) context - this is a feature, not a vulnerability
419
+ if (isBYOKContext(line, filePath)) {
420
+ // Skip BYOK patterns entirely if they're properly handled in server context
421
+ if (isServerFile && isEnvVarReference(line)) {
422
+ continue
423
+ }
424
+ // For client-side BYOK forms, we still don't flag - it's user input
425
+ // Only flag if it's a hardcoded key being exposed
426
+ if (!isEnvVarReference(line) && line.includes('=') && /['"`][a-zA-Z0-9_-]{20,}['"`]/.test(line)) {
427
+ // This might be a hardcoded default key in a BYOK context - needs review
428
+ } else {
429
+ continue
430
+ }
431
+ }
432
+
433
+ // Determine severity based on context
434
+ let adjustedSeverity = secretPattern.severity
435
+ let requiresAIValidation = false
436
+ let adjustedDescription = secretPattern.description
437
+ let adjustedConfidence: 'high' | 'medium' | 'low' = 'high'
438
+
439
+ // Check if this is a Supabase service role key or JWT
440
+ const isSupabaseOrJWT =
441
+ secretPattern.name.includes('Supabase') ||
442
+ secretPattern.name.includes('JWT') ||
443
+ /eyJ[a-zA-Z0-9_-]+\.eyJ/.test(value)
444
+
445
+ if (isSupabaseOrJWT) {
446
+ // Use the comprehensive service role context checker
447
+ const serviceRoleContext = getServiceRoleKeyContext(line, filePath)
448
+
449
+ if (serviceRoleContext === 'safe_server') {
450
+ // Server-only + env var = expected pattern, skip entirely
451
+ continue
452
+ } else if (serviceRoleContext === 'client_exposure') {
453
+ // Client exposure = critical
454
+ adjustedSeverity = 'critical'
455
+ requiresAIValidation = true
456
+ adjustedDescription = isNextPublicEnvVar(line)
457
+ ? `${secretPattern.description} - EXPOSED via NEXT_PUBLIC_ prefix (client-accessible)`
458
+ : `${secretPattern.description} - may be exposed to client bundle`
459
+ } else {
460
+ // needs_review - check more context
461
+ if (isEnvVarReference(line)) {
462
+ // Using env var but context unclear - validate
463
+ adjustedSeverity = 'medium'
464
+ requiresAIValidation = true
465
+ adjustedDescription = `${secretPattern.description} - verify this is not exposed to client`
466
+ } else if (isServerFile) {
467
+ // Hardcoded in server file - bad but not critical
468
+ adjustedSeverity = 'high'
469
+ requiresAIValidation = true
470
+ adjustedDescription = `${secretPattern.description} - hardcoded in server file, should use env var`
471
+ } else {
472
+ // Hardcoded in unknown context - critical + needs validation
473
+ adjustedSeverity = 'critical'
474
+ requiresAIValidation = true
475
+ adjustedDescription = `${secretPattern.description} - hardcoded secret may be exposed`
476
+ }
477
+ }
478
+ }
479
+
480
+ // Downgrade test file severity
481
+ if (isTestFile) {
482
+ if (adjustedSeverity === 'critical') {
483
+ adjustedSeverity = 'medium'
484
+ } else if (adjustedSeverity === 'high') {
485
+ adjustedSeverity = 'low'
486
+ } else {
487
+ adjustedSeverity = 'info'
488
+ }
489
+ adjustedConfidence = 'low'
490
+ adjustedDescription = `${adjustedDescription} (in test file)`
491
+ }
492
+
493
+ // Generic patterns always require AI validation
494
+ const isGenericPattern = secretPattern.name.includes('Generic')
495
+ const finalRequiresAIValidation = requiresAIValidation || isGenericPattern
496
+
497
+ vulnerabilities.push({
498
+ id: `pattern-${filePath}-${index + 1}-${secretPattern.name}`,
499
+ filePath,
500
+ lineNumber: index + 1,
501
+ lineContent: line.trim(),
502
+ severity: adjustedSeverity,
503
+ category: 'hardcoded_secret',
504
+ title: secretPattern.name,
505
+ description: adjustedDescription,
506
+ suggestedFix: 'Move this secret to an environment variable. Never commit secrets to version control.',
507
+ confidence: adjustedConfidence,
508
+ layer: 1,
509
+ requiresAIValidation: finalRequiresAIValidation,
510
+ })
511
+ }
512
+ })
513
+ }
514
+
515
+ return vulnerabilities
516
+ }