@oculum/scanner 1.0.12 → 1.0.13

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 (961) hide show
  1. package/dist/detect/ai-code/agent-tools.d.ts +22 -0
  2. package/dist/detect/ai-code/agent-tools.d.ts.map +1 -0
  3. package/dist/detect/ai-code/agent-tools.js +1509 -0
  4. package/dist/detect/ai-code/agent-tools.js.map +1 -0
  5. package/dist/detect/ai-code/byok-patterns.d.ts +15 -0
  6. package/dist/detect/ai-code/byok-patterns.d.ts.map +1 -0
  7. package/dist/detect/ai-code/byok-patterns.js +313 -0
  8. package/dist/detect/ai-code/byok-patterns.js.map +1 -0
  9. package/dist/detect/ai-code/endpoint-protection.d.ts +38 -0
  10. package/dist/detect/ai-code/endpoint-protection.d.ts.map +1 -0
  11. package/dist/detect/ai-code/endpoint-protection.js +349 -0
  12. package/dist/detect/ai-code/endpoint-protection.js.map +1 -0
  13. package/dist/detect/ai-code/execution-sinks.d.ts +21 -0
  14. package/dist/detect/ai-code/execution-sinks.d.ts.map +1 -0
  15. package/dist/detect/ai-code/execution-sinks.js +1158 -0
  16. package/dist/detect/ai-code/execution-sinks.js.map +1 -0
  17. package/dist/detect/ai-code/fingerprinting.d.ts +10 -0
  18. package/dist/detect/ai-code/fingerprinting.d.ts.map +1 -0
  19. package/dist/detect/ai-code/fingerprinting.js +665 -0
  20. package/dist/detect/ai-code/fingerprinting.js.map +1 -0
  21. package/dist/detect/ai-code/index.d.ts +12 -0
  22. package/dist/detect/ai-code/index.d.ts.map +1 -0
  23. package/dist/detect/ai-code/index.js +26 -0
  24. package/dist/detect/ai-code/index.js.map +1 -0
  25. package/dist/detect/ai-code/mcp-security.d.ts +20 -0
  26. package/dist/detect/ai-code/mcp-security.d.ts.map +1 -0
  27. package/dist/detect/ai-code/mcp-security.js +880 -0
  28. package/dist/detect/ai-code/mcp-security.js.map +1 -0
  29. package/dist/detect/ai-code/model-supply-chain.d.ts +23 -0
  30. package/dist/detect/ai-code/model-supply-chain.d.ts.map +1 -0
  31. package/dist/detect/ai-code/model-supply-chain.js +447 -0
  32. package/dist/detect/ai-code/model-supply-chain.js.map +1 -0
  33. package/dist/detect/ai-code/package-hallucination.d.ts +22 -0
  34. package/dist/detect/ai-code/package-hallucination.d.ts.map +1 -0
  35. package/dist/detect/ai-code/package-hallucination.js +841 -0
  36. package/dist/detect/ai-code/package-hallucination.js.map +1 -0
  37. package/dist/detect/ai-code/prompt-hygiene.d.ts +22 -0
  38. package/dist/detect/ai-code/prompt-hygiene.d.ts.map +1 -0
  39. package/dist/detect/ai-code/prompt-hygiene.js +1177 -0
  40. package/dist/detect/ai-code/prompt-hygiene.js.map +1 -0
  41. package/dist/detect/ai-code/rag-safety.d.ts +24 -0
  42. package/dist/detect/ai-code/rag-safety.d.ts.map +1 -0
  43. package/dist/detect/ai-code/rag-safety.js +913 -0
  44. package/dist/detect/ai-code/rag-safety.js.map +1 -0
  45. package/dist/detect/ai-code/schema-validation.d.ts +28 -0
  46. package/dist/detect/ai-code/schema-validation.d.ts.map +1 -0
  47. package/dist/detect/ai-code/schema-validation.js +378 -0
  48. package/dist/detect/ai-code/schema-validation.js.map +1 -0
  49. package/dist/detect/config/agent-skill-injection.d.ts +27 -0
  50. package/dist/detect/config/agent-skill-injection.d.ts.map +1 -0
  51. package/dist/detect/config/agent-skill-injection.js +472 -0
  52. package/dist/detect/config/agent-skill-injection.js.map +1 -0
  53. package/dist/detect/config/comments.d.ts +11 -0
  54. package/dist/detect/config/comments.d.ts.map +1 -0
  55. package/dist/detect/config/comments.js +206 -0
  56. package/dist/detect/config/comments.js.map +1 -0
  57. package/dist/detect/config/file-flags.d.ts +10 -0
  58. package/dist/detect/config/file-flags.d.ts.map +1 -0
  59. package/dist/detect/config/file-flags.js +124 -0
  60. package/dist/detect/config/file-flags.js.map +1 -0
  61. package/dist/detect/config/index.d.ts +7 -0
  62. package/dist/detect/config/index.d.ts.map +1 -0
  63. package/dist/detect/config/index.js +17 -0
  64. package/dist/detect/config/index.js.map +1 -0
  65. package/dist/detect/config/osv-check.d.ts +75 -0
  66. package/dist/detect/config/osv-check.d.ts.map +1 -0
  67. package/dist/detect/config/osv-check.js +309 -0
  68. package/dist/detect/config/osv-check.js.map +1 -0
  69. package/dist/detect/config/package-check.d.ts +63 -0
  70. package/dist/detect/config/package-check.d.ts.map +1 -0
  71. package/dist/detect/config/package-check.js +509 -0
  72. package/dist/detect/config/package-check.js.map +1 -0
  73. package/dist/detect/config/urls.d.ts +11 -0
  74. package/dist/detect/config/urls.d.ts.map +1 -0
  75. package/dist/detect/config/urls.js +450 -0
  76. package/dist/detect/config/urls.js.map +1 -0
  77. package/dist/detect/index.d.ts +37 -0
  78. package/dist/detect/index.d.ts.map +1 -0
  79. package/dist/detect/index.js +77 -0
  80. package/dist/detect/index.js.map +1 -0
  81. package/dist/detect/secrets/config-audit.d.ts +11 -0
  82. package/dist/detect/secrets/config-audit.d.ts.map +1 -0
  83. package/dist/detect/secrets/config-audit.js +315 -0
  84. package/dist/detect/secrets/config-audit.js.map +1 -0
  85. package/dist/detect/secrets/config-mcp-audit.d.ts +23 -0
  86. package/dist/detect/secrets/config-mcp-audit.d.ts.map +1 -0
  87. package/dist/detect/secrets/config-mcp-audit.js +243 -0
  88. package/dist/detect/secrets/config-mcp-audit.js.map +1 -0
  89. package/dist/detect/secrets/entropy.d.ts +11 -0
  90. package/dist/detect/secrets/entropy.d.ts.map +1 -0
  91. package/dist/detect/secrets/entropy.js +751 -0
  92. package/dist/detect/secrets/entropy.js.map +1 -0
  93. package/dist/detect/secrets/index.d.ts +36 -0
  94. package/dist/detect/secrets/index.d.ts.map +1 -0
  95. package/dist/detect/secrets/index.js +174 -0
  96. package/dist/detect/secrets/index.js.map +1 -0
  97. package/dist/detect/secrets/patterns.d.ts +11 -0
  98. package/dist/detect/secrets/patterns.d.ts.map +1 -0
  99. package/dist/detect/secrets/patterns.js +518 -0
  100. package/dist/detect/secrets/patterns.js.map +1 -0
  101. package/dist/detect/secrets/weak-crypto.d.ts +10 -0
  102. package/dist/detect/secrets/weak-crypto.d.ts.map +1 -0
  103. package/dist/detect/secrets/weak-crypto.js +432 -0
  104. package/dist/detect/secrets/weak-crypto.js.map +1 -0
  105. package/dist/detect/structural/auth-patterns.d.ts +22 -0
  106. package/dist/detect/structural/auth-patterns.d.ts.map +1 -0
  107. package/dist/detect/structural/auth-patterns.js +533 -0
  108. package/dist/detect/structural/auth-patterns.js.map +1 -0
  109. package/dist/detect/structural/dangerous-functions/child-process.d.ts +16 -0
  110. package/dist/detect/structural/dangerous-functions/child-process.d.ts.map +1 -0
  111. package/dist/detect/structural/dangerous-functions/child-process.js +74 -0
  112. package/dist/detect/structural/dangerous-functions/child-process.js.map +1 -0
  113. package/dist/detect/structural/dangerous-functions/dom-xss.d.ts +34 -0
  114. package/dist/detect/structural/dangerous-functions/dom-xss.d.ts.map +1 -0
  115. package/dist/detect/structural/dangerous-functions/dom-xss.js +230 -0
  116. package/dist/detect/structural/dangerous-functions/dom-xss.js.map +1 -0
  117. package/dist/detect/structural/dangerous-functions/index.d.ts +16 -0
  118. package/dist/detect/structural/dangerous-functions/index.d.ts.map +1 -0
  119. package/dist/detect/structural/dangerous-functions/index.js +1193 -0
  120. package/dist/detect/structural/dangerous-functions/index.js.map +1 -0
  121. package/dist/detect/structural/dangerous-functions/json-parse.d.ts +31 -0
  122. package/dist/detect/structural/dangerous-functions/json-parse.d.ts.map +1 -0
  123. package/dist/detect/structural/dangerous-functions/json-parse.js +326 -0
  124. package/dist/detect/structural/dangerous-functions/json-parse.js.map +1 -0
  125. package/dist/detect/structural/dangerous-functions/math-random.d.ts +111 -0
  126. package/dist/detect/structural/dangerous-functions/math-random.d.ts.map +1 -0
  127. package/dist/detect/structural/dangerous-functions/math-random.js +684 -0
  128. package/dist/detect/structural/dangerous-functions/math-random.js.map +1 -0
  129. package/dist/detect/structural/dangerous-functions/patterns.d.ts +21 -0
  130. package/dist/detect/structural/dangerous-functions/patterns.d.ts.map +1 -0
  131. package/dist/detect/structural/dangerous-functions/patterns.js +163 -0
  132. package/dist/detect/structural/dangerous-functions/patterns.js.map +1 -0
  133. package/dist/detect/structural/dangerous-functions/request-validation.d.ts +13 -0
  134. package/dist/detect/structural/dangerous-functions/request-validation.d.ts.map +1 -0
  135. package/dist/detect/structural/dangerous-functions/request-validation.js +126 -0
  136. package/dist/detect/structural/dangerous-functions/request-validation.js.map +1 -0
  137. package/dist/detect/structural/dangerous-functions/utils/control-flow.d.ts +24 -0
  138. package/dist/detect/structural/dangerous-functions/utils/control-flow.d.ts.map +1 -0
  139. package/dist/detect/structural/dangerous-functions/utils/control-flow.js +70 -0
  140. package/dist/detect/structural/dangerous-functions/utils/control-flow.js.map +1 -0
  141. package/dist/detect/structural/dangerous-functions/utils/helpers.d.ts +31 -0
  142. package/dist/detect/structural/dangerous-functions/utils/helpers.d.ts.map +1 -0
  143. package/dist/detect/structural/dangerous-functions/utils/helpers.js +147 -0
  144. package/dist/detect/structural/dangerous-functions/utils/helpers.js.map +1 -0
  145. package/dist/detect/structural/dangerous-functions/utils/index.d.ts +9 -0
  146. package/dist/detect/structural/dangerous-functions/utils/index.d.ts.map +1 -0
  147. package/dist/detect/structural/dangerous-functions/utils/index.js +23 -0
  148. package/dist/detect/structural/dangerous-functions/utils/index.js.map +1 -0
  149. package/dist/detect/structural/dangerous-functions/utils/schema-validation.d.ts +22 -0
  150. package/dist/detect/structural/dangerous-functions/utils/schema-validation.d.ts.map +1 -0
  151. package/dist/detect/structural/dangerous-functions/utils/schema-validation.js +102 -0
  152. package/dist/detect/structural/dangerous-functions/utils/schema-validation.js.map +1 -0
  153. package/dist/detect/structural/data-exposure.d.ts +19 -0
  154. package/dist/detect/structural/data-exposure.d.ts.map +1 -0
  155. package/dist/detect/structural/data-exposure.js +262 -0
  156. package/dist/detect/structural/data-exposure.js.map +1 -0
  157. package/dist/detect/structural/framework-checks.d.ts +10 -0
  158. package/dist/detect/structural/framework-checks.d.ts.map +1 -0
  159. package/dist/detect/structural/framework-checks.js +389 -0
  160. package/dist/detect/structural/framework-checks.js.map +1 -0
  161. package/dist/detect/structural/index.d.ts +71 -0
  162. package/dist/detect/structural/index.d.ts.map +1 -0
  163. package/dist/detect/structural/index.js +510 -0
  164. package/dist/detect/structural/index.js.map +1 -0
  165. package/dist/detect/structural/log-injection.d.ts +18 -0
  166. package/dist/detect/structural/log-injection.d.ts.map +1 -0
  167. package/dist/detect/structural/log-injection.js +217 -0
  168. package/dist/detect/structural/log-injection.js.map +1 -0
  169. package/dist/detect/structural/logic-gates.d.ts +10 -0
  170. package/dist/detect/structural/logic-gates.d.ts.map +1 -0
  171. package/dist/detect/structural/logic-gates.js +227 -0
  172. package/dist/detect/structural/logic-gates.js.map +1 -0
  173. package/dist/detect/structural/risky-imports.d.ts +10 -0
  174. package/dist/detect/structural/risky-imports.d.ts.map +1 -0
  175. package/dist/detect/structural/risky-imports.js +168 -0
  176. package/dist/detect/structural/risky-imports.js.map +1 -0
  177. package/dist/detect/structural/security-headers.d.ts +18 -0
  178. package/dist/detect/structural/security-headers.d.ts.map +1 -0
  179. package/dist/detect/structural/security-headers.js +196 -0
  180. package/dist/detect/structural/security-headers.js.map +1 -0
  181. package/dist/detect/structural/ssrf-detection.d.ts +18 -0
  182. package/dist/detect/structural/ssrf-detection.d.ts.map +1 -0
  183. package/dist/detect/structural/ssrf-detection.js +263 -0
  184. package/dist/detect/structural/ssrf-detection.js.map +1 -0
  185. package/dist/detect/structural/variables.d.ts +11 -0
  186. package/dist/detect/structural/variables.d.ts.map +1 -0
  187. package/dist/detect/structural/variables.js +159 -0
  188. package/dist/detect/structural/variables.js.map +1 -0
  189. package/dist/detect/structural/xxe-detection.d.ts +18 -0
  190. package/dist/detect/structural/xxe-detection.d.ts.map +1 -0
  191. package/dist/detect/structural/xxe-detection.js +245 -0
  192. package/dist/detect/structural/xxe-detection.js.map +1 -0
  193. package/dist/index.d.ts +17 -64
  194. package/dist/index.d.ts.map +1 -1
  195. package/dist/index.js +49 -1034
  196. package/dist/index.js.map +1 -1
  197. package/dist/layer2/framework-checks.d.ts.map +1 -1
  198. package/dist/layer2/framework-checks.js +1 -8
  199. package/dist/layer2/framework-checks.js.map +1 -1
  200. package/dist/layer2/index.d.ts +4 -0
  201. package/dist/layer2/index.d.ts.map +1 -1
  202. package/dist/layer2/index.js +50 -1
  203. package/dist/layer2/index.js.map +1 -1
  204. package/dist/layer2/log-injection.d.ts +18 -0
  205. package/dist/layer2/log-injection.d.ts.map +1 -0
  206. package/dist/layer2/log-injection.js +214 -0
  207. package/dist/layer2/log-injection.js.map +1 -0
  208. package/dist/layer2/security-headers.d.ts +18 -0
  209. package/dist/layer2/security-headers.d.ts.map +1 -0
  210. package/dist/layer2/security-headers.js +187 -0
  211. package/dist/layer2/security-headers.js.map +1 -0
  212. package/dist/layer2/ssrf-detection.d.ts +18 -0
  213. package/dist/layer2/ssrf-detection.d.ts.map +1 -0
  214. package/dist/layer2/ssrf-detection.js +252 -0
  215. package/dist/layer2/ssrf-detection.js.map +1 -0
  216. package/dist/layer2/xxe-detection.d.ts +18 -0
  217. package/dist/layer2/xxe-detection.d.ts.map +1 -0
  218. package/dist/layer2/xxe-detection.js +242 -0
  219. package/dist/layer2/xxe-detection.js.map +1 -0
  220. package/dist/layer3/anthropic/prompts/index.d.ts +1 -1
  221. package/dist/layer3/anthropic/prompts/index.d.ts.map +1 -1
  222. package/dist/layer3/anthropic/prompts/index.js +3 -1
  223. package/dist/layer3/anthropic/prompts/index.js.map +1 -1
  224. package/dist/layer3/anthropic/prompts/modules/ai-patterns.d.ts +19 -0
  225. package/dist/layer3/anthropic/prompts/modules/ai-patterns.d.ts.map +1 -0
  226. package/dist/layer3/anthropic/prompts/modules/ai-patterns.js +156 -0
  227. package/dist/layer3/anthropic/prompts/modules/ai-patterns.js.map +1 -0
  228. package/dist/layer3/anthropic/prompts/modules/auth-access.d.ts +9 -0
  229. package/dist/layer3/anthropic/prompts/modules/auth-access.d.ts.map +1 -0
  230. package/dist/layer3/anthropic/prompts/modules/auth-access.js +25 -0
  231. package/dist/layer3/anthropic/prompts/modules/auth-access.js.map +1 -0
  232. package/dist/layer3/anthropic/prompts/modules/common.d.ts +11 -0
  233. package/dist/layer3/anthropic/prompts/modules/common.d.ts.map +1 -0
  234. package/dist/layer3/anthropic/prompts/modules/common.js +152 -0
  235. package/dist/layer3/anthropic/prompts/modules/common.js.map +1 -0
  236. package/dist/layer3/anthropic/prompts/modules/index.d.ts +54 -0
  237. package/dist/layer3/anthropic/prompts/modules/index.d.ts.map +1 -0
  238. package/dist/layer3/anthropic/prompts/modules/index.js +185 -0
  239. package/dist/layer3/anthropic/prompts/modules/index.js.map +1 -0
  240. package/dist/layer3/anthropic/prompts/modules/owasp-classic.d.ts +8 -0
  241. package/dist/layer3/anthropic/prompts/modules/owasp-classic.d.ts.map +1 -0
  242. package/dist/layer3/anthropic/prompts/modules/owasp-classic.js +84 -0
  243. package/dist/layer3/anthropic/prompts/modules/owasp-classic.js.map +1 -0
  244. package/dist/layer3/anthropic/prompts/modules/secrets-crypto.d.ts +8 -0
  245. package/dist/layer3/anthropic/prompts/modules/secrets-crypto.d.ts.map +1 -0
  246. package/dist/layer3/anthropic/prompts/modules/secrets-crypto.js +68 -0
  247. package/dist/layer3/anthropic/prompts/modules/secrets-crypto.js.map +1 -0
  248. package/dist/layer3/anthropic/prompts/modules/xss-prompt.d.ts +8 -0
  249. package/dist/layer3/anthropic/prompts/modules/xss-prompt.d.ts.map +1 -0
  250. package/dist/layer3/anthropic/prompts/modules/xss-prompt.js +22 -0
  251. package/dist/layer3/anthropic/prompts/modules/xss-prompt.js.map +1 -0
  252. package/dist/layer3/anthropic/prompts/validation.d.ts +9 -3
  253. package/dist/layer3/anthropic/prompts/validation.d.ts.map +1 -1
  254. package/dist/layer3/anthropic/prompts/validation.js +14 -410
  255. package/dist/layer3/anthropic/prompts/validation.js.map +1 -1
  256. package/dist/layer3/anthropic/providers/anthropic.d.ts.map +1 -1
  257. package/dist/layer3/anthropic/providers/anthropic.js +6 -3
  258. package/dist/layer3/anthropic/providers/anthropic.js.map +1 -1
  259. package/dist/layer3/anthropic/providers/openai.d.ts.map +1 -1
  260. package/dist/layer3/anthropic/providers/openai.js +6 -3
  261. package/dist/layer3/anthropic/providers/openai.js.map +1 -1
  262. package/dist/layer3/anthropic/request-builder.d.ts +11 -4
  263. package/dist/layer3/anthropic/request-builder.d.ts.map +1 -1
  264. package/dist/layer3/anthropic/request-builder.js +32 -16
  265. package/dist/layer3/anthropic/request-builder.js.map +1 -1
  266. package/dist/layer3/anthropic/utils/context-extractor.d.ts +55 -0
  267. package/dist/layer3/anthropic/utils/context-extractor.d.ts.map +1 -0
  268. package/dist/layer3/anthropic/utils/context-extractor.js +161 -0
  269. package/dist/layer3/anthropic/utils/context-extractor.js.map +1 -0
  270. package/dist/layer3/anthropic/utils/index.d.ts +2 -0
  271. package/dist/layer3/anthropic/utils/index.d.ts.map +1 -1
  272. package/dist/layer3/anthropic/utils/index.js +4 -1
  273. package/dist/layer3/anthropic/utils/index.js.map +1 -1
  274. package/dist/model/auth-helper-detector.d.ts +56 -0
  275. package/dist/model/auth-helper-detector.d.ts.map +1 -0
  276. package/dist/model/auth-helper-detector.js +360 -0
  277. package/dist/model/auth-helper-detector.js.map +1 -0
  278. package/dist/model/cross-file-taint.d.ts +40 -0
  279. package/dist/model/cross-file-taint.d.ts.map +1 -0
  280. package/dist/model/cross-file-taint.js +290 -0
  281. package/dist/model/cross-file-taint.js.map +1 -0
  282. package/dist/model/framework-models/django.d.ts +9 -0
  283. package/dist/model/framework-models/django.d.ts.map +1 -0
  284. package/dist/model/framework-models/django.js +82 -0
  285. package/dist/model/framework-models/django.js.map +1 -0
  286. package/dist/model/framework-models/express.d.ts +9 -0
  287. package/dist/model/framework-models/express.d.ts.map +1 -0
  288. package/dist/model/framework-models/express.js +52 -0
  289. package/dist/model/framework-models/express.js.map +1 -0
  290. package/dist/model/framework-models/index.d.ts +20 -0
  291. package/dist/model/framework-models/index.d.ts.map +1 -0
  292. package/dist/model/framework-models/index.js +102 -0
  293. package/dist/model/framework-models/index.js.map +1 -0
  294. package/dist/model/framework-models/nextjs.d.ts +9 -0
  295. package/dist/model/framework-models/nextjs.d.ts.map +1 -0
  296. package/dist/model/framework-models/nextjs.js +71 -0
  297. package/dist/model/framework-models/nextjs.js.map +1 -0
  298. package/dist/model/framework-models/prisma.d.ts +10 -0
  299. package/dist/model/framework-models/prisma.d.ts.map +1 -0
  300. package/dist/model/framework-models/prisma.js +54 -0
  301. package/dist/model/framework-models/prisma.js.map +1 -0
  302. package/dist/model/framework-models/react.d.ts +9 -0
  303. package/dist/model/framework-models/react.d.ts.map +1 -0
  304. package/dist/model/framework-models/react.js +67 -0
  305. package/dist/model/framework-models/react.js.map +1 -0
  306. package/dist/model/framework-models/sequelize.d.ts +9 -0
  307. package/dist/model/framework-models/sequelize.d.ts.map +1 -0
  308. package/dist/model/framework-models/sequelize.js +62 -0
  309. package/dist/model/framework-models/sequelize.js.map +1 -0
  310. package/dist/model/framework-models/types.d.ts +43 -0
  311. package/dist/model/framework-models/types.d.ts.map +1 -0
  312. package/dist/model/framework-models/types.js +10 -0
  313. package/dist/model/framework-models/types.js.map +1 -0
  314. package/dist/model/function-classifier.d.ts +32 -0
  315. package/dist/model/function-classifier.d.ts.map +1 -0
  316. package/dist/model/function-classifier.js +143 -0
  317. package/dist/model/function-classifier.js.map +1 -0
  318. package/dist/model/import-resolver.d.ts +45 -0
  319. package/dist/model/import-resolver.d.ts.map +1 -0
  320. package/dist/model/import-resolver.js +410 -0
  321. package/dist/model/import-resolver.js.map +1 -0
  322. package/dist/model/imported-auth-detector.d.ts +38 -0
  323. package/dist/model/imported-auth-detector.d.ts.map +1 -0
  324. package/dist/model/imported-auth-detector.js +199 -0
  325. package/dist/model/imported-auth-detector.js.map +1 -0
  326. package/dist/model/index.d.ts +63 -0
  327. package/dist/model/index.d.ts.map +1 -0
  328. package/dist/model/index.js +272 -0
  329. package/dist/model/index.js.map +1 -0
  330. package/dist/model/middleware-detector.d.ts +55 -0
  331. package/dist/model/middleware-detector.d.ts.map +1 -0
  332. package/dist/model/middleware-detector.js +382 -0
  333. package/dist/model/middleware-detector.js.map +1 -0
  334. package/dist/model/module-graph.d.ts +46 -0
  335. package/dist/model/module-graph.d.ts.map +1 -0
  336. package/dist/model/module-graph.js +187 -0
  337. package/dist/model/module-graph.js.map +1 -0
  338. package/dist/model/oauth-flow-detector.d.ts +41 -0
  339. package/dist/model/oauth-flow-detector.d.ts.map +1 -0
  340. package/dist/model/oauth-flow-detector.js +202 -0
  341. package/dist/model/oauth-flow-detector.js.map +1 -0
  342. package/dist/model/project-context.d.ts +119 -0
  343. package/dist/model/project-context.d.ts.map +1 -0
  344. package/dist/model/project-context.js +534 -0
  345. package/dist/model/project-context.js.map +1 -0
  346. package/dist/model/route-auth-resolver.d.ts +27 -0
  347. package/dist/model/route-auth-resolver.d.ts.map +1 -0
  348. package/dist/model/route-auth-resolver.js +182 -0
  349. package/dist/model/route-auth-resolver.js.map +1 -0
  350. package/dist/model/route-discovery/express.d.ts +25 -0
  351. package/dist/model/route-discovery/express.d.ts.map +1 -0
  352. package/dist/model/route-discovery/express.js +225 -0
  353. package/dist/model/route-discovery/express.js.map +1 -0
  354. package/dist/model/route-discovery/index.d.ts +21 -0
  355. package/dist/model/route-discovery/index.d.ts.map +1 -0
  356. package/dist/model/route-discovery/index.js +67 -0
  357. package/dist/model/route-discovery/index.js.map +1 -0
  358. package/dist/model/route-discovery/nextjs.d.ts +16 -0
  359. package/dist/model/route-discovery/nextjs.d.ts.map +1 -0
  360. package/dist/model/route-discovery/nextjs.js +179 -0
  361. package/dist/model/route-discovery/nextjs.js.map +1 -0
  362. package/dist/model/route-discovery/python.d.ts +16 -0
  363. package/dist/model/route-discovery/python.d.ts.map +1 -0
  364. package/dist/model/route-discovery/python.js +181 -0
  365. package/dist/model/route-discovery/python.js.map +1 -0
  366. package/dist/model/route-discovery/types.d.ts +36 -0
  367. package/dist/model/route-discovery/types.d.ts.map +1 -0
  368. package/dist/model/route-discovery/types.js +16 -0
  369. package/dist/model/route-discovery/types.js.map +1 -0
  370. package/dist/model/route-discovery/utils.d.ts +18 -0
  371. package/dist/model/route-discovery/utils.d.ts.map +1 -0
  372. package/dist/model/route-discovery/utils.js +55 -0
  373. package/dist/model/route-discovery/utils.js.map +1 -0
  374. package/dist/model/route-hierarchy.d.ts +50 -0
  375. package/dist/model/route-hierarchy.d.ts.map +1 -0
  376. package/dist/model/route-hierarchy.js +226 -0
  377. package/dist/model/route-hierarchy.js.map +1 -0
  378. package/dist/model/sanitiser-detection.d.ts +27 -0
  379. package/dist/model/sanitiser-detection.d.ts.map +1 -0
  380. package/dist/model/sanitiser-detection.js +224 -0
  381. package/dist/model/sanitiser-detection.js.map +1 -0
  382. package/dist/model/sink-matcher.d.ts +17 -0
  383. package/dist/model/sink-matcher.d.ts.map +1 -0
  384. package/dist/model/sink-matcher.js +141 -0
  385. package/dist/model/sink-matcher.js.map +1 -0
  386. package/dist/model/sink-patterns.d.ts +19 -0
  387. package/dist/model/sink-patterns.d.ts.map +1 -0
  388. package/dist/model/sink-patterns.js +88 -0
  389. package/dist/model/sink-patterns.js.map +1 -0
  390. package/dist/model/source-discovery.d.ts +15 -0
  391. package/dist/model/source-discovery.d.ts.map +1 -0
  392. package/dist/model/source-discovery.js +170 -0
  393. package/dist/model/source-discovery.js.map +1 -0
  394. package/dist/model/taint-tracker.d.ts +21 -0
  395. package/dist/model/taint-tracker.d.ts.map +1 -0
  396. package/dist/model/taint-tracker.js +281 -0
  397. package/dist/model/taint-tracker.js.map +1 -0
  398. package/dist/model/taint-types.d.ts +74 -0
  399. package/dist/model/taint-types.d.ts.map +1 -0
  400. package/dist/model/taint-types.js +9 -0
  401. package/dist/model/taint-types.js.map +1 -0
  402. package/dist/model/trpc-analyzer.d.ts +78 -0
  403. package/dist/model/trpc-analyzer.d.ts.map +1 -0
  404. package/dist/model/trpc-analyzer.js +297 -0
  405. package/dist/model/trpc-analyzer.js.map +1 -0
  406. package/dist/parse/file-classifier.d.ts +228 -0
  407. package/dist/parse/file-classifier.d.ts.map +1 -0
  408. package/dist/parse/file-classifier.js +933 -0
  409. package/dist/parse/file-classifier.js.map +1 -0
  410. package/dist/parse/path-exclusions.d.ts +55 -0
  411. package/dist/parse/path-exclusions.d.ts.map +1 -0
  412. package/dist/parse/path-exclusions.js +224 -0
  413. package/dist/parse/path-exclusions.js.map +1 -0
  414. package/dist/pipeline/config.d.ts +39 -0
  415. package/dist/pipeline/config.d.ts.map +1 -0
  416. package/dist/pipeline/config.js +46 -0
  417. package/dist/pipeline/config.js.map +1 -0
  418. package/dist/pipeline/index.d.ts +34 -0
  419. package/dist/pipeline/index.d.ts.map +1 -0
  420. package/dist/pipeline/index.js +377 -0
  421. package/dist/pipeline/index.js.map +1 -0
  422. package/dist/pipeline/modes/incremental.d.ts +66 -0
  423. package/dist/pipeline/modes/incremental.d.ts.map +1 -0
  424. package/dist/pipeline/modes/incremental.js +200 -0
  425. package/dist/pipeline/modes/incremental.js.map +1 -0
  426. package/dist/postprocess/aggregation.d.ts +14 -0
  427. package/dist/postprocess/aggregation.d.ts.map +1 -0
  428. package/dist/postprocess/aggregation.js +63 -0
  429. package/dist/postprocess/aggregation.js.map +1 -0
  430. package/dist/postprocess/contradictions.d.ts +18 -0
  431. package/dist/postprocess/contradictions.d.ts.map +1 -0
  432. package/dist/postprocess/contradictions.js +99 -0
  433. package/dist/postprocess/contradictions.js.map +1 -0
  434. package/dist/postprocess/dedup.d.ts +13 -0
  435. package/dist/postprocess/dedup.d.ts.map +1 -0
  436. package/dist/postprocess/dedup.js +58 -0
  437. package/dist/postprocess/dedup.js.map +1 -0
  438. package/dist/postprocess/filtering/context-adjustments.d.ts +23 -0
  439. package/dist/postprocess/filtering/context-adjustments.d.ts.map +1 -0
  440. package/dist/postprocess/filtering/context-adjustments.js +100 -0
  441. package/dist/postprocess/filtering/context-adjustments.js.map +1 -0
  442. package/dist/postprocess/filtering/index.d.ts +3 -0
  443. package/dist/postprocess/filtering/index.d.ts.map +1 -0
  444. package/dist/postprocess/filtering/index.js +8 -0
  445. package/dist/postprocess/filtering/index.js.map +1 -0
  446. package/dist/postprocess/filtering/pipeline.d.ts +48 -0
  447. package/dist/postprocess/filtering/pipeline.d.ts.map +1 -0
  448. package/dist/postprocess/filtering/pipeline.js +76 -0
  449. package/dist/postprocess/filtering/pipeline.js.map +1 -0
  450. package/dist/postprocess/index.d.ts +41 -0
  451. package/dist/postprocess/index.d.ts.map +1 -0
  452. package/dist/postprocess/index.js +85 -0
  453. package/dist/postprocess/index.js.map +1 -0
  454. package/dist/postprocess/suppression/config-loader.d.ts +74 -0
  455. package/dist/postprocess/suppression/config-loader.d.ts.map +1 -0
  456. package/dist/postprocess/suppression/config-loader.js +424 -0
  457. package/dist/postprocess/suppression/config-loader.js.map +1 -0
  458. package/dist/postprocess/suppression/hash.d.ts +48 -0
  459. package/dist/postprocess/suppression/hash.d.ts.map +1 -0
  460. package/dist/postprocess/suppression/hash.js +88 -0
  461. package/dist/postprocess/suppression/hash.js.map +1 -0
  462. package/dist/postprocess/suppression/index.d.ts +11 -0
  463. package/dist/postprocess/suppression/index.d.ts.map +1 -0
  464. package/dist/postprocess/suppression/index.js +39 -0
  465. package/dist/postprocess/suppression/index.js.map +1 -0
  466. package/dist/postprocess/suppression/inline-parser.d.ts +39 -0
  467. package/dist/postprocess/suppression/inline-parser.d.ts.map +1 -0
  468. package/dist/postprocess/suppression/inline-parser.js +218 -0
  469. package/dist/postprocess/suppression/inline-parser.js.map +1 -0
  470. package/dist/postprocess/suppression/manager.d.ts +94 -0
  471. package/dist/postprocess/suppression/manager.d.ts.map +1 -0
  472. package/dist/postprocess/suppression/manager.js +292 -0
  473. package/dist/postprocess/suppression/manager.js.map +1 -0
  474. package/dist/postprocess/suppression/types.d.ts +151 -0
  475. package/dist/postprocess/suppression/types.d.ts.map +1 -0
  476. package/dist/postprocess/suppression/types.js +28 -0
  477. package/dist/postprocess/suppression/types.js.map +1 -0
  478. package/dist/postprocess/validation-cap.d.ts +17 -0
  479. package/dist/postprocess/validation-cap.d.ts.map +1 -0
  480. package/dist/postprocess/validation-cap.js +64 -0
  481. package/dist/postprocess/validation-cap.js.map +1 -0
  482. package/dist/report/build-result.d.ts +33 -0
  483. package/dist/report/build-result.d.ts.map +1 -0
  484. package/dist/report/build-result.js +59 -0
  485. package/dist/report/build-result.js.map +1 -0
  486. package/dist/report/enrichment.d.ts +19 -0
  487. package/dist/report/enrichment.d.ts.map +1 -0
  488. package/dist/report/enrichment.js +44 -0
  489. package/dist/report/enrichment.js.map +1 -0
  490. package/dist/report/formatters/ai-context.d.ts +23 -0
  491. package/dist/report/formatters/ai-context.d.ts.map +1 -0
  492. package/dist/report/formatters/ai-context.js +238 -0
  493. package/dist/report/formatters/ai-context.js.map +1 -0
  494. package/dist/report/formatters/cli-terminal.d.ts +65 -0
  495. package/dist/report/formatters/cli-terminal.d.ts.map +1 -0
  496. package/dist/report/formatters/cli-terminal.js +735 -0
  497. package/dist/report/formatters/cli-terminal.js.map +1 -0
  498. package/dist/report/formatters/github-comment.d.ts +41 -0
  499. package/dist/report/formatters/github-comment.d.ts.map +1 -0
  500. package/dist/report/formatters/github-comment.js +370 -0
  501. package/dist/report/formatters/github-comment.js.map +1 -0
  502. package/dist/report/formatters/grouping.d.ts +52 -0
  503. package/dist/report/formatters/grouping.d.ts.map +1 -0
  504. package/dist/report/formatters/grouping.js +152 -0
  505. package/dist/report/formatters/grouping.js.map +1 -0
  506. package/dist/report/formatters/ide/claude-code.d.ts +17 -0
  507. package/dist/report/formatters/ide/claude-code.d.ts.map +1 -0
  508. package/dist/report/formatters/ide/claude-code.js +94 -0
  509. package/dist/report/formatters/ide/claude-code.js.map +1 -0
  510. package/dist/report/formatters/ide/cursor.d.ts +13 -0
  511. package/dist/report/formatters/ide/cursor.d.ts.map +1 -0
  512. package/dist/report/formatters/ide/cursor.js +125 -0
  513. package/dist/report/formatters/ide/cursor.js.map +1 -0
  514. package/dist/report/formatters/ide/index.d.ts +62 -0
  515. package/dist/report/formatters/ide/index.d.ts.map +1 -0
  516. package/dist/report/formatters/ide/index.js +184 -0
  517. package/dist/report/formatters/ide/index.js.map +1 -0
  518. package/dist/report/formatters/ide/windsurf.d.ts +13 -0
  519. package/dist/report/formatters/ide/windsurf.d.ts.map +1 -0
  520. package/dist/report/formatters/ide/windsurf.js +117 -0
  521. package/dist/report/formatters/ide/windsurf.js.map +1 -0
  522. package/dist/report/formatters/index.d.ts +11 -0
  523. package/dist/report/formatters/index.d.ts.map +1 -0
  524. package/dist/report/formatters/index.js +54 -0
  525. package/dist/report/formatters/index.js.map +1 -0
  526. package/dist/report/formatters/vscode-diagnostic.d.ts +103 -0
  527. package/dist/report/formatters/vscode-diagnostic.d.ts.map +1 -0
  528. package/dist/report/formatters/vscode-diagnostic.js +151 -0
  529. package/dist/report/formatters/vscode-diagnostic.js.map +1 -0
  530. package/dist/report/summary.d.ts +27 -0
  531. package/dist/report/summary.d.ts.map +1 -0
  532. package/dist/report/summary.js +57 -0
  533. package/dist/report/summary.js.map +1 -0
  534. package/dist/rules/metadata.d.ts.map +1 -1
  535. package/dist/rules/metadata.js +66 -0
  536. package/dist/rules/metadata.js.map +1 -1
  537. package/dist/score/adjustments.d.ts +22 -0
  538. package/dist/score/adjustments.d.ts.map +1 -0
  539. package/dist/score/adjustments.js +373 -0
  540. package/dist/score/adjustments.js.map +1 -0
  541. package/dist/score/auto-dismiss.d.ts +28 -0
  542. package/dist/score/auto-dismiss.d.ts.map +1 -0
  543. package/dist/score/auto-dismiss.js +200 -0
  544. package/dist/score/auto-dismiss.js.map +1 -0
  545. package/dist/score/confidence.d.ts +19 -0
  546. package/dist/score/confidence.d.ts.map +1 -0
  547. package/dist/score/confidence.js +52 -0
  548. package/dist/score/confidence.js.map +1 -0
  549. package/dist/score/index.d.ts +61 -0
  550. package/dist/score/index.d.ts.map +1 -0
  551. package/dist/score/index.js +250 -0
  552. package/dist/score/index.js.map +1 -0
  553. package/dist/score/types.d.ts +160 -0
  554. package/dist/score/types.d.ts.map +1 -0
  555. package/dist/score/types.js +14 -0
  556. package/dist/score/types.js.map +1 -0
  557. package/dist/shared/ai-context/index.d.ts +6 -0
  558. package/dist/shared/ai-context/index.d.ts.map +1 -0
  559. package/dist/shared/ai-context/index.js +13 -0
  560. package/dist/shared/ai-context/index.js.map +1 -0
  561. package/dist/shared/ai-context/manager.d.ts +67 -0
  562. package/dist/shared/ai-context/manager.d.ts.map +1 -0
  563. package/dist/shared/ai-context/manager.js +104 -0
  564. package/dist/shared/ai-context/manager.js.map +1 -0
  565. package/dist/shared/baseline/diff.d.ts +32 -0
  566. package/dist/shared/baseline/diff.d.ts.map +1 -0
  567. package/dist/shared/baseline/diff.js +119 -0
  568. package/dist/shared/baseline/diff.js.map +1 -0
  569. package/dist/shared/baseline/index.d.ts +9 -0
  570. package/dist/shared/baseline/index.d.ts.map +1 -0
  571. package/dist/shared/baseline/index.js +19 -0
  572. package/dist/shared/baseline/index.js.map +1 -0
  573. package/dist/shared/baseline/manager.d.ts +67 -0
  574. package/dist/shared/baseline/manager.d.ts.map +1 -0
  575. package/dist/shared/baseline/manager.js +180 -0
  576. package/dist/shared/baseline/manager.js.map +1 -0
  577. package/dist/shared/baseline/types.d.ts +91 -0
  578. package/dist/shared/baseline/types.d.ts.map +1 -0
  579. package/dist/shared/baseline/types.js +12 -0
  580. package/dist/shared/baseline/types.js.map +1 -0
  581. package/dist/shared/category-filter.d.ts +125 -0
  582. package/dist/shared/category-filter.d.ts.map +1 -0
  583. package/dist/shared/category-filter.js +360 -0
  584. package/dist/shared/category-filter.js.map +1 -0
  585. package/dist/shared/code-analysis.d.ts +39 -0
  586. package/dist/shared/code-analysis.d.ts.map +1 -0
  587. package/dist/shared/code-analysis.js +159 -0
  588. package/dist/shared/code-analysis.js.map +1 -0
  589. package/dist/shared/comment-analyzer.d.ts +38 -0
  590. package/dist/shared/comment-analyzer.d.ts.map +1 -0
  591. package/dist/shared/comment-analyzer.js +218 -0
  592. package/dist/shared/comment-analyzer.js.map +1 -0
  593. package/dist/shared/diff-detector.d.ts +53 -0
  594. package/dist/shared/diff-detector.d.ts.map +1 -0
  595. package/dist/shared/diff-detector.js +104 -0
  596. package/dist/shared/diff-detector.js.map +1 -0
  597. package/dist/shared/diff-parser.d.ts +80 -0
  598. package/dist/shared/diff-parser.d.ts.map +1 -0
  599. package/dist/shared/diff-parser.js +202 -0
  600. package/dist/shared/diff-parser.js.map +1 -0
  601. package/dist/shared/environment-context.d.ts +76 -0
  602. package/dist/shared/environment-context.d.ts.map +1 -0
  603. package/dist/shared/environment-context.js +271 -0
  604. package/dist/shared/environment-context.js.map +1 -0
  605. package/dist/shared/intent-detector.d.ts +66 -0
  606. package/dist/shared/intent-detector.d.ts.map +1 -0
  607. package/dist/shared/intent-detector.js +282 -0
  608. package/dist/shared/intent-detector.js.map +1 -0
  609. package/dist/shared/parsed-file.d.ts +51 -0
  610. package/dist/shared/parsed-file.d.ts.map +1 -0
  611. package/dist/shared/parsed-file.js +95 -0
  612. package/dist/shared/parsed-file.js.map +1 -0
  613. package/dist/shared/registry-clients.d.ts +93 -0
  614. package/dist/shared/registry-clients.d.ts.map +1 -0
  615. package/dist/shared/registry-clients.js +273 -0
  616. package/dist/shared/registry-clients.js.map +1 -0
  617. package/dist/shared/rules/framework-fixes.d.ts +48 -0
  618. package/dist/shared/rules/framework-fixes.d.ts.map +1 -0
  619. package/dist/shared/rules/framework-fixes.js +439 -0
  620. package/dist/shared/rules/framework-fixes.js.map +1 -0
  621. package/dist/shared/rules/index.d.ts +8 -0
  622. package/dist/shared/rules/index.d.ts.map +1 -0
  623. package/dist/shared/rules/index.js +18 -0
  624. package/dist/shared/rules/index.js.map +1 -0
  625. package/dist/shared/rules/metadata.d.ts +43 -0
  626. package/dist/shared/rules/metadata.d.ts.map +1 -0
  627. package/dist/shared/rules/metadata.js +819 -0
  628. package/dist/shared/rules/metadata.js.map +1 -0
  629. package/dist/shared/schema-semantics.d.ts +45 -0
  630. package/dist/shared/schema-semantics.d.ts.map +1 -0
  631. package/dist/shared/schema-semantics.js +193 -0
  632. package/dist/shared/schema-semantics.js.map +1 -0
  633. package/dist/shared/types.d.ts +337 -0
  634. package/dist/shared/types.d.ts.map +1 -0
  635. package/dist/shared/types.js +126 -0
  636. package/dist/shared/types.js.map +1 -0
  637. package/dist/tiers.d.ts +2 -2
  638. package/dist/tiers.d.ts.map +1 -1
  639. package/dist/tiers.js +10 -0
  640. package/dist/tiers.js.map +1 -1
  641. package/dist/types.d.ts +1 -1
  642. package/dist/types.d.ts.map +1 -1
  643. package/dist/types.js.map +1 -1
  644. package/dist/validate/clients.d.ts +44 -0
  645. package/dist/validate/clients.d.ts.map +1 -0
  646. package/dist/validate/clients.js +81 -0
  647. package/dist/validate/clients.js.map +1 -0
  648. package/dist/validate/index.d.ts +41 -0
  649. package/dist/validate/index.d.ts.map +1 -0
  650. package/dist/validate/index.js +141 -0
  651. package/dist/validate/index.js.map +1 -0
  652. package/dist/validate/prompts/index.d.ts +8 -0
  653. package/dist/validate/prompts/index.d.ts.map +1 -0
  654. package/dist/validate/prompts/index.js +16 -0
  655. package/dist/validate/prompts/index.js.map +1 -0
  656. package/dist/validate/prompts/modules/ai-patterns.d.ts +19 -0
  657. package/dist/validate/prompts/modules/ai-patterns.d.ts.map +1 -0
  658. package/dist/validate/prompts/modules/ai-patterns.js +156 -0
  659. package/dist/validate/prompts/modules/ai-patterns.js.map +1 -0
  660. package/dist/validate/prompts/modules/auth-access.d.ts +9 -0
  661. package/dist/validate/prompts/modules/auth-access.d.ts.map +1 -0
  662. package/dist/validate/prompts/modules/auth-access.js +25 -0
  663. package/dist/validate/prompts/modules/auth-access.js.map +1 -0
  664. package/dist/validate/prompts/modules/common.d.ts +11 -0
  665. package/dist/validate/prompts/modules/common.d.ts.map +1 -0
  666. package/dist/validate/prompts/modules/common.js +186 -0
  667. package/dist/validate/prompts/modules/common.js.map +1 -0
  668. package/dist/validate/prompts/modules/index.d.ts +54 -0
  669. package/dist/validate/prompts/modules/index.d.ts.map +1 -0
  670. package/dist/validate/prompts/modules/index.js +186 -0
  671. package/dist/validate/prompts/modules/index.js.map +1 -0
  672. package/dist/validate/prompts/modules/owasp-classic.d.ts +8 -0
  673. package/dist/validate/prompts/modules/owasp-classic.d.ts.map +1 -0
  674. package/dist/validate/prompts/modules/owasp-classic.js +84 -0
  675. package/dist/validate/prompts/modules/owasp-classic.js.map +1 -0
  676. package/dist/validate/prompts/modules/secrets-crypto.d.ts +8 -0
  677. package/dist/validate/prompts/modules/secrets-crypto.d.ts.map +1 -0
  678. package/dist/validate/prompts/modules/secrets-crypto.js +68 -0
  679. package/dist/validate/prompts/modules/secrets-crypto.js.map +1 -0
  680. package/dist/validate/prompts/modules/xss-prompt.d.ts +8 -0
  681. package/dist/validate/prompts/modules/xss-prompt.d.ts.map +1 -0
  682. package/dist/validate/prompts/modules/xss-prompt.js +22 -0
  683. package/dist/validate/prompts/modules/xss-prompt.js.map +1 -0
  684. package/dist/validate/prompts/semantic-analysis.d.ts +15 -0
  685. package/dist/validate/prompts/semantic-analysis.d.ts.map +1 -0
  686. package/dist/validate/prompts/semantic-analysis.js +169 -0
  687. package/dist/validate/prompts/semantic-analysis.js.map +1 -0
  688. package/dist/validate/prompts/validation.d.ts +18 -0
  689. package/dist/validate/prompts/validation.d.ts.map +1 -0
  690. package/dist/validate/prompts/validation.js +25 -0
  691. package/dist/validate/prompts/validation.js.map +1 -0
  692. package/dist/validate/providers/anthropic.d.ts +17 -0
  693. package/dist/validate/providers/anthropic.d.ts.map +1 -0
  694. package/dist/validate/providers/anthropic.js +260 -0
  695. package/dist/validate/providers/anthropic.js.map +1 -0
  696. package/dist/validate/providers/index.d.ts +8 -0
  697. package/dist/validate/providers/index.d.ts.map +1 -0
  698. package/dist/validate/providers/index.js +13 -0
  699. package/dist/validate/providers/index.js.map +1 -0
  700. package/dist/validate/providers/openai.d.ts +14 -0
  701. package/dist/validate/providers/openai.d.ts.map +1 -0
  702. package/dist/validate/providers/openai.js +336 -0
  703. package/dist/validate/providers/openai.js.map +1 -0
  704. package/dist/validate/request-builder.d.ts +61 -0
  705. package/dist/validate/request-builder.d.ts.map +1 -0
  706. package/dist/validate/request-builder.js +346 -0
  707. package/dist/validate/request-builder.js.map +1 -0
  708. package/dist/validate/types.d.ts +88 -0
  709. package/dist/validate/types.d.ts.map +1 -0
  710. package/dist/validate/types.js +38 -0
  711. package/dist/validate/types.js.map +1 -0
  712. package/dist/validate/utils/context-extractor.d.ts +55 -0
  713. package/dist/validate/utils/context-extractor.d.ts.map +1 -0
  714. package/dist/validate/utils/context-extractor.js +161 -0
  715. package/dist/validate/utils/context-extractor.js.map +1 -0
  716. package/dist/validate/utils/index.d.ts +11 -0
  717. package/dist/validate/utils/index.d.ts.map +1 -0
  718. package/dist/validate/utils/index.js +27 -0
  719. package/dist/validate/utils/index.js.map +1 -0
  720. package/dist/validate/utils/path-helpers.d.ts +21 -0
  721. package/dist/validate/utils/path-helpers.d.ts.map +1 -0
  722. package/dist/validate/utils/path-helpers.js +69 -0
  723. package/dist/validate/utils/path-helpers.js.map +1 -0
  724. package/dist/validate/utils/response-parser.d.ts +40 -0
  725. package/dist/validate/utils/response-parser.d.ts.map +1 -0
  726. package/dist/validate/utils/response-parser.js +286 -0
  727. package/dist/validate/utils/response-parser.js.map +1 -0
  728. package/dist/validate/utils/retry.d.ts +15 -0
  729. package/dist/validate/utils/retry.d.ts.map +1 -0
  730. package/dist/validate/utils/retry.js +62 -0
  731. package/dist/validate/utils/retry.js.map +1 -0
  732. package/package.json +8 -7
  733. package/src/__tests__/benchmark/fixtures/layer1/agent-skill-injection.ts +204 -0
  734. package/src/__tests__/benchmark/fixtures/layer1/index.ts +3 -0
  735. package/src/__tests__/benchmark/fixtures/layer2/index.ts +15 -0
  736. package/src/__tests__/benchmark/fixtures/layer2/log-injection.ts +147 -0
  737. package/src/__tests__/benchmark/fixtures/layer2/security-headers.ts +197 -0
  738. package/src/__tests__/benchmark/fixtures/layer2/ssrf-detection.ts +210 -0
  739. package/src/__tests__/benchmark/fixtures/layer2/xxe-detection.ts +195 -0
  740. package/src/__tests__/benchmark/run-depth-validation.ts +3 -3
  741. package/src/__tests__/benchmark/run-real-world-test.ts +4 -4
  742. package/src/__tests__/benchmark/types.ts +1 -1
  743. package/src/__tests__/benchmark/utils/test-runner.ts +3 -3
  744. package/src/__tests__/category-filter.test.ts +2 -2
  745. package/src/__tests__/context-engine/cross-file-taint.test.ts +284 -0
  746. package/src/__tests__/context-engine/framework-models.test.ts +457 -0
  747. package/src/__tests__/context-engine/function-classifier.test.ts +146 -0
  748. package/src/__tests__/context-engine/import-resolver.test.ts +328 -0
  749. package/src/__tests__/context-engine/integration.test.ts +320 -0
  750. package/src/__tests__/context-engine/module-graph.test.ts +159 -0
  751. package/src/__tests__/context-engine/route-discovery/auth-resolver.test.ts +353 -0
  752. package/src/__tests__/context-engine/route-discovery/express.test.ts +150 -0
  753. package/src/__tests__/context-engine/route-discovery/nextjs.test.ts +138 -0
  754. package/src/__tests__/context-engine/route-discovery/python.test.ts +95 -0
  755. package/src/__tests__/context-engine/sanitiser-detection.test.ts +187 -0
  756. package/src/__tests__/context-engine/sink-matcher.test.ts +251 -0
  757. package/src/__tests__/context-engine/source-discovery.test.ts +186 -0
  758. package/src/__tests__/context-engine/taint-tracker.test.ts +182 -0
  759. package/src/__tests__/regression/agent-skill-benign.test.ts +174 -0
  760. package/src/__tests__/regression/known-false-positives.test.ts +312 -4
  761. package/src/__tests__/score/adjustments.test.ts +385 -0
  762. package/src/__tests__/score/confidence.test.ts +283 -0
  763. package/src/__tests__/score/framework-scoring.test.ts +275 -0
  764. package/src/__tests__/score/route-scoring.test.ts +156 -0
  765. package/src/__tests__/score/scoring-integration.test.ts +165 -0
  766. package/src/__tests__/score/taint-adjustments.test.ts +244 -0
  767. package/src/__tests__/snapshots/__snapshots__/anthropic-validation-refactor.test.ts.snap +37 -49
  768. package/src/__tests__/snapshots/__snapshots__/dangerous-functions-refactor.test.ts.snap +52 -0
  769. package/src/__tests__/snapshots/__snapshots__/scan-depth.test.ts.snap +3 -3
  770. package/src/__tests__/snapshots/anthropic-validation-refactor.test.ts +2 -2
  771. package/src/__tests__/snapshots/dangerous-functions-refactor.test.ts +1 -1
  772. package/src/__tests__/snapshots/scan-depth.test.ts +3 -3
  773. package/src/__tests__/validate/route-annotations.test.ts +138 -0
  774. package/src/__tests__/validation/analyze-results.ts +1 -1
  775. package/src/__tests__/validation/extract-for-triage.ts +1 -1
  776. package/src/__tests__/validation/fp-deep-analysis.ts +1 -1
  777. package/src/{layer2/ai-agent-tools.ts → detect/ai-code/agent-tools.ts} +23 -3
  778. package/src/{layer2 → detect/ai-code}/byok-patterns.ts +17 -5
  779. package/src/{layer2/ai-endpoint-protection.ts → detect/ai-code/endpoint-protection.ts} +8 -4
  780. package/src/{layer2/ai-execution-sinks.ts → detect/ai-code/execution-sinks.ts} +8 -4
  781. package/src/{layer2/ai-fingerprinting.ts → detect/ai-code/fingerprinting.ts} +20 -4
  782. package/src/detect/ai-code/index.ts +11 -0
  783. package/src/{layer2/ai-mcp-security.ts → detect/ai-code/mcp-security.ts} +7 -3
  784. package/src/{layer2 → detect/ai-code}/model-supply-chain.ts +7 -3
  785. package/src/{layer2/ai-package-hallucination.ts → detect/ai-code/package-hallucination.ts} +18 -3
  786. package/src/{layer2/ai-prompt-hygiene.ts → detect/ai-code/prompt-hygiene.ts} +25 -3
  787. package/src/{layer2/ai-rag-safety.ts → detect/ai-code/rag-safety.ts} +7 -3
  788. package/src/{layer2/ai-schema-validation.ts → detect/ai-code/schema-validation.ts} +7 -3
  789. package/src/detect/config/agent-skill-injection.ts +551 -0
  790. package/src/{layer1 → detect/config}/comments.ts +6 -2
  791. package/src/{layer1 → detect/config}/file-flags.ts +9 -3
  792. package/src/detect/config/index.ts +6 -0
  793. package/src/{layer3 → detect/config}/osv-check.ts +3 -2
  794. package/src/{layer3 → detect/config}/package-check.ts +3 -2
  795. package/src/{layer1 → detect/config}/urls.ts +12 -5
  796. package/src/detect/index.ts +131 -0
  797. package/src/{layer1 → detect/secrets}/config-audit.ts +7 -2
  798. package/src/{layer1 → detect/secrets}/config-mcp-audit.ts +8 -3
  799. package/src/{layer1 → detect/secrets}/entropy.ts +23 -11
  800. package/src/{layer1 → detect/secrets}/index.ts +31 -30
  801. package/src/{layer1 → detect/secrets}/patterns.ts +10 -3
  802. package/src/{layer1 → detect/secrets}/weak-crypto.ts +7 -2
  803. package/src/{layer2/auth-antipatterns.ts → detect/structural/auth-patterns.ts} +23 -11
  804. package/src/{layer2 → detect/structural}/dangerous-functions/dom-xss.ts +1 -1
  805. package/src/{layer2 → detect/structural}/dangerous-functions/index.ts +47 -24
  806. package/src/{layer2 → detect/structural}/dangerous-functions/json-parse.ts +10 -2
  807. package/src/{layer2 → detect/structural}/dangerous-functions/math-random.ts +2 -2
  808. package/src/{layer2 → detect/structural}/dangerous-functions/patterns.ts +1 -1
  809. package/src/{layer2 → detect/structural}/dangerous-functions/request-validation.ts +10 -2
  810. package/src/{layer2 → detect/structural}/dangerous-functions/utils/control-flow.ts +2 -2
  811. package/src/{layer2 → detect/structural}/data-exposure.ts +11 -3
  812. package/src/{layer2 → detect/structural}/framework-checks.ts +10 -11
  813. package/src/{layer2 → detect/structural}/index.ts +80 -77
  814. package/src/detect/structural/log-injection.ts +254 -0
  815. package/src/{layer2 → detect/structural}/logic-gates.ts +13 -5
  816. package/src/{layer2 → detect/structural}/risky-imports.ts +7 -3
  817. package/src/detect/structural/security-headers.ts +231 -0
  818. package/src/detect/structural/ssrf-detection.ts +300 -0
  819. package/src/{layer2 → detect/structural}/variables.ts +7 -3
  820. package/src/detect/structural/xxe-detection.ts +295 -0
  821. package/src/index.ts +39 -1291
  822. package/src/{utils → model}/auth-helper-detector.ts +1 -1
  823. package/src/model/cross-file-taint.ts +374 -0
  824. package/src/model/framework-models/django.ts +82 -0
  825. package/src/model/framework-models/express.ts +54 -0
  826. package/src/model/framework-models/index.ts +116 -0
  827. package/src/model/framework-models/nextjs.ts +69 -0
  828. package/src/model/framework-models/prisma.ts +57 -0
  829. package/src/model/framework-models/react.ts +63 -0
  830. package/src/model/framework-models/sequelize.ts +63 -0
  831. package/src/model/framework-models/types.ts +46 -0
  832. package/src/model/function-classifier.ts +184 -0
  833. package/src/model/import-resolver.ts +453 -0
  834. package/src/{utils → model}/imported-auth-detector.ts +21 -85
  835. package/src/model/index.ts +353 -0
  836. package/src/{utils → model}/middleware-detector.ts +156 -17
  837. package/src/model/module-graph.ts +254 -0
  838. package/src/{utils → model}/oauth-flow-detector.ts +1 -1
  839. package/src/{utils/project-context-builder.ts → model/project-context.ts} +1 -1
  840. package/src/model/route-auth-resolver.ts +216 -0
  841. package/src/model/route-discovery/express.ts +251 -0
  842. package/src/model/route-discovery/index.ts +83 -0
  843. package/src/model/route-discovery/nextjs.ts +216 -0
  844. package/src/model/route-discovery/python.ts +214 -0
  845. package/src/model/route-discovery/types.ts +48 -0
  846. package/src/model/route-discovery/utils.ts +54 -0
  847. package/src/model/sanitiser-detection.ts +268 -0
  848. package/src/model/sink-matcher.ts +178 -0
  849. package/src/model/sink-patterns.ts +109 -0
  850. package/src/model/source-discovery.ts +209 -0
  851. package/src/model/taint-tracker.ts +333 -0
  852. package/src/model/taint-types.ts +149 -0
  853. package/src/{utils → model}/trpc-analyzer.ts +1 -1
  854. package/src/{utils/context-helpers.ts → parse/file-classifier.ts} +54 -0
  855. package/src/{utils → parse}/path-exclusions.ts +1 -1
  856. package/src/pipeline/config.ts +81 -0
  857. package/src/pipeline/index.ts +437 -0
  858. package/src/{modes → pipeline/modes}/incremental.ts +5 -5
  859. package/src/postprocess/aggregation.ts +74 -0
  860. package/src/postprocess/contradictions.ts +128 -0
  861. package/src/postprocess/dedup.ts +62 -0
  862. package/src/{filtering → postprocess/filtering}/__tests__/pipeline.test.ts +1 -1
  863. package/src/{filtering → postprocess/filtering}/context-adjustments.ts +2 -2
  864. package/src/{filtering → postprocess/filtering}/pipeline.ts +2 -2
  865. package/src/postprocess/index.ts +118 -0
  866. package/src/{suppression → postprocess/suppression}/config-loader.ts +1 -1
  867. package/src/{suppression → postprocess/suppression}/hash.ts +1 -1
  868. package/src/{suppression → postprocess/suppression}/inline-parser.ts +1 -1
  869. package/src/{suppression → postprocess/suppression}/manager.ts +1 -1
  870. package/src/{suppression → postprocess/suppression}/types.ts +2 -2
  871. package/src/postprocess/validation-cap.ts +66 -0
  872. package/src/report/build-result.ts +94 -0
  873. package/src/report/enrichment.ts +52 -0
  874. package/src/{formatters → report/formatters}/ai-context.ts +1 -1
  875. package/src/{formatters → report/formatters}/cli-terminal.ts +11 -11
  876. package/src/{formatters → report/formatters}/github-comment.ts +1 -1
  877. package/src/{formatters → report/formatters}/grouping.ts +8 -8
  878. package/src/{formatters → report/formatters}/ide/claude-code.ts +1 -1
  879. package/src/{formatters → report/formatters}/ide/cursor.ts +1 -1
  880. package/src/{formatters → report/formatters}/ide/windsurf.ts +1 -1
  881. package/src/{formatters → report/formatters}/vscode-diagnostic.ts +1 -1
  882. package/src/report/summary.ts +70 -0
  883. package/src/score/adjustments.ts +387 -0
  884. package/src/{layer3/anthropic → score}/auto-dismiss.ts +15 -14
  885. package/src/score/confidence.ts +66 -0
  886. package/src/score/index.ts +316 -0
  887. package/src/score/types.ts +187 -0
  888. package/src/{baseline → shared/baseline}/__tests__/diff.test.ts +2 -2
  889. package/src/{baseline → shared/baseline}/diff.ts +1 -1
  890. package/src/{baseline → shared/baseline}/manager.ts +1 -1
  891. package/src/{category-filter.ts → shared/category-filter.ts} +1 -1
  892. package/src/{utils → shared}/code-analysis.ts +1 -1
  893. package/src/{rules → shared/rules}/__tests__/metadata.test.ts +7 -0
  894. package/src/{rules → shared/rules}/framework-fixes.ts +1 -1
  895. package/src/{rules → shared/rules}/metadata.ts +94 -0
  896. package/src/{types.ts → shared/types.ts} +22 -5
  897. package/src/tiers.ts +18 -1
  898. package/src/validate/__tests__/context-extractor.test.ts +191 -0
  899. package/src/validate/__tests__/prompt-assembly.test.ts +233 -0
  900. package/src/validate/__tests__/request-builder.test.ts +347 -0
  901. package/src/{layer3/anthropic → validate}/index.ts +8 -7
  902. package/src/{layer3/anthropic → validate}/prompts/index.ts +2 -0
  903. package/src/validate/prompts/modules/ai-patterns.ts +153 -0
  904. package/src/validate/prompts/modules/auth-access.ts +22 -0
  905. package/src/validate/prompts/modules/common.ts +183 -0
  906. package/src/validate/prompts/modules/index.ts +204 -0
  907. package/src/validate/prompts/modules/owasp-classic.ts +81 -0
  908. package/src/validate/prompts/modules/secrets-crypto.ts +65 -0
  909. package/src/validate/prompts/modules/xss-prompt.ts +19 -0
  910. package/src/validate/prompts/validation.ts +20 -0
  911. package/src/{layer3/anthropic → validate}/providers/anthropic.ts +28 -27
  912. package/src/validate/providers/index.ts +8 -0
  913. package/src/{layer3/anthropic → validate}/providers/openai.ts +30 -25
  914. package/src/validate/request-builder.ts +448 -0
  915. package/src/{layer3/anthropic → validate}/types.ts +1 -1
  916. package/src/validate/utils/context-extractor.ts +220 -0
  917. package/src/{layer3/anthropic → validate}/utils/index.ts +10 -0
  918. package/src/{layer3/anthropic → validate}/utils/response-parser.ts +2 -1
  919. package/src/layer3/anthropic/prompts/validation.ts +0 -419
  920. package/src/layer3/anthropic/providers/index.ts +0 -8
  921. package/src/layer3/anthropic/request-builder.ts +0 -150
  922. package/src/layer3/index.ts +0 -168
  923. /package/src/{layer3 → detect/config}/__tests__/osv-check.test.ts +0 -0
  924. /package/src/{layer2 → detect/structural}/__tests__/math-random-enhanced.test.ts +0 -0
  925. /package/src/{layer2 → detect/structural}/dangerous-functions/child-process.ts +0 -0
  926. /package/src/{layer2 → detect/structural}/dangerous-functions/utils/helpers.ts +0 -0
  927. /package/src/{layer2 → detect/structural}/dangerous-functions/utils/index.ts +0 -0
  928. /package/src/{layer2 → detect/structural}/dangerous-functions/utils/schema-validation.ts +0 -0
  929. /package/src/{utils → model}/route-hierarchy.ts +0 -0
  930. /package/src/{filtering → postprocess/filtering}/index.ts +0 -0
  931. /package/src/{suppression → postprocess/suppression}/__tests__/config-loader.test.ts +0 -0
  932. /package/src/{suppression → postprocess/suppression}/__tests__/hash.test.ts +0 -0
  933. /package/src/{suppression → postprocess/suppression}/__tests__/inline-parser.test.ts +0 -0
  934. /package/src/{suppression → postprocess/suppression}/__tests__/manager.test.ts +0 -0
  935. /package/src/{suppression → postprocess/suppression}/index.ts +0 -0
  936. /package/src/{formatters → report/formatters}/__tests__/ai-context.test.ts +0 -0
  937. /package/src/{formatters → report/formatters}/ide/__tests__/ide.test.ts +0 -0
  938. /package/src/{formatters → report/formatters}/ide/index.ts +0 -0
  939. /package/src/{formatters → report/formatters}/index.ts +0 -0
  940. /package/src/{utils → shared}/__tests__/code-analysis.test.ts +0 -0
  941. /package/src/{utils → shared}/__tests__/parsed-file.test.ts +0 -0
  942. /package/src/{ai-context → shared/ai-context}/__tests__/manager.test.ts +0 -0
  943. /package/src/{ai-context → shared/ai-context}/index.ts +0 -0
  944. /package/src/{ai-context → shared/ai-context}/manager.ts +0 -0
  945. /package/src/{baseline → shared/baseline}/__tests__/manager.test.ts +0 -0
  946. /package/src/{baseline → shared/baseline}/index.ts +0 -0
  947. /package/src/{baseline → shared/baseline}/types.ts +0 -0
  948. /package/src/{utils → shared}/comment-analyzer.ts +0 -0
  949. /package/src/{utils → shared}/diff-detector.ts +0 -0
  950. /package/src/{utils → shared}/diff-parser.ts +0 -0
  951. /package/src/{utils → shared}/environment-context.ts +0 -0
  952. /package/src/{utils → shared}/intent-detector.ts +0 -0
  953. /package/src/{utils → shared}/parsed-file.ts +0 -0
  954. /package/src/{utils → shared}/registry-clients.ts +0 -0
  955. /package/src/{rules → shared/rules}/__tests__/framework-fixes.test.ts +0 -0
  956. /package/src/{rules → shared/rules}/index.ts +0 -0
  957. /package/src/{utils → shared}/schema-semantics.ts +0 -0
  958. /package/src/{layer3/anthropic → validate}/clients.ts +0 -0
  959. /package/src/{layer3/anthropic → validate}/prompts/semantic-analysis.ts +0 -0
  960. /package/src/{layer3/anthropic → validate}/utils/path-helpers.ts +0 -0
  961. /package/src/{layer3/anthropic → validate}/utils/retry.ts +0 -0
