@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,689 @@
1
+ /**
2
+ * Tests for Framework-Aware Fix Suggestions (PRO-83)
3
+ */
4
+
5
+ import {
6
+ FRAMEWORK_FIX_REGISTRY,
7
+ getFrameworkFix,
8
+ } from '../framework-fixes'
9
+ import type { FrameworkContext, DataAccessContext } from '../../utils/project-context-builder'
10
+ import type { VulnerabilityCategory } from '../../types'
11
+
12
+ // Helper to create a minimal FrameworkContext
13
+ function createFrameworkContext(overrides: Partial<FrameworkContext> = {}): FrameworkContext {
14
+ return {
15
+ primary: undefined,
16
+ frontend: undefined,
17
+ isMonorepo: false,
18
+ usesTypeScript: false,
19
+ usesServerComponents: false,
20
+ usesServerActions: false,
21
+ ...overrides,
22
+ }
23
+ }
24
+
25
+ // Helper to create a minimal DataAccessContext
26
+ function createDataAccessContext(overrides: Partial<DataAccessContext> = {}): DataAccessContext {
27
+ return {
28
+ usesParameterizedQueries: false,
29
+ hasRLS: false,
30
+ ...overrides,
31
+ }
32
+ }
33
+
34
+ describe('Framework-Aware Fix Suggestions', () => {
35
+ describe('FRAMEWORK_FIX_REGISTRY coverage', () => {
36
+ it('should have sql_injection fixes for major ORMs', () => {
37
+ const sqlFixes = FRAMEWORK_FIX_REGISTRY.sql_injection
38
+ expect(sqlFixes).toBeDefined()
39
+
40
+ expect(sqlFixes!.prisma).toBeDefined()
41
+ expect(sqlFixes!.drizzle).toBeDefined()
42
+ expect(sqlFixes!.supabase).toBeDefined()
43
+ expect(sqlFixes!.mongoose).toBeDefined()
44
+ expect(sqlFixes!.knex).toBeDefined()
45
+ })
46
+
47
+ it('should have missing_auth fixes for backend frameworks', () => {
48
+ const authFixes = FRAMEWORK_FIX_REGISTRY.missing_auth
49
+ expect(authFixes).toBeDefined()
50
+
51
+ expect(authFixes!.nextjs).toBeDefined()
52
+ expect(authFixes!.express).toBeDefined()
53
+ expect(authFixes!.fastify).toBeDefined()
54
+ expect(authFixes!.nestjs).toBeDefined()
55
+ })
56
+
57
+ it('should have xss fixes for frontend frameworks', () => {
58
+ const xssFixes = FRAMEWORK_FIX_REGISTRY.xss
59
+ expect(xssFixes).toBeDefined()
60
+
61
+ expect(xssFixes!.react).toBeDefined()
62
+ expect(xssFixes!.vue).toBeDefined()
63
+ })
64
+
65
+ it('should have hardcoded_secret fixes for common frameworks', () => {
66
+ const secretFixes = FRAMEWORK_FIX_REGISTRY.hardcoded_secret
67
+ expect(secretFixes).toBeDefined()
68
+
69
+ expect(secretFixes!.nextjs).toBeDefined()
70
+ expect(secretFixes!.express).toBeDefined()
71
+ })
72
+
73
+ it('should have cors_misconfiguration fixes', () => {
74
+ const corsFixes = FRAMEWORK_FIX_REGISTRY.cors_misconfiguration
75
+ expect(corsFixes).toBeDefined()
76
+
77
+ expect(corsFixes!.nextjs).toBeDefined()
78
+ expect(corsFixes!.express).toBeDefined()
79
+ expect(corsFixes!.fastify).toBeDefined()
80
+ })
81
+
82
+ it('should have dangerous_function fixes for frontend frameworks', () => {
83
+ const dangerousFixes = FRAMEWORK_FIX_REGISTRY.dangerous_function
84
+ expect(dangerousFixes).toBeDefined()
85
+
86
+ expect(dangerousFixes!.react).toBeDefined()
87
+ expect(dangerousFixes!.vue).toBeDefined()
88
+ })
89
+ })
90
+
91
+ describe('Fix content quality', () => {
92
+ it('all fixes should have non-empty fixSteps', () => {
93
+ for (const [_category, frameworkFixes] of Object.entries(FRAMEWORK_FIX_REGISTRY)) {
94
+ for (const [_framework, fix] of Object.entries(frameworkFixes || {})) {
95
+ expect(fix.fixSteps).toBeDefined()
96
+ expect(Array.isArray(fix.fixSteps)).toBe(true)
97
+ expect(fix.fixSteps.length).toBeGreaterThan(0)
98
+
99
+ // Each step should be meaningful (at least 10 characters)
100
+ for (const step of fix.fixSteps) {
101
+ expect(step.length).toBeGreaterThan(10)
102
+ }
103
+ }
104
+ }
105
+ })
106
+
107
+ it('code examples should be present for key fixes', () => {
108
+ // SQL injection fixes should have code examples
109
+ const sqlFixes = FRAMEWORK_FIX_REGISTRY.sql_injection
110
+ expect(sqlFixes!.prisma!.codeExample).toBeDefined()
111
+ expect(sqlFixes!.drizzle!.codeExample).toBeDefined()
112
+ expect(sqlFixes!.supabase!.codeExample).toBeDefined()
113
+ expect(sqlFixes!.mongoose!.codeExample).toBeDefined()
114
+ expect(sqlFixes!.knex!.codeExample).toBeDefined()
115
+
116
+ // Auth fixes should have code examples
117
+ const authFixes = FRAMEWORK_FIX_REGISTRY.missing_auth
118
+ expect(authFixes!.nextjs!.codeExample).toBeDefined()
119
+ expect(authFixes!.express!.codeExample).toBeDefined()
120
+ expect(authFixes!.fastify!.codeExample).toBeDefined()
121
+ expect(authFixes!.nestjs!.codeExample).toBeDefined()
122
+
123
+ // XSS fixes should have code examples
124
+ const xssFixes = FRAMEWORK_FIX_REGISTRY.xss
125
+ expect(xssFixes!.react!.codeExample).toBeDefined()
126
+ expect(xssFixes!.vue!.codeExample).toBeDefined()
127
+
128
+ // CORS fixes should have code examples
129
+ const corsFixes = FRAMEWORK_FIX_REGISTRY.cors_misconfiguration
130
+ expect(corsFixes!.nextjs!.codeExample).toBeDefined()
131
+ expect(corsFixes!.express!.codeExample).toBeDefined()
132
+ expect(corsFixes!.fastify!.codeExample).toBeDefined()
133
+ })
134
+
135
+ it('fix steps should be actionable (start with verbs)', () => {
136
+ for (const [_category, frameworkFixes] of Object.entries(FRAMEWORK_FIX_REGISTRY)) {
137
+ for (const [_framework, fix] of Object.entries(frameworkFixes || {})) {
138
+ for (const step of fix.fixSteps) {
139
+ // Each step should start with a capital letter (typically a verb)
140
+ expect(step[0]).toBe(step[0].toUpperCase())
141
+ }
142
+ }
143
+ }
144
+ })
145
+
146
+ it('code examples should contain actual code patterns', () => {
147
+ // Prisma examples should reference prisma client
148
+ expect(FRAMEWORK_FIX_REGISTRY.sql_injection!.prisma!.codeExample).toMatch(/prisma\./i)
149
+
150
+ // React examples should reference JSX or React patterns
151
+ expect(FRAMEWORK_FIX_REGISTRY.xss!.react!.codeExample).toMatch(/dangerouslySetInnerHTML|<div>|DOMPurify/i)
152
+
153
+ // Express examples should reference middleware patterns
154
+ expect(FRAMEWORK_FIX_REGISTRY.missing_auth!.express!.codeExample).toMatch(/middleware|req|res|next/i)
155
+ })
156
+
157
+ it('fix steps should mention framework-specific concepts', () => {
158
+ // Prisma fixes should mention Prisma-specific concepts
159
+ const prismaFix = FRAMEWORK_FIX_REGISTRY.sql_injection!.prisma!
160
+ const prismaText = prismaFix.fixSteps.join(' ')
161
+ expect(prismaText).toMatch(/\$queryRaw|findUnique|Prisma/i)
162
+
163
+ // Next.js auth fixes should mention middleware.ts
164
+ const nextAuthFix = FRAMEWORK_FIX_REGISTRY.missing_auth!.nextjs!
165
+ const nextAuthText = nextAuthFix.fixSteps.join(' ')
166
+ expect(nextAuthText).toMatch(/middleware\.ts|auth\(\)|getServerSession/i)
167
+
168
+ // React XSS fixes should mention JSX auto-escaping
169
+ const reactXssFix = FRAMEWORK_FIX_REGISTRY.xss!.react!
170
+ const reactXssText = reactXssFix.fixSteps.join(' ')
171
+ expect(reactXssText).toMatch(/JSX|auto-escape|dangerouslySetInnerHTML/i)
172
+ })
173
+ })
174
+
175
+ describe('getFrameworkFix', () => {
176
+ describe('ORM priority for sql_injection', () => {
177
+ it('should return Prisma fix when Prisma is detected', () => {
178
+ const frameworks = createFrameworkContext({ primary: 'nextjs', frontend: 'react' })
179
+ const dataAccess = createDataAccessContext({ orm: 'prisma' })
180
+
181
+ const fix = getFrameworkFix('sql_injection', frameworks, dataAccess)
182
+
183
+ expect(fix).toBeDefined()
184
+ expect(fix!.fixSteps[0]).toContain('Prisma')
185
+ })
186
+
187
+ it('should return Drizzle fix when Drizzle is detected', () => {
188
+ const frameworks = createFrameworkContext({ primary: 'nextjs' })
189
+ const dataAccess = createDataAccessContext({ orm: 'drizzle' })
190
+
191
+ const fix = getFrameworkFix('sql_injection', frameworks, dataAccess)
192
+
193
+ expect(fix).toBeDefined()
194
+ expect(fix!.fixSteps[0]).toContain('Drizzle')
195
+ })
196
+
197
+ it('should return Supabase fix when Supabase is detected', () => {
198
+ const frameworks = createFrameworkContext({ primary: 'nextjs' })
199
+ const dataAccess = createDataAccessContext({ orm: 'supabase', hasRLS: true })
200
+
201
+ const fix = getFrameworkFix('sql_injection', frameworks, dataAccess)
202
+
203
+ expect(fix).toBeDefined()
204
+ expect(fix!.fixSteps[0]).toContain('Supabase')
205
+ })
206
+
207
+ it('should return Mongoose fix when Mongoose is detected', () => {
208
+ const frameworks = createFrameworkContext({ primary: 'express' })
209
+ const dataAccess = createDataAccessContext({ orm: 'mongoose' })
210
+
211
+ const fix = getFrameworkFix('sql_injection', frameworks, dataAccess)
212
+
213
+ expect(fix).toBeDefined()
214
+ expect(fix!.fixSteps[0]).toContain('Mongoose')
215
+ })
216
+
217
+ it('should return Knex fix when Knex is detected', () => {
218
+ const frameworks = createFrameworkContext({ primary: 'express' })
219
+ const dataAccess = createDataAccessContext({ orm: 'knex' })
220
+
221
+ const fix = getFrameworkFix('sql_injection', frameworks, dataAccess)
222
+
223
+ expect(fix).toBeDefined()
224
+ expect(fix!.fixSteps[0]).toContain('Knex')
225
+ })
226
+ })
227
+
228
+ describe('frontend framework priority for XSS', () => {
229
+ it('should return React fix for xss when React is detected', () => {
230
+ const frameworks = createFrameworkContext({ primary: 'nextjs', frontend: 'react' })
231
+
232
+ const fix = getFrameworkFix('xss', frameworks)
233
+
234
+ expect(fix).toBeDefined()
235
+ expect(fix!.fixSteps[0]).toContain('JSX')
236
+ })
237
+
238
+ it('should return Vue fix for xss when Vue is detected', () => {
239
+ const frameworks = createFrameworkContext({ primary: 'express', frontend: 'vue' })
240
+
241
+ const fix = getFrameworkFix('xss', frameworks)
242
+
243
+ expect(fix).toBeDefined()
244
+ expect(fix!.fixSteps[0]).toContain('v-text')
245
+ })
246
+
247
+ it('should return React fix for dangerous_function when React is detected', () => {
248
+ const frameworks = createFrameworkContext({ frontend: 'react' })
249
+
250
+ const fix = getFrameworkFix('dangerous_function', frameworks)
251
+
252
+ expect(fix).toBeDefined()
253
+ expect(fix!.fixSteps[0]).toContain('innerHTML')
254
+ })
255
+
256
+ it('should return Vue fix for dangerous_function when Vue is detected', () => {
257
+ const frameworks = createFrameworkContext({ frontend: 'vue' })
258
+
259
+ const fix = getFrameworkFix('dangerous_function', frameworks)
260
+
261
+ expect(fix).toBeDefined()
262
+ expect(fix!.fixSteps[0]).toContain('Vue')
263
+ })
264
+ })
265
+
266
+ describe('backend framework fixes', () => {
267
+ it('should return Next.js fix for missing_auth', () => {
268
+ const frameworks = createFrameworkContext({ primary: 'nextjs', frontend: 'react' })
269
+
270
+ const fix = getFrameworkFix('missing_auth', frameworks)
271
+
272
+ expect(fix).toBeDefined()
273
+ expect(fix!.fixSteps[0]).toContain('middleware.ts')
274
+ })
275
+
276
+ it('should return Express fix for missing_auth', () => {
277
+ const frameworks = createFrameworkContext({ primary: 'express' })
278
+
279
+ const fix = getFrameworkFix('missing_auth', frameworks)
280
+
281
+ expect(fix).toBeDefined()
282
+ expect(fix!.fixSteps[0]).toContain('middleware')
283
+ })
284
+
285
+ it('should return Fastify fix for missing_auth', () => {
286
+ const frameworks = createFrameworkContext({ primary: 'fastify' })
287
+
288
+ const fix = getFrameworkFix('missing_auth', frameworks)
289
+
290
+ expect(fix).toBeDefined()
291
+ expect(fix!.fixSteps[0]).toContain('@fastify')
292
+ })
293
+
294
+ it('should return NestJS fix for missing_auth', () => {
295
+ const frameworks = createFrameworkContext({ primary: 'nestjs' })
296
+
297
+ const fix = getFrameworkFix('missing_auth', frameworks)
298
+
299
+ expect(fix).toBeDefined()
300
+ expect(fix!.fixSteps[0]).toContain('@nestjs')
301
+ })
302
+
303
+ it('should return Next.js fix for hardcoded_secret', () => {
304
+ const frameworks = createFrameworkContext({ primary: 'nextjs' })
305
+
306
+ const fix = getFrameworkFix('hardcoded_secret', frameworks)
307
+
308
+ expect(fix).toBeDefined()
309
+ expect(fix!.fixSteps[0]).toContain('.env.local')
310
+ })
311
+
312
+ it('should return Express fix for hardcoded_secret', () => {
313
+ const frameworks = createFrameworkContext({ primary: 'express' })
314
+
315
+ const fix = getFrameworkFix('hardcoded_secret', frameworks)
316
+
317
+ expect(fix).toBeDefined()
318
+ expect(fix!.fixSteps[0]).toContain('dotenv')
319
+ })
320
+
321
+ it('should return Next.js fix for cors_misconfiguration', () => {
322
+ const frameworks = createFrameworkContext({ primary: 'nextjs' })
323
+
324
+ const fix = getFrameworkFix('cors_misconfiguration', frameworks)
325
+
326
+ expect(fix).toBeDefined()
327
+ expect(fix!.fixSteps[0]).toContain('next.config')
328
+ })
329
+
330
+ it('should return Express fix for cors_misconfiguration', () => {
331
+ const frameworks = createFrameworkContext({ primary: 'express' })
332
+
333
+ const fix = getFrameworkFix('cors_misconfiguration', frameworks)
334
+
335
+ expect(fix).toBeDefined()
336
+ expect(fix!.fixSteps[0]).toContain('cors')
337
+ })
338
+
339
+ it('should return Fastify fix for cors_misconfiguration', () => {
340
+ const frameworks = createFrameworkContext({ primary: 'fastify' })
341
+
342
+ const fix = getFrameworkFix('cors_misconfiguration', frameworks)
343
+
344
+ expect(fix).toBeDefined()
345
+ expect(fix!.fixSteps[0]).toContain('@fastify/cors')
346
+ })
347
+ })
348
+
349
+ describe('fallback behavior', () => {
350
+ it('should return undefined for categories without framework fixes', () => {
351
+ const frameworks = createFrameworkContext({ primary: 'nextjs' })
352
+
353
+ // These categories have no framework-specific fixes
354
+ const categoriesWithoutFixes: VulnerabilityCategory[] = [
355
+ 'suspicious_package',
356
+ 'root_container',
357
+ 'weak_crypto',
358
+ 'command_injection',
359
+ 'security_bypass',
360
+ 'insecure_config',
361
+ 'high_entropy_string',
362
+ 'sensitive_variable',
363
+ 'dangerous_file',
364
+ 'data_exposure',
365
+ 'ai_pattern',
366
+ 'ai_prompt_injection',
367
+ 'ai_unsafe_execution',
368
+ 'ai_overpermissive_tool',
369
+ 'ai_rag_exfiltration',
370
+ 'ai_endpoint_unprotected',
371
+ 'ai_schema_mismatch',
372
+ ]
373
+
374
+ for (const category of categoriesWithoutFixes) {
375
+ const fix = getFrameworkFix(category, frameworks)
376
+ expect(fix).toBeUndefined()
377
+ }
378
+ })
379
+
380
+ it('should return undefined when no framework is detected', () => {
381
+ const frameworks = createFrameworkContext() // All undefined
382
+
383
+ const fix = getFrameworkFix('missing_auth', frameworks)
384
+
385
+ expect(fix).toBeUndefined()
386
+ })
387
+
388
+ it('should return undefined when framework has no fix for category', () => {
389
+ const frameworks = createFrameworkContext({ primary: 'nestjs' })
390
+
391
+ // NestJS has missing_auth fix but no hardcoded_secret fix
392
+ const fix = getFrameworkFix('hardcoded_secret', frameworks)
393
+
394
+ expect(fix).toBeUndefined()
395
+ })
396
+
397
+ it('should return undefined for sql_injection when ORM has no fix', () => {
398
+ const frameworks = createFrameworkContext({ primary: 'express' })
399
+ const dataAccess = createDataAccessContext({ orm: 'sequelize' }) // Not in registry
400
+
401
+ const fix = getFrameworkFix('sql_injection', frameworks, dataAccess)
402
+
403
+ expect(fix).toBeUndefined()
404
+ })
405
+
406
+ it('should return undefined for sql_injection when no ORM detected', () => {
407
+ const frameworks = createFrameworkContext({ primary: 'express' })
408
+ const dataAccess = createDataAccessContext({ orm: undefined })
409
+
410
+ const fix = getFrameworkFix('sql_injection', frameworks, dataAccess)
411
+
412
+ expect(fix).toBeUndefined()
413
+ })
414
+
415
+ it('should return undefined for sql_injection when raw_sql is detected', () => {
416
+ const frameworks = createFrameworkContext({ primary: 'express' })
417
+ const dataAccess = createDataAccessContext({ orm: 'raw_sql' })
418
+
419
+ const fix = getFrameworkFix('sql_injection', frameworks, dataAccess)
420
+
421
+ // raw_sql is not in the registry (no framework-specific fix)
422
+ expect(fix).toBeUndefined()
423
+ })
424
+ })
425
+
426
+ describe('priority ordering', () => {
427
+ it('should prioritize ORM over backend framework for sql_injection', () => {
428
+ const frameworks = createFrameworkContext({ primary: 'express' }) // Express has no sql_injection fix
429
+ const dataAccess = createDataAccessContext({ orm: 'prisma' }) // Prisma has sql_injection fix
430
+
431
+ const fix = getFrameworkFix('sql_injection', frameworks, dataAccess)
432
+
433
+ // Should return Prisma fix, not undefined/fallback
434
+ expect(fix).toBeDefined()
435
+ expect(fix!.fixSteps[0]).toContain('Prisma')
436
+ })
437
+
438
+ it('should prioritize frontend framework for xss over backend', () => {
439
+ const frameworks = createFrameworkContext({ primary: 'express', frontend: 'react' })
440
+
441
+ const fix = getFrameworkFix('xss', frameworks)
442
+
443
+ // Should return React fix since frontend takes priority for XSS
444
+ expect(fix).toBeDefined()
445
+ expect(fix!.fixSteps[0]).toContain('JSX')
446
+ })
447
+
448
+ it('should use backend framework when no frontend detected for xss', () => {
449
+ const frameworks = createFrameworkContext({ primary: 'express', frontend: undefined })
450
+
451
+ const fix = getFrameworkFix('xss', frameworks)
452
+
453
+ // Express has no XSS fix, so should return undefined
454
+ expect(fix).toBeUndefined()
455
+ })
456
+
457
+ it('should prioritize frontend for dangerous_function over backend', () => {
458
+ const frameworks = createFrameworkContext({ primary: 'nextjs', frontend: 'vue' })
459
+
460
+ const fix = getFrameworkFix('dangerous_function', frameworks)
461
+
462
+ // Should return Vue fix since frontend takes priority
463
+ expect(fix).toBeDefined()
464
+ expect(fix!.fixSteps[0]).toContain('Vue')
465
+ })
466
+ })
467
+
468
+ describe('edge cases', () => {
469
+ it('should handle empty dataAccess object', () => {
470
+ const frameworks = createFrameworkContext({ primary: 'nextjs' })
471
+ const dataAccess = createDataAccessContext()
472
+
473
+ const fix = getFrameworkFix('sql_injection', frameworks, dataAccess)
474
+
475
+ // No ORM specified, should return undefined
476
+ expect(fix).toBeUndefined()
477
+ })
478
+
479
+ it('should handle undefined dataAccess', () => {
480
+ const frameworks = createFrameworkContext({ primary: 'nextjs' })
481
+
482
+ const fix = getFrameworkFix('sql_injection', frameworks, undefined)
483
+
484
+ // No dataAccess at all, should return undefined
485
+ expect(fix).toBeUndefined()
486
+ })
487
+
488
+ it('should handle unknown ORM type gracefully', () => {
489
+ const frameworks = createFrameworkContext({ primary: 'express' })
490
+ const dataAccess = createDataAccessContext({ orm: 'typeorm' as any }) // Not in registry
491
+
492
+ const fix = getFrameworkFix('sql_injection', frameworks, dataAccess)
493
+
494
+ expect(fix).toBeUndefined()
495
+ })
496
+
497
+ it('should handle unknown framework type gracefully', () => {
498
+ const frameworks = createFrameworkContext({ primary: 'django' as any }) // Not in registry
499
+
500
+ const fix = getFrameworkFix('missing_auth', frameworks)
501
+
502
+ expect(fix).toBeUndefined()
503
+ })
504
+
505
+ it('should handle unknown frontend framework gracefully', () => {
506
+ const frameworks = createFrameworkContext({ frontend: 'svelte' as any }) // Not in XSS registry
507
+
508
+ const fix = getFrameworkFix('xss', frameworks)
509
+
510
+ expect(fix).toBeUndefined()
511
+ })
512
+
513
+ it('should work with only frontend defined (no primary)', () => {
514
+ const frameworks = createFrameworkContext({ frontend: 'react' })
515
+
516
+ const fix = getFrameworkFix('xss', frameworks)
517
+
518
+ expect(fix).toBeDefined()
519
+ expect(fix!.fixSteps[0]).toContain('JSX')
520
+ })
521
+
522
+ it('should work with only ORM defined (no frameworks)', () => {
523
+ const frameworks = createFrameworkContext()
524
+ const dataAccess = createDataAccessContext({ orm: 'prisma' })
525
+
526
+ const fix = getFrameworkFix('sql_injection', frameworks, dataAccess)
527
+
528
+ expect(fix).toBeDefined()
529
+ expect(fix!.fixSteps[0]).toContain('Prisma')
530
+ })
531
+
532
+ it('should not throw on invalid category', () => {
533
+ const frameworks = createFrameworkContext({ primary: 'nextjs' })
534
+
535
+ // @ts-expect-error - testing invalid input
536
+ const fix = getFrameworkFix('invalid_category', frameworks)
537
+
538
+ expect(fix).toBeUndefined()
539
+ })
540
+
541
+ it('should handle category that exists in registry but has no matching framework', () => {
542
+ // hardcoded_secret has nextjs and express, but not fastify
543
+ const frameworks = createFrameworkContext({ primary: 'fastify' })
544
+
545
+ const fix = getFrameworkFix('hardcoded_secret', frameworks)
546
+
547
+ expect(fix).toBeUndefined()
548
+ })
549
+ })
550
+
551
+ describe('real-world scenarios', () => {
552
+ it('Next.js + Prisma + React stack', () => {
553
+ const frameworks = createFrameworkContext({
554
+ primary: 'nextjs',
555
+ frontend: 'react',
556
+ usesTypeScript: true,
557
+ usesServerComponents: true,
558
+ })
559
+ const dataAccess = createDataAccessContext({
560
+ orm: 'prisma',
561
+ usesParameterizedQueries: true,
562
+ })
563
+
564
+ // SQL injection should use Prisma fix
565
+ const sqlFix = getFrameworkFix('sql_injection', frameworks, dataAccess)
566
+ expect(sqlFix).toBeDefined()
567
+ expect(sqlFix!.fixSteps[0]).toContain('Prisma')
568
+
569
+ // XSS should use React fix
570
+ const xssFix = getFrameworkFix('xss', frameworks)
571
+ expect(xssFix).toBeDefined()
572
+ expect(xssFix!.fixSteps[0]).toContain('JSX')
573
+
574
+ // Auth should use Next.js fix
575
+ const authFix = getFrameworkFix('missing_auth', frameworks)
576
+ expect(authFix).toBeDefined()
577
+ expect(authFix!.fixSteps[0]).toContain('middleware.ts')
578
+
579
+ // Secrets should use Next.js fix
580
+ const secretFix = getFrameworkFix('hardcoded_secret', frameworks)
581
+ expect(secretFix).toBeDefined()
582
+ expect(secretFix!.fixSteps[0]).toContain('.env.local')
583
+ })
584
+
585
+ it('Express + Mongoose + Vue stack', () => {
586
+ const frameworks = createFrameworkContext({
587
+ primary: 'express',
588
+ frontend: 'vue',
589
+ usesTypeScript: false,
590
+ })
591
+ const dataAccess = createDataAccessContext({
592
+ orm: 'mongoose',
593
+ databaseType: 'mongodb',
594
+ })
595
+
596
+ // SQL injection should use Mongoose fix
597
+ const sqlFix = getFrameworkFix('sql_injection', frameworks, dataAccess)
598
+ expect(sqlFix).toBeDefined()
599
+ expect(sqlFix!.fixSteps[0]).toContain('Mongoose')
600
+
601
+ // XSS should use Vue fix
602
+ const xssFix = getFrameworkFix('xss', frameworks)
603
+ expect(xssFix).toBeDefined()
604
+ expect(xssFix!.fixSteps[0]).toContain('v-text')
605
+
606
+ // Auth should use Express fix
607
+ const authFix = getFrameworkFix('missing_auth', frameworks)
608
+ expect(authFix).toBeDefined()
609
+ expect(authFix!.fixSteps[0]).toContain('middleware')
610
+
611
+ // CORS should use Express fix
612
+ const corsFix = getFrameworkFix('cors_misconfiguration', frameworks)
613
+ expect(corsFix).toBeDefined()
614
+ expect(corsFix!.fixSteps[0]).toContain('cors')
615
+ })
616
+
617
+ it('Supabase + Next.js stack with RLS', () => {
618
+ const frameworks = createFrameworkContext({
619
+ primary: 'nextjs',
620
+ frontend: 'react',
621
+ })
622
+ const dataAccess = createDataAccessContext({
623
+ orm: 'supabase',
624
+ hasRLS: true,
625
+ databaseType: 'postgres',
626
+ })
627
+
628
+ // SQL injection should use Supabase fix
629
+ const sqlFix = getFrameworkFix('sql_injection', frameworks, dataAccess)
630
+ expect(sqlFix).toBeDefined()
631
+ expect(sqlFix!.fixSteps[0]).toContain('Supabase')
632
+ // Supabase fix should mention RLS
633
+ expect(sqlFix!.fixSteps.join(' ')).toContain('Row Level Security')
634
+ })
635
+
636
+ it('NestJS + TypeORM stack (partial coverage)', () => {
637
+ const frameworks = createFrameworkContext({
638
+ primary: 'nestjs',
639
+ usesTypeScript: true,
640
+ })
641
+ const dataAccess = createDataAccessContext({
642
+ orm: 'typeorm', // Not in registry
643
+ })
644
+
645
+ // SQL injection should return undefined (TypeORM not supported)
646
+ const sqlFix = getFrameworkFix('sql_injection', frameworks, dataAccess)
647
+ expect(sqlFix).toBeUndefined()
648
+
649
+ // Auth should use NestJS fix
650
+ const authFix = getFrameworkFix('missing_auth', frameworks)
651
+ expect(authFix).toBeDefined()
652
+ expect(authFix!.fixSteps[0]).toContain('@nestjs')
653
+
654
+ // Secrets should return undefined (NestJS not supported)
655
+ const secretFix = getFrameworkFix('hardcoded_secret', frameworks)
656
+ expect(secretFix).toBeUndefined()
657
+ })
658
+
659
+ it('API-only backend (no frontend framework)', () => {
660
+ const frameworks = createFrameworkContext({
661
+ primary: 'fastify',
662
+ frontend: undefined,
663
+ })
664
+ const dataAccess = createDataAccessContext({
665
+ orm: 'drizzle',
666
+ })
667
+
668
+ // SQL injection should use Drizzle fix
669
+ const sqlFix = getFrameworkFix('sql_injection', frameworks, dataAccess)
670
+ expect(sqlFix).toBeDefined()
671
+ expect(sqlFix!.fixSteps[0]).toContain('Drizzle')
672
+
673
+ // XSS should return undefined (no frontend)
674
+ const xssFix = getFrameworkFix('xss', frameworks)
675
+ expect(xssFix).toBeUndefined()
676
+
677
+ // Auth should use Fastify fix
678
+ const authFix = getFrameworkFix('missing_auth', frameworks)
679
+ expect(authFix).toBeDefined()
680
+ expect(authFix!.fixSteps[0]).toContain('@fastify')
681
+
682
+ // CORS should use Fastify fix
683
+ const corsFix = getFrameworkFix('cors_misconfiguration', frameworks)
684
+ expect(corsFix).toBeDefined()
685
+ expect(corsFix!.fixSteps[0]).toContain('@fastify/cors')
686
+ })
687
+ })
688
+ })
689
+ })