@oculum/scanner 1.0.9 → 1.0.11

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 (365) hide show
  1. package/dist/baseline/diff.d.ts +32 -0
  2. package/dist/baseline/diff.d.ts.map +1 -0
  3. package/dist/baseline/diff.js +119 -0
  4. package/dist/baseline/diff.js.map +1 -0
  5. package/dist/baseline/index.d.ts +9 -0
  6. package/dist/baseline/index.d.ts.map +1 -0
  7. package/dist/baseline/index.js +19 -0
  8. package/dist/baseline/index.js.map +1 -0
  9. package/dist/baseline/manager.d.ts +67 -0
  10. package/dist/baseline/manager.d.ts.map +1 -0
  11. package/dist/baseline/manager.js +180 -0
  12. package/dist/baseline/manager.js.map +1 -0
  13. package/dist/baseline/types.d.ts +91 -0
  14. package/dist/baseline/types.d.ts.map +1 -0
  15. package/dist/baseline/types.js +12 -0
  16. package/dist/baseline/types.js.map +1 -0
  17. package/dist/formatters/cli-terminal.d.ts +38 -0
  18. package/dist/formatters/cli-terminal.d.ts.map +1 -1
  19. package/dist/formatters/cli-terminal.js +365 -42
  20. package/dist/formatters/cli-terminal.js.map +1 -1
  21. package/dist/formatters/github-comment.d.ts +1 -1
  22. package/dist/formatters/github-comment.d.ts.map +1 -1
  23. package/dist/formatters/github-comment.js +75 -11
  24. package/dist/formatters/github-comment.js.map +1 -1
  25. package/dist/formatters/index.d.ts +1 -1
  26. package/dist/formatters/index.d.ts.map +1 -1
  27. package/dist/formatters/index.js +4 -1
  28. package/dist/formatters/index.js.map +1 -1
  29. package/dist/index.d.ts +7 -0
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +155 -16
  32. package/dist/index.js.map +1 -1
  33. package/dist/layer1/config-audit.d.ts.map +1 -1
  34. package/dist/layer1/config-audit.js +20 -3
  35. package/dist/layer1/config-audit.js.map +1 -1
  36. package/dist/layer1/config-mcp-audit.d.ts +20 -0
  37. package/dist/layer1/config-mcp-audit.d.ts.map +1 -0
  38. package/dist/layer1/config-mcp-audit.js +239 -0
  39. package/dist/layer1/config-mcp-audit.js.map +1 -0
  40. package/dist/layer1/index.d.ts +1 -0
  41. package/dist/layer1/index.d.ts.map +1 -1
  42. package/dist/layer1/index.js +9 -1
  43. package/dist/layer1/index.js.map +1 -1
  44. package/dist/layer2/ai-agent-tools.d.ts.map +1 -1
  45. package/dist/layer2/ai-agent-tools.js +303 -0
  46. package/dist/layer2/ai-agent-tools.js.map +1 -1
  47. package/dist/layer2/ai-endpoint-protection.d.ts.map +1 -1
  48. package/dist/layer2/ai-endpoint-protection.js +17 -3
  49. package/dist/layer2/ai-endpoint-protection.js.map +1 -1
  50. package/dist/layer2/ai-execution-sinks.d.ts.map +1 -1
  51. package/dist/layer2/ai-execution-sinks.js +462 -12
  52. package/dist/layer2/ai-execution-sinks.js.map +1 -1
  53. package/dist/layer2/ai-fingerprinting.d.ts.map +1 -1
  54. package/dist/layer2/ai-fingerprinting.js +3 -0
  55. package/dist/layer2/ai-fingerprinting.js.map +1 -1
  56. package/dist/layer2/ai-mcp-security.d.ts +17 -0
  57. package/dist/layer2/ai-mcp-security.d.ts.map +1 -0
  58. package/dist/layer2/ai-mcp-security.js +679 -0
  59. package/dist/layer2/ai-mcp-security.js.map +1 -0
  60. package/dist/layer2/ai-package-hallucination.d.ts +19 -0
  61. package/dist/layer2/ai-package-hallucination.d.ts.map +1 -0
  62. package/dist/layer2/ai-package-hallucination.js +696 -0
  63. package/dist/layer2/ai-package-hallucination.js.map +1 -0
  64. package/dist/layer2/ai-prompt-hygiene.d.ts.map +1 -1
  65. package/dist/layer2/ai-prompt-hygiene.js +495 -9
  66. package/dist/layer2/ai-prompt-hygiene.js.map +1 -1
  67. package/dist/layer2/ai-rag-safety.d.ts.map +1 -1
  68. package/dist/layer2/ai-rag-safety.js +372 -1
  69. package/dist/layer2/ai-rag-safety.js.map +1 -1
  70. package/dist/layer2/auth-antipatterns.d.ts.map +1 -1
  71. package/dist/layer2/auth-antipatterns.js +4 -0
  72. package/dist/layer2/auth-antipatterns.js.map +1 -1
  73. package/dist/layer2/byok-patterns.d.ts.map +1 -1
  74. package/dist/layer2/byok-patterns.js +3 -0
  75. package/dist/layer2/byok-patterns.js.map +1 -1
  76. package/dist/layer2/dangerous-functions/child-process.d.ts +16 -0
  77. package/dist/layer2/dangerous-functions/child-process.d.ts.map +1 -0
  78. package/dist/layer2/dangerous-functions/child-process.js +74 -0
  79. package/dist/layer2/dangerous-functions/child-process.js.map +1 -0
  80. package/dist/layer2/dangerous-functions/dom-xss.d.ts +29 -0
  81. package/dist/layer2/dangerous-functions/dom-xss.d.ts.map +1 -0
  82. package/dist/layer2/dangerous-functions/dom-xss.js +179 -0
  83. package/dist/layer2/dangerous-functions/dom-xss.js.map +1 -0
  84. package/dist/layer2/dangerous-functions/index.d.ts +13 -0
  85. package/dist/layer2/dangerous-functions/index.d.ts.map +1 -0
  86. package/dist/layer2/dangerous-functions/index.js +621 -0
  87. package/dist/layer2/dangerous-functions/index.js.map +1 -0
  88. package/dist/layer2/dangerous-functions/json-parse.d.ts +31 -0
  89. package/dist/layer2/dangerous-functions/json-parse.d.ts.map +1 -0
  90. package/dist/layer2/dangerous-functions/json-parse.js +319 -0
  91. package/dist/layer2/dangerous-functions/json-parse.js.map +1 -0
  92. package/dist/layer2/dangerous-functions/math-random.d.ts +61 -0
  93. package/dist/layer2/dangerous-functions/math-random.d.ts.map +1 -0
  94. package/dist/layer2/dangerous-functions/math-random.js +459 -0
  95. package/dist/layer2/dangerous-functions/math-random.js.map +1 -0
  96. package/dist/layer2/dangerous-functions/patterns.d.ts +21 -0
  97. package/dist/layer2/dangerous-functions/patterns.d.ts.map +1 -0
  98. package/dist/layer2/dangerous-functions/patterns.js +161 -0
  99. package/dist/layer2/dangerous-functions/patterns.js.map +1 -0
  100. package/dist/layer2/dangerous-functions/request-validation.d.ts +13 -0
  101. package/dist/layer2/dangerous-functions/request-validation.d.ts.map +1 -0
  102. package/dist/layer2/dangerous-functions/request-validation.js +119 -0
  103. package/dist/layer2/dangerous-functions/request-validation.js.map +1 -0
  104. package/dist/layer2/dangerous-functions/utils/control-flow.d.ts +23 -0
  105. package/dist/layer2/dangerous-functions/utils/control-flow.d.ts.map +1 -0
  106. package/dist/layer2/dangerous-functions/utils/control-flow.js +149 -0
  107. package/dist/layer2/dangerous-functions/utils/control-flow.js.map +1 -0
  108. package/dist/layer2/dangerous-functions/utils/helpers.d.ts +31 -0
  109. package/dist/layer2/dangerous-functions/utils/helpers.d.ts.map +1 -0
  110. package/dist/layer2/dangerous-functions/utils/helpers.js +124 -0
  111. package/dist/layer2/dangerous-functions/utils/helpers.js.map +1 -0
  112. package/dist/layer2/dangerous-functions/utils/index.d.ts +9 -0
  113. package/dist/layer2/dangerous-functions/utils/index.d.ts.map +1 -0
  114. package/dist/layer2/dangerous-functions/utils/index.js +23 -0
  115. package/dist/layer2/dangerous-functions/utils/index.js.map +1 -0
  116. package/dist/layer2/dangerous-functions/utils/schema-validation.d.ts +22 -0
  117. package/dist/layer2/dangerous-functions/utils/schema-validation.d.ts.map +1 -0
  118. package/dist/layer2/dangerous-functions/utils/schema-validation.js +89 -0
  119. package/dist/layer2/dangerous-functions/utils/schema-validation.js.map +1 -0
  120. package/dist/layer2/data-exposure.d.ts.map +1 -1
  121. package/dist/layer2/data-exposure.js +3 -0
  122. package/dist/layer2/data-exposure.js.map +1 -1
  123. package/dist/layer2/framework-checks.d.ts.map +1 -1
  124. package/dist/layer2/framework-checks.js +3 -0
  125. package/dist/layer2/framework-checks.js.map +1 -1
  126. package/dist/layer2/index.d.ts +3 -0
  127. package/dist/layer2/index.d.ts.map +1 -1
  128. package/dist/layer2/index.js +61 -2
  129. package/dist/layer2/index.js.map +1 -1
  130. package/dist/layer2/logic-gates.d.ts.map +1 -1
  131. package/dist/layer2/logic-gates.js +4 -0
  132. package/dist/layer2/logic-gates.js.map +1 -1
  133. package/dist/layer2/model-supply-chain.d.ts +20 -0
  134. package/dist/layer2/model-supply-chain.d.ts.map +1 -0
  135. package/dist/layer2/model-supply-chain.js +376 -0
  136. package/dist/layer2/model-supply-chain.js.map +1 -0
  137. package/dist/layer2/risky-imports.d.ts.map +1 -1
  138. package/dist/layer2/risky-imports.js +4 -0
  139. package/dist/layer2/risky-imports.js.map +1 -1
  140. package/dist/layer2/variables.d.ts.map +1 -1
  141. package/dist/layer2/variables.js +4 -0
  142. package/dist/layer2/variables.js.map +1 -1
  143. package/dist/layer3/anthropic/auto-dismiss.d.ts +24 -0
  144. package/dist/layer3/anthropic/auto-dismiss.d.ts.map +1 -0
  145. package/dist/layer3/anthropic/auto-dismiss.js +188 -0
  146. package/dist/layer3/anthropic/auto-dismiss.js.map +1 -0
  147. package/dist/layer3/anthropic/clients.d.ts +44 -0
  148. package/dist/layer3/anthropic/clients.d.ts.map +1 -0
  149. package/dist/layer3/anthropic/clients.js +81 -0
  150. package/dist/layer3/anthropic/clients.js.map +1 -0
  151. package/dist/layer3/anthropic/index.d.ts +41 -0
  152. package/dist/layer3/anthropic/index.d.ts.map +1 -0
  153. package/dist/layer3/anthropic/index.js +141 -0
  154. package/dist/layer3/anthropic/index.js.map +1 -0
  155. package/dist/layer3/anthropic/prompts/index.d.ts +8 -0
  156. package/dist/layer3/anthropic/prompts/index.d.ts.map +1 -0
  157. package/dist/layer3/anthropic/prompts/index.js +14 -0
  158. package/dist/layer3/anthropic/prompts/index.js.map +1 -0
  159. package/dist/layer3/anthropic/prompts/semantic-analysis.d.ts +15 -0
  160. package/dist/layer3/anthropic/prompts/semantic-analysis.d.ts.map +1 -0
  161. package/dist/layer3/anthropic/prompts/semantic-analysis.js +169 -0
  162. package/dist/layer3/anthropic/prompts/semantic-analysis.js.map +1 -0
  163. package/dist/layer3/anthropic/prompts/validation.d.ts +12 -0
  164. package/dist/layer3/anthropic/prompts/validation.d.ts.map +1 -0
  165. package/dist/layer3/anthropic/prompts/validation.js +421 -0
  166. package/dist/layer3/anthropic/prompts/validation.js.map +1 -0
  167. package/dist/layer3/anthropic/providers/anthropic.d.ts +21 -0
  168. package/dist/layer3/anthropic/providers/anthropic.d.ts.map +1 -0
  169. package/dist/layer3/anthropic/providers/anthropic.js +266 -0
  170. package/dist/layer3/anthropic/providers/anthropic.js.map +1 -0
  171. package/dist/layer3/anthropic/providers/index.d.ts +8 -0
  172. package/dist/layer3/anthropic/providers/index.d.ts.map +1 -0
  173. package/dist/layer3/anthropic/providers/index.js +15 -0
  174. package/dist/layer3/anthropic/providers/index.js.map +1 -0
  175. package/dist/layer3/anthropic/providers/openai.d.ts +18 -0
  176. package/dist/layer3/anthropic/providers/openai.d.ts.map +1 -0
  177. package/dist/layer3/anthropic/providers/openai.js +340 -0
  178. package/dist/layer3/anthropic/providers/openai.js.map +1 -0
  179. package/dist/layer3/anthropic/request-builder.d.ts +20 -0
  180. package/dist/layer3/anthropic/request-builder.d.ts.map +1 -0
  181. package/dist/layer3/anthropic/request-builder.js +134 -0
  182. package/dist/layer3/anthropic/request-builder.js.map +1 -0
  183. package/dist/layer3/anthropic/types.d.ts +88 -0
  184. package/dist/layer3/anthropic/types.d.ts.map +1 -0
  185. package/dist/layer3/anthropic/types.js +38 -0
  186. package/dist/layer3/anthropic/types.js.map +1 -0
  187. package/dist/layer3/anthropic/utils/index.d.ts +9 -0
  188. package/dist/layer3/anthropic/utils/index.d.ts.map +1 -0
  189. package/dist/layer3/anthropic/utils/index.js +24 -0
  190. package/dist/layer3/anthropic/utils/index.js.map +1 -0
  191. package/dist/layer3/anthropic/utils/path-helpers.d.ts +21 -0
  192. package/dist/layer3/anthropic/utils/path-helpers.d.ts.map +1 -0
  193. package/dist/layer3/anthropic/utils/path-helpers.js +69 -0
  194. package/dist/layer3/anthropic/utils/path-helpers.js.map +1 -0
  195. package/dist/layer3/anthropic/utils/response-parser.d.ts +40 -0
  196. package/dist/layer3/anthropic/utils/response-parser.d.ts.map +1 -0
  197. package/dist/layer3/anthropic/utils/response-parser.js +285 -0
  198. package/dist/layer3/anthropic/utils/response-parser.js.map +1 -0
  199. package/dist/layer3/anthropic/utils/retry.d.ts +15 -0
  200. package/dist/layer3/anthropic/utils/retry.d.ts.map +1 -0
  201. package/dist/layer3/anthropic/utils/retry.js +62 -0
  202. package/dist/layer3/anthropic/utils/retry.js.map +1 -0
  203. package/dist/layer3/index.d.ts +1 -0
  204. package/dist/layer3/index.d.ts.map +1 -1
  205. package/dist/layer3/index.js +16 -6
  206. package/dist/layer3/index.js.map +1 -1
  207. package/dist/layer3/osv-check.d.ts +75 -0
  208. package/dist/layer3/osv-check.d.ts.map +1 -0
  209. package/dist/layer3/osv-check.js +308 -0
  210. package/dist/layer3/osv-check.js.map +1 -0
  211. package/dist/rules/framework-fixes.d.ts +48 -0
  212. package/dist/rules/framework-fixes.d.ts.map +1 -0
  213. package/dist/rules/framework-fixes.js +439 -0
  214. package/dist/rules/framework-fixes.js.map +1 -0
  215. package/dist/rules/index.d.ts +8 -0
  216. package/dist/rules/index.d.ts.map +1 -0
  217. package/dist/rules/index.js +18 -0
  218. package/dist/rules/index.js.map +1 -0
  219. package/dist/rules/metadata.d.ts +43 -0
  220. package/dist/rules/metadata.d.ts.map +1 -0
  221. package/dist/rules/metadata.js +734 -0
  222. package/dist/rules/metadata.js.map +1 -0
  223. package/dist/suppression/config-loader.d.ts +74 -0
  224. package/dist/suppression/config-loader.d.ts.map +1 -0
  225. package/dist/suppression/config-loader.js +424 -0
  226. package/dist/suppression/config-loader.js.map +1 -0
  227. package/dist/suppression/hash.d.ts +48 -0
  228. package/dist/suppression/hash.d.ts.map +1 -0
  229. package/dist/suppression/hash.js +88 -0
  230. package/dist/suppression/hash.js.map +1 -0
  231. package/dist/suppression/index.d.ts +11 -0
  232. package/dist/suppression/index.d.ts.map +1 -0
  233. package/dist/suppression/index.js +39 -0
  234. package/dist/suppression/index.js.map +1 -0
  235. package/dist/suppression/inline-parser.d.ts +39 -0
  236. package/dist/suppression/inline-parser.d.ts.map +1 -0
  237. package/dist/suppression/inline-parser.js +218 -0
  238. package/dist/suppression/inline-parser.js.map +1 -0
  239. package/dist/suppression/manager.d.ts +94 -0
  240. package/dist/suppression/manager.d.ts.map +1 -0
  241. package/dist/suppression/manager.js +292 -0
  242. package/dist/suppression/manager.js.map +1 -0
  243. package/dist/suppression/types.d.ts +151 -0
  244. package/dist/suppression/types.d.ts.map +1 -0
  245. package/dist/suppression/types.js +28 -0
  246. package/dist/suppression/types.js.map +1 -0
  247. package/dist/tiers.d.ts +1 -1
  248. package/dist/tiers.d.ts.map +1 -1
  249. package/dist/tiers.js +27 -0
  250. package/dist/tiers.js.map +1 -1
  251. package/dist/types.d.ts +62 -1
  252. package/dist/types.d.ts.map +1 -1
  253. package/dist/types.js.map +1 -1
  254. package/dist/utils/context-helpers.d.ts +4 -0
  255. package/dist/utils/context-helpers.d.ts.map +1 -1
  256. package/dist/utils/context-helpers.js +13 -9
  257. package/dist/utils/context-helpers.js.map +1 -1
  258. package/package.json +4 -2
  259. package/src/__tests__/benchmark/fixtures/layer1/mcp-config-audit.json +31 -0
  260. package/src/__tests__/benchmark/fixtures/layer2/ai-execution-sinks.ts +1489 -82
  261. package/src/__tests__/benchmark/fixtures/layer2/ai-mcp-security.ts +495 -0
  262. package/src/__tests__/benchmark/fixtures/layer2/ai-package-hallucination.ts +255 -0
  263. package/src/__tests__/benchmark/fixtures/layer2/ai-prompt-hygiene.ts +300 -1
  264. package/src/__tests__/benchmark/fixtures/layer2/ai-rag-safety.ts +139 -0
  265. package/src/__tests__/benchmark/fixtures/layer2/byok-patterns.ts +7 -0
  266. package/src/__tests__/benchmark/fixtures/layer2/data-exposure.ts +63 -0
  267. package/src/__tests__/benchmark/fixtures/layer2/excessive-agency.ts +221 -0
  268. package/src/__tests__/benchmark/fixtures/layer2/index.ts +18 -0
  269. package/src/__tests__/benchmark/fixtures/layer2/model-supply-chain.ts +204 -0
  270. package/src/__tests__/benchmark/fixtures/layer2/phase1-enhancements.ts +157 -0
  271. package/src/__tests__/snapshots/__snapshots__/anthropic-validation-refactor.test.ts.snap +758 -0
  272. package/src/__tests__/snapshots/__snapshots__/dangerous-functions-refactor.test.ts.snap +503 -0
  273. package/src/__tests__/snapshots/anthropic-validation-refactor.test.ts +321 -0
  274. package/src/__tests__/snapshots/dangerous-functions-refactor.test.ts +439 -0
  275. package/src/baseline/__tests__/diff.test.ts +261 -0
  276. package/src/baseline/__tests__/manager.test.ts +225 -0
  277. package/src/baseline/diff.ts +135 -0
  278. package/src/baseline/index.ts +29 -0
  279. package/src/baseline/manager.ts +230 -0
  280. package/src/baseline/types.ts +97 -0
  281. package/src/formatters/cli-terminal.ts +444 -41
  282. package/src/formatters/github-comment.ts +79 -11
  283. package/src/formatters/index.ts +4 -0
  284. package/src/index.ts +197 -14
  285. package/src/layer1/config-audit.ts +24 -3
  286. package/src/layer1/config-mcp-audit.ts +276 -0
  287. package/src/layer1/index.ts +16 -6
  288. package/src/layer2/ai-agent-tools.ts +336 -0
  289. package/src/layer2/ai-endpoint-protection.ts +16 -3
  290. package/src/layer2/ai-execution-sinks.ts +516 -12
  291. package/src/layer2/ai-fingerprinting.ts +5 -1
  292. package/src/layer2/ai-mcp-security.ts +730 -0
  293. package/src/layer2/ai-package-hallucination.ts +791 -0
  294. package/src/layer2/ai-prompt-hygiene.ts +547 -9
  295. package/src/layer2/ai-rag-safety.ts +382 -3
  296. package/src/layer2/auth-antipatterns.ts +5 -0
  297. package/src/layer2/byok-patterns.ts +5 -1
  298. package/src/layer2/dangerous-functions/child-process.ts +98 -0
  299. package/src/layer2/dangerous-functions/dom-xss.ts +220 -0
  300. package/src/layer2/dangerous-functions/index.ts +949 -0
  301. package/src/layer2/dangerous-functions/json-parse.ts +385 -0
  302. package/src/layer2/dangerous-functions/math-random.ts +537 -0
  303. package/src/layer2/dangerous-functions/patterns.ts +174 -0
  304. package/src/layer2/dangerous-functions/request-validation.ts +145 -0
  305. package/src/layer2/dangerous-functions/utils/control-flow.ts +162 -0
  306. package/src/layer2/dangerous-functions/utils/helpers.ts +170 -0
  307. package/src/layer2/dangerous-functions/utils/index.ts +25 -0
  308. package/src/layer2/dangerous-functions/utils/schema-validation.ts +91 -0
  309. package/src/layer2/data-exposure.ts +5 -1
  310. package/src/layer2/framework-checks.ts +5 -0
  311. package/src/layer2/index.ts +63 -1
  312. package/src/layer2/logic-gates.ts +5 -0
  313. package/src/layer2/model-supply-chain.ts +456 -0
  314. package/src/layer2/risky-imports.ts +5 -0
  315. package/src/layer2/variables.ts +5 -0
  316. package/src/layer3/__tests__/osv-check.test.ts +384 -0
  317. package/src/layer3/anthropic/auto-dismiss.ts +212 -0
  318. package/src/layer3/anthropic/clients.ts +84 -0
  319. package/src/layer3/anthropic/index.ts +170 -0
  320. package/src/layer3/anthropic/prompts/index.ts +14 -0
  321. package/src/layer3/anthropic/prompts/semantic-analysis.ts +173 -0
  322. package/src/layer3/anthropic/prompts/validation.ts +419 -0
  323. package/src/layer3/anthropic/providers/anthropic.ts +310 -0
  324. package/src/layer3/anthropic/providers/index.ts +8 -0
  325. package/src/layer3/anthropic/providers/openai.ts +384 -0
  326. package/src/layer3/anthropic/request-builder.ts +150 -0
  327. package/src/layer3/anthropic/types.ts +148 -0
  328. package/src/layer3/anthropic/utils/index.ts +26 -0
  329. package/src/layer3/anthropic/utils/path-helpers.ts +68 -0
  330. package/src/layer3/anthropic/utils/response-parser.ts +322 -0
  331. package/src/layer3/anthropic/utils/retry.ts +75 -0
  332. package/src/layer3/index.ts +18 -5
  333. package/src/layer3/osv-check.ts +420 -0
  334. package/src/rules/__tests__/framework-fixes.test.ts +689 -0
  335. package/src/rules/__tests__/metadata.test.ts +218 -0
  336. package/src/rules/framework-fixes.ts +470 -0
  337. package/src/rules/index.ts +21 -0
  338. package/src/rules/metadata.ts +831 -0
  339. package/src/suppression/__tests__/config-loader.test.ts +382 -0
  340. package/src/suppression/__tests__/hash.test.ts +166 -0
  341. package/src/suppression/__tests__/inline-parser.test.ts +212 -0
  342. package/src/suppression/__tests__/manager.test.ts +415 -0
  343. package/src/suppression/config-loader.ts +462 -0
  344. package/src/suppression/hash.ts +95 -0
  345. package/src/suppression/index.ts +51 -0
  346. package/src/suppression/inline-parser.ts +273 -0
  347. package/src/suppression/manager.ts +379 -0
  348. package/src/suppression/types.ts +174 -0
  349. package/src/tiers.ts +36 -0
  350. package/src/types.ts +90 -0
  351. package/src/utils/context-helpers.ts +13 -9
  352. package/dist/layer2/dangerous-functions.d.ts +0 -7
  353. package/dist/layer2/dangerous-functions.d.ts.map +0 -1
  354. package/dist/layer2/dangerous-functions.js +0 -1701
  355. package/dist/layer2/dangerous-functions.js.map +0 -1
  356. package/dist/layer3/anthropic.d.ts +0 -87
  357. package/dist/layer3/anthropic.d.ts.map +0 -1
  358. package/dist/layer3/anthropic.js +0 -1948
  359. package/dist/layer3/anthropic.js.map +0 -1
  360. package/dist/layer3/openai.d.ts +0 -25
  361. package/dist/layer3/openai.d.ts.map +0 -1
  362. package/dist/layer3/openai.js +0 -238
  363. package/dist/layer3/openai.js.map +0 -1
  364. package/src/layer2/dangerous-functions.ts +0 -1940
  365. package/src/layer3/anthropic.ts +0 -2257