package/src/index.ts CHANGED
@@ -1,1299 +1,47 @@
1
1
  /**
2
- * Scanner Orchestrator
3
- * Coordinates all three scanning layers and produces final results
2
+ * Scanner Public API
3
+ *
4
+ * Re-exports from stage modules. All prior exports preserved.
4
5
  */
5
6
 
6
- import type {
7
- ScanFile,
8
- ScanResult,
9
- Vulnerability,
10
- SeverityCounts,
11
- CategoryCounts,
12
- VulnerabilityCategory,
13
- ScanMode,
14
- ScanModeConfig,
15
- ScanDepth,
16
- CancellationToken,
17
- SuppressedVulnerabilitySummary,
18
- DetectorContext,
19
- } from './types'
20
- import { createEmptyDetectorContext } from './types'
21
- import { SCANNABLE_EXTENSIONS, SPECIAL_FILES, MAX_FILE_SIZE, SCAN_MODE_DEFAULTS } from './types'
22
- import { runLayer1Scan } from './layer1'
23
- import { runLayer2Scan } from './layer2'
24
- import { runLayer3Scan } from './layer3'
25
- import { validateFindingsWithAI, applyAutoDismissRules, type ValidationStats } from './layer3/anthropic'
26
- import { aggregateLocalhostFindings } from './layer1/urls'
27
- import { detectGlobalAuthMiddleware, type MiddlewareAuthConfig, getRoutePathFromFile, isRouteProtectedByMiddleware } from './utils/middleware-detector'
28
- import { detectAuthHelpers } from './utils/auth-helper-detector'
29
- import { buildFileAuthImports } from './utils/imported-auth-detector'
30
- // Tier system imports for filtering by scan depth
31
- import {
32
- type DetectorTier,
33
- type TierStats,
34
- getTierForCategory,
35
- isTierVisibleAtDepth,
36
- shouldValidateWithAI,
37
- computeTierStats,
38
- formatTierStats,
39
- } from './tiers'
40
- // Suppression system
41
- import { SuppressionManager } from './suppression'
42
- // Rule metadata for actionable output (PRO-82)
43
- import { getRuleMetadata } from './rules'
44
- // Framework-aware fix suggestions (PRO-83)
45
- import { getFrameworkFix } from './rules/framework-fixes'
46
- // Project context for framework detection
47
- import { buildProjectContext, type ProjectContext } from './utils/project-context-builder'
48
- // File context helpers
49
- import {
50
- isServerOnlyFile,
51
- isClientBundledFile,
52
- isTestOrMockFile,
53
- isToolingDirectory,
54
- isLocaleFile,
55
- } from './utils/context-helpers'
56
- // Shared ranking utilities
57
- import { severityRank, confidenceRank } from './utils/parsed-file'
58
- // Centralized context-based filtering
59
- import { applyContextAdjustments } from './filtering/context-adjustments'
60
- import { FilterPipeline } from './filtering/pipeline'
61
-
62
- // Maximum candidates per file to send to AI validation (cost control)
63
- const MAX_VALIDATION_CANDIDATES_PER_FILE = 10
64
-
65
7
  // ============================================================================
