@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,26 @@
1
+ /**
2
+ * Utility Functions Index
3
+ *
4
+ * Re-exports all utility functions from the anthropic module.
5
+ */
6
+
7
+ export {
8
+ normalizePathForComparison,
9
+ findMatchingFilePath,
10
+ getLanguageFromPath,
11
+ } from './path-helpers'
12
+
13
+ export {
14
+ makeAnthropicRequestWithRetry,
15
+ makeOpenAIRequestWithRetry,
16
+ } from './retry'
17
+
18
+ export {
19
+ parseAIResponse,
20
+ parseValidationResponse,
21
+ parseMultiFileValidationResponse,
22
+ applyValidationResults,
23
+ validateSeverity,
24
+ validateCategory,
25
+ getLineContent,
26
+ } from './response-parser'
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Path Normalization Helpers
3
+ *
4
+ * Functions for normalizing and matching file paths in AI responses.
5
+ * AI models may return paths in different formats than expected.
6
+ */
7
+
8
+ /**
9
+ * Normalize a file path for comparison purposes.
10
+ * Handles common variations: ./src/file.ts, src/file.ts, /src/file.ts
11
+ */
12
+ export function normalizePathForComparison(path: string): string {
13
+ return path
14
+ .replace(/^\.\//, '') // Remove leading ./
15
+ .replace(/^\//, '') // Remove leading /
16
+ .replace(/\\/g, '/') // Normalize Windows backslashes
17
+ }
18
+
19
+ /**
20
+ * Find a matching file path from expected paths, handling path format variations.
21
+ * AI responses may use different path formats than what we sent.
22
+ */
23
+ export function findMatchingFilePath(responsePath: string, expectedPaths: string[]): string | null {
24
+ // Exact match first
25
+ if (expectedPaths.includes(responsePath)) return responsePath
26
+
27
+ // Normalized match
28
+ const normalized = normalizePathForComparison(responsePath)
29
+ for (const expected of expectedPaths) {
30
+ if (normalizePathForComparison(expected) === normalized) {
31
+ console.log(`[AI Validation] Path fuzzy matched: "${responsePath}" -> "${expected}"`)
32
+ return expected
33
+ }
34
+ }
35
+
36
+ // Basename match (only if unique) - handles cases like "file.ts" matching "src/api/file.ts"
37
+ const basename = responsePath.split('/').pop() || responsePath
38
+ const matches = expectedPaths.filter(p => (p.split('/').pop() || p) === basename)
39
+ if (matches.length === 1) {
40
+ console.log(`[AI Validation] Path basename matched: "${responsePath}" -> "${matches[0]}"`)
41
+ return matches[0]
42
+ }
43
+
44
+ return null
45
+ }
46
+
47
+ /**
48
+ * Get language identifier from file path extension
49
+ */
50
+ export function getLanguageFromPath(path: string): string {
51
+ const ext = path.split('.').pop()?.toLowerCase()
52
+ const langMap: Record<string, string> = {
53
+ ts: 'typescript',
54
+ tsx: 'tsx',
55
+ js: 'javascript',
56
+ jsx: 'jsx',
57
+ py: 'python',
58
+ rb: 'ruby',
59
+ go: 'go',
60
+ java: 'java',
61
+ php: 'php',
62
+ cs: 'csharp',
63
+ json: 'json',
64
+ yaml: 'yaml',
65
+ yml: 'yaml',
66
+ }
67
+ return langMap[ext || ''] || ext || 'text'
68
+ }
@@ -0,0 +1,322 @@
1
+ /**
2
+ * AI Response Parsing Utilities
3
+ *
4
+ * Functions for parsing validation responses from AI models.
5
+ */
6
+
7
+ import type { VulnerabilitySeverity, VulnerabilityCategory, Vulnerability, ValidationStatus } from '../../../types'
8
+ import type { ValidationResult, AIFinding } from '../types'
9
+ import { findMatchingFilePath } from './path-helpers'
10
+
11
+ /**
12
+ * Extract the first top-level JSON array from text.
13
+ * The model may include prose before/after the JSON.
14
+ */
15
+ function extractTopLevelArray(text: string): string | null {
16
+ const startIndex = text.indexOf('[')
17
+ if (startIndex === -1) return null
18
+
19
+ let depth = 0
20
+ let inString = false
21
+ let stringChar: '"' | "'" | null = null
22
+ let escape = false
23
+
24
+ for (let i = startIndex; i < text.length; i++) {
25
+ const ch = text[i]
26
+
27
+ if (inString) {
28
+ if (escape) {
29
+ escape = false
30
+ continue
31
+ }
32
+
33
+ if (ch === '\\') {
34
+ escape = true
35
+ continue
36
+ }
37
+
38
+ if (stringChar && ch === stringChar) {
39
+ inString = false
40
+ stringChar = null
41
+ }
42
+ continue
43
+ }
44
+
45
+ if (ch === '"' || ch === "'") {
46
+ inString = true
47
+ stringChar = ch as '"' | "'"
48
+ continue
49
+ }
50
+
51
+ if (ch === '[') {
52
+ depth++
53
+ } else if (ch === ']') {
54
+ depth--
55
+ if (depth === 0) {
56
+ return text.slice(startIndex, i + 1)
57
+ }
58
+ }
59
+ }
60
+
61
+ return null
62
+ }
63
+
64
+ /**
65
+ * Parse AI response for single file validation
66
+ */
67
+ export function parseAIResponse(response: string): AIFinding[] {
68
+ try {
69
+ // Try to extract JSON from the response
70
+ const jsonMatch = response.match(/\[[\s\S]*\]/)
71
+ if (!jsonMatch) {
72
+ return []
73
+ }
74
+
75
+ const parsed = JSON.parse(jsonMatch[0])
76
+
77
+ // Validate the structure
78
+ if (!Array.isArray(parsed)) {
79
+ return []
80
+ }
81
+
82
+ return parsed.filter(item =>
83
+ typeof item.lineNumber === 'number' &&
84
+ typeof item.severity === 'string' &&
85
+ typeof item.category === 'string' &&
86
+ typeof item.title === 'string' &&
87
+ typeof item.description === 'string'
88
+ ).map(item => ({
89
+ lineNumber: item.lineNumber,
90
+ severity: validateSeverity(item.severity),
91
+ category: validateCategory(item.category),
92
+ title: item.title,
93
+ description: item.description,
94
+ suggestedFix: item.suggestedFix || 'Review and fix the security issue',
95
+ }))
96
+ } catch (error) {
97
+ console.error('Failed to parse AI response:', error)
98
+ return []
99
+ }
100
+ }
101
+
102
+ /**
103
+ * Parse single-file validation response
104
+ */
105
+ export function parseValidationResponse(response: string): ValidationResult[] {
106
+ try {
107
+ const jsonSlice = extractTopLevelArray(response)
108
+ if (!jsonSlice) return []
109
+
110
+ const parsed = JSON.parse(jsonSlice)
111
+ if (!Array.isArray(parsed)) return []
112
+
113
+ return parsed
114
+ .filter(item =>
115
+ typeof item.index === 'number' &&
116
+ typeof item.keep === 'boolean'
117
+ )
118
+ .map(item => {
119
+ // Normalize notes field: prefer new 'notes', fallback to legacy 'reason' or 'validationNotes'
120
+ const notes = item.notes || item.validationNotes || item.reason || undefined
121
+
122
+ return {
123
+ index: item.index,
124
+ keep: item.keep,
125
+ notes,
126
+ adjustedSeverity: item.adjustedSeverity || null,
127
+ // Keep legacy fields for backward compatibility
128
+ reason: item.reason,
129
+ validationNotes: item.validationNotes,
130
+ // Actionable output fields (PRO-82)
131
+ impact: item.impact || undefined,
132
+ fixSuggestion: item.fixSuggestion || undefined,
133
+ }
134
+ })
135
+ } catch (error) {
136
+ console.error('Failed to parse validation response:', error)
137
+ return []
138
+ }
139
+ }
140
+
141
+ /**
142
+ * Parse multi-file validation response (Phase 2)
143
+ * Returns a map of file path -> validation results
144
+ */
145
+ export function parseMultiFileValidationResponse(
146
+ response: string,
147
+ expectedFiles: string[]
148
+ ): Map<string, ValidationResult[]> {
149
+ const resultMap = new Map<string, ValidationResult[]>()
150
+
151
+ try {
152
+ const jsonSlice = extractTopLevelArray(response)
153
+ if (!jsonSlice) {
154
+ console.error('[AI Validation] Multi-file: No JSON array found in response')
155
+ return resultMap
156
+ }
157
+
158
+ const parsed = JSON.parse(jsonSlice)
159
+ if (!Array.isArray(parsed)) {
160
+ console.error('[AI Validation] Multi-file: Parsed result is not an array')
161
+ return resultMap
162
+ }
163
+
164
+ // Process each file's results
165
+ for (const fileResult of parsed) {
166
+ if (!fileResult.file || !Array.isArray(fileResult.validations)) {
167
+ console.warn('[AI Validation] Multi-file: Invalid file result structure, skipping')
168
+ continue
169
+ }
170
+
171
+ // Use path normalization to match AI response paths to expected paths
172
+ const responsePath = fileResult.file
173
+ const matchedPath = findMatchingFilePath(responsePath, expectedFiles)
174
+
175
+ if (!matchedPath) {
176
+ console.warn(`[AI Validation] Multi-file: Could not match path "${responsePath}" to any expected file`)
177
+ continue
178
+ }
179
+
180
+ const validations: ValidationResult[] = fileResult.validations
181
+ .filter((item: any) =>
182
+ typeof item.index === 'number' &&
183
+ typeof item.keep === 'boolean'
184
+ )
185
+ .map((item: any) => {
186
+ // Normalize notes field: prefer new 'notes', fallback to legacy 'reason' or 'validationNotes'
187
+ const notes = item.notes || item.validationNotes || item.reason || undefined
188
+
189
+ return {
190
+ index: item.index,
191
+ keep: item.keep,
192
+ notes,
193
+ adjustedSeverity: item.adjustedSeverity || null,
194
+ // Keep legacy fields for backward compatibility
195
+ reason: item.reason,
196
+ validationNotes: item.validationNotes,
197
+ // Actionable output fields (PRO-82)
198
+ impact: item.impact || undefined,
199
+ fixSuggestion: item.fixSuggestion || undefined,
200
+ }
201
+ })
202
+
203
+ resultMap.set(matchedPath, validations)
204
+ }
205
+
206
+ // Log any files that weren't in the response (these will be REJECTED by default)
207
+ const missingFiles = expectedFiles.filter(f => !resultMap.has(f))
208
+ if (missingFiles.length > 0) {
209
+ console.warn(`[AI Validation] Multi-file: Missing ${missingFiles.length} files from response: ${missingFiles.join(', ')}`)
210
+ }
211
+
212
+ } catch (error) {
213
+ console.error('[AI Validation] Multi-file: Failed to parse response:', error)
214
+ }
215
+
216
+ return resultMap
217
+ }
218
+
219
+ /**
220
+ * Apply validation results to findings
221
+ */
222
+ export function applyValidationResults(
223
+ findings: Vulnerability[],
224
+ validationResults: ValidationResult[]
225
+ ): { processed: Vulnerability[]; dismissedCount: number } {
226
+ const processed: Vulnerability[] = []
227
+ let dismissedCount = 0
228
+
229
+ for (let i = 0; i < findings.length; i++) {
230
+ const finding = findings[i]
231
+ const validation = validationResults.find(v => v.index === i)
232
+
233
+ if (!validation) {
234
+ // No validation result - REJECT by default (conservative approach)
235
+ // If AI doesn't explicitly validate a finding, assume it's a false positive
236
+ console.warn(`[AI Validation] No result for finding ${i}: ${finding.title} - REJECTING`)
237
+ dismissedCount++
238
+ continue // Don't add to processed - finding is removed
239
+ }
240
+
241
+ if (validation.keep) {
242
+ // Keep the finding
243
+ const adjustedFinding: Vulnerability = {
244
+ ...finding,
245
+ validatedByAI: true,
246
+ confidence: 'high',
247
+ }
248
+
249
+ // Extract notes from optimized or legacy format
250
+ const validationNotes = validation.notes || validation.validationNotes || validation.reason || undefined
251
+
252
+ if (validation.adjustedSeverity && validation.adjustedSeverity !== finding.severity) {
253
+ // Severity was adjusted
254
+ adjustedFinding.originalSeverity = finding.severity
255
+ adjustedFinding.severity = validation.adjustedSeverity
256
+ adjustedFinding.validationStatus = 'downgraded' as ValidationStatus
257
+ adjustedFinding.validationNotes = validationNotes || 'Severity adjusted by AI validation'
258
+ } else {
259
+ // Confirmed at original severity
260
+ adjustedFinding.validationStatus = 'confirmed' as ValidationStatus
261
+ adjustedFinding.validationNotes = validationNotes
262
+ }
263
+
264
+ // Apply AI-generated actionable fields (PRO-82)
265
+ if (validation.impact) {
266
+ adjustedFinding.impact = validation.impact
267
+ adjustedFinding.aiEnhanced = true
268
+ }
269
+ if (validation.fixSuggestion) {
270
+ // AI-generated fix becomes the primary fix step
271
+ adjustedFinding.fixSteps = [validation.fixSuggestion]
272
+ adjustedFinding.aiEnhanced = true
273
+ }
274
+
275
+ processed.push(adjustedFinding)
276
+ } else {
277
+ // Finding was dismissed - only log in debug mode to reduce noise
278
+ if (process.env.DEBUG || process.env.OCULUM_DEBUG) {
279
+ console.log(`[AI Validation] Rejected: ${finding.title} at ${finding.filePath}:${finding.lineNumber}`)
280
+ }
281
+ dismissedCount++
282
+ // Don't add to processed - finding is removed
283
+ }
284
+ }
285
+
286
+ return { processed, dismissedCount }
287
+ }
288
+
289
+ /**
290
+ * Validate severity value from AI response
291
+ */
292
+ export function validateSeverity(severity: string): VulnerabilitySeverity {
293
+ const valid: VulnerabilitySeverity[] = ['critical', 'high', 'medium', 'low', 'info']
294
+ return valid.includes(severity as VulnerabilitySeverity)
295
+ ? severity as VulnerabilitySeverity
296
+ : 'medium'
297
+ }
298
+
299
+ /**
300
+ * Validate category value from AI response
301
+ */
302
+ export function validateCategory(category: string): VulnerabilityCategory {
303
+ const valid: VulnerabilityCategory[] = [
304
+ 'sql_injection', 'xss', 'command_injection', 'missing_auth',
305
+ 'dangerous_function', 'hardcoded_secret', 'high_entropy_string',
306
+ 'sensitive_variable', 'security_bypass', 'insecure_config',
307
+ 'suspicious_package', 'cors_misconfiguration', 'root_container',
308
+ 'weak_crypto', 'sensitive_url', 'ai_pattern', 'dangerous_file',
309
+ 'data_exposure', // For logging/exposing sensitive data
310
+ ]
311
+ return valid.includes(category as VulnerabilityCategory)
312
+ ? category as VulnerabilityCategory
313
+ : 'dangerous_function'
314
+ }
315
+
316
+ /**
317
+ * Get line content from file content by line number
318
+ */
319
+ export function getLineContent(content: string, lineNumber: number): string {
320
+ const lines = content.split('\n')
321
+ return lines[lineNumber - 1]?.trim() || ''
322
+ }
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Retry Logic for AI API Calls
3
+ *
4
+ * Implements exponential backoff for rate limit handling.
5
+ */
6
+
7
+ /**
8
+ * Helper function to make Anthropic API calls with retry logic for rate limiting
9
+ * Implements exponential backoff for 429 (rate limit) errors
10
+ */
11
+ export async function makeAnthropicRequestWithRetry<T>(
12
+ requestFn: () => Promise<T>,
13
+ maxRetries: number = 3,
14
+ initialDelayMs: number = 1000
15
+ ): Promise<T> {
16
+ let lastError: Error | null = null
17
+
18
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
19
+ try {
20
+ return await requestFn()
21
+ } catch (error: any) {
22
+ lastError = error
23
+
24
+ // Check if it's a rate limit error (429)
25
+ const isRateLimit = error?.status === 429 || error?.message?.includes('rate limit')
26
+
27
+ if (isRateLimit && attempt < maxRetries) {
28
+ // Exponential backoff: 1s, 2s, 4s
29
+ const delayMs = initialDelayMs * Math.pow(2, attempt)
30
+ console.log(`[AI Validation] Rate limit hit, retrying in ${delayMs}ms (attempt ${attempt + 1}/${maxRetries})`)
31
+ await new Promise(resolve => setTimeout(resolve, delayMs))
32
+ continue
33
+ }
34
+
35
+ // If not rate limit or max retries reached, throw
36
+ throw error
37
+ }
38
+ }
39
+
40
+ throw lastError || new Error('Max retries exceeded')
41
+ }
42
+
43
+ /**
44
+ * Helper to make OpenAI requests with retry logic for rate limits
45
+ */
46
+ export async function makeOpenAIRequestWithRetry<T>(
47
+ requestFn: () => Promise<T>,
48
+ maxRetries = 3,
49
+ initialDelayMs = 1000
50
+ ): Promise<T> {
51
+ let lastError: Error | null = null
52
+
53
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
54
+ try {
55
+ return await requestFn()
56
+ } catch (error: any) {
57
+ lastError = error
58
+
59
+ // Check if it's a rate limit error (429) - but NOT insufficient_quota
60
+ const isRateLimit = error?.status === 429 && error?.code !== 'insufficient_quota'
61
+
62
+ if (isRateLimit && attempt < maxRetries) {
63
+ const delayMs = initialDelayMs * Math.pow(2, attempt)
64
+ console.log(`[OpenAI Validation] Rate limit hit, retrying in ${delayMs}ms (attempt ${attempt + 1}/${maxRetries})`)
65
+ await new Promise(resolve => setTimeout(resolve, delayMs))
66
+ continue
67
+ }
68
+
69
+ // If it's a quota error or max retries reached, throw
70
+ throw error
71
+ }
72
+ }
73
+
74
+ throw lastError || new Error('Max retries exceeded')
75
+ }
@@ -6,6 +6,7 @@
6
6
  import type { Vulnerability, ScanFile, CancellationToken } from '../types'