@@ -0,0 +1,384 @@
1
+ /**
2
+ * OpenAI Provider Implementation
3
+ *
4
+ * Validation using OpenAI GPT-5-mini model.
5
+ */
6
+
7
+ import type { Vulnerability, ScanFile, ValidationStatus } from '../../../types'
8
+ import type { ProjectContext } from '../../../utils/project-context-builder'
9
+ import { buildProjectContext } from '../../../utils/project-context-builder'
10
+ import type { ValidationStats, AIValidationResult, StatsAccumulator } from '../types'
11
+ import { createStatsAccumulator } from '../types'
12
+ import { getOpenAIClient, GPT5_MINI_PRICING, FILES_PER_API_BATCH, PARALLEL_API_BATCHES } from '../clients'
13
+ import { makeOpenAIRequestWithRetry } from '../utils/retry'
14
+ import { parseMultiFileValidationResponse, parseValidationResponse, applyValidationResults } from '../utils/response-parser'
15
+ import { buildMultiFileValidationRequest } from '../request-builder'
16
+ import { HIGH_CONTEXT_VALIDATION_PROMPT } from '../prompts/validation'
17
+
18
+ // Cache for project context (built once per scan)
19
+ let cachedProjectContext: ProjectContext | null = null
20
+
21
+ /**
22
+ * Validate findings using OpenAI GPT-5-mini
23
+ * This mirrors the Anthropic validation flow but uses OpenAI's API
24
+ */
25
+ export async function validateWithOpenAI(
26
+ findings: Vulnerability[],
27
+ files: ScanFile[],
28
+ projectContext: ProjectContext | undefined,
29
+ stats: ValidationStats
30
+ ): Promise<AIValidationResult> {
31
+ const client = getOpenAIClient()
32
+
33
+ // Build or use cached project context
34
+ const context = projectContext || cachedProjectContext || buildProjectContext(files)
35
+ if (!projectContext && !cachedProjectContext) {
36
+ cachedProjectContext = context
37
+ console.log('[OpenAI Validation] Built project context:', {
38
+ hasAuthMiddleware: context.auth.hasGlobalMiddleware,
39
+ authProvider: context.auth.authProvider,
40
+ orm: context.dataAccess.orm,
41
+ framework: context.frameworks.primary,
42
+ })
43
+ }
44
+
45
+ // Group findings by file for efficient validation
46
+ const findingsByFile = new Map<string, Vulnerability[]>()
47
+ for (const finding of findings) {
48
+ const existing = findingsByFile.get(finding.filePath) || []
49
+ existing.push(finding)
50
+ findingsByFile.set(finding.filePath, existing)
51
+ }
52
+
53
+ const validatedFindings: Vulnerability[] = []
54
+ const fileEntries = Array.from(findingsByFile.entries())
55
+
56
+ // Track metrics (thread-safe accumulator)
57
+ let totalApiBatches = 0
58
+ const statsLock: StatsAccumulator = createStatsAccumulator()
59
+
60
+ const totalFileBatches = Math.ceil(fileEntries.length / FILES_PER_API_BATCH)
61
+ console.log(`[OpenAI Validation] Processing ${fileEntries.length} files in ${totalFileBatches} API batch(es) (${PARALLEL_API_BATCHES} parallel)`)
62
+
63
+ // Create all batch definitions
64
+ const allBatches: Array<{
65
+ batchNum: number
66
+ fileBatch: Array<[string, Vulnerability[]]>
67
+ }> = []
68
+
69
+ for (let batchStart = 0; batchStart < fileEntries.length; batchStart += FILES_PER_API_BATCH) {
70
+ const fileBatch = fileEntries.slice(batchStart, batchStart + FILES_PER_API_BATCH)
71
+ const batchNum = Math.floor(batchStart / FILES_PER_API_BATCH) + 1
72
+ allBatches.push({ batchNum, fileBatch })
73
+ }
74
+
75
+ // Process a single batch - returns validated findings for that batch
76
+ const processBatch = async (
77
+ batchDef: { batchNum: number; fileBatch: Array<[string, Vulnerability[]]> }
78
+ ): Promise<Vulnerability[]> => {
79
+ const { batchNum, fileBatch } = batchDef
80
+ const batchFindings: Vulnerability[] = []
81
+
82
+ // Prepare file data for batch request
83
+ const fileDataList: Array<{ file: ScanFile; findings: Vulnerability[]; filePath: string }> = []
84
+ const filesWithoutContent: Array<{ filePath: string; findings: Vulnerability[] }> = []
85
+
86
+ for (const [filePath, fileFindings] of fileBatch) {
87
+ const file = files.find(f => f.path === filePath)
88
+ if (!file) {
89
+ filesWithoutContent.push({ filePath, findings: fileFindings })
90
+ } else {
91
+ fileDataList.push({ file, findings: fileFindings, filePath })
92
+ }
93
+ }
94
+
95
+ // Handle files without content
96
+ for (const { findings: fileFindings } of filesWithoutContent) {
97
+ for (const f of fileFindings) {
98
+ batchFindings.push({
99
+ ...f,
100
+ validatedByAI: false,
101
+ validationStatus: 'not_validated' as ValidationStatus,
102
+ validationNotes: 'File content not available for validation',
103
+ })
104
+ }
105
+ }
106
+
107
+ if (fileDataList.length === 0) {
108
+ return batchFindings
109
+ }
110
+
111
+ try {
112
+ // Build multi-file validation request
113
+ const validationRequest = buildMultiFileValidationRequest(
114
+ fileDataList.map(({ file, findings: fileFindings }) => ({ file, findings: fileFindings })),
115
+ context
116
+ )
117
+
118
+ // Call OpenAI GPT-5-mini with retry logic
119
+ const response = await makeOpenAIRequestWithRetry(async () =>
120
+ client.chat.completions.create({
121
+ model: 'gpt-5-mini-2025-08-07',
122
+ messages: [
123
+ { role: 'system', content: HIGH_CONTEXT_VALIDATION_PROMPT },
124
+ { role: 'user', content: validationRequest },
125
+ ],
126
+ max_completion_tokens: 4096, // Sufficient for larger batches with many findings
127
+ response_format: {
128
+ type: 'json_schema',
129
+ json_schema: {
130
+ name: 'validation_response',
131
+ strict: true,
132
+ schema: {
133
+ type: 'object',
134
+ properties: {
135
+ validations: {
136
+ type: 'array',
137
+ items: {
138
+ type: 'object',
139
+ properties: {
140
+ file: { type: 'string' },
141
+ validations: {
142
+ type: 'array',
143
+ items: {
144
+ type: 'object',
145
+ properties: {
146
+ index: { type: 'number' },
147
+ keep: { type: 'boolean' },
148
+ notes: {
149
+ type: ['string', 'null'],
150
+ default: null
151
+ },
152
+ adjustedSeverity: {
153
+ type: ['string', 'null'],
154
+ enum: ['critical', 'high', 'medium', 'low', 'info', null],
155
+ default: null
156
+ },
157
+ impact: {
158
+ type: ['string', 'null'],
159
+ description: 'Context-aware explanation of why this matters (1-2 sentences)',
160
+ default: null
161
+ },
162
+ fixSuggestion: {
163
+ type: ['string', 'null'],
164
+ description: 'Specific, actionable fix for this code context',
165
+ default: null
166
+ }
167
+ },
168
+ required: ['index', 'keep', 'notes', 'adjustedSeverity', 'impact', 'fixSuggestion'],
169
+ additionalProperties: false
170
+ }
171
+ }
172
+ },
173
+ required: ['file', 'validations'],
174
+ additionalProperties: false
175
+ }
176
+ }
177
+ },
178
+ required: ['validations'],
179
+ additionalProperties: false
180
+ }
181
+ }
182
+ }
183
+ })
184
+ )
185
+
186
+ // Track API call stats (accumulate to shared stats)
187
+ statsLock.apiCalls++
188
+
189
+ // Extract token usage from OpenAI response
190
+ const usage = response.usage
191
+ if (usage) {
192
+ const promptTokens = usage.prompt_tokens || 0
193
+ const completionTokens = usage.completion_tokens || 0
194
+ const cachedTokens = (usage as any).prompt_tokens_details?.cached_tokens || 0
195
+ const freshInputTokens = promptTokens - cachedTokens
196
+
197
+ statsLock.estimatedInputTokens += freshInputTokens
198
+ statsLock.estimatedOutputTokens += completionTokens
199
+ statsLock.cacheReadTokens += cachedTokens
200
+
201
+ console.log(`[OpenAI] Batch ${batchNum} tokens: ${promptTokens} input (${cachedTokens} cached), ${completionTokens} output`)
202
+
203
+ const freshCost = (freshInputTokens * GPT5_MINI_PRICING.input) / 1_000_000
204
+ const cachedCost = (cachedTokens * GPT5_MINI_PRICING.cached) / 1_000_000
205
+ const outputCost = (completionTokens * GPT5_MINI_PRICING.output) / 1_000_000
206
+ statsLock.estimatedCost += freshCost + cachedCost + outputCost
207
+ }
208
+
209
+ // Parse response content
210
+ const content = response.choices[0]?.message?.content
211
+ if (!content) {
212
+ for (const { findings: fileFindings } of fileDataList) {
213
+ for (const f of fileFindings) {
214
+ batchFindings.push({
215
+ ...f,
216
+ validatedByAI: false,
217
+ validationStatus: 'not_validated' as ValidationStatus,
218
+ validationNotes: 'No valid response from OpenAI',
219
+ })
220
+ }
221
+ }
222
+ return batchFindings
223
+ }
224
+
225
+ // Parse structured JSON response (with validations wrapper from response_format)
226
+ let parsedContent: any
227
+ try {
228
+ parsedContent = JSON.parse(content)
229
+ console.log(`[OpenAI Debug] Raw parsed content keys:`, Object.keys(parsedContent))
230
+ // Unwrap the validations array if present (from structured output)
231
+ if (parsedContent.validations && Array.isArray(parsedContent.validations)) {
232
+ console.log(`[OpenAI Debug] Unwrapping 'validations' array with ${parsedContent.validations.length} items`)
233
+ parsedContent = parsedContent.validations
234
+ } else if (Array.isArray(parsedContent)) {
235
+ console.log(`[OpenAI Debug] Content is already an array with ${parsedContent.length} items`)
236
+ } else {
237
+ console.log(`[OpenAI Debug] Content structure:`, typeof parsedContent, Array.isArray(parsedContent))
238
+ }
239
+ } catch (e) {
240
+ console.warn('[OpenAI] Failed to parse JSON response:', e)
241
+ parsedContent = content
242
+ }
243
+
244
+ // Parse multi-file response
245
+ const expectedFiles = fileDataList.map(({ filePath }) => filePath)
246
+ const validationResultsMap = parseMultiFileValidationResponse(
247
+ typeof parsedContent === 'string' ? parsedContent : JSON.stringify(parsedContent),
248
+ expectedFiles
249
+ )
250
+
251
+ console.log(`[OpenAI] Batch ${batchNum} parsed ${validationResultsMap.size} file results from ${fileDataList.length} files`)
252
+ if (validationResultsMap.size === 0) {
253
+ console.warn(`[OpenAI] WARNING: No file results parsed! Content type: ${typeof parsedContent}, isArray: ${Array.isArray(parsedContent)}`)
254
+ if (Array.isArray(parsedContent) && parsedContent.length > 0) {
255
+ console.log(`[OpenAI] First item structure:`, Object.keys(parsedContent[0]))
256
+ }
257
+ }
258
+
259
+ // Log any missing files from the response (these will be REJECTED)
260
+ if (validationResultsMap.size !== fileDataList.length) {
261
+ const missing = fileDataList
262
+ .filter(({ filePath }) => !validationResultsMap.has(filePath))
263
+ .map(({ filePath }) => filePath)
264
+ if (missing.length > 0) {
265
+ console.warn(`[OpenAI] Missing ${missing.length} files from response (will be REJECTED): ${missing.join(', ')}`)
266
+ }
267
+ }
268
+
269
+ // Apply results per file
270
+ for (const { filePath, findings: fileFindings } of fileDataList) {
271
+ const fileResults = validationResultsMap.get(filePath)
272
+ console.log(`[OpenAI] File ${filePath}: ${fileResults?.length || 0} validation results for ${fileFindings.length} findings`)
273
+
274
+ if (!fileResults || fileResults.length === 0) {
275
+ const singleFileResults = parseValidationResponse(content)
276
+ if (singleFileResults.length > 0 && fileDataList.length === 1) {
277
+ const { processed: processedFindings, dismissedCount } = applyValidationResults(fileFindings, singleFileResults)
278
+ statsLock.validatedFindings += processedFindings.length + dismissedCount
279
+ statsLock.dismissedFindings += dismissedCount
280
+ for (const processed of processedFindings) {
281
+ if (processed.validationStatus === 'confirmed') statsLock.confirmedFindings++
282
+ else if (processed.validationStatus === 'downgraded') statsLock.downgradedFindings++
283
+ batchFindings.push(processed)
284
+ }
285
+ } else {
286
+ // No validation results - REJECT all findings for this file (conservative approach)
287
+ console.warn(`[OpenAI] No validation results for ${filePath} - REJECTING ${fileFindings.length} findings`)
288
+ statsLock.validatedFindings += fileFindings.length
289
+ statsLock.dismissedFindings += fileFindings.length
290
+ // Don't add to batchFindings - findings are rejected
291
+ }
292
+ } else {
293
+ const { processed: processedFindings, dismissedCount } = applyValidationResults(fileFindings, fileResults)
294
+ statsLock.validatedFindings += processedFindings.length + dismissedCount
295
+ statsLock.dismissedFindings += dismissedCount
296
+ for (const processed of processedFindings) {
297
+ if (processed.validationStatus === 'confirmed') statsLock.confirmedFindings++
298
+ else if (processed.validationStatus === 'downgraded') statsLock.downgradedFindings++
299
+ batchFindings.push(processed)
300
+ }
301
+ }
302
+ }
303
+
304
+ } catch (error) {
305
+ console.error(`[OpenAI Validation] Error in batch ${batchNum}:`, error)
306
+ for (const { findings: fileFindings } of fileDataList) {
307
+ for (const f of fileFindings) {
308
+ batchFindings.push({
309
+ ...f,
310
+ validatedByAI: false,
311
+ validationStatus: 'not_validated' as ValidationStatus,
312
+ validationNotes: 'Validation failed due to API error',
313
+ })
314
+ }
315
+ }
316
+ }
317
+
318
+ return batchFindings
319
+ }
320
+
321
+ // Process batches in parallel groups
322
+ const startTime = Date.now()
323
+ for (let i = 0; i < allBatches.length; i += PARALLEL_API_BATCHES) {
324
+ const parallelGroup = allBatches.slice(i, i + PARALLEL_API_BATCHES)
325
+ const batchNums = parallelGroup.map(b => b.batchNum).join(', ')
326
+ console.log(`[OpenAI Validation] Processing batches ${batchNums} in parallel`)
327
+
328
+ const results = await Promise.all(parallelGroup.map(processBatch))
329
+ for (const batchResults of results) {
330
+ validatedFindings.push(...batchResults)
331
+ }
332
+ totalApiBatches += parallelGroup.length
333
+ }
334
+ const totalDuration = Date.now() - startTime
335
+
336
+ // Copy accumulated stats back
337
+ stats.apiCalls = statsLock.apiCalls
338
+ stats.estimatedInputTokens = statsLock.estimatedInputTokens
339
+ stats.estimatedOutputTokens = statsLock.estimatedOutputTokens
340
+ stats.cacheReadTokens = statsLock.cacheReadTokens
341
+ stats.estimatedCost = statsLock.estimatedCost
342
+ stats.validatedFindings = statsLock.validatedFindings
343
+ stats.confirmedFindings = statsLock.confirmedFindings
344
+ stats.dismissedFindings = statsLock.dismissedFindings
345
+ stats.downgradedFindings = statsLock.downgradedFindings
346
+
347
+ // Calculate cache hit rate
348
+ const totalCacheableTokens = stats.cacheCreationTokens + stats.cacheReadTokens
349
+ stats.cacheHitRate = totalCacheableTokens > 0
350
+ ? stats.cacheReadTokens / totalCacheableTokens
351
+ : 0
352
+
353
+ // Log validation stats
354
+ const avgTimePerFileMs = fileEntries.length > 0
355
+ ? (totalDuration / fileEntries.length).toFixed(0)
356
+ : '0'
357
+ const totalDurationSec = (totalDuration / 1000).toFixed(1)
358
+
359
+ console.log(`[OpenAI Validation] Stats:`)
360
+ console.log(` - Total findings: ${stats.totalFindings}`)
361
+ console.log(` - AI validated: ${stats.validatedFindings}`)
362
+ console.log(` - Confirmed: ${stats.confirmedFindings}`)
363
+ console.log(` - Dismissed: ${stats.dismissedFindings}`)
364
+ console.log(` - Downgraded: ${stats.downgradedFindings}`)
365
+ console.log(` - API calls: ${stats.apiCalls}`)
366
+ console.log(` - Performance:`)
367
+ console.log(` - Total duration: ${totalDurationSec}s`)
368
+ console.log(` - Total API batches: ${totalApiBatches}`)
369
+ console.log(` - Avg time per file: ${avgTimePerFileMs}ms`)
370
+ console.log(` - Token usage:`)
371
+ console.log(` - Input (fresh): ${stats.estimatedInputTokens} tokens`)
372
+ console.log(` - Cached: ${stats.cacheReadTokens} tokens`)
373
+ console.log(` - Output: ${stats.estimatedOutputTokens} tokens`)
374
+ console.log(` - Estimated cost: $${stats.estimatedCost.toFixed(4)}`)
375
+
376
+ return { vulnerabilities: validatedFindings, stats }
377
+ }
378
+
379
+ /**
380
+ * Clear cached project context (called after validation complete)
381
+ */
382
+ export function clearOpenAICache(): void {
383
+ cachedProjectContext = null
384
+ }
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Request Builders for AI Validation
3
+ *
4
+ * Functions for building validation requests with full file context.
5
+ */
6
+
7
+ import type { Vulnerability, ScanFile } from '../../types'
8
+ import type { ProjectContext } from '../../utils/project-context-builder'
9
+ import { getFileValidationContext } from '../../utils/project-context-builder'
10
+ import { getLanguageFromPath } from './utils/path-helpers'
11
+
12
+ // ============================================================================
13
+ // Single-File Request Builder
14
+ // ============================================================================
15
+
16
+ /**
17
+ * Build a high-context validation request with full file content
18
+ */
19
+ export function buildHighContextValidationRequest(
20
+ file: ScanFile,
21
+ findings: Vulnerability[],
22
+ projectContext: ProjectContext
23
+ ): string {
24
+ // Add line numbers to full file content
25
+ const numberedContent = file.content
26
+ .split('\n')
27
+ .map((line, i) => `${String(i + 1).padStart(4, ' ')} | ${line}`)
28
+ .join('\n')
29
+
30
+ // Build candidate findings list
31
+ const candidatesText = findings.map((f, idx) => {
32
+ return `### Candidate ${idx}
33
+ - **Rule**: ${f.title}
34
+ - **Category**: ${f.category}
35
+ - **Original Severity**: ${f.severity}
36
+ - **Line**: ${f.lineNumber}
37
+ - **Detection Layer**: ${f.layer}
38
+ - **Description**: ${f.description}
39
+ - **Flagged Code**: \`${f.lineContent.trim()}\``
40
+ }).join('\n\n')
41
+
42
+ // Get file-specific context
43
+ const fileContext = getFileValidationContext(file, projectContext)
44
+
45
+ return `## Project Context
46
+ ${projectContext.summary}
47
+
48
+ ${fileContext}
49
+
50
+ ## Full File Content
51
+ \`\`\`${file.language || getLanguageFromPath(file.path)}
52
+ ${numberedContent}
53
+ \`\`\`
54
+
55
+ ## Candidate Findings to Validate (${findings.length} total)
56
+
57
+ ${candidatesText}
58
+
59
+ ---
60
+
61
+ Please validate each candidate finding. Return a JSON array with your decision for each.
62
+ Remember: Be AGGRESSIVE in rejecting false positives. Use the full file context and project architecture to make informed decisions.`
63
+ }
64
+
65
+ // ============================================================================
66
+ // Multi-File Request Builder (Phase 2 Optimization)
67
+ // ============================================================================
68
+
69
+ /**
70
+ * Build a multi-file validation request (Phase 2 optimization)
71
+ * Batches multiple files into a single API call to reduce overhead
72
+ */
73
+ export function buildMultiFileValidationRequest(
74
+ fileDataList: Array<{ file: ScanFile; findings: Vulnerability[] }>,
75
+ projectContext: ProjectContext
76
+ ): string {
77
+ const filesContent = fileDataList.map(({ file, findings }, fileIndex) => {
78
+ // Add line numbers to full file content
79
+ const numberedContent = file.content
80
+ .split('\n')
81
+ .map((line, i) => `${String(i + 1).padStart(4, ' ')} | ${line}`)
82
+ .join('\n')
83
+
84
+ // Build candidate findings list with file-specific indices
85
+ const candidatesText = findings.map((f, idx) => {
86
+ return `### Candidate ${idx}
87
+ - **Rule**: ${f.title}
88
+ - **Category**: ${f.category}
89
+ - **Original Severity**: ${f.severity}
90
+ - **Line**: ${f.lineNumber}
91
+ - **Detection Layer**: ${f.layer}
92
+ - **Description**: ${f.description}
93
+ - **Flagged Code**: \`${f.lineContent.trim()}\``
94
+ }).join('\n\n')
95
+
96
+ // Get file-specific context
97
+ const fileContext = getFileValidationContext(file, projectContext)
98
+
99
+ return `
100
+ ================================================================================
101
+ FILE ${fileIndex + 1}: ${file.path}
102
+ ================================================================================
103
+
104
+ ${fileContext}
105
+
106
+ ### Full File Content
107
+ \`\`\`${file.language || getLanguageFromPath(file.path)}
108
+ ${numberedContent}
109
+ \`\`\`
110
+
111
+ ### Candidate Findings to Validate (${findings.length} total)
112
+
113
+ ${candidatesText}`
114
+ }).join('\n\n')
115
+
116
+ return `## Project Context
117
+ ${projectContext.summary}
118
+
119
+ ${filesContent}
120
+
121
+ ---
122
+
123
+ ## Response Format
124
+
125
+ For EACH file, provide a JSON object with the file path and validation results.
126
+ Return a JSON array where each element has:
127
+ - "file": the file path (e.g., "${fileDataList[0]?.file.path || 'path/to/file.ts'}")
128
+ - "validations": array of validation results for that file's candidates
129
+
130
+ Example response format:
131
+ \`\`\`json
132
+ [
133
+ {
134
+ "file": "src/auth.ts",
135
+ "validations": [
136
+ { "index": 0, "keep": true, "adjustedSeverity": "medium", "notes": "Protected by middleware" },
137
+ { "index": 1, "keep": false }
138
+ ]
139
+ },
140
+ {
141
+ "file": "src/api.ts",
142
+ "validations": [
143
+ { "index": 0, "keep": true, "notes": "User input flows to SQL query" }
144
+ ]
145
+ }
146
+ ]
147
+ \`\`\`
148
+
149
+ Remember: Be AGGRESSIVE in rejecting false positives. Use the full file context and project architecture to make informed decisions.`
150
+ }
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Type definitions for AI validation module
3
+ */
4
+
5
+ import type { Vulnerability, VulnerabilitySeverity, VulnerabilityCategory, ValidationStatus } from '../../types'
6
+
7
+ // ============================================================================
8
+ // Cost Monitoring Types
9
+ // ============================================================================
10
+
11
+ export interface ValidationStats {
12
+ /** Total findings processed (input) */
13
+ totalFindings: number
14
+ /** Findings that went through AI validation */
15
+ validatedFindings: number
16
+ /** Findings confirmed as true positives */
17
+ confirmedFindings: number
18
+ /** Findings dismissed as false positives */
19
+ dismissedFindings: number
20
+ /** Findings with severity adjusted down */
21
+ downgradedFindings: number
22
+ /** Findings auto-dismissed before AI (test files, etc.) */
23
+ autoDismissedFindings: number
24
+ /** Estimated input tokens used */
25
+ estimatedInputTokens: number
26
+ /** Estimated output tokens used */
27
+ estimatedOutputTokens: number
28
+ /** Estimated cost in USD (based on Haiku pricing) */
29
+ estimatedCost: number
30
+ /** Number of API calls made */
31
+ apiCalls: number
32
+ /** Cache creation tokens (first write to cache) */
33
+ cacheCreationTokens: number
34
+ /** Cache read tokens (subsequent reads from cache) */
35
+ cacheReadTokens: number
36
+ /** Cache hit rate (0-1) */
37
+ cacheHitRate: number
38
+ }
39
+
40
+ export interface AIValidationResult {
41
+ vulnerabilities: Vulnerability[]
42
+ stats: ValidationStats
43
+ }
44
+
45
+ // ============================================================================
46
+ // Validation Types
47
+ // ============================================================================
48
+
49
+ export interface ValidationResult {
50
+ index: number
51
+ keep: boolean
52
+ // Optimized format: single notes field (replaces reason + validationNotes)
53
+ notes?: string // Only for keep=true, concise explanation
54
+ adjustedSeverity?: VulnerabilitySeverity | null
55
+ // Legacy fields for backward compatibility during parsing
56
+ reason?: string
57
+ validationNotes?: string
58
+ // Actionable output fields (PRO-82) - only for kept findings
59
+ impact?: string // Context-aware explanation of why this matters
60
+ fixSuggestion?: string // Specific fix recommendation for this code context
61
+ }
62
+
63
+ export interface AIFinding {
64
+ lineNumber: number
65
+ severity: VulnerabilitySeverity
66
+ category: VulnerabilityCategory
67
+ title: string
68
+ description: string
69
+ suggestedFix: string
70
+ }
71
+
72
+ // ============================================================================
73
+ // Context Types
74
+ // ============================================================================
75
+
76
+ export interface Layer3Context {
77
+ /** Middleware configuration from project scan */
78
+ middlewareConfig?: {
79
+ hasAuthMiddleware: boolean
80
+ authType?: string
81
+ protectedPaths: string[]
82
+ }
83
+ /** Auth helper context */
84
+ authHelpers?: {
85
+ hasThrowingHelpers: boolean
86
+ summary: string
87
+ }
88
+ /** Additional context string */
89
+ additionalContext?: string
90
+ }
91
+
92
+ // ============================================================================
93
+ // Auto-Dismiss Types
94
+ // ============================================================================
95
+
96
+ export interface AutoDismissRule {
97
+ name: string
98
+ check: (finding: Vulnerability, fileContent?: string) => boolean
99
+ reason: string
100
+ }
101
+
102
+ // ============================================================================
103
+ // Stats Accumulator (for thread-safe parallel processing)
104
+ // ============================================================================
105
+
106
+ export interface StatsAccumulator {
107
+ apiCalls: number
108
+ estimatedInputTokens: number
109
+ estimatedOutputTokens: number
110
+ cacheReadTokens: number
111
+ estimatedCost: number
112
+ validatedFindings: number
113
+ confirmedFindings: number
114
+ dismissedFindings: number
115
+ downgradedFindings: number
116
+ }
117
+
118
+ export function createStatsAccumulator(): StatsAccumulator {
119
+ return {
120
+ apiCalls: 0,
121
+ estimatedInputTokens: 0,
122
+ estimatedOutputTokens: 0,
123
+ cacheReadTokens: 0,
124
+ estimatedCost: 0,
125
+ validatedFindings: 0,
126
+ confirmedFindings: 0,
127
+ dismissedFindings: 0,
128
+ downgradedFindings: 0,
129
+ }
130
+ }
131
+
132
+ export function createInitialStats(totalFindings: number): ValidationStats {
133
+ return {
134
+ totalFindings,
135
+ validatedFindings: 0,
136
+ confirmedFindings: 0,
137
+ dismissedFindings: 0,
138
+ downgradedFindings: 0,
139
+ autoDismissedFindings: 0,
140
+ estimatedInputTokens: 0,
141
+ estimatedOutputTokens: 0,
142
+ estimatedCost: 0,
143
+ apiCalls: 0,
144
+ cacheCreationTokens: 0,
145
+ cacheReadTokens: 0,
146
+ cacheHitRate: 0,
147
+ }
148
+ }