66
- // Detector Context Builder (Phase 2b)
8
+ // Core Scanner API
67
9
  // ============================================================================
68
10
 
69
- /**
70
- * Build a DetectorContext from ProjectContext
71
- * This provides rich context to Layer 2 detectors for smarter decisions
72
- */
73
- function buildDetectorContext(
74
- projectContext: ProjectContext,
75
- middlewareConfig: MiddlewareAuthConfig | undefined
76
- ): DetectorContext {
77
- // Map ProjectContext to DetectorContext format
78
- const context = createEmptyDetectorContext()
79
-
80
- // Auth context
81
- if (middlewareConfig?.hasAuthMiddleware) {
82
- context.auth.middleware = {
83
- hasAuthMiddleware: true,
84
- authType: middlewareConfig.authType ?? null,
85
- protectedPaths: middlewareConfig.protectedPaths,
86
- publicPaths: middlewareConfig.publicPaths,
87
- }
88
- }
89
-
90
- // Auth helpers from project context
91
- if (projectContext.auth.throwingAuthHelpers.length > 0) {
92
- context.auth.authHelpers = {
93
- hasThrowingHelpers: true,
94
- throwingHelperNames: projectContext.auth.throwingAuthHelpers.map(h => h.name),
95
- }
96
- }
97
-
98
- // Public endpoints
99
- context.auth.publicEndpoints = projectContext.auth.publicPaths
100
-
101
- // Framework context
102
- context.framework = {
103
- primary: projectContext.frameworks.primary ?? null,
104
- frontend: projectContext.frameworks.frontend ?? null,
105
- hasTypeScript: projectContext.frameworks.usesTypeScript,
106
- }
107
-
108
- // Data access context
109
- context.dataAccess = {
110
- orm: mapOrmToDataAccessOrm(projectContext.dataAccess.orm),
111
- hasRLS: projectContext.dataAccess.hasRLS,
112
- validationLib: mapValidationLib(projectContext.dataAccess.validationLibrary),
113
- }
114
-
115
- // Note: File context is populated per-file in buildFileDetectorContext
116
-
117
- return context
118
- }
119
-
120
- /**
121
- * Build file-specific context for a single file
122
- * This is merged with the project-level DetectorContext
123
- */
124
- function buildFileDetectorContext(
125
- baseContext: DetectorContext,
126
- filePath: string
127
- ): DetectorContext {
128
- // Clone the base context and add file-specific info
129
- return {
130
- ...baseContext,
131
- file: {
132
- isServerOnly: isServerOnlyFile(filePath),
133
- isClientBundled: isClientBundledFile(filePath),
134
- isTestFile: isTestOrMockFile(filePath),
135
- isConfigFile: isConfigFile(filePath),
136
- isToolingDir: isToolingDirectory(filePath),
137
- },
138
- }
139
- }
140
-
141
- /**
142
- * Check if file is a configuration file
143
- */
144
- function isConfigFile(filePath: string): boolean {
145
- const configPatterns = [
146
- /config\.(ts|js|json|yaml|yml)$/i,
147
- /settings\.(ts|js|json)$/i,
148
- /constants\.(ts|js)$/i,
149
- /\.config\.(ts|js|mjs|cjs)$/i,
150
- /\.env/i,
151
- /tsconfig\.json$/i,
152
- /package\.json$/i,
153
- /jest\.config/i,
154
- /vitest\.config/i,
155
- /eslint/i,
156
- /prettier/i,
157
- ]
158
- return configPatterns.some(p => p.test(filePath))
159
- }
160
-
161
- /**
162
- * Map ProjectContext ORM to DetectorContext ORM
163
- */
164
- function mapOrmToDataAccessOrm(
165
- orm: ProjectContext['dataAccess']['orm']
166
- ): DetectorContext['dataAccess']['orm'] {
167
- if (!orm) return null
168
- switch (orm) {
169
- case 'prisma':
170
- case 'drizzle':
171
- case 'typeorm':
172
- case 'sequelize':
173
- case 'knex':
174
- return orm
175
- default:
176
- return null
177
- }
178
- }
179
-
180
- /**
181
- * Map validation library to DetectorContext format
182
- */
183
- function mapValidationLib(
184
- lib: string | undefined
185
- ): DetectorContext['dataAccess']['validationLib'] {
186
- if (!lib) return null
187
- switch (lib) {
188
- case 'zod':
189
- case 'yup':
190
- case 'joi':
191
- case 'valibot':
192
- return lib
193
- default:
194
- return null
195
- }
196
- }
197
-
198
- /**
199
- * Classify vulnerabilities into tier groups for filtering
200
- */
201
- interface TierClassification {
202
- /** Tier A (core): High-precision, always visible */
203
- core: Vulnerability[]
204
- /** Tier B (ai_assisted): Context-heavy, needs AI validation */
205
- ai_assisted: Vulnerability[]
206
- /** Tier C (experimental): Hidden from users, internal use only */
207
- experimental: Vulnerability[]
208
- }
209
-
210
- /**
211
- * Classify vulnerabilities by their detector tier
212
- */
213
- function classifyByTier(vulnerabilities: Vulnerability[]): TierClassification {
214
- const result: TierClassification = {
215
- core: [],
216
- ai_assisted: [],
217
- experimental: [],
218
- }
219
-
220
- for (const vuln of vulnerabilities) {
221
- const tier = getTierForCategory(vuln.category, vuln.layer)
222
- result[tier].push(vuln)
223
- }
224
-
225
- return result
226
- }
227
-
228
- /**
229
- * Filter vulnerabilities based on scan depth and tier
230
- *
231
- * - cheap: Only Tier A (core) findings are surfaced
232
- * - validated: Tier A visible, Tier B goes through AI validation
233
- * - deep: Same as validated, plus Layer 3 semantic analysis
234
- *
235
- * Tier C (experimental) is NEVER surfaced to users, regardless of depth.
236
- */
237
- function filterByTierAndDepth(
238
- vulnerabilities: Vulnerability[],
239
- depth: ScanDepth
240
- ): {
241
- /** Findings to surface directly (no AI validation needed) */
242
- toSurface: Vulnerability[]
243
- /** Findings to send through AI validation */
244
- toValidate: Vulnerability[]
245
- /** Findings hidden from users (Tier C) */
246
- hidden: Vulnerability[]
247
- /** Tier stats for logging */
248
- tierStats: TierStats
249
- } {
250
- const classified = classifyByTier(vulnerabilities)
251
- const tierStats = computeTierStats(
252
- vulnerabilities.map(v => ({ category: v.category, layer: v.layer }))
253
- )
254
-
255
- switch (depth) {
256
- case 'local':
257
- // Only Tier A is visible, no AI validation
258
- return {
259
- toSurface: classified.core,
260
- toValidate: [],
261
- hidden: [...classified.ai_assisted, ...classified.experimental],
262
- tierStats,
263
- }
264
-
265
- case 'verified':
266
- case 'deep':
267
- // Tier A always surfaces, Tier B goes to AI validation
268
- // For findings that already require AI validation, only include Tier A/B
269
- const coreRequiringValidation = classified.core.filter(v =>
270
- v.requiresAIValidation ||
271
- v.category === 'high_entropy_string' ||
272
- v.category === 'hardcoded_secret' ||
273
- (v.category === 'sensitive_url' && v.severity !== 'info')
274
- )
275
- const coreNotRequiringValidation = classified.core.filter(v =>
276
- !coreRequiringValidation.includes(v)
277
- )
278
-
279
- return {
280
- toSurface: coreNotRequiringValidation,
281
- toValidate: [...coreRequiringValidation, ...classified.ai_assisted],
282
- hidden: classified.experimental,
283
- tierStats,
284
- }
285
-
286
- default:
287
- // Fallback to local behavior for unknown depths
288
- console.warn(`[Scanner] Unknown scan depth "${depth}", falling back to "local"`)
289
- return {
290
- toSurface: classified.core,
291
- toValidate: [],
292
- hidden: [...classified.ai_assisted, ...classified.experimental],
293
- tierStats,
294
- }
295
- }
296
- }
297
-
298
- export interface ScanOptions {
299
- /** Enable AI-powered validation and analysis */
300
- enableAI?: boolean
301
- /** Maximum files to scan */
302
- maxFiles?: number
303
- /** Branch being scanned */
304
- branch?: string
305
- /** Scan mode configuration (full vs incremental) */
306
- scanMode?: ScanMode | ScanModeConfig
307
- /** Scan depth (cheap/validated/deep) - controls AI usage */
308
- scanDepth?: ScanDepth
309
- /** Suppress console.log output (for interactive CLI mode) */
310
- quiet?: boolean
311
- /** Cancellation token for aborting scans gracefully */
312
- cancellationToken?: CancellationToken
313
- /** Project path for loading suppression config (defaults to cwd) */
314
- projectPath?: string
315
- /** Include suppressed findings in output (for --show-suppressed) */
316
- showSuppressed?: boolean
317
- /** Include filter audit trail in output (for debugging/explaining dismissals) */
318
- includeFilterAudit?: boolean
319
- }
320
-
321
- export interface ScanProgress {
322
- status: 'fetching' | 'layer1' | 'layer2' | 'layer3' | 'validating' | 'complete' | 'failed'
323
- message: string
324
- filesProcessed: number
325
- totalFiles: number
326
- vulnerabilitiesFound: number
327
- }
328
-
329
- export type ProgressCallback = (progress: ScanProgress) => void
330
-
331
- /**
332
- * Resolve scan mode configuration from options
333
- *
334
- * Handles both scan mode (full/incremental) and depth (local/verified/deep).
335
- * Depth controls AI usage:
336
- * - local: skip AI validation + skip Layer 3
337
- * - verified: run AI validation, skip Layer 3
338
- * - deep: run AI validation + Layer 3
339
- */
340
- function resolveScanModeConfig(options: ScanOptions): ScanModeConfig {
341
- const scanModeOption = options.scanMode
342
-
343
- // Determine base mode
344
- const mode: ScanMode = !scanModeOption ? 'full'
345
- : typeof scanModeOption === 'string' ? scanModeOption
346
- : scanModeOption.mode
347
-
348
- const defaults = SCAN_MODE_DEFAULTS[mode]
349
-
350
- // Build config from defaults + explicit overrides
351
- let config: ScanModeConfig = {
352
- ...defaults,
353
- mode,
354
- ...(typeof scanModeOption === 'object' ? scanModeOption : {}),
355
- }
356
-
357
- // Apply depth mode (default: 'local')
358
- const depth = options.scanDepth ?? 'local'
359
- config.scanDepth = depth
360
-
361
- // Map depth to skipAIValidation/skipLayer3 unless explicitly overridden
362
- const hasExplicitSkipAI = typeof scanModeOption === 'object' && scanModeOption.skipAIValidation !== undefined
363
- const hasExplicitSkipL3 = typeof scanModeOption === 'object' && scanModeOption.skipLayer3 !== undefined
364
-
365
- if (!hasExplicitSkipAI) {
366
- config.skipAIValidation = depth === 'local'
367
- }
368
- if (!hasExplicitSkipL3) {
369
- config.skipLayer3 = depth !== 'deep'
370
- }
371
-
372
- return config
373
- }
374
-
375
- /**
376
- * Run a complete security scan on the provided files
377
- *
378
- * Supports two scan modes:
379
- * - full: Complete scan with AI validation on all files (initial onboarding, deep audits)
380
- * - incremental: Focused scan on changed files only (CI/CD, fast feedback)
381
- */
382
- export async function runScan(
383
- files: ScanFile[],
384
- repoInfo: { name: string; url: string; branch: string },
385
- options: ScanOptions = {},
386
- onProgress?: ProgressCallback
387
- ): Promise<ScanResult> {
388
- const startTime = Date.now()
389
- const allVulnerabilities: Vulnerability[] = []
390
-
391
- // Filter audit pipeline (zero overhead when disabled)
392
- const filterPipeline = new FilterPipeline(options.includeFilterAudit ?? false)
393
- const fid = (v: Pick<Vulnerability, 'filePath' | 'lineNumber' | 'category'>) => `${v.filePath}:${v.lineNumber}:${v.category}`
394
-
395
- // Resolve scan mode configuration
396
- const scanModeConfig = resolveScanModeConfig(options)
397
- const isIncremental = scanModeConfig.mode === 'incremental'
398
- const depth = scanModeConfig.scanDepth || 'local'
399
- const quiet = options.quiet ?? false
400
- const cancellationToken = options.cancellationToken
401
-
402
- // Conditional logging helper - suppresses output in quiet mode (interactive CLI)
403
- const log = (message: string) => {
404
- if (!quiet) {
405
- console.log(message)
406
- }
407
- }
408
-
409
- // Helper to check cancellation and throw if cancelled
410
- const checkCancelled = () => {
411
- if (cancellationToken?.cancelled) {
412
- throw new Error(`Scan cancelled: ${cancellationToken.reason || 'user requested'}`)
413
- }
414
- }
415
-
416
- log(`[Scanner] repo=${repoInfo.name} mode=${scanModeConfig.mode} depth=${depth} files=${files.length}`)
417
- if (isIncremental && scanModeConfig.changedFiles) {
418
- log(`[Scanner] repo=${repoInfo.name} incremental_files=${scanModeConfig.changedFiles.length}`)
419
- }
420
-
421
- // Filter out locale/i18n files - they contain translations, not code
422
- // This dramatically reduces false positives and AI validation costs
423
- const localeFileCount = files.filter(f => isLocaleFile(f.path)).length
424
- if (localeFileCount > 0) {
425
- files = files.filter(f => !isLocaleFile(f.path))
426
- log(`[Scanner] repo=${repoInfo.name} skipped_locale_files=${localeFileCount} remaining_files=${files.length}`)
427
- }
428
-
429
- // Report progress helper
430
- const reportProgress = (
431
- status: ScanProgress['status'],
432
- message: string,
433
- vulnerabilitiesFound: number = allVulnerabilities.length
434
- ) => {
435
- if (onProgress) {
436
- onProgress({
437
- status,
438
- message,
439
- filesProcessed: files.length,
440
- totalFiles: files.length,
441
- vulnerabilitiesFound,
442
- })
443
- }
444
- }
445
-
446
- // For incremental scans, filter to only changed files for AI-heavy operations
447
- // but still run static analysis on all files for context
448
- const filesForAI = isIncremental && scanModeConfig.changedFiles
449
- ? files.filter(f => scanModeConfig.changedFiles!.some(cf => f.path.endsWith(cf) || f.path.includes(cf)))
450
- : files
451
-
452
- // Declare variables that need to be accessible in catch block
453
- let middlewareConfig: MiddlewareAuthConfig | undefined
454
- let capturedValidationStats: ValidationStats | undefined
455
-
456
- try {
457
- checkCancelled()
458
-
459
- // Detect global auth middleware before scanning (always on all files for context)
460
- middlewareConfig = detectGlobalAuthMiddleware(files)
461
- if (middlewareConfig.hasAuthMiddleware) {
462
- log(`[Scanner] repo=${repoInfo.name} auth_middleware=${middlewareConfig.authType || 'unknown'} file=${middlewareConfig.middlewareFile}`)
463
- }
464
-
465
- // Build imported auth registry for cross-file middleware detection
466
- const fileAuthImports = buildFileAuthImports(files)
467
- const filesWithImportedAuth = Array.from(fileAuthImports.values()).filter(f => f.usesImportedAuth).length
468
- if (filesWithImportedAuth > 0) {
469
- log(`[Scanner] repo=${repoInfo.name} files_with_imported_auth=${filesWithImportedAuth}`)
470
- }
471
-
472
- checkCancelled()
473
-
474
- // Phase timing tracking
475
- const phaseTiming: { layer1?: number; layer2?: number; aiValidation?: number; layer3?: number } = {}
476
-
477
- // Layer 1: Surface Scan
478
- const layer1Start = Date.now()
479
- reportProgress('layer1', 'Running surface scan (patterns, entropy, config)...')
480
- let layer1Result = await runLayer1Scan(files, onProgress, cancellationToken)
481
-
482
- // Aggregate repeated localhost findings
483
- const layer1RawCount = layer1Result.vulnerabilities.length
484
- const layer1BeforeAggregation = layer1Result.vulnerabilities
485
- layer1Result = {
486
- ...layer1Result,
487
- vulnerabilities: aggregateLocalhostFindings(layer1Result.vulnerabilities)
488
- }
489
- if (filterPipeline.isEnabled) {
490
- const afterIds = new Set(layer1Result.vulnerabilities.map(fid))
491
- for (const v of layer1BeforeAggregation) {
492
- if (!afterIds.has(fid(v))) {
493
- filterPipeline.record(fid(v), { stage: 'localhost_aggregation', action: 'aggregated', reason: 'Aggregated repeated localhost finding' })
494
- }
495
- }
496
- }
497
- phaseTiming.layer1 = Date.now() - layer1Start
498
- log(`[Layer1] repo=${repoInfo.name} findings_raw=${layer1RawCount} findings_deduped=${layer1Result.vulnerabilities.length} duration=${phaseTiming.layer1}ms`)
499
-
500
- checkCancelled()
501
-
502
- // Build project context early - used by Layer 2 DetectorContext and framework-aware fixes
503
- // This detects frameworks (Next.js, Express), ORMs (Prisma, Drizzle), and frontend libs (React, Vue)
504
- const projectContext = buildProjectContext(files)
505
-
506
- // Build DetectorContext for context-aware Layer 2 detection (Phase 2b)
507
- const detectorContext = buildDetectorContext(projectContext, middlewareConfig)
508
-
509
- // Layer 2: Structural Scan
510
- const layer2Start = Date.now()
511
- reportProgress('layer2', 'Running structural scan (variables, logic gates)...', layer1Result.vulnerabilities.length)
512
- const layer2Result = await runLayer2Scan(
513
- files,
514
- { middlewareConfig, fileAuthImports, detectorContext },
515
- onProgress,
516
- cancellationToken
517
- )
518
-
519
- // Format heuristic breakdown for logging
520
- const heuristicBreakdown = Object.entries(layer2Result.stats.raw)
521
- .filter(([, count]) => count > 0)
522
- .map(([name, count]) => `${name}:${count}`)
523
- .join(',')
524
- phaseTiming.layer2 = Date.now() - layer2Start
525
- log(`[Layer2] repo=${repoInfo.name} findings_raw=${Object.values(layer2Result.stats.raw).reduce((a, b) => a + b, 0)} findings_deduped=${layer2Result.vulnerabilities.length} duration=${phaseTiming.layer2}ms heuristic_breakdown={${heuristicBreakdown}}`)
526
-
527
- // Combine Layer 1 and Layer 2 findings
528
- const layer12Findings = [...layer1Result.vulnerabilities, ...layer2Result.vulnerabilities]
529
-
530
- // Aggregate noisy findings BEFORE tier filtering to reduce noise
531
- const beforeAggregationCount = layer12Findings.length
532
- const aggregatedFindings = aggregateNoisyFindings(layer12Findings)
533
- const aggregatedCount = beforeAggregationCount - aggregatedFindings.length
534
- if (filterPipeline.isEnabled) {
535
- const afterIds = new Set(aggregatedFindings.map(fid))
536
- for (const v of layer12Findings) {
537
- if (!afterIds.has(fid(v))) {
538
- filterPipeline.record(fid(v), { stage: 'noisy_aggregation', action: 'aggregated', reason: 'Aggregated noisy finding (3+ similar per file)' })
539
- }
540
- }
541
- }
542
-
543
- // Enrich findings with metadata from rule registry (PRO-82)
544
- // PRO-83: Uses projectContext for framework-specific fix suggestions
545
- // This provides default impact, evidence, fixSteps, references for all findings
546
- // AI validation can override these later with context-aware content
547
- const enrichedFindings = enrichWithMetadata(aggregatedFindings, projectContext)
548
-
549
- // Apply tier-based filtering based on scan depth
550
- // This is the key integration point for the detector tier system
551
- const tierFiltered = filterByTierAndDepth(enrichedFindings, depth)
552
-
553
- if (filterPipeline.isEnabled) {
554
- for (const v of tierFiltered.hidden) {
555
- filterPipeline.record(fid(v), { stage: 'tier_filter', action: 'dismissed', reason: `Hidden by tier filter at depth=${depth}` })
556
- }
557
- }
558
- // Log tier breakdown
559
- log(`[Scanner] repo=${repoInfo.name} tier_breakdown=${formatTierStats(tierFiltered.tierStats)}`)
560
- log(`[Scanner] repo=${repoInfo.name} depth=${depth} tier_routing: surface=${tierFiltered.toSurface.length} validate=${tierFiltered.toValidate.length} hidden=${tierFiltered.hidden.length}`)
561
-
562
- // For cheap scans: Tier A surfaces directly, Tier B/C are hidden
563
- // For validated/deep: Tier A surfaces, Tier B goes through AI validation, Tier C hidden
564
-
565
- // Some Tier A findings still need AI validation (entropy, secrets, AI-specific)
566
- const additionalValidation = tierFiltered.toSurface.filter(v =>
567
- v.requiresAIValidation ||
568
- v.category === 'ai_prompt_injection' || // Story B1: Prompt hygiene
569
- v.category === 'ai_unsafe_execution' || // Story B2: LLM output execution
570
- v.category === 'ai_overpermissive_tool' // Story B4: Agent tool permissions
571
- )
572
-
573
- // Surface findings that don't need validation (excluding those that do)
574
- const noValidationNeededRaw = tierFiltered.toSurface.filter(v => !additionalValidation.includes(v))
575
-
576
- // Apply auto-dismiss rules to direct-surface findings (mode='surface')
577
- // Uses 'surface' mode to exclude cost-saving rules like 'info_severity_core_only'
578
- // This ensures test/scanner/example files are dismissed, but info-severity findings still surface
579
- const { toValidate: noValidationNeeded, dismissed: surfaceDismissed } = applyAutoDismissRules(noValidationNeededRaw, 'surface')
580
-
581
- // Combine tier-filtered validation candidates with additional ones
582
- const requiresValidation = [...tierFiltered.toValidate, ...additionalValidation]
11
+ // Pipeline orchestrator — main entry point
12
+ export { runScan, type ScanOptions } from './pipeline'
583
13
 
584
- // Apply smart auto-dismiss rules BEFORE AI validation (saves API costs)
585
- const { toValidate: afterAutoDismiss, dismissed: autoDismissed } = applyAutoDismissRules(requiresValidation)
14
+ // Summary utilities (public API for backfilling legacy scans etc.)
15
+ export { computeIssueMixFromVulnerabilities } from './report/summary'
586
16
 
587
- // Record auto-dismiss decisions
588
- if (filterPipeline.isEnabled) {
589
- for (const d of surfaceDismissed) {
590
- filterPipeline.record(fid(d.finding), { stage: 'auto_dismiss', action: 'dismissed', reason: d.reason, ruleName: d.rule })
591
- }
592
- for (const d of autoDismissed) {
593
- filterPipeline.record(fid(d.finding), { stage: 'auto_dismiss', action: 'dismissed', reason: d.reason, ruleName: d.rule })
594
- }
595
- }
596
-
597
- // Combine all dismissed findings for logging
598
- const allDismissed = [...surfaceDismissed, ...autoDismissed]
599
-
600
- // Track auto-dismiss by severity and category for logging
601
- const autoDismissBySeverity: Record<string, number> = { info: 0, low: 0, medium: 0, high: 0, critical: 0 }
602
- const autoDismissByCategory: Record<string, number> = {}
603
- for (const d of allDismissed) {
604
- autoDismissBySeverity[d.finding.severity] = (autoDismissBySeverity[d.finding.severity] || 0) + 1
605
- autoDismissByCategory[d.finding.category] = (autoDismissByCategory[d.finding.category] || 0) + 1
606
- }
607
- if (allDismissed.length > 0) {
608
- const categoryBreakdown = Object.entries(autoDismissByCategory)
609
- .sort(([, a], [, b]) => b - a)
610
- .map(([cat, count]) => `${cat}:${count}`)
611
- .join(',')
612
- log(`[AutoDismiss] repo=${repoInfo.name} total=${allDismissed.length} (surface=${surfaceDismissed.length} validation=${autoDismissed.length}) by_severity={info:${autoDismissBySeverity.info},low:${autoDismissBySeverity.low},medium:${autoDismissBySeverity.medium},high:${autoDismissBySeverity.high}} by_category={${categoryBreakdown}}`)
613
- }
614
-
615
- // Apply per-file cap to validation candidates (cost control)
616
- // Use scan mode config for max files
617
- const maxValidationFiles = scanModeConfig.maxAIValidationFiles || MAX_VALIDATION_CANDIDATES_PER_FILE
618
- const cappedValidation = capValidationCandidatesPerFile(afterAutoDismiss, maxValidationFiles)
619
-
620
- checkCancelled()
621
-
622
- // AI Validation of selected findings (if AI is enabled and not skipped by scan mode)
623
- let validatedFindings = cappedValidation
624
- let capturedValidationStats: ValidationStats | undefined = undefined
625
- const shouldValidate = options.enableAI !== false && !scanModeConfig.skipAIValidation && cappedValidation.length > 0
626
-
627
- if (shouldValidate) {
628
- checkCancelled()
629
- const aiValidationStart = Date.now()
630
- reportProgress('validating', 'AI validating findings (entropy, secrets, AI patterns)...', cappedValidation.length)
631
-
632
- // For incremental scans, only validate findings in changed files
633
- const findingsToValidate = isIncremental && scanModeConfig.changedFiles
634
- ? cappedValidation.filter(v => scanModeConfig.changedFiles!.some(cf => v.filePath.endsWith(cf) || v.filePath.includes(cf)))
635
- : cappedValidation
636
-
637
- if (findingsToValidate.length > 0) {
638
- const validationResult = await validateFindingsWithAI(
639
- findingsToValidate,
640
- filesForAI,
641
- undefined, // projectContext (uses default)
642
- onProgress ? (progress) => {
643
- // Convert AI validation progress to ScanProgress format
644
- onProgress({
645
- status: 'validating',
646
- message: progress.status,
647
- filesProcessed: progress.filesProcessed,
648
- totalFiles: progress.totalFiles,
649
- vulnerabilitiesFound: allVulnerabilities.length,
650
- })
651
- } : undefined
652
- )
653
- validatedFindings = validationResult.vulnerabilities
654
- const { stats: validationStats } = validationResult
655
- capturedValidationStats = validationStats // Capture for return
656
- phaseTiming.aiValidation = Date.now() - aiValidationStart
657
-
658
- if (filterPipeline.isEnabled) {
659
- const validatedIds = new Set(validatedFindings.map(fid))
660
- for (const v of findingsToValidate) {
661
- if (!validatedIds.has(fid(v))) {
662
- filterPipeline.record(fid(v), { stage: 'ai_validation', action: 'dismissed', reason: 'Dismissed by AI validation' })
663
- }
664
- }
665
- for (const v of validatedFindings) {
666
- if (v.validationNotes?.toLowerCase().includes('downgrad')) {
667
- filterPipeline.record(fid(v), { stage: 'ai_validation', action: 'downgraded', reason: v.validationNotes || 'Downgraded by AI validation', originalSeverity: undefined, newSeverity: v.severity })
668
- }
669
- }
670
- }
671
- log(`[AI Validation] repo=${repoInfo.name} depth=${depth} duration=${phaseTiming.aiValidation}ms candidates=${findingsToValidate.length} capped_from=${requiresValidation.length} auto_dismissed=${autoDismissed.length} kept=${validationStats.confirmedFindings} rejected=${validationStats.dismissedFindings} downgraded=${validationStats.downgradedFindings}`)
672
- log(`[AI Validation] cost_estimate: input_tokens=${validationStats.estimatedInputTokens} output_tokens=${validationStats.estimatedOutputTokens} cost=$${validationStats.estimatedCost.toFixed(4)} api_calls=${validationStats.apiCalls}`)
673
-
674
- // Add back findings that weren't validated (not in changed files)
675
- // Mark them as skipped rather than failed validation
676
- const notValidated = cappedValidation.filter(v => !findingsToValidate.includes(v)).map(v => ({
677
- ...v,
678
- validatedByAI: false,
679
- validationStatus: 'not_validated' as const,
680
- validationNotes: 'Skipped validation (not in changed files for incremental scan)',
681
- }))
682
- validatedFindings.push(...notValidated)
683
- }
684
- } else if (scanModeConfig.skipAIValidation) {
685
- log(`[AI Validation] repo=${repoInfo.name} depth=${depth} skipped=true reason=scan_mode_config findings_requiring_validation=${cappedValidation.length}`)
686
- // In cheap mode, don't surface findings that require AI validation
687
- // These are low-confidence without validation and would be noise
688
- // Only surface high-confidence findings that don't need validation
689
- validatedFindings = []
690
- }
691
-
692
- // Combine validated and non-validated findings
693
- allVulnerabilities.push(...validatedFindings, ...noValidationNeeded)
694
-
695
- // Layer 3: AI Semantic Analysis (new findings)
696
- // Skip for incremental scans by default (configurable)
697
- const shouldRunLayer3 = options.enableAI !== false && !scanModeConfig.skipLayer3
698
-
699
- if (shouldRunLayer3) {
700
- checkCancelled()
701
- const layer3Start = Date.now()
702
- reportProgress('layer3', 'Running AI semantic analysis...', allVulnerabilities.length)
703
-
704
- // For incremental scans, only analyze changed files
705
- const filesToAnalyze = isIncremental ? filesForAI : files
706
- const maxLayer3Files = scanModeConfig.maxLayer3Files || 10
707
-
708
- // Detect auth helpers for Layer 3 context
709
- const authHelperContext = detectAuthHelpers(files)
710
-
711
- const layer3Result = await runLayer3Scan(filesToAnalyze, {
712
- enableAI: options.enableAI,
713
- maxFiles: maxLayer3Files,
714
- projectContext: {
715
- middlewareConfig: middlewareConfig.hasAuthMiddleware ? {
716
- hasAuthMiddleware: true,
717
- authType: middlewareConfig.authType,
718
- protectedPaths: middlewareConfig.protectedPaths,
719
- } : undefined,
720
- authHelpers: authHelperContext.hasThrowingHelpers ? {
721
- hasThrowingHelpers: true,
722
- summary: authHelperContext.summary,
723
- } : undefined,
724
- },
725
- cancellationToken,
726
- })
727
- phaseTiming.layer3 = Date.now() - layer3Start
728
- allVulnerabilities.push(...layer3Result.vulnerabilities)
729
- log(`[Layer3] repo=${repoInfo.name} depth=${depth} duration=${phaseTiming.layer3}ms files_analyzed=${layer3Result.aiAnalyzed} findings=${layer3Result.vulnerabilities.length}`)
730
- } else if (scanModeConfig.skipLayer3) {
731
- log(`[Layer3] repo=${repoInfo.name} depth=${depth} skipped=true reason=scan_mode_config`)
732
- }
733
-
734
- // Log phase timing summary
735
- const phaseTimingStr = Object.entries(phaseTiming)
736
- .filter(([, ms]) => ms !== undefined)
737
- .map(([phase, ms]) => `${phase}=${ms}ms`)
738
- .join(' ')
739
- if (phaseTimingStr) {
740
- log(`[Scanner] repo=${repoInfo.name} phase_timing: ${phaseTimingStr}`)
741
- }
742
-
743
- // Deduplicate vulnerabilities
744
- const uniqueVulnerabilities = deduplicateVulnerabilities(allVulnerabilities)
745
- if (filterPipeline.isEnabled) {
746
- const uniqueIds = new Set(uniqueVulnerabilities.map(fid))
747
- for (const v of allVulnerabilities) {
748
- if (!uniqueIds.has(fid(v))) {
749
- filterPipeline.record(fid(v), { stage: 'deduplication', action: 'deduplicated', reason: 'Duplicate finding removed' })
750
- }
751
- }
752
- }
753
-
754
- // Apply file context-based severity adjustments (Phase 2b)
755
- // This applies to ALL findings from ALL layers (no FileContext = path-based adjustments only)
756
- const contextAdjustedVulnerabilities = applyContextAdjustments(uniqueVulnerabilities)
757
- if (filterPipeline.isEnabled) {
758
- for (let i = 0; i < uniqueVulnerabilities.length; i++) {
759
- const before = uniqueVulnerabilities[i]
760
- const after = contextAdjustedVulnerabilities[i]
761
- if (before.severity !== after.severity) {
762
- filterPipeline.record(fid(after), { stage: 'global_context', action: 'downgraded', reason: after.validationNotes || 'Context-based severity downgrade', originalSeverity: before.severity, newSeverity: after.severity })
763
- }
764
- }
765
- }
766
-
767
- // Resolve contradictions (e.g., middleware-protected INFO vs missing-auth CRITICAL on same route)
768
- const resolvedVulnerabilities = resolveContradictions(contextAdjustedVulnerabilities, middlewareConfig)
769
- if (filterPipeline.isEnabled) {
770
- const resolvedIds = new Set(resolvedVulnerabilities.map(fid))
771
- for (const v of contextAdjustedVulnerabilities) {
772
- if (!resolvedIds.has(fid(v))) {
773
- filterPipeline.record(fid(v), { stage: 'contradiction_resolution', action: 'dismissed', reason: 'Removed by contradiction resolution' })
774
- }
775
- }
776
- for (let i = 0; i < contextAdjustedVulnerabilities.length; i++) {
777
- const before = contextAdjustedVulnerabilities[i]
778
- const after = resolvedVulnerabilities.find(v => fid(v) === fid(before))
779
- if (after && before.severity !== after.severity) {
780
- filterPipeline.record(fid(after), { stage: 'contradiction_resolution', action: 'downgraded', reason: 'Downgraded by contradiction resolution', originalSeverity: before.severity, newSeverity: after.severity })
781
- }
782
- }
783
- }
784
-
785
- // Apply suppressions (inline comments + config file)
786
- const projectPath = options.projectPath || process.cwd()
787
- const suppressionManager = new SuppressionManager({ projectPath })
788
- const suppressionResult = suppressionManager.applySuppressions(resolvedVulnerabilities, files)
789
-
790
- if (filterPipeline.isEnabled) {
791
- for (const s of suppressionResult.suppressed) {
792
- filterPipeline.record(fid(s.vulnerability), { stage: 'user_suppression', action: 'suppressed', reason: s.suppression.reason || `Suppressed by ${s.suppression.type}` })
793
- }
794
- }
795
-
796
- // Log suppression stats if any were suppressed
797
- if (suppressionResult.suppressed.length > 0 || suppressionResult.expiredSuppressions > 0) {
798
- log(`[Suppression] repo=${repoInfo.name} suppressed=${suppressionResult.suppressed.length} (inline=${suppressionResult.stats.inlineSuppressed} config_finding=${suppressionResult.stats.configFindingSuppressed} config_rule=${suppressionResult.stats.configRuleSuppressed}) expired=${suppressionResult.expiredSuppressions}`)
799
- }
800
-
801
- // Use the filtered findings (after suppression)
802
- const afterSuppression = suppressionResult.findings
803
-
804
- // Sort by severity
805
- const sortedVulnerabilities = sortBySeverity(afterSuppression)
806
-
807
- // Compute issue-mix counts (based on unsuppressed findings)
808
- const severityCounts = computeSeverityCounts(sortedVulnerabilities)
809
- const categoryCounts = computeCategoryCounts(sortedVulnerabilities)
810
- const hasBlockingIssues = severityCounts.critical > 0 || severityCounts.high > 0
811
-
812
- // Build suppressed vulnerabilities summary (for --show-suppressed)
813
- const suppressedVulnerabilities: SuppressedVulnerabilitySummary[] | undefined = options.showSuppressed
814
- ? suppressionResult.suppressed.map(s => ({
815
- hash: s.suppression.hash,
816
- filePath: s.vulnerability.filePath,
817
- lineNumber: s.vulnerability.lineNumber,
818
- category: s.vulnerability.category,
819
- severity: s.vulnerability.severity,
820
- title: s.vulnerability.title,
821
- suppressionType: s.suppression.type,
822
- suppressionReason: s.suppression.reason,
823
- expires: s.suppression.expires,
824
- }))
825
- : undefined
826
-
827
- reportProgress('complete', 'Scan complete!', sortedVulnerabilities.length)
828
-
829
- return {
830
- repoName: repoInfo.name,
831
- repoUrl: repoInfo.url,
832
- branch: repoInfo.branch,
833
- filesScanned: files.length,
834
- filesSkipped: 0, // TODO: track skipped files
835
- vulnerabilities: sortedVulnerabilities,
836
- severityCounts,
837
- categoryCounts,
838
- hasBlockingIssues,
839
- scanDuration: Date.now() - startTime,
840
- timestamp: new Date().toISOString(),
841
- validationStats: capturedValidationStats,
842
- suppressionStats: suppressionResult.suppressed.length > 0 || suppressionResult.expiredSuppressions > 0
843
- ? suppressionResult.stats
844
- : undefined,
845
- suppressedVulnerabilities,
846
- filterAuditTrail: filterPipeline.isEnabled ? filterPipeline.getAuditTrail() : undefined,
847
- }
848
- } catch (error) {
849
- if (cancellationToken?.cancelled) {
850
- // Return partial results on cancellation
851
- reportProgress('failed', 'Scan cancelled')
852
-
853
- // Compute partial results
854
- const uniqueVulnerabilities = deduplicateVulnerabilities(allVulnerabilities)
855
- const resolvedVulnerabilities = resolveContradictions(uniqueVulnerabilities, middlewareConfig)
856
- const sortedVulnerabilities = sortBySeverity(resolvedVulnerabilities)
857
- const severityCounts = computeSeverityCounts(sortedVulnerabilities)
858
- const categoryCounts = computeCategoryCounts(sortedVulnerabilities)
859
-
860
- return {
861
- repoName: repoInfo.name,
862
- repoUrl: repoInfo.url,
863
- branch: repoInfo.branch,
864
- filesScanned: files.length,
865
- filesSkipped: 0,
866
- vulnerabilities: sortedVulnerabilities,
867
- severityCounts,
868
- categoryCounts,
869
- hasBlockingIssues: false, // Don't block on partial results
870
- scanDuration: Date.now() - startTime,
871
- timestamp: new Date().toISOString(),
872
- validationStats: capturedValidationStats,
873
- cancelled: true,
874
- cancelReason: cancellationToken.reason,
875
- }
876
- }
877
-
878
- reportProgress('failed', `Scan failed: ${error}`)
879
- throw error
880
- }
881
- }
882
-
883
- /**
884
- * Enrich findings with metadata from the rule registry (PRO-82)
885
- * Sets default impact, evidence, fixSteps, and references from registry
886
- *
887
- * PRO-83: When projectContext is provided, uses framework-aware fix suggestions
888
- * that are tailored to the user's detected tech stack (e.g., Prisma-specific
889
- * SQL injection fixes instead of generic advice).
890
- *
891
- * These can be overridden later by AI-generated content
892
- */
893
- function enrichWithMetadata(
894
- findings: Vulnerability[],
895
- projectContext?: ProjectContext
896
- ): Vulnerability[] {
897
- return findings.map(f => {
898
- const metadata = getRuleMetadata(f.category)
899
- if (!metadata) return f
900
-
901
- // PRO-83: Check for framework-specific fix suggestions
902
- let fixSteps = metadata.fixSteps
903
- if (projectContext) {
904
- const frameworkFix = getFrameworkFix(
905
- f.category,
906
- projectContext.frameworks,
907
- projectContext.dataAccess
908
- )
909
- if (frameworkFix) {
910
- fixSteps = frameworkFix.fixSteps
911
- // Optionally append code example to description if available
912
- // This makes the fix more actionable
913
- }
914
- }
915
-
916
- return {
917
- ...f,
918
- // Set defaults from registry (AI can override later)
919
- impact: f.impact || metadata.whyItMatters,
920
- evidence: f.evidence || metadata.evidence,
921
- fixSteps: f.fixSteps || fixSteps,
922
- references: f.references || metadata.references,
923
- }
924
- })
925
- }
926
-
927
- /**
928
- * Aggregate noisy findings in the same file to reduce clutter
929
- * Groups repeated findings with same filePath + category + title
930
- * Especially useful for AI pattern spam
931
- */
932
- function aggregateNoisyFindings(vulnerabilities: Vulnerability[]): Vulnerability[] {
933
- // Group findings by file + category + title
934
- const groups = new Map<string, Vulnerability[]>()
935
-
936
- for (const vuln of vulnerabilities) {
937
- // Create grouping key: same file, category, and base title (without line-specific info)
938
- const baseTitle = vuln.title.replace(/\s*\(\d+ instances?\)/, '').trim()
939
- const key = `${vuln.filePath}|${vuln.category}|${baseTitle}`
940
-
941
- const existing = groups.get(key) || []
942
- existing.push(vuln)
943
- groups.set(key, existing)
944
- }
945
-
946
- const result: Vulnerability[] = []
947
-
948
- for (const [key, group] of groups) {
949
- // If only 1-2 findings, keep them as-is
950
- if (group.length <= 2) {
951
- result.push(...group)
952
- continue
953
- }
954
-
955
- // For 3+ similar findings in same file, aggregate into one
956
- const first = group[0]
957
- const lineNumbers = group.map(v => v.lineNumber).sort((a, b) => a - b)
958
- const uniqueLines = [...new Set(lineNumbers)]
959
-
960
- // Format line numbers nicely (show first few, then "...")
961
- const lineDisplay = uniqueLines.length > 5
962
- ? `${uniqueLines.slice(0, 5).join(', ')}... (${uniqueLines.length} total)`
963
- : uniqueLines.join(', ')
964
-
965
- // Keep highest severity from the group
966
- const highestSeverity = group.reduce((max, v) =>
967
- severityRank(v.severity) > severityRank(max.severity) ? v : max
968
- , group[0]).severity
969
-
970
- // Create aggregated finding
971
- const aggregated: Vulnerability = {
972
- id: `${first.id}-aggregated`,
973
- filePath: first.filePath,
974
- lineNumber: uniqueLines[0], // First occurrence
975
- lineContent: `${group.length} instances across this file`,
976
- severity: highestSeverity,
977
- category: first.category,
978
- title: `${first.title.replace(/\s*\(\d+ instances?\)/, '')} (${group.length} instances)`,
979
- description: `${first.description}\n\nFound ${group.length} occurrences at lines: ${lineDisplay}`,
980
- suggestedFix: first.suggestedFix,
981
- confidence: first.confidence,
982
- layer: first.layer,
983
- requiresAIValidation: first.requiresAIValidation,
984
- }
985
-
986
- result.push(aggregated)
987
- }
988
-
989
- return result
990
- }
991
-
992
- /**
993
- * Cap validation candidates per file to control AI costs
994
- * Prioritizes by:
995
- * 1. Tier (ai_assisted findings need AI most, so they get priority)
996
- * 2. Severity (critical > high > medium > low > info)
997
- * 3. Category importance (secrets/URLs/auth before cosmetic patterns)
998
- */
999
- function capValidationCandidatesPerFile(
1000
- vulnerabilities: Vulnerability[],
1001
- maxPerFile: number = MAX_VALIDATION_CANDIDATES_PER_FILE
1002
- ): Vulnerability[] {
1003
- // Group by file
1004
- const byFile = new Map<string, Vulnerability[]>()
1005
- for (const vuln of vulnerabilities) {
1006
- const existing = byFile.get(vuln.filePath) || []
1007
- existing.push(vuln)
1008
- byFile.set(vuln.filePath, existing)
1009
- }
1010
-
1011
- const result: Vulnerability[] = []
1012
-
1013
- for (const [filePath, fileVulns] of byFile) {
1014
- // Sort by priority: tier first (ai_assisted needs AI most), then severity, then category
1015
- const sorted = [...fileVulns].sort((a, b) => {
1016
- // Tier comparison: ai_assisted (needs AI) > core (high precision) > experimental (hidden anyway)
1017
- const tierPriority = (v: Vulnerability): number => {
1018
- const tier = getTierForCategory(v.category, v.layer)
1019
- if (tier === 'ai_assisted') return 10 // Highest priority - these NEED AI validation
1020
- if (tier === 'core') return 5 // High precision, but AI can still help
1021
- return 1 // Experimental - shouldn't even be here
1022
- }
1023
- const tierDiff = tierPriority(b) - tierPriority(a)
1024
- if (tierDiff !== 0) return tierDiff
1025
-
1026
- // Severity comparison (higher severity = higher priority)
1027
- const severityDiff = severityRank(b.severity) - severityRank(a.severity)
1028
- if (severityDiff !== 0) return severityDiff
1029
-
1030
- // Category importance (secrets/URLs/auth before AI patterns)
1031
- const categoryPriority = (v: Vulnerability): number => {
1032
- if (v.category === 'hardcoded_secret') return 10
1033
- if (v.category === 'high_entropy_string') return 9
1034
- if (v.category === 'sensitive_url') return 8
1035
- if (v.category === 'missing_auth') return 7
1036
- if (v.category === 'ai_pattern') return 3
1037
- return 5
1038
- }
1039
- return categoryPriority(b) - categoryPriority(a)
1040
- })
1041
-
1042
- // Take top N per file
1043
- const capped = sorted.slice(0, maxPerFile)
1044
- result.push(...capped)
1045
-
1046
- // Note: Capping log removed to support quiet mode - this is debug info only
1047
- }
1048
-
1049
- return result
1050
- }
1051
-
1052
- // applyGlobalContextAdjustments consolidated into filtering/context-adjustments.ts
1053
-
1054
- /**
1055
- * Remove duplicate vulnerabilities (same file, line, category)
1056
- * Also handles cross-layer URL duplicates (sensitive_url + ai_pattern)
1057
- */
1058
- function deduplicateVulnerabilities(vulnerabilities: Vulnerability[]): Vulnerability[] {
1059
- const seen = new Map<string, Vulnerability>()
1060
- const urlDedupMap = new Map<string, Vulnerability>()
1061
-
1062
- for (const vuln of vulnerabilities) {
1063
- // Special handling for URL duplicates across layers
1064
- // (e.g., Layer 1 detects as sensitive_url, Layer 2 detects as ai_pattern)
1065
- if ((vuln.category === 'sensitive_url' || vuln.category === 'ai_pattern') &&
1066
- /url|localhost|127\.0\.0\.1|http/i.test(vuln.lineContent)) {
1067
-
1068
- // Create compound key that ignores category differences for URLs
1069
- const urlKey = `${vuln.filePath}:${vuln.lineNumber}:url_finding`
1070
- const existing = urlDedupMap.get(urlKey)
1071
-
1072
- if (!existing) {
1073
- urlDedupMap.set(urlKey, vuln)
1074
- } else {
1075
- // Keep Layer 1 (more specific) over Layer 2 AI pattern
1076
- // Or keep higher severity
1077
- if (vuln.layer < existing.layer ||
1078
- severityRank(vuln.severity) > severityRank(existing.severity)) {
1079
- urlDedupMap.set(urlKey, vuln)
1080
- }
1081
- }
1082
- continue
1083
- }
1084
-
1085
- // Standard deduplication for non-URL findings
1086
- const key = `${vuln.filePath}:${vuln.lineNumber}:${vuln.category}`
1087
- const existing = seen.get(key)
1088
-
1089
- // Keep the higher severity or higher confidence finding
1090
- if (!existing) {
1091
- seen.set(key, vuln)
1092
- } else if (severityRank(vuln.severity) > severityRank(existing.severity)) {
1093
- seen.set(key, vuln)
1094
- } else if (
1095
- severityRank(vuln.severity) === severityRank(existing.severity) &&
1096
- confidenceRank(vuln.confidence) > confidenceRank(existing.confidence)
1097
- ) {
1098
- seen.set(key, vuln)
1099
- }
1100
- }
1101
-
1102
- // Combine URL and non-URL findings
1103
- return [...Array.from(seen.values()), ...Array.from(urlDedupMap.values())]
1104
- }
1105
-
1106
- /**
1107
- * Resolve contradictions in findings
1108
- *
1109
- * Key contradiction types:
1110
- * 1. Same route has both "protected by middleware" (info) AND "missing auth" (high/critical)
1111
- * → Keep only the info-level "protected by middleware" finding
1112
- * 2. Same file has BYOK "transient use" (info) AND "key stored insecurely" (high)
1113
- * → Keep only the most accurate one based on context
1114
- * 3. Same line has conflicting severities from different layers
1115
- * → Prefer the lower severity if one explicitly notes protection
1116
- */
1117
- function resolveContradictions(
1118
- vulnerabilities: Vulnerability[],
1119
- middlewareConfig?: MiddlewareAuthConfig
1120
- ): Vulnerability[] {
1121
- // Group findings by file path for contradiction analysis
1122
- const byFile = new Map<string, Vulnerability[]>()
1123
- for (const vuln of vulnerabilities) {
1124
- const existing = byFile.get(vuln.filePath) || []
1125
- existing.push(vuln)
1126
- byFile.set(vuln.filePath, existing)
1127
- }
1128
-
1129
- const result: Vulnerability[] = []
1130
-
1131
- for (const [filePath, fileVulns] of byFile) {
1132
- // Check for auth contradictions in this file
1133
- const authFindings = fileVulns.filter(v => v.category === 'missing_auth')
1134
- const otherFindings = fileVulns.filter(v => v.category !== 'missing_auth')
1135
-
1136
- // Identify protected routes (middleware or auth helper)
1137
- const protectedInfos = authFindings.filter(v =>
1138
- v.severity === 'info' &&
1139
- (v.validationNotes === 'MIDDLEWARE_PROTECTED' ||
1140
- v.validationNotes === 'AUTH_HELPER_PROTECTED' ||
1141
- v.title.includes('protected by middleware') ||
1142
- v.title.includes('uses auth helper'))
1143
- )
1144
-
1145
- // NEW: Check if this file's route is protected by middleware (even without explicit info finding)
1146
- // This catches Layer 3 findings that don't have the Layer 2 protection info
1147
- const routePath = getRoutePathFromFile(filePath)
1148
- const isAPIRouteProtected = routePath && middlewareConfig?.hasAuthMiddleware
1149
- ? isRouteProtectedByMiddleware(routePath, middlewareConfig).isProtected
1150
- : false
1151
-
1152
- // Also check if file is a client component calling protected API routes
1153
- // Client components (components/, app/ without route.ts) calling /api/** are safe
1154
- const isClientCallingProtectedAPI =
1155
- middlewareConfig?.hasAuthMiddleware &&
1156
- (filePath.includes('/components/') ||
1157
- (filePath.includes('/app/') && !filePath.includes('route.ts')))
1158
-
1159
- // If we have protected route info findings OR the route is protected by middleware
1160
- if (protectedInfos.length > 0 || isAPIRouteProtected || isClientCallingProtectedAPI) {
1161
- // Keep the protected info findings
1162
- result.push(...protectedInfos)
1163
-
1164
- // For other auth findings on same file, either drop or downgrade to info
1165
- const otherAuthFindings = authFindings.filter(v => !protectedInfos.includes(v))
1166
-
1167
- for (const vuln of otherAuthFindings) {
1168
- // If it's high/critical missing auth on a protected route, drop it entirely
1169
- // (the middleware/helper protection supersedes)
1170
- if (vuln.severity === 'critical' || vuln.severity === 'high') {
1171
- const reason = isAPIRouteProtected
1172
- ? 'API route protected by middleware'
1173
- : isClientCallingProtectedAPI
1174
- ? 'client component calling protected API'
1175
- : 'route is protected'
1176
- // Note: Contradiction log removed to support quiet mode - this is debug info only
1177
- continue // Skip this finding
1178
- }
1179
-
1180
- // Keep lower severity auth findings as-is
1181
- result.push(vuln)
1182
- }
1183
- } else {
1184
- // No protection detected - keep all auth findings as-is
1185
- result.push(...authFindings)
1186
- }
1187
-
1188
- // Handle BYOK contradictions
1189
- const byokFindings = otherFindings.filter(v =>
1190
- v.category === 'ai_pattern' && v.title.toLowerCase().includes('byok')
1191
- )
1192
- const nonByokFindings = otherFindings.filter(v =>
1193
- !(v.category === 'ai_pattern' && v.title.toLowerCase().includes('byok'))
1194
- )
1195
-
1196
- if (byokFindings.length > 0) {
1197
- // Check if we have both transient (low) and storage (high) BYOK findings
1198
- const transientByok = byokFindings.filter(v =>
1199
- v.severity === 'low' || v.severity === 'info'
1200
- )
1201
- const storageByok = byokFindings.filter(v =>
1202
- v.severity === 'high' || v.severity === 'medium'
1203
- )
1204
-
1205
- if (transientByok.length > 0 && storageByok.length > 0) {
1206
- // If we detected transient usage, prefer the lower severity
1207
- // The higher severity ones may be false positives
1208
- result.push(...transientByok)
1209
- // Mark high-severity BYOK for review
1210
- for (const vuln of storageByok) {
1211
- result.push({
1212
- ...vuln,
1213
- severity: 'low',
1214
- validationNotes: `${vuln.validationNotes || ''} (downgraded: transient BYOK usage detected in same file)`.trim(),
1215
- })
1216
- }
1217
- } else {
1218
- // Keep all BYOK findings as-is
1219
- result.push(...byokFindings)
1220
- }
1221
- }
1222
-
1223
- // Add non-BYOK other findings
1224
- result.push(...nonByokFindings)
1225
- }
1226
-
1227
- return result
1228
- }
1229
-
1230
- /**
1231
- * Sort vulnerabilities by severity (critical first)
1232
- */
1233
- function sortBySeverity(vulnerabilities: Vulnerability[]): Vulnerability[] {
1234
- return [...vulnerabilities].sort((a, b) => {
1235
- const severityDiff = severityRank(b.severity) - severityRank(a.severity)
1236
- if (severityDiff !== 0) return severityDiff
1237
-
1238
- // Secondary sort by confidence
1239
- return confidenceRank(b.confidence) - confidenceRank(a.confidence)
1240
- })
1241
- }
1242
-
1243
- // severityRank and confidenceRank imported from utils/parsed-file
1244
-
1245
- /**
1246
- * Compute severity counts from vulnerabilities
1247
- */
1248
- function computeSeverityCounts(vulnerabilities: Vulnerability[]): SeverityCounts {
1249
- const counts: SeverityCounts = { critical: 0, high: 0, medium: 0, low: 0, info: 0 }
1250
-
1251
- for (const vuln of vulnerabilities) {
1252
- if (vuln.severity in counts) {
1253
- counts[vuln.severity]++
1254
- }
1255
- }
1256
-
1257
- return counts
1258
- }
1259
-
1260
- /**
1261
- * Compute category counts from vulnerabilities
1262
- */
1263
- function computeCategoryCounts(vulnerabilities: Vulnerability[]): CategoryCounts {
1264
- const counts: CategoryCounts = {}
1265
-
1266
- for (const vuln of vulnerabilities) {
1267
- const category = vuln.category as VulnerabilityCategory
1268
- counts[category] = (counts[category] || 0) + 1
1269
- }
1270
-
1271
- return counts
1272
- }
17
+ // ============================================================================
18
+ // Re-export types and utilities
19
+ // ============================================================================
1273
20
 