7
7
  import { batchAnalyzeWithAI, type Layer3Context } from './anthropic'
8
8
  import { checkPackages } from './package-check'
9
+ import { checkPackageAdvisories } from './osv-check'
9
10
 
10
11
  export interface Layer3Result {
11
12
  vulnerabilities: Vulnerability[]
@@ -58,14 +59,25 @@ export async function runLayer3Scan(
58
59
  }
59
60
  }
60
61
 
61
- // 1. Check packages (always run, fast)
62
- const packageFiles = files.filter(f => f.path.endsWith('package.json'))
63
- for (const file of packageFiles) {
62
+ // 1. Check packages for hallucination/risk (package.json and requirements.txt)
63
+ const packageManifests = files.filter(f =>
64
+ f.path.endsWith('package.json') ||
65
+ f.path.endsWith('requirements.txt')
66
+ )
67
+
68
+ for (const file of packageManifests) {
64
69
  // Check for cancellation in package loop
65
70
  if (options.cancellationToken?.cancelled) break
66
71
 
67
- const packageFindings = await checkPackages(file.content, file.path)
68
- vulnerabilities.push(...packageFindings)
72
+ // Run hallucination/risk check (package-check only handles package.json)
73
+ if (file.path.endsWith('package.json')) {
74
+ const packageFindings = await checkPackages(file.content, file.path)
75
+ vulnerabilities.push(...packageFindings)
76
+ }
77
+
78
+ // Run OSV advisory check (handles both npm and Python)
79
+ const osvFindings = await checkPackageAdvisories(file.content, file.path)
80
+ vulnerabilities.push(...osvFindings)
69
81
  }
70
82
 
71
83
  // Check for cancellation before AI analysis
@@ -153,3 +165,4 @@ function getAIPriorityScore(file: ScanFile): number {
153
165
 
154
166
  export { analyzeWithAI, batchAnalyzeWithAI, type Layer3Context } from './anthropic'
155
167
  export { checkPackages } from './package-check'
168
+ export { checkPackageAdvisories } from './osv-check'