@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,456 @@
1
+ /**
2
+ * Layer 2: Model Supply Chain Security Detection
3
+ * Detects unsafe model loading patterns that can lead to arbitrary code execution
4
+ *
5
+ * Covers AI Detection Roadmap Phase 2:
6
+ * - Pickle/joblib deserialization RCE
7
+ * - torch.load without weights_only=True
8
+ * - Unverified model sources
9
+ * - Unsafe fine-tuning on user data
10
+ *
11
+ * References:
12
+ * - OWASP LLM05: Supply Chain Vulnerabilities
13
+ * - CWE-502: Deserialization of Untrusted Data
14
+ */
15
+
16
+ import type { Vulnerability, VulnerabilitySeverity, VulnerabilityCategory } from '../types'
17
+ import {
18
+ isComment,
19
+ isTestOrMockFile,
20
+ isScannerOrFixtureFile,
21
+ isExampleDirectory,
22
+ isLibraryCode,
23
+ } from '../utils/context-helpers'
24
+
25
+ // ============================================================================
26
+ // Context Detection
27
+ // ============================================================================
28
+
29
+ /**
30
+ * Check if file is in an ML/model context based on path and content
31
+ */
32
+ function isMLContextFile(filePath: string, content: string): boolean {
33
+ // File path indicators
34
+ const mlPathPatterns = [
35
+ /\/(models?|ml|training|inference|weights|checkpoints)\//i,
36
+ /\/(transformers|huggingface|pytorch|tensorflow|keras)\//i,
37
+ /(train|model|inference|fine[-_]?tune).*\.(py|ts|js)$/i,
38
+ ]
39
+
40
+ if (mlPathPatterns.some(p => p.test(filePath))) {
41
+ return true
42
+ }
43
+
44
+ // Content patterns suggesting ML usage
45
+ const mlContentPatterns = [
46
+ /import\s+(?:torch|tensorflow|keras|transformers|joblib|pickle)/i,
47
+ /from\s+(?:torch|tensorflow|keras|transformers|joblib|pickle)\s+import/i,
48
+ /\.load_model\s*\(/i,
49
+ /\.from_pretrained\s*\(/i,
50
+ /torch\.load\s*\(/i,
51
+ /pickle\.load/i,
52
+ /joblib\.load/i,
53
+ /Trainer|TrainingArguments/i,
54
+ /model\.save|model\.load/i,
55
+ ]
56
+
57
+ return mlContentPatterns.some(p => p.test(content))
58
+ }
59
+
60
+ // ============================================================================
61
+ // Safe Pattern Detection
62
+ // ============================================================================
63
+
64
+ /**
65
+ * Check if torch.load has weights_only=True (safe mode)
66
+ */
67
+ function hasTorchWeightsOnly(lineContent: string, surroundingContext: string): boolean {
68
+ const fullContext = lineContent + '\n' + surroundingContext
69
+ return /weights_only\s*=\s*True/i.test(fullContext)
70
+ }
71
+
72
+ /**
73
+ * Check if TensorFlow/Keras load has safe_mode=True
74
+ */
75
+ function hasKerasSafeMode(lineContent: string, surroundingContext: string): boolean {
76
+ const fullContext = lineContent + '\n' + surroundingContext
77
+ return /safe_mode\s*=\s*True/i.test(fullContext)
78
+ }
79
+
80
+ /**
81
+ * Check if model source is from a trusted provider
82
+ */
83
+ function isTrustedModelSource(lineContent: string): boolean {
84
+ const trustedPatterns = [
85
+ // Official Hugging Face repos
86
+ /huggingface\.co\/(meta-llama|openai|anthropic|google|microsoft|facebook|EleutherAI)/i,
87
+ // Specific trusted model IDs (no http prefix)
88
+ /['"`](meta-llama|openai|anthropic|google|microsoft|facebook)\//i,
89
+ // Local file paths (not URLs)
90
+ /['"`]\.\/|['"`]\.\.\/|['"`]\//,
91
+ // Environment variables for paths
92
+ /os\.environ|process\.env/i,
93
+ ]
94
+
95
+ return trustedPatterns.some(p => p.test(lineContent))
96
+ }
97
+
98
+ /**
99
+ * Check if integrity verification is present
100
+ */
101
+ function hasIntegrityCheck(content: string, lineNumber: number): boolean {
102
+ const lines = content.split('\n')
103
+ const contextStart = Math.max(0, lineNumber - 20)
104
+ const contextEnd = Math.min(lines.length, lineNumber + 10)
105
+ const context = lines.slice(contextStart, contextEnd).join('\n')
106
+
107
+ const integrityPatterns = [
108
+ /checksum|sha256|sha512|md5|verify_hash|hash_check/i,
109
+ /verify.*integrity|integrity.*verify/i,
110
+ /revision\s*=\s*['"`][a-f0-9]{40}/i, // Git SHA pinning
111
+ /safetensors/i, // SafeTensors format (safe by design)
112
+ /\.ckpt\.sha256|\.bin\.sha256/i, // Hash files
113
+ ]
114
+
115
+ return integrityPatterns.some(p => p.test(context))
116
+ }
117
+
118
+ /**
119
+ * Check if using SafeTensors format (safe by design)
120
+ */
121
+ function usesSafeTensors(lineContent: string, surroundingContext: string): boolean {
122
+ const fullContext = lineContent + '\n' + surroundingContext
123
+ return /safetensors|\.safetensors|from_safetensors|load_safetensors/i.test(fullContext)
124
+ }
125
+
126
+ /**
127
+ * Check if data validation is present for training
128
+ */
129
+ function hasDataValidation(content: string, lineNumber: number): boolean {
130
+ const lines = content.split('\n')
131
+ const contextStart = Math.max(0, lineNumber - 30)
132
+ const contextEnd = Math.min(lines.length, lineNumber + 10)
133
+ const context = lines.slice(contextStart, contextEnd).join('\n')
134
+
135
+ const validationPatterns = [
136
+ /validate|sanitize|clean|filter/i,
137
+ /schema\.(parse|validate)/i,
138
+ /content_moderation|moderate_content/i,
139
+ /review.*data|data.*review/i,
140
+ /verified.*dataset|trusted.*data/i,
141
+ ]
142
+
143
+ return validationPatterns.some(p => p.test(context))
144
+ }
145
+
146
+ // ============================================================================
147
+ // Pattern Definitions
148
+ // ============================================================================
149
+
150
+ interface ModelSupplyChainPattern {
151
+ name: string
152
+ pattern: RegExp
153
+ category: VulnerabilityCategory
154
+ baseSeverity: VulnerabilitySeverity
155
+ description: string
156
+ suggestedFix: string
157
+ checkSafeMode?: 'torch' | 'keras'
158
+ checkTrustedSource?: boolean
159
+ checkIntegrity?: boolean
160
+ checkDataValidation?: boolean
161
+ }
162
+
163
+ const MODEL_SUPPLY_CHAIN_PATTERNS: ModelSupplyChainPattern[] = [
164
+ // ========== Pickle Deserialization (RCE) ==========
165
+ {
166
+ name: 'Pickle deserialization',
167
+ pattern: /pickle\.load\s*\(|pickle\.loads\s*\(/gi,
168
+ category: 'ai_unsafe_model_load',
169
+ baseSeverity: 'critical',
170
+ description: 'pickle.load() executes arbitrary Python code embedded in pickle files. Attackers can craft malicious pickle files that execute code when loaded.',
171
+ suggestedFix: 'Use SafeTensors format for ML models. For other data, use JSON or a safe serialization format. If pickle is unavoidable, only load from trusted, verified sources.',
172
+ },
173
+ {
174
+ name: 'Joblib deserialization',
175
+ pattern: /joblib\.load\s*\(/gi,
176
+ category: 'ai_unsafe_model_load',
177
+ baseSeverity: 'critical',
178
+ description: 'joblib.load() uses pickle internally and can execute arbitrary code. This is commonly used for sklearn models but is unsafe for untrusted files.',
179
+ suggestedFix: 'Use ONNX format for sklearn models. Alternatively, use skops.io which provides secure model persistence. Only load from verified sources.',
180
+ },
181
+
182
+ // ========== PyTorch Loading ==========
183
+ {
184
+ name: 'torch.load without weights_only',
185
+ pattern: /torch\.load\s*\([^)]*\)/gi,
186
+ category: 'ai_unsafe_model_load',
187
+ baseSeverity: 'high',
188
+ description: 'torch.load() without weights_only=True can execute arbitrary code embedded in model files via pickle.',
189
+ suggestedFix: 'Use torch.load(path, weights_only=True) to load only tensor data safely. Or use SafeTensors format: from safetensors.torch import load_file',
190
+ checkSafeMode: 'torch',
191
+ },
192
+
193
+ // ========== TensorFlow/Keras Loading ==========
194
+ {
195
+ name: 'Keras load_model without safe_mode',
196
+ pattern: /(?:keras\.models\.)?load_model\s*\([^)]*\)/gi,
197
+ category: 'ai_unsafe_model_load',
198
+ baseSeverity: 'high',
199
+ description: 'Keras load_model() can execute arbitrary code from Lambda layers or custom objects in model files.',
200
+ suggestedFix: 'Use tf.keras.models.load_model(path, safe_mode=True) in TensorFlow 2.13+. Alternatively, save/load only weights with model.save_weights()/load_weights().',
201
+ checkSafeMode: 'keras',
202
+ },
203
+
204
+ // ========== Unverified Model Sources ==========
205
+ {
206
+ name: 'Model from HTTP URL',
207
+ pattern: /from_pretrained\s*\(\s*['"`]https?:\/\/[^'"`)]+['"`]/gi,
208
+ category: 'ai_unverified_model',
209
+ baseSeverity: 'high',
210
+ description: 'Loading models directly from HTTP URLs bypasses the Hugging Face Hub\'s verification. Attackers could serve malicious models via MITM attacks.',
211
+ suggestedFix: 'Load from Hugging Face Hub by model ID instead of direct URL. Pin to specific revision: from_pretrained("org/model", revision="abc123")',
212
+ checkTrustedSource: true,
213
+ },
214
+ {
215
+ name: 'trust_remote_code=True',
216
+ pattern: /trust_remote_code\s*=\s*True/gi,
217
+ category: 'ai_unsafe_model_load',
218
+ baseSeverity: 'high',
219
+ description: 'trust_remote_code=True allows model repos to execute arbitrary Python code during loading. Malicious repos could compromise your system.',
220
+ suggestedFix: 'Avoid trust_remote_code=True. Use models that don\'t require custom code. If necessary, audit the model repo code before enabling.',
221
+ },
222
+ {
223
+ name: 'Model download without verification',
224
+ pattern: /(?:urllib|requests|wget|curl).*(?:\.pth|\.pt|\.bin|\.ckpt|\.h5|\.keras|model)/gi,
225
+ category: 'ai_unverified_model',
226
+ baseSeverity: 'medium',
227
+ description: 'Downloading model files without integrity verification. Attackers could serve modified files via compromised mirrors or MITM.',
228
+ suggestedFix: 'Verify downloaded files against known checksums: sha256sum file.pth. Use official download APIs (transformers, torch.hub) which include verification.',
229
+ checkIntegrity: true,
230
+ },
231
+
232
+ // ========== Unsafe Fine-tuning ==========
233
+ {
234
+ name: 'Training on user uploads',
235
+ pattern: /(?:trainer|model)\.(?:train|fit|fine_tune)\s*\([^)]*(?:user_uploads?|user_data|uploaded|untrusted)/gi,
236
+ category: 'ai_unsafe_finetuning',
237
+ baseSeverity: 'high',
238
+ description: 'Training/fine-tuning directly on user-uploaded data without validation enables data poisoning attacks.',
239
+ suggestedFix: 'Validate and sanitize all training data. Implement content moderation. Use separate data pipelines for user content with review steps.',
240
+ checkDataValidation: true,
241
+ },
242
+ {
243
+ name: 'Trainer with user data',
244
+ pattern: /Trainer\s*\([^)]*(?:train_dataset|eval_dataset)\s*=\s*(?:user_uploads?|user_data|uploaded_data|untrusted_data)/gi,
245
+ category: 'ai_unsafe_finetuning',
246
+ baseSeverity: 'high',
247
+ description: 'Trainer initialized with user-uploaded data without validation. Data poisoning attacks can manipulate model behavior.',
248
+ suggestedFix: 'Validate and sanitize all training data before passing to Trainer. Implement content moderation and review pipelines.',
249
+ checkDataValidation: true,
250
+ },
251
+ {
252
+ name: 'Fine-tuning with external dataset',
253
+ pattern: /(?:load_dataset|datasets\.load)\s*\([^)]*(?:path|url|http)/gi,
254
+ category: 'ai_unsafe_finetuning',
255
+ baseSeverity: 'medium',
256
+ description: 'Loading training datasets from external sources without verification. Data could be poisoned.',
257
+ suggestedFix: 'Use verified datasets from trusted sources. Implement data validation pipelines. Consider dataset hashing/signing.',
258
+ checkDataValidation: true,
259
+ },
260
+ {
261
+ name: 'Training config from user input',
262
+ pattern: /TrainingArguments\s*\([^)]*(?:request|user|input|body)\./gi,
263
+ category: 'ai_unsafe_finetuning',
264
+ baseSeverity: 'medium',
265
+ description: 'Training arguments derived from user input. Attackers could manipulate training hyperparameters to compromise model behavior.',
266
+ suggestedFix: 'Validate and sanitize all training arguments. Use allowlists for hyperparameter values. Don\'t allow users to specify output paths.',
267
+ },
268
+
269
+ // ========== TensorFlow Specific ==========
270
+ {
271
+ name: 'TensorFlow SavedModel from path',
272
+ pattern: /tf\.saved_model\.load\s*\([^)]*(?:http|ftp|user|upload)/gi,
273
+ category: 'ai_unverified_model',
274
+ baseSeverity: 'high',
275
+ description: 'Loading TensorFlow SavedModel from untrusted path. SavedModels can contain arbitrary ops that execute code.',
276
+ suggestedFix: 'Only load SavedModels from trusted, verified sources. Use TF Serving with model verification for production.',
277
+ checkTrustedSource: true,
278
+ },
279
+ ]
280
+
281
+ // ============================================================================
282
+ // Helper Functions
283
+ // ============================================================================
284
+
285
+ /**
286
+ * Get surrounding context for analysis
287
+ */
288
+ function getSurroundingContext(content: string, lineIndex: number, windowSize: number = 15): string {
289
+ const lines = content.split('\n')
290
+ const start = Math.max(0, lineIndex - windowSize)
291
+ const end = Math.min(lines.length, lineIndex + windowSize)
292
+ return lines.slice(start, end).join('\n')
293
+ }
294
+
295
+ /**
296
+ * Calculate severity based on mitigations
297
+ */
298
+ function calculateSeverity(
299
+ baseSeverity: VulnerabilitySeverity,
300
+ isMitigated: boolean,
301
+ isPartiallyMitigated: boolean,
302
+ isTestFile: boolean,
303
+ isExample: boolean,
304
+ isLibrary: boolean
305
+ ): VulnerabilitySeverity {
306
+ if (isTestFile || isExample || isLibrary) {
307
+ return 'info'
308
+ }
309
+
310
+ if (isMitigated) {
311
+ return 'info'
312
+ }
313
+
314
+ if (isPartiallyMitigated) {
315
+ if (baseSeverity === 'critical') return 'high'
316
+ if (baseSeverity === 'high') return 'medium'
317
+ if (baseSeverity === 'medium') return 'low'
318
+ }
319
+
320
+ return baseSeverity
321
+ }
322
+
323
+ // ============================================================================
324
+ // Main Detection Function
325
+ // ============================================================================
326
+
327
+ /**
328
+ * Main detection function for model supply chain security issues
329
+ */
330
+ export function detectModelSupplyChain(
331
+ content: string,
332
+ filePath: string
333
+ ): Vulnerability[] {
334
+ const vulnerabilities: Vulnerability[] = []
335
+
336
+ // Skip non-applicable files
337
+ if (isScannerOrFixtureFile(filePath)) return vulnerabilities
338
+
339
+ // Only scan files that appear to be in ML context
340
+ if (!isMLContextFile(filePath, content)) {
341
+ return vulnerabilities
342
+ }
343
+
344
+ const lines = content.split('\n')
345
+ const isTestFile = isTestOrMockFile(filePath)
346
+ const isExample = isExampleDirectory(filePath)
347
+ const isLibrary = isLibraryCode(filePath)
348
+
349
+ for (const pattern of MODEL_SUPPLY_CHAIN_PATTERNS) {
350
+ const regex = new RegExp(pattern.pattern.source, pattern.pattern.flags)
351
+ let match
352
+
353
+ while ((match = regex.exec(content)) !== null) {
354
+ const lineNumber = content.substring(0, match.index).split('\n').length
355
+ const lineContent = lines[lineNumber - 1]?.trim() || ''
356
+
357
+ // Skip comments
358
+ if (isComment(lineContent)) continue
359
+
360
+ const surroundingContext = getSurroundingContext(content, lineNumber - 1)
361
+
362
+ // Check for mitigations
363
+ let isMitigated = false
364
+ let isPartiallyMitigated = false
365
+ let description = pattern.description
366
+
367
+ // Check SafeTensors usage (mitigates most pickle/torch issues)
368
+ if (usesSafeTensors(lineContent, surroundingContext)) {
369
+ isMitigated = true
370
+ description += ' (SafeTensors format detected - safe.)'
371
+ }
372
+
373
+ // Check torch weights_only
374
+ if (pattern.checkSafeMode === 'torch') {
375
+ if (hasTorchWeightsOnly(lineContent, surroundingContext)) {
376
+ isMitigated = true
377
+ description += ' (weights_only=True detected - safe.)'
378
+ }
379
+ }
380
+
381
+ // Check keras safe_mode
382
+ if (pattern.checkSafeMode === 'keras') {
383
+ if (hasKerasSafeMode(lineContent, surroundingContext)) {
384
+ isMitigated = true
385
+ description += ' (safe_mode=True detected - safe.)'
386
+ }
387
+ }
388
+
389
+ // Check trusted source
390
+ if (pattern.checkTrustedSource) {
391
+ if (isTrustedModelSource(lineContent)) {
392
+ isPartiallyMitigated = true
393
+ description += ' (Trusted source detected.)'
394
+ }
395
+ }
396
+
397
+ // Check integrity verification
398
+ if (pattern.checkIntegrity) {
399
+ if (hasIntegrityCheck(content, lineNumber)) {
400
+ isMitigated = true
401
+ description += ' (Integrity verification detected.)'
402
+ }
403
+ }
404
+
405
+ // Check data validation for training
406
+ if (pattern.checkDataValidation) {
407
+ if (hasDataValidation(content, lineNumber)) {
408
+ isPartiallyMitigated = true
409
+ description += ' (Data validation detected nearby.)'
410
+ }
411
+ }
412
+
413
+ // Skip if fully mitigated
414
+ if (isMitigated) continue
415
+
416
+ // Calculate final severity
417
+ const severity = calculateSeverity(
418
+ pattern.baseSeverity,
419
+ isMitigated,
420
+ isPartiallyMitigated,
421
+ isTestFile,
422
+ isExample,
423
+ isLibrary
424
+ )
425
+
426
+ // Add context notes
427
+ if (isTestFile) {
428
+ description += ' (In test file.)'
429
+ } else if (isExample) {
430
+ description += ' (In example/demo directory.)'
431
+ } else if (isLibrary) {
432
+ description += ' (Library code.)'
433
+ }
434
+
435
+ // Skip info-level in non-ML focused files to reduce noise
436
+ if (severity === 'info' && !isMLContextFile(filePath, content)) continue
437
+
438
+ vulnerabilities.push({
439
+ id: `model-supply-${filePath}-${lineNumber}-${pattern.name.replace(/\s+/g, '-')}`,
440
+ filePath,
441
+ lineNumber,
442
+ lineContent,
443
+ severity,
444
+ category: pattern.category,
445
+ title: pattern.name,
446
+ description,
447
+ suggestedFix: pattern.suggestedFix,
448
+ confidence: severity === 'info' ? 'low' : 'high',
449
+ layer: 2,
450
+ requiresAIValidation: severity !== 'info' && severity !== 'low',
451
+ })
452
+ }
453
+ }
454
+
455
+ return vulnerabilities
456
+ }
@@ -4,6 +4,7 @@
4
4
  */
5
5
 
6
6
  import type { Vulnerability, VulnerabilitySeverity } from '../types'
7
+ import { isScannerOrFixtureFile } from '../utils/context-helpers'
7
8
 
8
9
  interface RiskyPackage {
9
10
  name: string
@@ -154,6 +155,10 @@ export function detectRiskyImports(
154
155
  filePath: string
155
156
  ): Vulnerability[] {
156
157
  const vulnerabilities: Vulnerability[] = []
158
+
159
+ // Skip scanner/fixture files to avoid self-detection
160
+ if (isScannerOrFixtureFile(filePath)) return vulnerabilities
161
+
157
162
  const lines = content.split('\n')
158
163
 
159
164
  lines.forEach((line, index) => {
@@ -4,6 +4,7 @@
4
4
  */
5
5
 
6
6
  import type { Vulnerability, SensitiveVariablePattern } from '../types'
7
+ import { isScannerOrFixtureFile } from '../utils/context-helpers'
7
8
 
8
9
  // Patterns for sensitive variable names
9
10
  export const SENSITIVE_VARIABLE_PATTERNS: SensitiveVariablePattern[] = [
@@ -122,6 +123,10 @@ export function detectSensitiveVariables(
122
123
  filePath: string
123
124
  ): Vulnerability[] {
124
125
  const vulnerabilities: Vulnerability[] = []
126
+
127
+ // Skip scanner/fixture files to avoid self-detection
128
+ if (isScannerOrFixtureFile(filePath)) return vulnerabilities
129
+
125
130
  const lines = content.split('\n')
126
131
 
127
132
  lines.forEach((line, index) => {