1274
- /**
1275
- * Helper to compute counts from vulnerabilities (for backfilling legacy scans)
1276
- */
1277
- export function computeIssueMixFromVulnerabilities(vulnerabilities: Vulnerability[]): {
1278
- severityCounts: SeverityCounts
1279
- categoryCounts: CategoryCounts
1280
- hasBlockingIssues: boolean
1281
- } {
1282
- const severityCounts = computeSeverityCounts(vulnerabilities)
1283
- const categoryCounts = computeCategoryCounts(vulnerabilities)
1284
- const hasBlockingIssues = severityCounts.critical > 0 || severityCounts.high > 0
1285
-
1286
- return { severityCounts, categoryCounts, hasBlockingIssues }
1287
- }
21
+ export * from './shared/types'
22
+ export { runLayer1Scan } from './detect/secrets'
23
+ export { runLayer2Scan } from './detect/structural'
24
+ export { buildProjectContext, type ProjectContext } from './model/project-context'
25
+ export { validateFindingsWithAI, type ValidationStats, type AIValidationResult } from './validate'
26
+ export { createCancellationToken } from './shared/types'
1288
27
 
1289
- // Re-export types and utilities
1290
- export * from './types'
1291
- export { runLayer1Scan } from './layer1'
1292
- export { runLayer2Scan } from './layer2'
1293
- export { runLayer3Scan } from './layer3'
1294
- export { buildProjectContext, type ProjectContext } from './utils/project-context-builder'
1295
- export { validateFindingsWithAI, type ValidationStats, type AIValidationResult } from './layer3/anthropic'
1296
- export { createCancellationToken } from './types'
28
+ // Confidence scoring exports (Phase 2B)
29
+ export {
30
+ scoreFindings,
31
+ buildAdjustmentContext,
32
+ buildConfidenceLog,
33
+ DEPTH_CONFIGS,
34
+ type ScoredFinding,
35
+ type ConfidenceComputation,
36
+ type ConfidenceAdjustment,
37
+ type ConfidenceLog,
38
+ type AdjustmentRule,
39
+ type AdjustmentContext,
40
+ type DepthConfig,
41
+ } from './score'
42
+
43
+ // Context Engine types
44
+ export type { ContextEngineResult } from './model/taint-types'
1297
45
 
