@oculum/scanner 1.0.10 → 1.0.12

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 (520) hide show
  1. package/dist/ai-context/index.d.ts +6 -0
  2. package/dist/ai-context/index.d.ts.map +1 -0
  3. package/dist/ai-context/index.js +13 -0
  4. package/dist/ai-context/index.js.map +1 -0
  5. package/dist/ai-context/manager.d.ts +67 -0
  6. package/dist/ai-context/manager.d.ts.map +1 -0
  7. package/dist/ai-context/manager.js +104 -0
  8. package/dist/ai-context/manager.js.map +1 -0
  9. package/dist/baseline/diff.d.ts +32 -0
  10. package/dist/baseline/diff.d.ts.map +1 -0
  11. package/dist/baseline/diff.js +119 -0
  12. package/dist/baseline/diff.js.map +1 -0
  13. package/dist/baseline/index.d.ts +9 -0
  14. package/dist/baseline/index.d.ts.map +1 -0
  15. package/dist/baseline/index.js +19 -0
  16. package/dist/baseline/index.js.map +1 -0
  17. package/dist/baseline/manager.d.ts +67 -0
  18. package/dist/baseline/manager.d.ts.map +1 -0
  19. package/dist/baseline/manager.js +180 -0
  20. package/dist/baseline/manager.js.map +1 -0
  21. package/dist/baseline/types.d.ts +91 -0
  22. package/dist/baseline/types.d.ts.map +1 -0
  23. package/dist/baseline/types.js +12 -0
  24. package/dist/baseline/types.js.map +1 -0
  25. package/dist/category-filter.d.ts +125 -0
  26. package/dist/category-filter.d.ts.map +1 -0
  27. package/dist/category-filter.js +360 -0
  28. package/dist/category-filter.js.map +1 -0
  29. package/dist/filtering/context-adjustments.d.ts +23 -0
  30. package/dist/filtering/context-adjustments.d.ts.map +1 -0
  31. package/dist/filtering/context-adjustments.js +100 -0
  32. package/dist/filtering/context-adjustments.js.map +1 -0
  33. package/dist/filtering/index.d.ts +3 -0
  34. package/dist/filtering/index.d.ts.map +1 -0
  35. package/dist/filtering/index.js +8 -0
  36. package/dist/filtering/index.js.map +1 -0
  37. package/dist/filtering/pipeline.d.ts +48 -0
  38. package/dist/filtering/pipeline.d.ts.map +1 -0
  39. package/dist/filtering/pipeline.js +76 -0
  40. package/dist/filtering/pipeline.js.map +1 -0
  41. package/dist/formatters/ai-context.d.ts +23 -0
  42. package/dist/formatters/ai-context.d.ts.map +1 -0
  43. package/dist/formatters/ai-context.js +238 -0
  44. package/dist/formatters/ai-context.js.map +1 -0
  45. package/dist/formatters/cli-terminal.d.ts +38 -0
  46. package/dist/formatters/cli-terminal.d.ts.map +1 -1
  47. package/dist/formatters/cli-terminal.js +365 -42
  48. package/dist/formatters/cli-terminal.js.map +1 -1
  49. package/dist/formatters/github-comment.d.ts +2 -2
  50. package/dist/formatters/github-comment.d.ts.map +1 -1
  51. package/dist/formatters/github-comment.js +77 -13
  52. package/dist/formatters/github-comment.js.map +1 -1
  53. package/dist/formatters/ide/claude-code.d.ts +17 -0
  54. package/dist/formatters/ide/claude-code.d.ts.map +1 -0
  55. package/dist/formatters/ide/claude-code.js +94 -0
  56. package/dist/formatters/ide/claude-code.js.map +1 -0
  57. package/dist/formatters/ide/cursor.d.ts +13 -0
  58. package/dist/formatters/ide/cursor.d.ts.map +1 -0
  59. package/dist/formatters/ide/cursor.js +125 -0
  60. package/dist/formatters/ide/cursor.js.map +1 -0
  61. package/dist/formatters/ide/index.d.ts +62 -0
  62. package/dist/formatters/ide/index.d.ts.map +1 -0
  63. package/dist/formatters/ide/index.js +184 -0
  64. package/dist/formatters/ide/index.js.map +1 -0
  65. package/dist/formatters/ide/windsurf.d.ts +13 -0
  66. package/dist/formatters/ide/windsurf.d.ts.map +1 -0
  67. package/dist/formatters/ide/windsurf.js +117 -0
  68. package/dist/formatters/ide/windsurf.js.map +1 -0
  69. package/dist/formatters/index.d.ts +3 -1
  70. package/dist/formatters/index.d.ts.map +1 -1
  71. package/dist/formatters/index.js +20 -1
  72. package/dist/formatters/index.js.map +1 -1
  73. package/dist/index.d.ts +11 -0
  74. package/dist/index.d.ts.map +1 -1
  75. package/dist/index.js +423 -56
  76. package/dist/index.js.map +1 -1
  77. package/dist/layer1/comments.d.ts +4 -1
  78. package/dist/layer1/comments.d.ts.map +1 -1
  79. package/dist/layer1/comments.js +1 -1
  80. package/dist/layer1/comments.js.map +1 -1
  81. package/dist/layer1/config-audit.d.ts +4 -1
  82. package/dist/layer1/config-audit.d.ts.map +1 -1
  83. package/dist/layer1/config-audit.js +65 -14
  84. package/dist/layer1/config-audit.js.map +1 -1
  85. package/dist/layer1/config-mcp-audit.d.ts +23 -0
  86. package/dist/layer1/config-mcp-audit.d.ts.map +1 -0
  87. package/dist/layer1/config-mcp-audit.js +239 -0
  88. package/dist/layer1/config-mcp-audit.js.map +1 -0
  89. package/dist/layer1/entropy.d.ts +4 -1
  90. package/dist/layer1/entropy.d.ts.map +1 -1
  91. package/dist/layer1/entropy.js +212 -1
  92. package/dist/layer1/entropy.js.map +1 -1
  93. package/dist/layer1/file-flags.d.ts +4 -1
  94. package/dist/layer1/file-flags.d.ts.map +1 -1
  95. package/dist/layer1/file-flags.js +12 -5
  96. package/dist/layer1/file-flags.js.map +1 -1
  97. package/dist/layer1/index.d.ts +1 -0
  98. package/dist/layer1/index.d.ts.map +1 -1
  99. package/dist/layer1/index.js +22 -19
  100. package/dist/layer1/index.js.map +1 -1
  101. package/dist/layer1/patterns.d.ts +4 -1
  102. package/dist/layer1/patterns.d.ts.map +1 -1
  103. package/dist/layer1/patterns.js +34 -4
  104. package/dist/layer1/patterns.js.map +1 -1
  105. package/dist/layer1/urls.d.ts +4 -1
  106. package/dist/layer1/urls.d.ts.map +1 -1
  107. package/dist/layer1/urls.js +162 -14
  108. package/dist/layer1/urls.js.map +1 -1
  109. package/dist/layer1/weak-crypto.d.ts +4 -1
  110. package/dist/layer1/weak-crypto.d.ts.map +1 -1
  111. package/dist/layer1/weak-crypto.js +144 -7
  112. package/dist/layer1/weak-crypto.js.map +1 -1
  113. package/dist/layer2/ai-agent-tools.d.ts +4 -1
  114. package/dist/layer2/ai-agent-tools.d.ts.map +1 -1
  115. package/dist/layer2/ai-agent-tools.js +964 -2
  116. package/dist/layer2/ai-agent-tools.js.map +1 -1
  117. package/dist/layer2/ai-endpoint-protection.d.ts +2 -0
  118. package/dist/layer2/ai-endpoint-protection.d.ts.map +1 -1
  119. package/dist/layer2/ai-endpoint-protection.js +18 -4
  120. package/dist/layer2/ai-endpoint-protection.js.map +1 -1
  121. package/dist/layer2/ai-execution-sinks.d.ts +4 -1
  122. package/dist/layer2/ai-execution-sinks.d.ts.map +1 -1
  123. package/dist/layer2/ai-execution-sinks.js +688 -29
  124. package/dist/layer2/ai-execution-sinks.js.map +1 -1
  125. package/dist/layer2/ai-fingerprinting.d.ts +4 -1
  126. package/dist/layer2/ai-fingerprinting.d.ts.map +1 -1
  127. package/dist/layer2/ai-fingerprinting.js +28 -32
  128. package/dist/layer2/ai-fingerprinting.js.map +1 -1
  129. package/dist/layer2/ai-mcp-security.d.ts +20 -0
  130. package/dist/layer2/ai-mcp-security.d.ts.map +1 -0
  131. package/dist/layer2/ai-mcp-security.js +877 -0
  132. package/dist/layer2/ai-mcp-security.js.map +1 -0
  133. package/dist/layer2/ai-package-hallucination.d.ts +22 -0
  134. package/dist/layer2/ai-package-hallucination.d.ts.map +1 -0
  135. package/dist/layer2/ai-package-hallucination.js +828 -0
  136. package/dist/layer2/ai-package-hallucination.js.map +1 -0
  137. package/dist/layer2/ai-prompt-hygiene.d.ts +4 -1
  138. package/dist/layer2/ai-prompt-hygiene.d.ts.map +1 -1
  139. package/dist/layer2/ai-prompt-hygiene.js +817 -17
  140. package/dist/layer2/ai-prompt-hygiene.js.map +1 -1
  141. package/dist/layer2/ai-rag-safety.d.ts +4 -1
  142. package/dist/layer2/ai-rag-safety.d.ts.map +1 -1
  143. package/dist/layer2/ai-rag-safety.js +454 -3
  144. package/dist/layer2/ai-rag-safety.js.map +1 -1
  145. package/dist/layer2/ai-schema-validation.d.ts +4 -1
  146. package/dist/layer2/ai-schema-validation.d.ts.map +1 -1
  147. package/dist/layer2/ai-schema-validation.js +2 -2
  148. package/dist/layer2/ai-schema-validation.js.map +1 -1
  149. package/dist/layer2/auth-antipatterns.d.ts +2 -0
  150. package/dist/layer2/auth-antipatterns.d.ts.map +1 -1
  151. package/dist/layer2/auth-antipatterns.js +209 -20
  152. package/dist/layer2/auth-antipatterns.js.map +1 -1
  153. package/dist/layer2/byok-patterns.d.ts +4 -1
  154. package/dist/layer2/byok-patterns.d.ts.map +1 -1
  155. package/dist/layer2/byok-patterns.js +5 -2
  156. package/dist/layer2/byok-patterns.js.map +1 -1
  157. package/dist/layer2/dangerous-functions/child-process.d.ts +16 -0
  158. package/dist/layer2/dangerous-functions/child-process.d.ts.map +1 -0
  159. package/dist/layer2/dangerous-functions/child-process.js +74 -0
  160. package/dist/layer2/dangerous-functions/child-process.js.map +1 -0
  161. package/dist/layer2/dangerous-functions/dom-xss.d.ts +34 -0
  162. package/dist/layer2/dangerous-functions/dom-xss.d.ts.map +1 -0
  163. package/dist/layer2/dangerous-functions/dom-xss.js +230 -0
  164. package/dist/layer2/dangerous-functions/dom-xss.js.map +1 -0
  165. package/dist/layer2/dangerous-functions/index.d.ts +16 -0
  166. package/dist/layer2/dangerous-functions/index.d.ts.map +1 -0
  167. package/dist/layer2/dangerous-functions/index.js +1152 -0
  168. package/dist/layer2/dangerous-functions/index.js.map +1 -0
  169. package/dist/layer2/dangerous-functions/json-parse.d.ts +31 -0
  170. package/dist/layer2/dangerous-functions/json-parse.d.ts.map +1 -0
  171. package/dist/layer2/dangerous-functions/json-parse.js +319 -0
  172. package/dist/layer2/dangerous-functions/json-parse.js.map +1 -0
  173. package/dist/layer2/dangerous-functions/math-random.d.ts +111 -0
  174. package/dist/layer2/dangerous-functions/math-random.d.ts.map +1 -0
  175. package/dist/layer2/dangerous-functions/math-random.js +684 -0
  176. package/dist/layer2/dangerous-functions/math-random.js.map +1 -0
  177. package/dist/layer2/dangerous-functions/patterns.d.ts +21 -0
  178. package/dist/layer2/dangerous-functions/patterns.d.ts.map +1 -0
  179. package/dist/layer2/dangerous-functions/patterns.js +163 -0
  180. package/dist/layer2/dangerous-functions/patterns.js.map +1 -0
  181. package/dist/layer2/dangerous-functions/request-validation.d.ts +13 -0
  182. package/dist/layer2/dangerous-functions/request-validation.d.ts.map +1 -0
  183. package/dist/layer2/dangerous-functions/request-validation.js +119 -0
  184. package/dist/layer2/dangerous-functions/request-validation.js.map +1 -0
  185. package/dist/layer2/dangerous-functions/utils/control-flow.d.ts +24 -0
  186. package/dist/layer2/dangerous-functions/utils/control-flow.d.ts.map +1 -0
  187. package/dist/layer2/dangerous-functions/utils/control-flow.js +70 -0
  188. package/dist/layer2/dangerous-functions/utils/control-flow.js.map +1 -0
  189. package/dist/layer2/dangerous-functions/utils/helpers.d.ts +31 -0
  190. package/dist/layer2/dangerous-functions/utils/helpers.d.ts.map +1 -0
  191. package/dist/layer2/dangerous-functions/utils/helpers.js +147 -0
  192. package/dist/layer2/dangerous-functions/utils/helpers.js.map +1 -0
  193. package/dist/layer2/dangerous-functions/utils/index.d.ts +9 -0
  194. package/dist/layer2/dangerous-functions/utils/index.d.ts.map +1 -0
  195. package/dist/layer2/dangerous-functions/utils/index.js +23 -0
  196. package/dist/layer2/dangerous-functions/utils/index.js.map +1 -0
  197. package/dist/layer2/dangerous-functions/utils/schema-validation.d.ts +22 -0
  198. package/dist/layer2/dangerous-functions/utils/schema-validation.d.ts.map +1 -0
  199. package/dist/layer2/dangerous-functions/utils/schema-validation.js +102 -0
  200. package/dist/layer2/dangerous-functions/utils/schema-validation.js.map +1 -0
  201. package/dist/layer2/data-exposure.d.ts +4 -1
  202. package/dist/layer2/data-exposure.d.ts.map +1 -1
  203. package/dist/layer2/data-exposure.js +14 -38
  204. package/dist/layer2/data-exposure.js.map +1 -1
  205. package/dist/layer2/framework-checks.d.ts +4 -1
  206. package/dist/layer2/framework-checks.d.ts.map +1 -1
  207. package/dist/layer2/framework-checks.js +5 -2
  208. package/dist/layer2/framework-checks.js.map +1 -1
  209. package/dist/layer2/index.d.ts +12 -1
  210. package/dist/layer2/index.d.ts.map +1 -1
  211. package/dist/layer2/index.js +110 -45
  212. package/dist/layer2/index.js.map +1 -1
  213. package/dist/layer2/logic-gates.d.ts +4 -1
  214. package/dist/layer2/logic-gates.d.ts.map +1 -1
  215. package/dist/layer2/logic-gates.js +58 -20
  216. package/dist/layer2/logic-gates.js.map +1 -1
  217. package/dist/layer2/model-supply-chain.d.ts +23 -0
  218. package/dist/layer2/model-supply-chain.d.ts.map +1 -0
  219. package/dist/layer2/model-supply-chain.js +444 -0
  220. package/dist/layer2/model-supply-chain.js.map +1 -0
  221. package/dist/layer2/risky-imports.d.ts +4 -1
  222. package/dist/layer2/risky-imports.d.ts.map +1 -1
  223. package/dist/layer2/risky-imports.js +6 -2
  224. package/dist/layer2/risky-imports.js.map +1 -1
  225. package/dist/layer2/variables.d.ts +4 -1
  226. package/dist/layer2/variables.d.ts.map +1 -1
  227. package/dist/layer2/variables.js +6 -2
  228. package/dist/layer2/variables.js.map +1 -1
  229. package/dist/layer3/anthropic/auto-dismiss.d.ts +24 -0
  230. package/dist/layer3/anthropic/auto-dismiss.d.ts.map +1 -0
  231. package/dist/layer3/anthropic/auto-dismiss.js +199 -0
  232. package/dist/layer3/anthropic/auto-dismiss.js.map +1 -0
  233. package/dist/layer3/anthropic/clients.d.ts +44 -0
  234. package/dist/layer3/anthropic/clients.d.ts.map +1 -0
  235. package/dist/layer3/anthropic/clients.js +81 -0
  236. package/dist/layer3/anthropic/clients.js.map +1 -0
  237. package/dist/layer3/anthropic/index.d.ts +41 -0
  238. package/dist/layer3/anthropic/index.d.ts.map +1 -0
  239. package/dist/layer3/anthropic/index.js +141 -0
  240. package/dist/layer3/anthropic/index.js.map +1 -0
  241. package/dist/layer3/anthropic/prompts/index.d.ts +8 -0
  242. package/dist/layer3/anthropic/prompts/index.d.ts.map +1 -0
  243. package/dist/layer3/anthropic/prompts/index.js +14 -0
  244. package/dist/layer3/anthropic/prompts/index.js.map +1 -0
  245. package/dist/layer3/anthropic/prompts/semantic-analysis.d.ts +15 -0
  246. package/dist/layer3/anthropic/prompts/semantic-analysis.d.ts.map +1 -0
  247. package/dist/layer3/anthropic/prompts/semantic-analysis.js +169 -0
  248. package/dist/layer3/anthropic/prompts/semantic-analysis.js.map +1 -0
  249. package/dist/layer3/anthropic/prompts/validation.d.ts +12 -0
  250. package/dist/layer3/anthropic/prompts/validation.d.ts.map +1 -0
  251. package/dist/layer3/anthropic/prompts/validation.js +421 -0
  252. package/dist/layer3/anthropic/prompts/validation.js.map +1 -0
  253. package/dist/layer3/anthropic/providers/anthropic.d.ts +21 -0
  254. package/dist/layer3/anthropic/providers/anthropic.d.ts.map +1 -0
  255. package/dist/layer3/anthropic/providers/anthropic.js +266 -0
  256. package/dist/layer3/anthropic/providers/anthropic.js.map +1 -0
  257. package/dist/layer3/anthropic/providers/index.d.ts +8 -0
  258. package/dist/layer3/anthropic/providers/index.d.ts.map +1 -0
  259. package/dist/layer3/anthropic/providers/index.js +15 -0
  260. package/dist/layer3/anthropic/providers/index.js.map +1 -0
  261. package/dist/layer3/anthropic/providers/openai.d.ts +18 -0
  262. package/dist/layer3/anthropic/providers/openai.d.ts.map +1 -0
  263. package/dist/layer3/anthropic/providers/openai.js +340 -0
  264. package/dist/layer3/anthropic/providers/openai.js.map +1 -0
  265. package/dist/layer3/anthropic/request-builder.d.ts +20 -0
  266. package/dist/layer3/anthropic/request-builder.d.ts.map +1 -0
  267. package/dist/layer3/anthropic/request-builder.js +134 -0
  268. package/dist/layer3/anthropic/request-builder.js.map +1 -0
  269. package/dist/layer3/anthropic/types.d.ts +88 -0
  270. package/dist/layer3/anthropic/types.d.ts.map +1 -0
  271. package/dist/layer3/anthropic/types.js +38 -0
  272. package/dist/layer3/anthropic/types.js.map +1 -0
  273. package/dist/layer3/anthropic/utils/index.d.ts +9 -0
  274. package/dist/layer3/anthropic/utils/index.d.ts.map +1 -0
  275. package/dist/layer3/anthropic/utils/index.js +24 -0
  276. package/dist/layer3/anthropic/utils/index.js.map +1 -0
  277. package/dist/layer3/anthropic/utils/path-helpers.d.ts +21 -0
  278. package/dist/layer3/anthropic/utils/path-helpers.d.ts.map +1 -0
  279. package/dist/layer3/anthropic/utils/path-helpers.js +69 -0
  280. package/dist/layer3/anthropic/utils/path-helpers.js.map +1 -0
  281. package/dist/layer3/anthropic/utils/response-parser.d.ts +40 -0
  282. package/dist/layer3/anthropic/utils/response-parser.d.ts.map +1 -0
  283. package/dist/layer3/anthropic/utils/response-parser.js +285 -0
  284. package/dist/layer3/anthropic/utils/response-parser.js.map +1 -0
  285. package/dist/layer3/anthropic/utils/retry.d.ts +15 -0
  286. package/dist/layer3/anthropic/utils/retry.d.ts.map +1 -0
  287. package/dist/layer3/anthropic/utils/retry.js +62 -0
  288. package/dist/layer3/anthropic/utils/retry.js.map +1 -0
  289. package/dist/layer3/index.d.ts +1 -0
  290. package/dist/layer3/index.d.ts.map +1 -1
  291. package/dist/layer3/index.js +16 -6
  292. package/dist/layer3/index.js.map +1 -1
  293. package/dist/layer3/osv-check.d.ts +75 -0
  294. package/dist/layer3/osv-check.d.ts.map +1 -0
  295. package/dist/layer3/osv-check.js +308 -0
  296. package/dist/layer3/osv-check.js.map +1 -0
  297. package/dist/modes/incremental.js +1 -1
  298. package/dist/rules/framework-fixes.d.ts +48 -0
  299. package/dist/rules/framework-fixes.d.ts.map +1 -0
  300. package/dist/rules/framework-fixes.js +439 -0
  301. package/dist/rules/framework-fixes.js.map +1 -0
  302. package/dist/rules/index.d.ts +8 -0
  303. package/dist/rules/index.d.ts.map +1 -0
  304. package/dist/rules/index.js +18 -0
  305. package/dist/rules/index.js.map +1 -0
  306. package/dist/rules/metadata.d.ts +43 -0
  307. package/dist/rules/metadata.d.ts.map +1 -0
  308. package/dist/rules/metadata.js +734 -0
  309. package/dist/rules/metadata.js.map +1 -0
  310. package/dist/suppression/config-loader.d.ts +74 -0
  311. package/dist/suppression/config-loader.d.ts.map +1 -0
  312. package/dist/suppression/config-loader.js +424 -0
  313. package/dist/suppression/config-loader.js.map +1 -0
  314. package/dist/suppression/hash.d.ts +48 -0
  315. package/dist/suppression/hash.d.ts.map +1 -0
  316. package/dist/suppression/hash.js +88 -0
  317. package/dist/suppression/hash.js.map +1 -0
  318. package/dist/suppression/index.d.ts +11 -0
  319. package/dist/suppression/index.d.ts.map +1 -0
  320. package/dist/suppression/index.js +39 -0
  321. package/dist/suppression/index.js.map +1 -0
  322. package/dist/suppression/inline-parser.d.ts +39 -0
  323. package/dist/suppression/inline-parser.d.ts.map +1 -0
  324. package/dist/suppression/inline-parser.js +218 -0
  325. package/dist/suppression/inline-parser.js.map +1 -0
  326. package/dist/suppression/manager.d.ts +94 -0
  327. package/dist/suppression/manager.d.ts.map +1 -0
  328. package/dist/suppression/manager.js +292 -0
  329. package/dist/suppression/manager.js.map +1 -0
  330. package/dist/suppression/types.d.ts +151 -0
  331. package/dist/suppression/types.d.ts.map +1 -0
  332. package/dist/suppression/types.js +28 -0
  333. package/dist/suppression/types.js.map +1 -0
  334. package/dist/tiers.d.ts +3 -3
  335. package/dist/tiers.d.ts.map +1 -1
  336. package/dist/tiers.js +34 -7
  337. package/dist/tiers.js.map +1 -1
  338. package/dist/types.d.ts +140 -9
  339. package/dist/types.d.ts.map +1 -1
  340. package/dist/types.js +34 -0
  341. package/dist/types.js.map +1 -1
  342. package/dist/utils/code-analysis.d.ts +39 -0
  343. package/dist/utils/code-analysis.d.ts.map +1 -0
  344. package/dist/utils/code-analysis.js +159 -0
  345. package/dist/utils/code-analysis.js.map +1 -0
  346. package/dist/utils/comment-analyzer.d.ts +38 -0
  347. package/dist/utils/comment-analyzer.d.ts.map +1 -0
  348. package/dist/utils/comment-analyzer.js +218 -0
  349. package/dist/utils/comment-analyzer.js.map +1 -0
  350. package/dist/utils/context-helpers.d.ts +112 -1
  351. package/dist/utils/context-helpers.d.ts.map +1 -1
  352. package/dist/utils/context-helpers.js +364 -11
  353. package/dist/utils/context-helpers.js.map +1 -1
  354. package/dist/utils/environment-context.d.ts +76 -0
  355. package/dist/utils/environment-context.d.ts.map +1 -0
  356. package/dist/utils/environment-context.js +271 -0
  357. package/dist/utils/environment-context.js.map +1 -0
  358. package/dist/utils/intent-detector.d.ts +66 -0
  359. package/dist/utils/intent-detector.d.ts.map +1 -0
  360. package/dist/utils/intent-detector.js +282 -0
  361. package/dist/utils/intent-detector.js.map +1 -0
  362. package/dist/utils/parsed-file.d.ts +51 -0
  363. package/dist/utils/parsed-file.d.ts.map +1 -0
  364. package/dist/utils/parsed-file.js +95 -0
  365. package/dist/utils/parsed-file.js.map +1 -0
  366. package/dist/utils/route-hierarchy.d.ts +50 -0
  367. package/dist/utils/route-hierarchy.d.ts.map +1 -0
  368. package/dist/utils/route-hierarchy.js +226 -0
  369. package/dist/utils/route-hierarchy.js.map +1 -0
  370. package/dist/utils/schema-semantics.d.ts +45 -0
  371. package/dist/utils/schema-semantics.d.ts.map +1 -0
  372. package/dist/utils/schema-semantics.js +193 -0
  373. package/dist/utils/schema-semantics.js.map +1 -0
  374. package/package.json +4 -2
  375. package/src/__tests__/benchmark/fixtures/layer1/mcp-config-audit.json +31 -0
  376. package/src/__tests__/benchmark/fixtures/layer2/ai-execution-sinks.ts +1489 -82
  377. package/src/__tests__/benchmark/fixtures/layer2/ai-mcp-security.ts +495 -0
  378. package/src/__tests__/benchmark/fixtures/layer2/ai-package-hallucination.ts +255 -0
  379. package/src/__tests__/benchmark/fixtures/layer2/ai-prompt-hygiene.ts +300 -1
  380. package/src/__tests__/benchmark/fixtures/layer2/ai-rag-safety.ts +139 -0
  381. package/src/__tests__/benchmark/fixtures/layer2/byok-patterns.ts +7 -0
  382. package/src/__tests__/benchmark/fixtures/layer2/data-exposure.ts +63 -0
  383. package/src/__tests__/benchmark/fixtures/layer2/excessive-agency.ts +221 -0
  384. package/src/__tests__/benchmark/fixtures/layer2/index.ts +30 -0
  385. package/src/__tests__/benchmark/fixtures/layer2/model-supply-chain.ts +204 -0
  386. package/src/__tests__/benchmark/fixtures/layer2/phase1-enhancements.ts +157 -0
  387. package/src/__tests__/benchmark/fixtures/layer2/phase5-excessive-agency.ts +580 -0
  388. package/src/__tests__/benchmark/fixtures/layer2/sprint6-ai-enhancements.ts +515 -0
  389. package/src/__tests__/benchmark/run-depth-validation.ts +9 -9
  390. package/src/__tests__/category-filter.test.ts +478 -0
  391. package/src/__tests__/regression/known-false-positives.test.ts +490 -0
  392. package/src/__tests__/snapshots/__snapshots__/anthropic-validation-refactor.test.ts.snap +762 -0
  393. package/src/__tests__/snapshots/__snapshots__/dangerous-functions-refactor.test.ts.snap +503 -0
  394. package/src/__tests__/snapshots/__snapshots__/scan-depth.test.ts.snap +0 -9
  395. package/src/__tests__/snapshots/anthropic-validation-refactor.test.ts +321 -0
  396. package/src/__tests__/snapshots/dangerous-functions-refactor.test.ts +439 -0
  397. package/src/__tests__/validation/run-validation.ts +7 -7
  398. package/src/ai-context/__tests__/manager.test.ts +193 -0
  399. package/src/ai-context/index.ts +15 -0
  400. package/src/ai-context/manager.ts +145 -0
  401. package/src/baseline/__tests__/diff.test.ts +261 -0
  402. package/src/baseline/__tests__/manager.test.ts +225 -0
  403. package/src/baseline/diff.ts +135 -0
  404. package/src/baseline/index.ts +29 -0
  405. package/src/baseline/manager.ts +230 -0
  406. package/src/baseline/types.ts +97 -0
  407. package/src/category-filter.ts +400 -0
  408. package/src/filtering/__tests__/pipeline.test.ts +134 -0
  409. package/src/filtering/context-adjustments.ts +111 -0
  410. package/src/filtering/index.ts +10 -0
  411. package/src/filtering/pipeline.ts +130 -0
  412. package/src/formatters/__tests__/ai-context.test.ts +254 -0
  413. package/src/formatters/ai-context.ts +302 -0
  414. package/src/formatters/cli-terminal.ts +444 -41
  415. package/src/formatters/github-comment.ts +82 -14
  416. package/src/formatters/ide/__tests__/ide.test.ts +319 -0
  417. package/src/formatters/ide/claude-code.ts +110 -0
  418. package/src/formatters/ide/cursor.ts +147 -0
  419. package/src/formatters/ide/index.ts +216 -0
  420. package/src/formatters/ide/windsurf.ts +135 -0
  421. package/src/formatters/index.ts +28 -0
  422. package/src/index.ts +506 -45
  423. package/src/layer1/comments.ts +3 -1
  424. package/src/layer1/config-audit.ts +74 -14
  425. package/src/layer1/config-mcp-audit.ts +278 -0
  426. package/src/layer1/entropy.ts +234 -1
  427. package/src/layer1/file-flags.ts +17 -6
  428. package/src/layer1/index.ts +29 -23
  429. package/src/layer1/patterns.ts +42 -4
  430. package/src/layer1/urls.ts +188 -14
  431. package/src/layer1/weak-crypto.ts +168 -16
  432. package/src/layer2/ai-agent-tools.ts +1043 -2
  433. package/src/layer2/ai-endpoint-protection.ts +19 -4
  434. package/src/layer2/ai-execution-sinks.ts +755 -29
  435. package/src/layer2/ai-fingerprinting.ts +33 -33
  436. package/src/layer2/ai-mcp-security.ts +933 -0
  437. package/src/layer2/ai-package-hallucination.ts +940 -0
  438. package/src/layer2/ai-prompt-hygiene.ts +898 -17
  439. package/src/layer2/ai-rag-safety.ts +467 -5
  440. package/src/layer2/ai-schema-validation.ts +4 -2
  441. package/src/layer2/auth-antipatterns.ts +235 -20
  442. package/src/layer2/byok-patterns.ts +9 -3
  443. package/src/layer2/dangerous-functions/child-process.ts +98 -0
  444. package/src/layer2/dangerous-functions/dom-xss.ts +292 -0
  445. package/src/layer2/dangerous-functions/index.ts +1533 -0
  446. package/src/layer2/dangerous-functions/json-parse.ts +385 -0
  447. package/src/layer2/dangerous-functions/math-random.ts +789 -0
  448. package/src/layer2/dangerous-functions/patterns.ts +176 -0
  449. package/src/layer2/dangerous-functions/request-validation.ts +145 -0
  450. package/src/layer2/dangerous-functions/utils/control-flow.ts +35 -0
  451. package/src/layer2/dangerous-functions/utils/helpers.ts +170 -0
  452. package/src/layer2/dangerous-functions/utils/index.ts +25 -0
  453. package/src/layer2/dangerous-functions/utils/schema-validation.ts +106 -0
  454. package/src/layer2/data-exposure.ts +18 -39
  455. package/src/layer2/framework-checks.ts +9 -2
  456. package/src/layer2/index.ts +124 -43
  457. package/src/layer2/logic-gates.ts +64 -22
  458. package/src/layer2/model-supply-chain.ts +531 -0
  459. package/src/layer2/risky-imports.ts +9 -2
  460. package/src/layer2/variables.ts +9 -2
  461. package/src/layer3/__tests__/osv-check.test.ts +384 -0
  462. package/src/layer3/anthropic/auto-dismiss.ts +223 -0
  463. package/src/layer3/anthropic/clients.ts +84 -0
  464. package/src/layer3/anthropic/index.ts +170 -0
  465. package/src/layer3/anthropic/prompts/index.ts +14 -0
  466. package/src/layer3/anthropic/prompts/semantic-analysis.ts +173 -0
  467. package/src/layer3/anthropic/prompts/validation.ts +419 -0
  468. package/src/layer3/anthropic/providers/anthropic.ts +310 -0
  469. package/src/layer3/anthropic/providers/index.ts +8 -0
  470. package/src/layer3/anthropic/providers/openai.ts +384 -0
  471. package/src/layer3/anthropic/request-builder.ts +150 -0
  472. package/src/layer3/anthropic/types.ts +148 -0
  473. package/src/layer3/anthropic/utils/index.ts +26 -0
  474. package/src/layer3/anthropic/utils/path-helpers.ts +68 -0
  475. package/src/layer3/anthropic/utils/response-parser.ts +322 -0
  476. package/src/layer3/anthropic/utils/retry.ts +75 -0
  477. package/src/layer3/index.ts +18 -5
  478. package/src/layer3/osv-check.ts +420 -0
  479. package/src/modes/incremental.ts +1 -1
  480. package/src/rules/__tests__/framework-fixes.test.ts +689 -0
  481. package/src/rules/__tests__/metadata.test.ts +218 -0
  482. package/src/rules/framework-fixes.ts +470 -0
  483. package/src/rules/index.ts +21 -0
  484. package/src/rules/metadata.ts +831 -0
  485. package/src/suppression/__tests__/config-loader.test.ts +382 -0
  486. package/src/suppression/__tests__/hash.test.ts +166 -0
  487. package/src/suppression/__tests__/inline-parser.test.ts +212 -0
  488. package/src/suppression/__tests__/manager.test.ts +415 -0
  489. package/src/suppression/config-loader.ts +462 -0
  490. package/src/suppression/hash.ts +95 -0
  491. package/src/suppression/index.ts +51 -0
  492. package/src/suppression/inline-parser.ts +273 -0
  493. package/src/suppression/manager.ts +379 -0
  494. package/src/suppression/types.ts +174 -0
  495. package/src/tiers.ts +45 -9
  496. package/src/types.ts +212 -8
  497. package/src/utils/__tests__/code-analysis.test.ts +165 -0
  498. package/src/utils/__tests__/parsed-file.test.ts +124 -0
  499. package/src/utils/code-analysis.ts +179 -0
  500. package/src/utils/comment-analyzer.ts +249 -0
  501. package/src/utils/context-helpers.ts +421 -11
  502. package/src/utils/environment-context.ts +304 -0
  503. package/src/utils/intent-detector.ts +318 -0
  504. package/src/utils/parsed-file.ts +103 -0
  505. package/src/utils/route-hierarchy.ts +250 -0
  506. package/src/utils/schema-semantics.ts +233 -0
  507. package/dist/layer2/dangerous-functions.d.ts +0 -7
  508. package/dist/layer2/dangerous-functions.d.ts.map +0 -1
  509. package/dist/layer2/dangerous-functions.js +0 -1701
  510. package/dist/layer2/dangerous-functions.js.map +0 -1
  511. package/dist/layer3/anthropic.d.ts +0 -87
  512. package/dist/layer3/anthropic.d.ts.map +0 -1
  513. package/dist/layer3/anthropic.js +0 -1948
  514. package/dist/layer3/anthropic.js.map +0 -1
  515. package/dist/layer3/openai.d.ts +0 -25
  516. package/dist/layer3/openai.d.ts.map +0 -1
  517. package/dist/layer3/openai.js +0 -238
  518. package/dist/layer3/openai.js.map +0 -1
  519. package/src/layer2/dangerous-functions.ts +0 -1940
  520. package/src/layer3/anthropic.ts +0 -2257
