@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,758 @@
1
+ /**
2
+ * Layer 2: AI Code Fingerprinting
3
+ * Detects patterns commonly found in AI-generated code that may indicate security risks
4
+ */
5
+
6
+ import type { Vulnerability, VulnerabilitySeverity } from '../types'
7
+ import { isExampleFile, isTestOrMockFile, isPlaceholderValue } from '../utils/context-helpers'
8
+
9
+ interface AIFingerprint {
10
+ name: string
11
+ pattern: RegExp
12
+ severity: VulnerabilitySeverity
13
+ description: string
14
+ suggestedFix: string
15
+ confidence: 'high' | 'medium' | 'low'
16
+ }
17
+
18
+ const AI_FINGERPRINTS: AIFingerprint[] = [
19
+ // ==================== Placeholder/TODO patterns - downgraded ====================
20
+ {
21
+ name: 'AI placeholder comment',
22
+ pattern: /\/\/\s*(TODO|FIXME|XXX|HACK):\s*(implement|add|replace|update|fix)\s+(this|here|later|authentication|validation|error handling)/gi,
23
+ severity: 'low', // Downgraded from medium - often harmless or addressed
24
+ description: 'AI-generated placeholder that may indicate incomplete implementation',
25
+ suggestedFix: 'Complete the implementation before deploying',
26
+ confidence: 'low', // Downgraded
27
+ },
28
+ {
29
+ name: 'Placeholder implementation',
30
+ // More specific: only match "placeholder implementation/code/function" not just "placeholder" in any context
31
+ pattern: /\/\/\s*placeholder\s+(implementation|code|function|method|logic|here)|\/\/\s*stub\s+(implementation|code|function|method)|\/\/\s*mock\s+implementation|\/\/\s*temporary\s+(implementation|code|fix|hack|workaround)/gi,
32
+ severity: 'low', // Downgraded from medium
33
+ description: 'Placeholder code that should be replaced with real implementation',
34
+ suggestedFix: 'Replace placeholder with actual implementation',
35
+ confidence: 'low', // Downgraded
36
+ },
37
+
38
+ // ==================== Overly permissive patterns ====================
39
+ {
40
+ name: 'AI catch-all error handler',
41
+ pattern: /catch\s*\([^)]*\)\s*\{\s*(console\.(log|error)|\/\/\s*handle)/gi,
42
+ severity: 'info', // Downgraded from low - this is standard practice
43
+ description: 'Generic error handling pattern - consider more specific handling',
44
+ suggestedFix: 'Add specific error handling based on error types',
45
+ confidence: 'low', // Downgraded
46
+ },
47
+ {
48
+ name: 'AI permissive CORS',
49
+ pattern: /cors\s*\(\s*\)|Access-Control-Allow-Origin['": ]*\*/gi,
50
+ severity: 'medium', // Downgraded from high - often intentional in dev
51
+ description: 'Overly permissive CORS configuration - verify if intentional',
52
+ suggestedFix: 'Restrict CORS to specific trusted origins in production',
53
+ confidence: 'medium', // Downgraded
54
+ },
55
+ // NOTE: 'any' type detection is now handled by detectSmartAnyUsage() function below
56
+ // This prevents overwhelming noise from internal utility 'any' usage
57
+
58
+ // ==================== Incomplete security patterns - heavily downgraded ====================
59
+ {
60
+ name: 'AI incomplete validation',
61
+ pattern: /if\s*\(\s*!?\s*(input|data|value|body|params)\s*\)\s*\{?\s*(return|throw)/gi,
62
+ severity: 'info', // Downgraded from low - this is often fine
63
+ description: 'Basic existence check - consider adding type validation if needed',
64
+ suggestedFix: 'Add comprehensive input validation with type and format checks',
65
+ confidence: 'low',
66
+ },
67
+ // NOTE: Removed 'AI basic auth check' pattern entirely - too many false positives
68
+ // Basic auth checks like if (!user) return are correct and common
69
+
70
+ // ==================== AI-specific comment patterns - suppressed (style only) ====================
71
+ // NOTE: Removed 'AI explanatory comment' pattern - verbose comments are style, not security
72
+ // NOTE: Removed 'AI step-by-step comment' pattern - step comments are style, not security
73
+
74
+ // ==================== Dangerous AI patterns ====================
75
+ {
76
+ name: 'AI hardcoded secret pattern',
77
+ pattern: /const\s+(API_KEY|SECRET|PASSWORD|TOKEN)\s*=\s*['"][^'"]+['"]/gi,
78
+ severity: 'critical',
79
+ description: 'Hardcoded secret - common mistake in AI-generated code',
80
+ suggestedFix: 'Move secrets to environment variables',
81
+ confidence: 'high',
82
+ },
83
+ {
84
+ name: 'AI example credentials',
85
+ pattern: /(admin|test|demo|example|sample|your)[_-]?(password|secret|key|token)\s*[=:]\s*['"][^'"]+['"]/gi,
86
+ severity: 'high',
87
+ description: 'Example/placeholder credentials that should be replaced',
88
+ suggestedFix: 'Replace example credentials with proper secret management',
89
+ confidence: 'high',
90
+ },
91
+ // NOTE: localhost/example URL detection moved to special handling below
92
+ // to allow context-aware skipping for config/example files
93
+
94
+ // ==================== AI code smell patterns - heavily downgraded ====================
95
+ {
96
+ name: 'AI console.log debugging',
97
+ pattern: /console\.log\s*\(\s*['"]?(debug|testing|here|check|log|data|result|response)/gi,
98
+ severity: 'info', // Downgraded from low - debug logs are common in dev
99
+ description: 'Debug logging that could be removed before production',
100
+ suggestedFix: 'Consider removing debug console.log statements or use proper logging',
101
+ confidence: 'low', // Downgraded
102
+ },
103
+ // NOTE: Removed 'AI magic number' pattern - magic numbers are style, not security
104
+ {
105
+ name: 'AI empty function body',
106
+ pattern: /function\s+\w+\s*\([^)]*\)\s*\{\s*(\/\/.*)?(\n\s*)?\}|=>\s*\{\s*(\/\/.*)?(\n\s*)?\}/gi,
107
+ severity: 'low', // Downgraded from medium
108
+ description: 'Empty function body - may be incomplete implementation',
109
+ suggestedFix: 'Implement the function or remove if not needed',
110
+ confidence: 'low', // Downgraded
111
+ },
112
+
113
+ // ==================== AI boilerplate patterns - heavily downgraded ====================
114
+ {
115
+ name: 'AI boilerplate error message',
116
+ pattern: /['"]Something went wrong['"]|['"]An error occurred['"]|['"]Error processing request['"]/gi,
117
+ severity: 'info', // Downgraded from low - generic messages are acceptable
118
+ description: 'Generic error message - consider more specific information',
119
+ suggestedFix: 'Replace with specific, actionable error messages',
120
+ confidence: 'low', // Downgraded
121
+ },
122
+ // NOTE: Removed 'AI success message' pattern - success messages are style, not security
123
+
124
+ // ==================== AI security bypass patterns - moderated ====================
125
+ {
126
+ name: 'AI disabled security for testing',
127
+ pattern: /\/\/\s*(disable|skip|bypass|ignore)\s*(for\s+)?(testing|development|now|temporarily)/gi,
128
+ severity: 'medium', // Downgraded from high - often intentional in dev
129
+ description: 'Security may be disabled for testing - verify production config',
130
+ suggestedFix: 'Remove testing bypasses and implement proper security',
131
+ confidence: 'medium', // Downgraded
132
+ },
133
+ {
134
+ name: 'AI TODO security',
135
+ pattern: /\/\/\s*TODO:\s*(add|implement|fix)\s*(security|auth|validation|sanitization)/gi,
136
+ severity: 'low', // Downgraded from high - often outdated or already addressed
137
+ description: 'Security feature marked as TODO - verify if addressed',
138
+ suggestedFix: 'Implement the security feature or remove if already done',
139
+ confidence: 'low', // Downgraded
140
+ },
141
+ ]
142
+
143
+ // ==================== Smart 'any' Type Detection ====================
144
+
145
+ interface AnyUsageContext {
146
+ lineNumber: number
147
+ lineContent: string
148
+ context: 'api_boundary' | 'database_layer' | 'auth_handler' | 'internal_util' | 'type_definition'
149
+ priority: number
150
+ }
151
+
152
+ /**
153
+ * Check if 'any' usage is a safe/common ORM pattern that should be ignored
154
+ */
155
+ function isSafeORMPattern(line: string): boolean {
156
+ const safePatterns = [
157
+ // Dexie/IndexedDB patterns
158
+ /\.equals\s*\(\s*null\s+as\s+any/i,
159
+ /\.equals\s*\(\s*\d+\s+as\s+any/i,
160
+ /\.equals\s*\(\s*['"`][^'"`]*['"`]\s+as\s+any/i,
161
+ /\.where\s*\(\s*.*as\s+any\s*\)/i,
162
+ /\.filter\s*\(\s*.*as\s+any\s*\)/i,
163
+
164
+ // Prisma patterns
165
+ /prisma\.\w+\.findMany/i,
166
+ /prisma\.\w+\.findFirst/i,
167
+ /prisma\.\w+\.findUnique/i,
168
+
169
+ // Supabase patterns
170
+ /supabase\.from\s*\(/i,
171
+
172
+ // Internal array maps over DB records (not untrusted input)
173
+ /\.map\s*\(\s*\(\s*\w+\s*:\s*any\s*\)\s*=>/i,
174
+ /\.filter\s*\(\s*\(\s*\w+\s*:\s*any\s*\)\s*=>/i,
175
+ /\.forEach\s*\(\s*\(\s*\w+\s*:\s*any\s*\)\s*=>/i,
176
+ /\.reduce\s*\(\s*\(\s*\w+\s*,\s*\w+\s*:\s*any\s*\)\s*=>/i,
177
+
178
+ // Type coercion for internal data
179
+ /\[\s*\d+\s*\]\s+as\s+any/, // Array index access
180
+ /\.data\s+as\s+any/i, // .data as any (common ORM pattern)
181
+ /\.result\s+as\s+any/i, // .result as any
182
+ /\.rows?\s+as\s+any/i, // .row or .rows as any
183
+ /\.records?\s+as\s+any/i, // .record or .records as any
184
+ ]
185
+
186
+ return safePatterns.some(p => p.test(line))
187
+ }
188
+
189
+ /**
190
+ * Check if 'any' usage is in a browser API event handler (safe pattern)
191
+ * These APIs often have incomplete TypeScript typings and require 'any' as a workaround
192
+ */
193
+ function isBrowserAPIEventHandler(line: string, filePath: string): boolean {
194
+ // Skip if this is likely a server-side file
195
+ if (/\/(api|server|backend|lib\/supabase)\//i.test(filePath)) {
196
+ return false
197
+ }
198
+
199
+ const browserAPIPatterns = [
200
+ // Web Speech API (SpeechRecognition)
201
+ /\.onresult\s*=\s*\(?.*:\s*any/i,
202
+ /\.onerror\s*=\s*\(?.*:\s*any/i,
203
+ /\.onend\s*=\s*\(?.*:\s*any/i,
204
+ /\.onstart\s*=\s*\(?.*:\s*any/i,
205
+ /\.onaudiostart\s*=\s*\(?.*:\s*any/i,
206
+ /\.onaudioend\s*=\s*\(?.*:\s*any/i,
207
+ /\.onspeechstart\s*=\s*\(?.*:\s*any/i,
208
+ /\.onspeechend\s*=\s*\(?.*:\s*any/i,
209
+ /speechRecognition/i,
210
+ /SpeechRecognition/i,
211
+ /webkitSpeechRecognition/i,
212
+
213
+ // MediaRecorder / Media APIs
214
+ /\.ondataavailable\s*=\s*\(?.*:\s*any/i,
215
+ /\.onstop\s*=\s*\(?.*:\s*any/i,
216
+ /\.onpause\s*=\s*\(?.*:\s*any/i,
217
+ /\.onresume\s*=\s*\(?.*:\s*any/i,
218
+ /mediaRecorder/i,
219
+ /MediaRecorder/i,
220
+ /MediaStream/i,
221
+ /getUserMedia/i,
222
+
223
+ // WebSocket events
224
+ /\.onopen\s*=\s*\(?.*:\s*any/i,
225
+ /\.onclose\s*=\s*\(?.*:\s*any/i,
226
+ /\.onmessage\s*=\s*\(?.*:\s*any/i,
227
+ /webSocket/i,
228
+ /WebSocket/i,
229
+
230
+ // WebRTC / PeerConnection
231
+ /\.onicecandidate\s*=\s*\(?.*:\s*any/i,
232
+ /\.ontrack\s*=\s*\(?.*:\s*any/i,
233
+ /\.onnegotiationneeded\s*=\s*\(?.*:\s*any/i,
234
+ /RTCPeerConnection/i,
235
+ /peerConnection/i,
236
+
237
+ // Generic browser event handlers with common event names
238
+ /\.(on[a-z]+)\s*=\s*\(\s*(?:event|e|evt)\s*:\s*any\s*\)\s*=>/i,
239
+ /addEventListener\s*\([^,]+,\s*\([^:]+:\s*any\)/i,
240
+
241
+ // React/UI library event handler patterns (Tiptap, ProseMirror, etc.)
242
+ /onStart\s*:\s*\(?.*:\s*any/i,
243
+ /onUpdate\s*:\s*\(?.*:\s*any/i,
244
+ /onTransaction\s*:\s*\(?.*:\s*any/i,
245
+ /onSelectionChange\s*:\s*\(?.*:\s*any/i,
246
+ /onBlur\s*:\s*\(?.*:\s*any/i,
247
+ /onFocus\s*:\s*\(?.*:\s*any/i,
248
+ /props\s*:\s*any/i, // Third-party library props (common workaround)
249
+
250
+ // Intersection Observer, Resize Observer, etc.
251
+ /IntersectionObserver/i,
252
+ /ResizeObserver/i,
253
+ /MutationObserver/i,
254
+ ]
255
+
256
+ return browserAPIPatterns.some(p => p.test(line))
257
+ }
258
+
259
+ /**
260
+ * Check if 'any' usage is on untrusted external input
261
+ */
262
+ function isUntrustedInputContext(line: string): boolean {
263
+ const untrustedPatterns = [
264
+ // Request body/params parsing
265
+ /await\s+request\.json\s*\(\s*\)\s*as\s+any/i,
266
+ /req\.body\s+as\s+any/i,
267
+ /request\.body\s+as\s+any/i,
268
+ /req\.params\s+as\s+any/i,
269
+ /req\.query\s+as\s+any/i,
270
+ /event\.body\s+as\s+any/i,
271
+
272
+ // External API responses (if not validated)
273
+ /fetch\s*\([^)]+\).*as\s+any/i,
274
+ /axios\.[^)]+\).*as\s+any/i,
275
+
276
+ // Direct parameter typing without validation
277
+ /\(\s*\w+\s*:\s*any\s*\)\s*=>\s*\{/, // Arrow function with any param (if in API context)
278
+ ]
279
+
280
+ return untrustedPatterns.some(p => p.test(line))
281
+ }
282
+
283
+ /**
284
+ * Categorize TypeScript 'any' usage by security context
285
+ * Returns sorted list by priority (highest risk first)
286
+ */
287
+ function categorizeAnyUsage(
288
+ lines: string[],
289
+ filePath: string
290
+ ): AnyUsageContext[] {
291
+ const usages: AnyUsageContext[] = []
292
+ const isAPIFile = /api|route|handler|controller|endpoint/.test(filePath.toLowerCase())
293
+ const isDBFile = /repository|model|database|query|prisma|supabase|dexie|db/.test(filePath.toLowerCase())
294
+ const isAuthFile = /auth|login|session|token|password|credential/.test(filePath.toLowerCase())
295
+
296
+ lines.forEach((line, idx) => {
297
+ // Skip if line doesn't contain 'any' type
298
+ if (!/:\s*any\b|<any>|as any/.test(line)) return
299
+
300
+ // Skip comments
301
+ const trimmed = line.trim()
302
+ if (
303
+ trimmed.startsWith('//') ||
304
+ trimmed.startsWith('/*') ||
305
+ trimmed.startsWith('*')
306
+ ) {
307
+ return
308
+ }
309
+
310
+ // Skip safe ORM/database patterns (Dexie, Prisma, Supabase, internal array maps)
311
+ if (isSafeORMPattern(line)) {
312
+ return
313
+ }
314
+
315
+ // Skip browser API event handlers (SpeechRecognition, MediaRecorder, WebSocket, etc.)
316
+ // These APIs often have incomplete TypeScript typings and 'any' is a legitimate workaround
317
+ if (isBrowserAPIEventHandler(line, filePath)) {
318
+ return
319
+ }
320
+
321
+ let context: AnyUsageContext['context'] = 'internal_util'
322
+ let priority = 1
323
+
324
+ // Check if this is untrusted input (highest priority)
325
+ if (isUntrustedInputContext(line)) {
326
+ context = 'api_boundary'
327
+ priority = 10
328
+ }
329
+ // API boundary detection - only if actually on untrusted data
330
+ else if (isAPIFile && /\b(req|request)\.(body|params|query|json)\b/.test(line)) {
331
+ context = 'api_boundary'
332
+ priority = 10
333
+ }
334
+ // Auth handler detection (high priority for auth bypass)
335
+ else if (isAuthFile && /\b(password|token|session|auth|verify|jwt|credential)\b/i.test(line)) {
336
+ context = 'auth_handler'
337
+ priority = 9
338
+ }
339
+ // Database layer - only flag if it's SQL string interpolation, not ORM methods
340
+ else if (isDBFile && /\.(execute|query|raw)\s*\(/i.test(line)) {
341
+ context = 'database_layer'
342
+ priority = 8
343
+ }
344
+ // Type definitions (lowest priority - often unavoidable)
345
+ else if (/\btype\s+\w+|interface\s+\w+|declare\s+/.test(line)) {
346
+ context = 'type_definition'
347
+ priority = 1
348
+ }
349
+ // Internal utilities / array operations on DB results (low priority, skip entirely for now)
350
+ else if (isDBFile || /\.map\s*\(|\.filter\s*\(|\.forEach\s*\(|\.reduce\s*\(/.test(line)) {
351
+ // Skip internal array operations - they're operating on already-fetched data
352
+ return
353
+ }
354
+ // Other internal utilities
355
+ else {
356
+ context = 'internal_util'
357
+ priority = 3
358
+ }
359
+
360
+ usages.push({
361
+ lineNumber: idx + 1,
362
+ lineContent: line.trim(),
363
+ context,
364
+ priority
365
+ })
366
+ })
367
+
368
+ // Sort by priority (highest first)
369
+ return usages.sort((a, b) => b.priority - a.priority)
370
+ }
371
+
372
+ /**
373
+ * Detect TypeScript 'any' usage at security boundaries ONLY
374
+ * Returns vulnerabilities for high-priority 'any' usage, capped at top 5 per file
375
+ */
376
+ function detectSmartAnyUsage(
377
+ lines: string[],
378
+ filePath: string
379
+ ): Vulnerability[] {
380
+ const vulnerabilities: Vulnerability[] = []
381
+
382
+ // Only scan TypeScript files
383
+ if (!/\.(ts|tsx)$/.test(filePath)) {
384
+ return vulnerabilities
385
+ }
386
+
387
+ // Categorize all 'any' usages by context
388
+ const anyUsageByContext = categorizeAnyUsage(lines, filePath)
389
+
390
+ // Only report high-priority 'any' usages (security boundaries)
391
+ const priorityAny = anyUsageByContext.filter(usage =>
392
+ usage.context === 'api_boundary' ||
393
+ usage.context === 'database_layer' ||
394
+ usage.context === 'auth_handler'
395
+ )
396
+
397
+ // Cap reporting to top 5 per file to avoid overwhelming reports
398
+ const cappedAny = priorityAny.slice(0, 5)
399
+
400
+ if (cappedAny.length === 0) {
401
+ return vulnerabilities
402
+ }
403
+
404
+ // If there are many 'any' usages, create a grouped finding
405
+ if (cappedAny.length >= 3) {
406
+ // Create single grouped vulnerability
407
+ const contexts = [...new Set(cappedAny.map(a => a.context))]
408
+ const contextDescriptions = contexts.map(ctx => {
409
+ const count = cappedAny.filter(a => a.context === ctx).length
410
+ const names: Record<string, string> = {
411
+ 'api_boundary': 'API request/response handlers',
412
+ 'database_layer': 'Database queries',
413
+ 'auth_handler': 'Authentication logic'
414
+ }
415
+ return `${count}x in ${names[ctx] || ctx}`
416
+ })
417
+
418
+ vulnerabilities.push({
419
+ id: `ai-fingerprint-any-${filePath}`,
420
+ filePath,
421
+ lineNumber: cappedAny[0].lineNumber,
422
+ lineContent: `Multiple TypeScript 'any' usages at security boundaries`,
423
+ severity: 'low',
424
+ category: 'ai_pattern',
425
+ title: `[AI Pattern] TypeScript 'any' at security boundaries (${cappedAny.length} instances)`,
426
+ description: `Found ${cappedAny.length} 'any' types at critical security boundaries: ${contextDescriptions.join(', ')}. ` +
427
+ `Lines: ${cappedAny.map(a => a.lineNumber).join(', ')}. ` +
428
+ `Consider using explicit types for type safety and to prevent type confusion vulnerabilities.`,
429
+ suggestedFix: 'Replace "any" with explicit types. For request handlers use typed schemas (Zod, Yup). For database queries use typed ORM models.',
430
+ confidence: 'medium',
431
+ layer: 2,
432
+ })
433
+ } else {
434
+ // Report individual findings for 1-2 high-priority 'any' usages
435
+ for (const usage of cappedAny) {
436
+ const contextNames: Record<string, string> = {
437
+ 'api_boundary': 'API request/response handler',
438
+ 'database_layer': 'Database query',
439
+ 'auth_handler': 'Authentication logic'
440
+ }
441
+
442
+ vulnerabilities.push({
443
+ id: `ai-fingerprint-any-${filePath}-${usage.lineNumber}`,
444
+ filePath,
445
+ lineNumber: usage.lineNumber,
446
+ lineContent: usage.lineContent,
447
+ severity: 'low',
448
+ category: 'ai_pattern',
449
+ title: `[AI Pattern] TypeScript 'any' in ${contextNames[usage.context] || usage.context}`,
450
+ description: `Using 'any' type at a security boundary bypasses type checking and can lead to type confusion vulnerabilities. ` +
451
+ `This is especially risky in ${contextNames[usage.context] || usage.context}.`,
452
+ suggestedFix: 'Replace "any" with an explicit type. Use typed request schemas, ORM models, or interface definitions.',
453
+ confidence: 'medium',
454
+ layer: 2,
455
+ })
456
+ }
457
+ }
458
+
459
+ return vulnerabilities
460
+ }
461
+
462
+ /**
463
+ * Detect managed AI endpoints without rate limiting (cost abuse risk)
464
+ * Finds routes using provider env keys without rate limiting protection
465
+ */
466
+ function detectManagedAICostAbuse(
467
+ content: string,
468
+ filePath: string,
469
+ lines: string[]
470
+ ): Vulnerability[] {
471
+ const vulnerabilities: Vulnerability[] = []
472
+
473
+ // Only check actual API route files, not utility/handler files
474
+ const isActualRouteFile = /\/(route|page)\.(ts|js|tsx|jsx)$/i.test(filePath) ||
475
+ /\/(api|routes?)\/.*\/index\.(ts|js)$/i.test(filePath)
476
+
477
+ // Files named as handlers, helpers, utils, fixtures etc. are NOT actual routes
478
+ const isUtilityFile = /(handler|helper|util|mock|test|fixture|safe|example|config)/i.test(filePath)
479
+
480
+ if (!isActualRouteFile || isUtilityFile) return vulnerabilities
481
+
482
+ // Check if file uses managed provider keys (from environment)
483
+ const managedKeyPatterns = [
484
+ /process\.env\.OPENAI_API_KEY/i,
485
+ /process\.env\.ANTHROPIC_API_KEY/i,
486
+ /process\.env\.\w*_(API_KEY|SECRET_KEY)/i,
487
+ /import\.meta\.env\.OPENAI/i,
488
+ /import\.meta\.env\.ANTHROPIC/i,
489
+ ]
490
+
491
+ const usesManagedKey = managedKeyPatterns.some(p => p.test(content))
492
+ if (!usesManagedKey) return vulnerabilities
493
+
494
+ // Skip if this is a config check (checking if key exists) rather than actual API usage
495
+ // Pattern: if (!process.env.OPENAI_API_KEY) or if (process.env.OPENAI_API_KEY === undefined)
496
+ const isConfigCheck = /if\s*\(\s*!?\s*process\.env\.\w*_API_KEY\s*[=!]|!process\.env\.\w*_API_KEY/i.test(content)
497
+ const hasActualAPICall = /\.chat\.completions|\.messages\.create|\.complete\(|anthropic\.\w+\(/i.test(content)
498
+
499
+ // If it's just a config check without actual API calls, skip
500
+ if (isConfigCheck && !hasActualAPICall) return vulnerabilities
501
+
502
+ // Check for rate limiting patterns nearby
503
+ const rateLimitPatterns = [
504
+ /rateLimit/i,
505
+ /rateLimiter/i,
506
+ /limiter/i,
507
+ /throttle/i,
508
+ /upstash.*ratelimit/i,
509
+ /redis.*limit/i,
510
+ /bucket/i,
511
+ /token.*bucket/i,
512
+ /sliding.*window/i,
513
+ /@upstash\/ratelimit/i,
514
+ /rate-limiter-flexible/i,
515
+ ]
516
+
517
+ const hasRateLimiting = rateLimitPatterns.some(p => p.test(content))
518
+
519
+ // Check for auth patterns - expanded to catch more middleware patterns
520
+ const authPatterns = [
521
+ /getServerSession/i,
522
+ /auth\(\)/i,
523
+ /auth\.protect/i,
524
+ /currentUser/i,
525
+ /getCurrentUser/i,
526
+ /getCurrentUserId/i,
527
+ /requireAuth/i,
528
+ /verifyToken/i,
529
+ /session\.user/i,
530
+ /authorization/i,
531
+ /withAuth/i,
532
+ /isAuthenticated/i,
533
+ /checkAuth/i,
534
+ /validateSession/i,
535
+ /clerk/i, // Clerk auth
536
+ /supabase.*auth/i, // Supabase auth
537
+ /nextauth/i, // NextAuth
538
+ /authMiddleware/i,
539
+ /protectedRoute/i,
540
+ /requireSession/i,
541
+ /userId.*=.*auth/i, // userId from auth
542
+ /user\.id/i, // Accessing user.id implies auth
543
+ ]
544
+
545
+ const hasAuth = authPatterns.some(p => p.test(content))
546
+
547
+ // Check if route is likely protected by middleware (file path based)
548
+ const isLikelyMiddlewareProtected =
549
+ /\/api\/(protected|private|admin|user|account|dashboard)\//i.test(filePath) ||
550
+ /\/\(authenticated\)\//i.test(filePath) || // Next.js route groups
551
+ /\/\(protected\)\//i.test(filePath) ||
552
+ /\/\(auth\)\//i.test(filePath)
553
+
554
+ // Determine severity based on auth + rate limiting
555
+ if (!hasRateLimiting) {
556
+ // Find the line with the env key usage
557
+ let keyLine = 1
558
+ for (let i = 0; i < lines.length; i++) {
559
+ if (managedKeyPatterns.some(p => p.test(lines[i]))) {
560
+ keyLine = i + 1
561
+ break
562
+ }
563
+ }
564
+
565
+ // If route is authenticated (inline or via middleware), this is just operational concern
566
+ if (hasAuth || isLikelyMiddlewareProtected) {
567
+ // Authenticated route without rate limiting - operational concern, not security vuln
568
+ vulnerabilities.push({
569
+ id: `ai-cost-abuse-${filePath}`,
570
+ filePath,
571
+ lineNumber: keyLine,
572
+ lineContent: lines[keyLine - 1]?.trim() || 'process.env.*_API_KEY',
573
+ severity: 'info',
574
+ category: 'ai_pattern',
575
+ title: 'Managed AI endpoint without rate limiting (authenticated)',
576
+ description: 'This authenticated API route uses a managed AI provider key but lacks rate limiting. Authenticated users could potentially abuse the endpoint. This is an operational concern, not a security vulnerability.',
577
+ suggestedFix: 'Consider adding per-user rate limiting (e.g., @upstash/ratelimit) to prevent cost abuse by authenticated users.',
578
+ confidence: 'low',
579
+ layer: 2,
580
+ })
581
+ } else {
582
+ // Unauthenticated route - higher risk
583
+ vulnerabilities.push({
584
+ id: `ai-cost-abuse-${filePath}`,
585
+ filePath,
586
+ lineNumber: keyLine,
587
+ lineContent: lines[keyLine - 1]?.trim() || 'process.env.*_API_KEY',
588
+ severity: 'medium',
589
+ category: 'ai_pattern',
590
+ title: 'Managed AI endpoint without authentication or rate limiting',
591
+ description: 'This API route uses a managed AI provider key without apparent authentication or rate limiting. This could allow unauthenticated cost abuse.',
592
+ suggestedFix: 'Add authentication or rate limiting (e.g., @upstash/ratelimit, rate-limiter-flexible) to prevent cost abuse.',
593
+ confidence: 'medium',
594
+ layer: 2,
595
+ })
596
+ }
597
+ }
598
+
599
+ return vulnerabilities
600
+ }
601
+
602
+ /**
603
+ * Check if line contains clearly placeholder credential values
604
+ */
605
+ function isPlaceholderCredential(line: string): boolean {
606
+ const placeholderPatterns = [
607
+ /your[-_]?api[-_]?key/i,
608
+ /your[-_]?secret/i,
609
+ /your[-_]?password/i,
610
+ /replace[-_]?with/i,
611
+ /example[-_]?key/i,
612
+ /sample[-_]?key/i,
613
+ /demo[-_]?key/i,
614
+ /test[-_]?key/i,
615
+ /fake[-_]?key/i,
616
+ /mock[-_]?key/i,
617
+ /placeholder/i,
618
+ /<.*>/, // <YOUR_KEY>
619
+ /\[.*\]/, // [API_KEY]
620
+ /xxx+/i,
621
+ ]
622
+ return placeholderPatterns.some(p => p.test(line))
623
+ }
624
+
625
+ /**
626
+ * Check if file path indicates a config/settings file
627
+ */
628
+ function isConfigFile(filePath: string): boolean {
629
+ const lowerPath = filePath.toLowerCase()
630
+ return /config|settings|constants|urls|endpoints|env/i.test(lowerPath)
631
+ }
632
+
633
+ export function detectAIFingerprints(
634
+ content: string,
635
+ filePath: string
636
+ ): Vulnerability[] {
637
+ const vulnerabilities: Vulnerability[] = []
638
+ const lines = content.split('\n')
639
+
640
+ // Skip example/demo files entirely - they contain placeholder code by design
641
+ if (isExampleFile(filePath)) {
642
+ return vulnerabilities
643
+ }
644
+
645
+ const isTestFile = isTestOrMockFile(filePath)
646
+ const isConfigOrSettings = isConfigFile(filePath)
647
+
648
+ // First, run smart 'any' detection (TypeScript files only, context-aware)
649
+ const anyVulns = detectSmartAnyUsage(lines, filePath)
650
+ vulnerabilities.push(...anyVulns)
651
+
652
+ // Detect managed AI cost abuse risk
653
+ const costAbuseVulns = detectManagedAICostAbuse(content, filePath, lines)
654
+ vulnerabilities.push(...costAbuseVulns)
655
+
656
+ // Track AI pattern density for file-level assessment
657
+ let aiPatternCount = 0
658
+
659
+ lines.forEach((line, index) => {
660
+ for (const fingerprint of AI_FINGERPRINTS) {
661
+ const regex = new RegExp(fingerprint.pattern.source, fingerprint.pattern.flags)
662
+
663
+ if (regex.test(line)) {
664
+ // Skip placeholder/example credentials for the "AI example credentials" pattern
665
+ if (fingerprint.name === 'AI example credentials') {
666
+ if (isPlaceholderCredential(line) || isPlaceholderValue('', line) || isTestFile) {
667
+ continue // Skip this pattern, check others
668
+ }
669
+ }
670
+
671
+ aiPatternCount++
672
+
673
+ // Downgrade severity for test files
674
+ let severity = fingerprint.severity
675
+ let confidence = fingerprint.confidence
676
+ if (isTestFile) {
677
+ if (severity === 'critical') severity = 'medium'
678
+ else if (severity === 'high') severity = 'low'
679
+ else severity = 'info'
680
+ confidence = 'low'
681
+ }
682
+
683
+ vulnerabilities.push({
684
+ id: `ai-fingerprint-${filePath}-${index + 1}-${fingerprint.name}`,
685
+ filePath,
686
+ lineNumber: index + 1,
687
+ lineContent: line.trim(),
688
+ severity,
689
+ category: 'ai_pattern',
690
+ title: `[AI Pattern] ${fingerprint.name}`,
691
+ description: fingerprint.description + (isTestFile ? ' (in test file)' : ''),
692
+ suggestedFix: fingerprint.suggestedFix,
693
+ confidence,
694
+ layer: 2,
695
+ })
696
+ break // Only report once per line
697
+ }
698
+ }
699
+ })
700
+
701
+ // Context-aware localhost/example URL detection
702
+ // Skip for config files and test files (they legitimately contain example URLs)
703
+ if (!isConfigOrSettings && !isTestFile) {
704
+ const localhostPattern = /['"]https?:\/\/(localhost|127\.0\.0\.1|example\.com|your-domain|api\.example)[^'"]*['"]/gi
705
+ lines.forEach((line, index) => {
706
+ if (localhostPattern.test(line)) {
707
+ // Reset regex state
708
+ localhostPattern.lastIndex = 0
709
+ // Skip if it's a comment
710
+ const trimmed = line.trim()
711
+ if (trimmed.startsWith('//') || trimmed.startsWith('/*') || trimmed.startsWith('*')) {
712
+ return
713
+ }
714
+ // Skip if it looks like env var fallback (process.env.X || "http://localhost")
715
+ if (/process\.env\.\w+\s*\|\|\s*['"]/.test(line)) {
716
+ return
717
+ }
718
+ vulnerabilities.push({
719
+ id: `ai-fingerprint-${filePath}-${index + 1}-localhost-url`,
720
+ filePath,
721
+ lineNumber: index + 1,
722
+ lineContent: line.trim(),
723
+ severity: 'medium',
724
+ category: 'ai_pattern',
725
+ title: '[AI Pattern] AI localhost/example URL',
726
+ description: 'Placeholder URL that should be replaced with actual endpoint',
727
+ suggestedFix: 'Replace with actual production URL from environment variable',
728
+ confidence: 'high',
729
+ layer: 2,
730
+ })
731
+ aiPatternCount++
732
+ }
733
+ })
734
+ }
735
+
736
+ // If file has high density of AI patterns, add a summary finding
737
+ const lineCount = lines.length
738
+ const aiDensity = aiPatternCount / Math.max(lineCount, 1)
739
+
740
+ // Raised threshold to 10% and require high-severity patterns to reduce noise
741
+ if (aiDensity > 0.10 && aiPatternCount >= 5) {
742
+ vulnerabilities.push({
743
+ id: `ai-fingerprint-${filePath}-summary`,
744
+ filePath,
745
+ lineNumber: 1,
746
+ lineContent: `File contains ${aiPatternCount} AI-generated code patterns`,
747
+ severity: 'medium',
748
+ category: 'ai_pattern',
749
+ title: '[AI Pattern] High AI-generated code density',
750
+ description: `This file shows ${aiPatternCount} patterns commonly found in AI-generated code. Consider a thorough security review.`,
751
+ suggestedFix: 'Review this file carefully for security issues, incomplete implementations, and placeholder code',
752
+ confidence: 'medium',
753
+ layer: 2,
754
+ })
755
+ }
756
+
757
+ return vulnerabilities
758
+ }