1298
46
  // Suppression system exports
1299
47
  export {
@@ -1312,7 +60,7 @@ export {
1312
60
  type RuleSuppression,
1313
61
  type SuppressionResult,
1314
62
  type SuppressedVulnerability,
1315
- } from './suppression'
63
+ } from './postprocess/suppression'
1316
64
 
1317
65
  // Baseline system exports
1318
66
  export {
@@ -1330,7 +78,7 @@ export {
1330
78
  type LoadBaselineResult,
1331
79
  type SaveBaselineResult,
1332
80
  type ClearBaselineResult,
1333
- } from './baseline'
81
+ } from './shared/baseline'
1334
82
 
1335
83
  // Rule metadata exports (PRO-82)
1336
84
  export {
@@ -1339,7 +87,7 @@ export {
1339
87
  getAllCategories,
1340
88
  hasMetadata,
1341
89
  type RuleMetadata,
1342
- } from './rules'
90
+ } from './shared/rules'
1343
91
 
1344
92
  // AI Context exports
1345
93
  export {
@@ -1350,7 +98,7 @@ export {
1350
98
  type SaveContextResult,
1351
99
  type LoadContextResult,
1352
100
  type ClearContextResult,
1353
- } from './ai-context'
101
+ } from './shared/ai-context'
1354
102
 
1355
103
  // Category filter exports (Phase 2: Category-Based Filtering)
1356
104
  export {
@@ -1365,4 +113,4 @@ export {
1365
113
  validateCategories,
1366
114
  getAvailableCategoryGroups,
1367
115
  getCategoryGroupCounts,
1368
- } from './category-filter'
116
+ } from './shared/category-filter'