@@ -0,0 +1,400 @@
1
+ /**
2
+ * Category-Based Filtering
3
+ *
4
+ * Enables CI to fail only on specific vulnerability categories,
5
+ * allowing gradual rollout (e.g., "only block prompt injection")
6
+ * and fine-grained control over which findings are blocking.
7
+ *
8
+ * @example
9
+ * // Fail only on AI-related and secret categories
10
+ * --fail-on-categories ai-*,secrets-*
11
+ *
12
+ * @example
13
+ * // Combined with severity
14
+ * --fail-on high --fail-on-categories ai-*
15
+ * // Only fail on high+ AI findings
16
+ */
17
+
18
+ import type { VulnerabilityCategory, Vulnerability, VulnerabilitySeverity } from './types'
19
+ import { severityRank } from './utils/parsed-file'
20
+
21
+ /**
22
+ * Category group definitions for wildcard expansion
23
+ *
24
+ * These groups allow users to specify broad categories like "ai-*"
25
+ * which expand to all AI-related vulnerability categories.
26
+ */
27
+ export const CATEGORY_GROUPS: Record<string, VulnerabilityCategory[]> = {
28
+ 'ai-*': [
29
+ 'ai_pattern',
30
+ 'ai_prompt_injection',
31
+ 'ai_unsafe_execution',
32
+ 'ai_overpermissive_tool',
33
+ 'ai_rag_exfiltration',
34
+ 'ai_endpoint_unprotected',
35
+ 'ai_schema_mismatch',
36
+ 'ai_package_hallucination',
37
+ 'ai_rag_corpus_poisoning',
38
+ 'ai_rag_pii_leakage',
39
+ 'ai_mcp_tool_poisoning',
40
+ 'ai_mcp_credential_issue',
41
+ 'ai_mcp_confused_deputy',
42
+ 'ai_mcp_description_injection',
43
+ 'ai_mcp_server_shadowing',
44
+ 'ai_mcp_config_secrets',
45
+ 'ai_mcp_config_permissions',
46
+ 'ai_rag_query_injection',
47
+ 'ai_rag_embedding_poisoning',
48
+ 'ai_rag_chunk_injection',
49
+ 'ai_package_typosquat',
50
+ 'ai_package_malicious',
51
+ 'ai_unsafe_model_load',
52
+ 'ai_unverified_model',
53
+ 'ai_unsafe_finetuning',
54
+ 'ai_excessive_agency',
55
+ ],
56
+ 'secrets-*': [
57
+ 'hardcoded_secret',
58
+ 'high_entropy_string',
59
+ 'sensitive_variable',
60
+ ],
61
+ 'owasp-*': [
62
+ 'sql_injection',
63
+ 'xss',
64
+ 'command_injection',
65
+ 'missing_auth',
66
+ 'security_bypass',
67
+ 'insecure_config',
68
+ 'cors_misconfiguration',
69
+ 'data_exposure',
70
+ 'weak_crypto',
71
+ ],
72
+ }
73
+
74
+ /**
75
+ * All known valid category names for validation
76
+ */
77
+ export const ALL_CATEGORIES: VulnerabilityCategory[] = [
78
+ 'hardcoded_secret',
79
+ 'high_entropy_string',
80
+ 'sensitive_variable',
81
+ 'security_bypass',
82
+ 'dangerous_function',
83
+ 'sql_injection',
84
+ 'xss',
85
+ 'command_injection',
86
+ 'insecure_config',
87
+ 'missing_auth',
88
+ 'suspicious_package',
89
+ 'cors_misconfiguration',
90
+ 'root_container',
91
+ 'dangerous_file',
92
+ 'ai_pattern',
93
+ 'sensitive_url',
94
+ 'weak_crypto',
95
+ 'data_exposure',
96
+ 'ai_prompt_injection',
97
+ 'ai_unsafe_execution',
98
+ 'ai_overpermissive_tool',
99
+ 'ai_rag_exfiltration',
100
+ 'ai_endpoint_unprotected',
101
+ 'ai_schema_mismatch',
102
+ 'ai_package_hallucination',
103
+ 'ai_rag_corpus_poisoning',
104
+ 'ai_rag_pii_leakage',
105
+ 'ai_mcp_tool_poisoning',
106
+ 'ai_mcp_credential_issue',
107
+ 'ai_mcp_confused_deputy',
108
+ 'ai_mcp_description_injection',
109
+ 'ai_mcp_server_shadowing',
110
+ 'ai_mcp_config_secrets',
111
+ 'ai_mcp_config_permissions',
112
+ 'ai_rag_query_injection',
113
+ 'ai_rag_embedding_poisoning',
114
+ 'ai_rag_chunk_injection',
115
+ 'ai_package_typosquat',
116
+ 'ai_package_malicious',
117
+ 'ai_unsafe_model_load',
118
+ 'ai_unverified_model',
119
+ 'ai_unsafe_finetuning',
120
+ 'ai_excessive_agency',
121
+ ]
122
+
123
+ /**
124
+ * Normalize category name for comparison
125
+ * - Converts to lowercase
126
+ * - Converts hyphens to underscores
127
+ * - Trims whitespace
128
+ *
129
+ * @example
130
+ * normalizeCategory('SQL-Injection') // 'sql_injection'
131
+ * normalizeCategory('high_entropy_string') // 'high_entropy_string'
132
+ */
133
+ export function normalizeCategory(category: string): string {
134
+ return category
135
+ .toLowerCase()
136
+ .trim()
137
+ .replace(/-/g, '_')
138
+ }
139
+
140
+ /**
141
+ * Check if a string is a valid wildcard pattern
142
+ * Valid wildcards end with '-*' or '_*'
143
+ */
144
+ function isWildcardPattern(pattern: string): boolean {
145
+ return pattern.endsWith('-*') || pattern.endsWith('_*')
146
+ }
147
+
148
+ /**
149
+ * Expand a wildcard pattern or single category to a list of categories
150
+ *
151
+ * @param pattern - Category name or wildcard (e.g., 'sql_injection', 'ai-*')
152
+ * @returns Array of matching categories
153
+ *
154
+ * @example
155
+ * expandCategoryPattern('ai-*') // Returns all ai_* categories
156
+ * expandCategoryPattern('sql_injection') // Returns ['sql_injection']
157
+ * expandCategoryPattern('unknown-*') // Returns []
158
+ */
159
+ export function expandCategoryPattern(pattern: string): VulnerabilityCategory[] {
160
+ const normalized = normalizeCategory(pattern)
161
+
162
+ // Check for wildcard patterns
163
+ if (isWildcardPattern(normalized)) {
164
+ // Normalize the wildcard pattern (both ai-* and ai_* should work)
165
+ const normalizedWildcard = normalized.replace('_*', '-*')
166
+
167
+ // Look up in category groups
168
+ const expanded = CATEGORY_GROUPS[normalizedWildcard]
169
+ if (expanded) {
170
+ return [...expanded]
171
+ }
172
+
173
+ // Unknown wildcard - return empty
174
+ return []
175
+ }
176
+
177
+ // Single category - validate and return
178
+ // Handle both hyphenated and underscored versions
179
+ const normalizedCategory = normalized as VulnerabilityCategory
180
+ if (ALL_CATEGORIES.includes(normalizedCategory)) {
181
+ return [normalizedCategory]
182
+ }
183
+
184
+ // Try hyphenated version converted to underscore
185
+ const underscored = normalized.replace(/-/g, '_') as VulnerabilityCategory
186
+ if (ALL_CATEGORIES.includes(underscored)) {
187
+ return [underscored]
188
+ }
189
+
190
+ return []
191
+ }
192
+
193
+ /**
194
+ * Check if a category matches any pattern in the filter list
195
+ *
196
+ * @param category - The vulnerability category to check
197
+ * @param patterns - Array of category patterns (names or wildcards)
198
+ * @returns true if the category matches any pattern
199
+ *
200
+ * @example
201
+ * matchesAnyCategory('ai_prompt_injection', ['ai-*']) // true
202
+ * matchesAnyCategory('sql_injection', ['ai-*']) // false
203
+ * matchesAnyCategory('sql_injection', ['sql_injection', 'xss']) // true
204
+ */
205
+ export function matchesAnyCategory(
206
+ category: VulnerabilityCategory,
207
+ patterns: string[]
208
+ ): boolean {
209
+ if (!patterns || patterns.length === 0) {
210
+ return false
211
+ }
212
+
213
+ const normalizedCategory = normalizeCategory(category)
214
+
215
+ for (const pattern of patterns) {
216
+ const expanded = expandCategoryPattern(pattern)
217
+
218
+ // Check if category is in the expanded list
219
+ if (expanded.some(c => normalizeCategory(c) === normalizedCategory)) {
220
+ return true
221
+ }
222
+ }
223
+
224
+ return false
225
+ }
226
+
227
+ // severityRank imported from utils/parsed-file
228
+
229
+ /**
230
+ * Check if vulnerabilities should cause failure based on category filter
231
+ *
232
+ * When both category patterns and severity threshold are provided,
233
+ * BOTH conditions must match for a finding to cause failure.
234
+ *
235
+ * @param vulnerabilities - List of vulnerabilities to check
236
+ * @param categoryPatterns - Category patterns to filter on
237
+ * @param severityThreshold - Optional severity threshold (both must match)
238
+ * @returns true if any vulnerability matches and should cause failure
239
+ *
240
+ * @example
241
+ * // Only fail on AI findings
242
+ * shouldFailOnCategories(vulns, ['ai-*'])
243
+ *
244
+ * @example
245
+ * // Only fail on HIGH+ AI findings
246
+ * shouldFailOnCategories(vulns, ['ai-*'], 'high')
247
+ */
248
+ export function shouldFailOnCategories(
249
+ vulnerabilities: Vulnerability[],
250
+ categoryPatterns: string[],
251
+ severityThreshold?: VulnerabilitySeverity
252
+ ): boolean {
253
+ if (!vulnerabilities || vulnerabilities.length === 0) {
254
+ return false
255
+ }
256
+
257
+ if (!categoryPatterns || categoryPatterns.length === 0) {
258
+ return false
259
+ }
260
+
261
+ const thresholdRank = severityThreshold ? severityRank(severityThreshold) : 0
262
+
263
+ for (const vuln of vulnerabilities) {
264
+ // Check if category matches any pattern
265
+ if (!matchesAnyCategory(vuln.category, categoryPatterns)) {
266
+ continue
267
+ }
268
+
269
+ // If no severity threshold specified, any matching category triggers failure
270
+ if (!severityThreshold) {
271
+ return true
272
+ }
273
+
274
+ // Check if severity meets threshold
275
+ if (severityRank(vuln.severity) >= thresholdRank) {
276
+ return true
277
+ }
278
+ }
279
+
280
+ return false
281
+ }
282
+
283
+ /**
284
+ * Get the categories that matched the filter from vulnerabilities
285
+ * Useful for error messages showing which categories caused failure
286
+ */
287
+ export function getMatchingCategories(
288
+ vulnerabilities: Vulnerability[],
289
+ categoryPatterns: string[],
290
+ severityThreshold?: VulnerabilitySeverity
291
+ ): VulnerabilityCategory[] {
292
+ if (!vulnerabilities || vulnerabilities.length === 0) {
293
+ return []
294
+ }
295
+
296
+ if (!categoryPatterns || categoryPatterns.length === 0) {
297
+ return []
298
+ }
299
+
300
+ const thresholdRank = severityThreshold ? severityRank(severityThreshold) : 0
301
+ const matched = new Set<VulnerabilityCategory>()
302
+
303
+ for (const vuln of vulnerabilities) {
304
+ // Check if category matches any pattern
305
+ if (!matchesAnyCategory(vuln.category, categoryPatterns)) {
306
+ continue
307
+ }
308
+
309
+ // If no severity threshold, or severity meets threshold
310
+ if (!severityThreshold || severityRank(vuln.severity) >= thresholdRank) {
311
+ matched.add(vuln.category)
312
+ }
313
+ }
314
+
315
+ return Array.from(matched)
316
+ }
317
+
318
+ /**
319
+ * Parse comma-separated category string into array
320
+ *
321
+ * @param input - Comma-separated category string
322
+ * @returns Array of trimmed category patterns
323
+ *
324
+ * @example
325
+ * parseCategoryList('ai-*, secrets-*') // ['ai-*', 'secrets-*']
326
+ * parseCategoryList('sql_injection, xss') // ['sql_injection', 'xss']
327
+ */
328
+ export function parseCategoryList(input: string): string[] {
329
+ if (!input || typeof input !== 'string') {
330
+ return []
331
+ }
332
+
333
+ return input
334
+ .split(',')
335
+ .map(s => s.trim())
336
+ .filter(s => s.length > 0)
337
+ }
338
+
339
+ /**
340
+ * Validate category names, separating valid from invalid
341
+ *
342
+ * @param categories - Array of category patterns to validate
343
+ * @returns Object with valid and invalid category arrays
344
+ *
345
+ * @example
346
+ * validateCategories(['ai-*', 'sql_injection', 'fake_category'])
347
+ * // { valid: ['ai-*', 'sql_injection'], invalid: ['fake_category'] }
348
+ */
349
+ export function validateCategories(categories: string[]): {
350
+ valid: string[]
351
+ invalid: string[]
352
+ } {
353
+ const valid: string[] = []
354
+ const invalid: string[] = []
355
+
356
+ for (const category of categories) {
357
+ const normalized = normalizeCategory(category)
358
+
359
+ // Check if it's a valid wildcard
360
+ if (isWildcardPattern(normalized)) {
361
+ const normalizedWildcard = normalized.replace('_*', '-*')
362
+ if (CATEGORY_GROUPS[normalizedWildcard]) {
363
+ valid.push(category)
364
+ } else {
365
+ invalid.push(category)
366
+ }
367
+ continue
368
+ }
369
+
370
+ // Check if it's a valid category name
371
+ const expanded = expandCategoryPattern(category)
372
+ if (expanded.length > 0) {
373
+ valid.push(category)
374
+ } else {
375
+ invalid.push(category)
376
+ }
377
+ }
378
+
379
+ return { valid, invalid }
380
+ }
381
+
382
+ /**
383
+ * Get a human-readable list of available category groups
384
+ * Useful for help text and error messages
385
+ */
386
+ export function getAvailableCategoryGroups(): string[] {
387
+ return Object.keys(CATEGORY_GROUPS)
388
+ }
389
+
390
+ /**
391
+ * Get the count of categories in each group
392
+ * Useful for documentation and help text
393
+ */
394
+ export function getCategoryGroupCounts(): Record<string, number> {
395
+ const counts: Record<string, number> = {}
396
+ for (const [group, categories] of Object.entries(CATEGORY_GROUPS)) {
397
+ counts[group] = categories.length
398
+ }
399
+ return counts
400
+ }
@@ -0,0 +1,134 @@
1
+ import { FilterPipeline } from '../pipeline'
2
+ import type { FilterDecision } from '../pipeline'
3
+
4
+ describe('FilterPipeline', () => {
5
+ describe('when disabled', () => {
6
+ it('does not record decisions', () => {
7
+ const pipeline = new FilterPipeline(false)
8
+ pipeline.record('finding-1', {
9
+ stage: 'auto_dismiss',
10
+ action: 'dismissed',
11
+ reason: 'test file',
12
+ })
13
+ expect(pipeline.getAuditTrail()).toEqual([])
14
+ })
15
+
16
+ it('reports isEnabled as false', () => {
17
+ expect(new FilterPipeline(false).isEnabled).toBe(false)
18
+ expect(new FilterPipeline().isEnabled).toBe(false)
19
+ })
20
+ })
21
+
22
+ describe('when enabled', () => {
23
+ let pipeline: FilterPipeline
24
+
25
+ beforeEach(() => {
26
+ pipeline = new FilterPipeline(true)
27
+ })
28
+
29
+ it('reports isEnabled as true', () => {
30
+ expect(pipeline.isEnabled).toBe(true)
31
+ })
32
+
33
+ it('records and retrieves decisions', () => {
34
+ const decision: FilterDecision = {
35
+ stage: 'auto_dismiss',
36
+ action: 'dismissed',
37
+ reason: 'test file detected',
38
+ ruleName: 'test_file_dismiss',
39
+ }
40
+ pipeline.record('finding-1', decision)
41
+
42
+ const trail = pipeline.getAuditTrail()
43
+ expect(trail).toHaveLength(1)
44
+ expect(trail[0].findingId).toBe('finding-1')
45
+ expect(trail[0].decisions).toEqual([decision])
46
+ expect(trail[0].finalStatus).toBe('dismissed')
47
+ })
48
+
49
+ it('accumulates multiple decisions for the same finding', () => {
50
+ pipeline.record('finding-1', {
51
+ stage: 'file_context_adjustment',
52
+ action: 'downgraded',
53
+ reason: 'tooling directory',
54
+ originalSeverity: 'medium',
55
+ newSeverity: 'info',
56
+ })
57
+ pipeline.record('finding-1', {
58
+ stage: 'ai_validation',
59
+ action: 'kept',
60
+ reason: 'AI confirmed the finding',
61
+ })
62
+
63
+ const decisions = pipeline.explainDismissal('finding-1')
64
+ expect(decisions).toHaveLength(2)
65
+ expect(decisions[0].stage).toBe('file_context_adjustment')
66
+ expect(decisions[1].stage).toBe('ai_validation')
67
+ })
68
+
69
+ it('marks final status as surfaced when no dismissal', () => {
70
+ pipeline.record('finding-1', {
71
+ stage: 'ai_validation',
72
+ action: 'kept',
73
+ reason: 'confirmed',
74
+ })
75
+
76
+ const trail = pipeline.getAuditTrail()
77
+ expect(trail[0].finalStatus).toBe('surfaced')
78
+ })
79
+
80
+ it('marks final status as dismissed for deduplicated findings', () => {
81
+ pipeline.record('finding-1', {
82
+ stage: 'deduplication',
83
+ action: 'deduplicated',
84
+ reason: 'duplicate of finding-2',
85
+ })
86
+
87
+ const trail = pipeline.getAuditTrail()
88
+ expect(trail[0].finalStatus).toBe('dismissed')
89
+ })
90
+
91
+ it('marks final status as dismissed for suppressed findings', () => {
92
+ pipeline.record('finding-1', {
93
+ stage: 'user_suppression',
94
+ action: 'suppressed',
95
+ reason: 'inline oculum-ignore comment',
96
+ })
97
+
98
+ const trail = pipeline.getAuditTrail()
99
+ expect(trail[0].finalStatus).toBe('dismissed')
100
+ })
101
+
102
+ it('returns empty array for unknown finding in explainDismissal', () => {
103
+ expect(pipeline.explainDismissal('nonexistent')).toEqual([])
104
+ })
105
+ })
106
+
107
+ describe('getSummaryStats', () => {
108
+ it('aggregates counts per stage and action', () => {
109
+ const pipeline = new FilterPipeline(true)
110
+
111
+ pipeline.record('f1', { stage: 'auto_dismiss', action: 'dismissed', reason: 'test' })
112
+ pipeline.record('f2', { stage: 'auto_dismiss', action: 'dismissed', reason: 'css' })
113
+ pipeline.record('f3', { stage: 'auto_dismiss', action: 'kept', reason: 'real finding' })
114
+ pipeline.record('f4', { stage: 'ai_validation', action: 'downgraded', reason: 'lower severity' })
115
+ pipeline.record('f5', { stage: 'deduplication', action: 'deduplicated', reason: 'dup' })
116
+
117
+ const stats = pipeline.getSummaryStats()
118
+
119
+ expect(stats.auto_dismiss.dismissed).toBe(2)
120
+ expect(stats.auto_dismiss.kept).toBe(1)
121
+ expect(stats.ai_validation.downgraded).toBe(1)
122
+ expect(stats.deduplication.deduplicated).toBe(1)
123
+ expect(stats.localhost_aggregation.dismissed).toBe(0)
124
+ })
125
+
126
+ it('returns zero counts when no decisions recorded', () => {
127
+ const pipeline = new FilterPipeline(true)
128
+ const stats = pipeline.getSummaryStats()
129
+
130
+ expect(stats.auto_dismiss.dismissed).toBe(0)
131
+ expect(stats.tier_filter.kept).toBe(0)
132
+ })
133
+ })
134
+ })
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Centralized Context-Based Severity Adjustments
3
+ *
4
+ * Consolidates the duplicate filter logic from:
5
+ * - layer2/index.ts applyFileContextAdjustments() — per-file with full FileContext
6
+ * - index.ts applyGlobalContextAdjustments() — post-layers with tooling-dir only
7
+ *
8
+ * Both functions are replaced by a single applyContextAdjustments() that handles
9
+ * all context-based severity downgrades in one place.
10
+ */
11
+
12
+ import type { Vulnerability } from '../types'
13
+ import type { FileContext } from '../utils/context-helpers'
14
+
15
+ /** Categories that are expected and not risky in tooling directories */
16
+ const TOOLING_DOWNGRADABLE_CATEGORIES = [
17
+ 'dangerous_function', // exec, spawn, etc. are expected in scripts
18
+ 'command_injection', // child_process usage is expected in build scripts
19
+ 'weak_crypto', // Math.random() for seeding is fine in scripts
20
+ 'data_exposure', // Logging is expected in tooling
21
+ 'ai_pattern', // AI patterns in scripts are typically fine
22
+ ]
23
+
24
+ /** Categories that are expected and not risky in test files */
25
+ const TEST_DOWNGRADABLE_CATEGORIES = [
26
+ 'ai_pattern',
27
+ 'dangerous_function',
28
+ 'sensitive_variable',
29
+ 'data_exposure',
30
+ ]
31
+
32
+ /**
33
+ * Apply context-based severity adjustments to findings.
34
+ *
35
+ * When called with a FileContext (from Layer 2 per-file processing), applies
36
+ * full context-aware rules: test files, tooling dirs, server-only files.
37
+ *
38
+ * When called without a FileContext (from the orchestrator for Layer 1 findings),
39
+ * applies only tooling-directory downgrades based on file path.
40
+ */
41
+ export function applyContextAdjustments(
42
+ findings: Vulnerability[],
43
+ fileContext?: FileContext
44
+ ): Vulnerability[] {
45
+ return findings.map(finding => {
46
+ // Never downgrade critical findings
47
+ if (finding.severity === 'critical') {
48
+ return finding
49
+ }
50
+
51
+ if (fileContext) {
52
+ // Full per-file context available (Layer 2 path)
53
+
54
+ // Test files: Downgrade specific categories
55
+ if (fileContext.isTestFile) {
56
+ if (TEST_DOWNGRADABLE_CATEGORIES.includes(finding.category)) {
57
+ return {
58
+ ...finding,
59
+ severity: 'info' as const,
60
+ validationNotes: 'Downgraded: test/fixture file',
61
+ }
62
+ }
63
+ }
64
+
65
+ // Tooling directories: Downgrade expected patterns
66
+ if (fileContext.isToolingDir) {
67
+ if (TOOLING_DOWNGRADABLE_CATEGORIES.includes(finding.category)) {
68
+ return {
69
+ ...finding,
70
+ severity: 'info' as const,
71
+ validationNotes: 'Downgraded: tooling/scripts directory',
72
+ }
73
+ }
74
+ }
75
+
76
+ // Server-only files: Client-exposure concerns don't apply
77
+ if (fileContext.isServerOnly) {
78
+ if (finding.category === 'hardcoded_secret' &&
79
+ finding.title.toLowerCase().includes('service role')) {
80
+ return {
81
+ ...finding,
82
+ severity: 'info' as const,
83
+ validationNotes: 'Downgraded: server-only file (service role key expected)',
84
+ }
85
+ }
86
+ }
87
+ } else {
88
+ // No file context — apply path-based adjustments only (orchestrator path)
89
+ // This catches Layer 1 findings that didn't go through Layer 2's per-file adjustments
90
+ const inToolingDir = isToolingPath(finding.filePath)
91
+
92
+ if (inToolingDir && TOOLING_DOWNGRADABLE_CATEGORIES.includes(finding.category)) {
93
+ return {
94
+ ...finding,
95
+ severity: 'info' as const,
96
+ validationNotes: `${finding.validationNotes || ''} Downgraded: tooling/scripts directory`.trim(),
97
+ }
98
+ }
99
+ }
100
+
101
+ return finding
102
+ })
103
+ }
104
+
105
+ /**
106
+ * Tooling-path check for findings without FileContext.
107
+ * Uses the same regex as utils/context-helpers.ts isToolingDirectory().
108
+ */
109
+ function isToolingPath(filePath: string): boolean {
110
+ return /\/(scripts?|cli|tools?|bin|devtools|build|tasks)\//i.test(filePath)
111
+ }
@@ -0,0 +1,10 @@
1
+ export {
2
+ FilterPipeline,
3
+ type FilterStage,
4
+ type FilterAction,
5
+ type FilterDecision,
6
+ type AnnotatedFinding,
7
+ type FilterStageSummary,
8
+ } from './pipeline'
9
+
10
+ export { applyContextAdjustments } from './context-adjustments'