@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,679 @@
1
+ "use strict";
2
+ /**
3
+ * Layer 2: MCP (Model Context Protocol) Security Detection
4
+ * Detects security issues in MCP tool implementations
5
+ *
6
+ * Background: MCP enables AI agents to call external tools. Security risks include:
7
+ * - Tool Poisoning: External content returned without validation (CVE-2025-6514)
8
+ * - Credential Issues: Credentials in tool parameters/responses
9
+ * - Confused Deputy: Operations without proper user context
10
+ *
11
+ * Reference: https://modelcontextprotocol.io, 13,000+ MCP servers deployed
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.detectMCPSecurity = detectMCPSecurity;
15
+ const context_helpers_1 = require("../utils/context-helpers");
16
+ // ============================================================================
17
+ // Context Detection
18
+ // ============================================================================
19
+ /**
20
+ * Check if file is an MCP server/tool file based on imports and patterns
21
+ */
22
+ function isMCPFile(content, filePath) {
23
+ // Import patterns for MCP SDK
24
+ const mcpImportPatterns = [
25
+ /@modelcontextprotocol\/sdk/i,
26
+ /from\s+['"]mcp['"]/i,
27
+ /from\s+['"]@mcp\//i,
28
+ /McpServer/i,
29
+ /mcp\.server/i,
30
+ /server\.tool\s*\(/i,
31
+ /@server\.tool/i,
32
+ ];
33
+ if (mcpImportPatterns.some(p => p.test(content))) {
34
+ return true;
35
+ }
36
+ // Path patterns
37
+ const mcpPathPatterns = [
38
+ /\/mcp\//i,
39
+ /mcp[-_]?server/i,
40
+ /mcp[-_]?tools?/i,
41
+ ];
42
+ return mcpPathPatterns.some(p => p.test(filePath));
43
+ }
44
+ /**
45
+ * Check if line/context has content sanitization
46
+ */
47
+ function hasContentSanitization(context) {
48
+ const sanitizationPatterns = [
49
+ /sanitize|DOMPurify|purify/i,
50
+ /escapeHtml|escape_html|html\.escape/i,
51
+ /strip(?:Tags|Html|Scripts)/i,
52
+ /validate(?:Content|Input|Schema)/i,
53
+ /zod\.parse|schema\.parse|safeParse/i,
54
+ /filterHtml|cleanHtml/i,
55
+ /ALLOWED_TAGS/i,
56
+ // Safe return patterns - returning only safe fields
57
+ /\.map\s*\([^)]*\{\s*id|title|name|summary\s*:/i,
58
+ // Static content patterns
59
+ /loadStaticDocs|staticContent|publicData/i,
60
+ // Pure computation
61
+ /mathjs\.evaluate|calculate/i,
62
+ ];
63
+ return sanitizationPatterns.some(p => p.test(context));
64
+ }
65
+ /**
66
+ * Check if the return is for a safe/static data source
67
+ */
68
+ function isSafeDataSource(context) {
69
+ const safePatterns = [
70
+ // Static/public data
71
+ /(?:static|public)(?:Data|Docs|Content)/i,
72
+ // Mathematical operations
73
+ /mathjs|calculate|compute/i,
74
+ // Internal API with server-side auth
75
+ /process\.env\.INTERNAL|SERVER_SIDE/i,
76
+ // User's own data explicitly
77
+ /findByUser|getByUser|user\.(?:files|documents|records)/i,
78
+ // Returns only safe fields like id, name, title
79
+ /return\s*\{[^}]*:\s*\{[^}]*(?:only|safe|id|title|name)[^}]*\}/i,
80
+ ];
81
+ return safePatterns.some(p => p.test(context));
82
+ }
83
+ /**
84
+ * Check if tool has user context access
85
+ */
86
+ function hasUserContext(context) {
87
+ const userContextPatterns = [
88
+ /context\.user/i,
89
+ /context\.userId/i,
90
+ /context\.session/i,
91
+ /context\.auth/i,
92
+ /getCurrentUser/i,
93
+ /request\.user/i,
94
+ /req\.user/i,
95
+ /user\.id/i,
96
+ /userId/i,
97
+ /session\.user/i,
98
+ /auth\(\)/i,
99
+ /tenantId/i,
100
+ /tenant\.id/i,
101
+ /orgId/i,
102
+ ];
103
+ return userContextPatterns.some(p => p.test(context));
104
+ }
105
+ /**
106
+ * Check if there's an authorization check in context
107
+ */
108
+ function hasAuthorizationCheck(context) {
109
+ const authCheckPatterns = [
110
+ /if\s*\([^)]*\.ownerId\s*[!=]==?\s*/i,
111
+ /if\s*\([^)]*userId\s*[!=]==?\s*/i,
112
+ /if\s*\([^)]*tenantId\s*[!=]==?\s*/i,
113
+ /Not\s*authorized/i,
114
+ /Forbidden/i,
115
+ /checkPermission/i,
116
+ /checkAccess/i,
117
+ /canAccess/i,
118
+ /hasPermission/i,
119
+ /isAuthorized/i,
120
+ /throw.*Error.*auth/i,
121
+ ];
122
+ return authCheckPatterns.some(p => p.test(context));
123
+ }
124
+ /**
125
+ * Get surrounding context for analysis
126
+ */
127
+ function getSurroundingContext(content, lineIndex, windowSize = 30) {
128
+ const lines = content.split('\n');
129
+ const start = Math.max(0, lineIndex - windowSize);
130
+ const end = Math.min(lines.length, lineIndex + windowSize);
131
+ return lines.slice(start, end).join('\n');
132
+ }
133
+ /**
134
+ * Tool Poisoning Patterns
135
+ * Detect tools that return external content without validation
136
+ */
137
+ const TOOL_POISONING_PATTERNS = [
138
+ // Raw HTTP response content (JS and Python)
139
+ {
140
+ name: 'Raw HTTP response in tool',
141
+ pattern: /(?:return|=>)\s*[{(]\s*[{"]?[^}]*(?:content|body|text|html)['"]\s*[:=]\s*(?:await\s+)?(?:response|res)\.(?:text|json|body)/gi,
142
+ category: 'tool_poisoning',
143
+ baseSeverity: 'high',
144
+ description: 'MCP tool returns raw HTTP response content without sanitization. External content could contain prompt injection payloads.',
145
+ suggestedFix: 'Sanitize external content before returning: return { content: sanitize(response.text()) }',
146
+ },
147
+ // Raw fetch result
148
+ {
149
+ name: 'Fetch result returned directly',
150
+ pattern: /return\s*[{(]\s*[{"]?[^}]*[:=]\s*await\s+fetch\([^)]+\)\.(?:text|json)\(\)/gi,
151
+ category: 'tool_poisoning',
152
+ baseSeverity: 'high',
153
+ description: 'Fetch result returned directly in tool response. Content may contain malicious instructions.',
154
+ suggestedFix: 'Validate and sanitize fetch results before including in response.',
155
+ },
156
+ // Database query results (JS)
157
+ {
158
+ name: 'Raw database content in response',
159
+ pattern: /return\s*\{[^}]*(?:data|results?|rows|documents?|items?)\s*:\s*(?:await\s+)?(?:db|database|client|collection|query)\.(?:query|find|search|execute)/gi,
160
+ category: 'tool_poisoning',
161
+ baseSeverity: 'medium',
162
+ description: 'Database query results returned without filtering. Stored content could be poisoned.',
163
+ suggestedFix: 'Validate and sanitize database content. Consider returning only safe fields.',
164
+ },
165
+ // Database query results (Python)
166
+ {
167
+ name: 'Raw database content in Python response',
168
+ pattern: /return\s*\{[^}]*["'](?:data|results?|documents?)["']\s*:\s*(?:await\s+)?(?:db|database|results)[\.\[]/gi,
169
+ category: 'tool_poisoning',
170
+ baseSeverity: 'medium',
171
+ description: 'Database query results returned without filtering in Python MCP tool.',
172
+ suggestedFix: 'Validate and sanitize database content. Consider returning only safe fields.',
173
+ },
174
+ // File content
175
+ {
176
+ name: 'File content returned without validation',
177
+ pattern: /return\s*[{(]\s*[{"]?[^}]*content['"]\s*[:=]\s*(?:await\s+)?(?:fs|file|readFile|readFileSync)/gi,
178
+ category: 'tool_poisoning',
179
+ baseSeverity: 'high',
180
+ description: 'File content returned without validation. Files could contain malicious instructions.',
181
+ suggestedFix: 'Validate file content and type. Sanitize before returning to the model.',
182
+ },
183
+ // Email content
184
+ {
185
+ name: 'Email content in response',
186
+ pattern: /return\s*[{(]\s*[{"]?[^}]*(?:body|content|text)['"]\s*[:=]\s*(?:email|message|mail)\.(?:body|content|text|html)/gi,
187
+ category: 'tool_poisoning',
188
+ baseSeverity: 'high',
189
+ description: 'Email content returned to model. Emails are common vectors for prompt injection.',
190
+ suggestedFix: 'Sanitize email content. Strip HTML, scripts, and instruction-like patterns.',
191
+ },
192
+ // RSS/feed content
193
+ {
194
+ name: 'RSS/feed content in response',
195
+ pattern: /return\s*[{(]\s*[{"]?[^}]*(?:items?|entries?|feed)['"]\s*[:=]\s*(?:feed|rss|parser)\.(?:items?|entries?|parse)/gi,
196
+ category: 'tool_poisoning',
197
+ baseSeverity: 'medium',
198
+ description: 'RSS/feed content returned without filtering. Feed titles and descriptions could be poisoned.',
199
+ suggestedFix: 'Sanitize feed content. Filter to safe fields only (id, title summary).',
200
+ },
201
+ // Generic raw content return (JS)
202
+ {
203
+ name: 'Raw content in tool response',
204
+ pattern: /server\.tool\s*\([^)]+,\s*async[^{]+\{[^}]*return\s*\{[^}]*:\s*(?:await\s+)?response\.text\(\)/gi,
205
+ category: 'tool_poisoning',
206
+ baseSeverity: 'high',
207
+ description: 'MCP tool returns raw text content from external source.',
208
+ suggestedFix: 'Add content sanitization layer before returning external content.',
209
+ },
210
+ // Python httpx response text
211
+ {
212
+ name: 'Raw HTTP response in Python tool',
213
+ pattern: /return\s*\{[^}]*["']content["']\s*:\s*(?:await\s+)?response\.text/gi,
214
+ category: 'tool_poisoning',
215
+ baseSeverity: 'high',
216
+ description: 'Python MCP tool returns raw HTTP response content.',
217
+ suggestedFix: 'Sanitize external content before returning to the model.',
218
+ },
219
+ // Variable-based: HTTP response assigned then returned
220
+ {
221
+ name: 'HTTP response variable in MCP tool',
222
+ pattern: /(?:const|let|var)\s+\w+\s*=\s*(?:await\s+)?response\.text\(\)[^}]+return\s*\{[^}]*content/gis,
223
+ category: 'tool_poisoning',
224
+ baseSeverity: 'high',
225
+ description: 'HTTP response text stored in variable and returned. External content could be poisoned.',
226
+ suggestedFix: 'Sanitize the content before returning: const sanitized = sanitize(html)',
227
+ },
228
+ // Variable-based: File read assigned then returned
229
+ {
230
+ name: 'File read variable in MCP tool',
231
+ pattern: /(?:const|let|var)\s+\w+\s*=\s*(?:await\s+)?(?:fs\.readFile|readFile)[^}]+return\s*\{[^}]*content/gis,
232
+ category: 'tool_poisoning',
233
+ baseSeverity: 'high',
234
+ description: 'File content stored in variable and returned. File content could contain malicious instructions.',
235
+ suggestedFix: 'Validate and sanitize file content before returning.',
236
+ },
237
+ // Database query result in return (shorthand property)
238
+ {
239
+ name: 'Database query in MCP return',
240
+ pattern: /(?:const|let|var)\s+(?:results?|data|rows)\s*=\s*(?:await\s+)?(?:db|database|client)\.(?:query|find|search)[^}]+return\s*\{[^}]*(?:data|results?|rows)/gis,
241
+ category: 'tool_poisoning',
242
+ baseSeverity: 'medium',
243
+ description: 'Database query results returned in MCP tool. Stored content could be poisoned.',
244
+ suggestedFix: 'Validate and sanitize database content before returning.',
245
+ },
246
+ // Email body returned
247
+ {
248
+ name: 'Email body in MCP return',
249
+ pattern: /(?:email|message|mail)\s*=\s*(?:await)?[^}]+return\s*\{[^}]*body\s*:\s*(?:email|message|mail)\.body/gis,
250
+ category: 'tool_poisoning',
251
+ baseSeverity: 'high',
252
+ description: 'Email body content returned in MCP tool. Emails are common prompt injection vectors.',
253
+ suggestedFix: 'Sanitize email content. Strip HTML and instruction-like patterns.',
254
+ },
255
+ // Feed/RSS items returned
256
+ {
257
+ name: 'RSS/feed items in MCP return',
258
+ pattern: /(?:feed|rss)\s*=\s*(?:await)?[^}]+return\s*\{[^}]*items?\s*:\s*(?:feed|rss)\.items?/gis,
259
+ category: 'tool_poisoning',
260
+ baseSeverity: 'medium',
261
+ description: 'RSS/feed items returned in MCP tool. Feed content could be poisoned.',
262
+ suggestedFix: 'Sanitize feed content. Filter to safe fields only.',
263
+ },
264
+ ];
265
+ /**
266
+ * Credential Issue Patterns
267
+ * Detect credentials in tool parameters or responses
268
+ */
269
+ const CREDENTIAL_PATTERNS = [
270
+ // API key in parameter
271
+ {
272
+ name: 'API key in tool parameter',
273
+ pattern: /server\.tool\s*\([^)]+,\s*async\s*\(\s*\{[^}]*(?:apiKey|api_key|token|secret|password|privateKey|private_key|accessToken|access_token|authToken|auth_token)/gi,
274
+ category: 'credential_issue',
275
+ baseSeverity: 'high',
276
+ description: 'Tool accepts credentials as parameter. Credentials should not flow through the model.',
277
+ suggestedFix: 'Use server-side credential storage. Remove credential parameter and use environment variables or secret manager.',
278
+ },
279
+ // Python decorator with credentials
280
+ {
281
+ name: 'Python tool with credential parameter',
282
+ pattern: /@server\.tool[^)]*\)\s*(?:async\s+)?def\s+\w+\s*\([^)]*(?:api_key|token|secret|password|private_key|access_token|auth_token)/gi,
283
+ category: 'credential_issue',
284
+ baseSeverity: 'high',
285
+ description: 'Python MCP tool accepts credentials as parameter.',
286
+ suggestedFix: 'Use server-side credential management. Do not pass secrets through tool parameters.',
287
+ },
288
+ // Returning credentials in response
289
+ {
290
+ name: 'Credentials in tool response',
291
+ pattern: /return\s*\{[^}]*(?:apiKey|api_key|token|password|secret|privateKey|private_key|accessToken|access_token|refreshToken|refresh_token|jwt)\s*:/gi,
292
+ category: 'credential_issue',
293
+ baseSeverity: 'critical',
294
+ description: 'Tool response includes credentials. Exposing secrets to the model is dangerous.',
295
+ suggestedFix: 'Never return credentials in tool responses. Return success status or user-safe identifiers only.',
296
+ },
297
+ // Connection string in parameter
298
+ {
299
+ name: 'Connection string in tool parameter',
300
+ pattern: /server\.tool\s*\([^)]+,\s*async\s*\(\s*\{[^}]*(?:connectionString|connection_string|dsn|dbUrl|db_url|databaseUrl|database_url)/gi,
301
+ category: 'credential_issue',
302
+ baseSeverity: 'high',
303
+ description: 'Database connection string passed as tool parameter. Connection strings contain credentials.',
304
+ suggestedFix: 'Use server-side database configuration. Do not accept connection strings as parameters.',
305
+ },
306
+ // Environment secrets in response
307
+ {
308
+ name: 'Environment secrets in response',
309
+ pattern: /return\s*\{[^}]*:\s*process\.env\.(?:.*(?:KEY|SECRET|TOKEN|PASSWORD|CREDENTIAL))/gi,
310
+ category: 'credential_issue',
311
+ baseSeverity: 'critical',
312
+ description: 'Environment secrets returned in tool response.',
313
+ suggestedFix: 'Never return environment secrets. Use them server-side only.',
314
+ },
315
+ ];
316
+ /**
317
+ * Confused Deputy Patterns
318
+ * Detect operations without proper user context
319
+ */
320
+ const CONFUSED_DEPUTY_PATTERNS = [
321
+ // Data operation without user context
322
+ {
323
+ name: 'Data deletion without user context',
324
+ pattern: /server\.tool\s*\([^)]+,\s*async\s*\(\s*\{[^}]*(?:Id|id)\s*\}[^)]*\)\s*(?:=>|:)[^{]*\{[^}]*(?:\.delete|\.remove|\.destroy)\s*\(/gi,
325
+ category: 'confused_deputy',
326
+ baseSeverity: 'high',
327
+ description: 'Tool deletes data using only an ID parameter without user context verification.',
328
+ suggestedFix: 'Add user context parameter and verify ownership: if (record.ownerId !== context.user.id) throw new Error("Unauthorized")',
329
+ },
330
+ // Update operation without auth check
331
+ {
332
+ name: 'Data update without authorization',
333
+ pattern: /server\.tool\s*\([^)]+,\s*async\s*\(\s*\{[^}]*(?:Id|id)[^}]*data[^}]*\}[^)]*\)[^{]*\{[^}]*(?:\.update|\.set|\.save)\s*\(/gi,
334
+ category: 'confused_deputy',
335
+ baseSeverity: 'high',
336
+ description: 'Tool updates data without verifying the user owns the record.',
337
+ suggestedFix: 'Validate user ownership before update. Add authorization check.',
338
+ },
339
+ // Reading user-specific data without context
340
+ {
341
+ name: 'User data access without context',
342
+ pattern: /server\.tool\s*\([^)]+(?:user|file|record|document|message)[^)]+,\s*async\s*\(\s*\{[^}]*(?:Id|id)\s*\}/gi,
343
+ category: 'confused_deputy',
344
+ baseSeverity: 'medium',
345
+ description: 'Tool accesses user-specific data with only an ID. Missing user context verification.',
346
+ suggestedFix: 'Add user context and verify access permissions for the requested resource.',
347
+ },
348
+ // Admin/privileged operation without auth
349
+ {
350
+ name: 'Privileged operation without authorization',
351
+ pattern: /server\.tool\s*\([^)]+(?:admin|grant|revoke|elevate|promote)[^)]*,\s*async/gi,
352
+ category: 'confused_deputy',
353
+ baseSeverity: 'critical',
354
+ description: 'Privileged/admin tool without visible authorization check.',
355
+ suggestedFix: 'Add strict authorization check. Verify caller has admin privileges before executing.',
356
+ },
357
+ // Send email/message as user
358
+ {
359
+ name: 'Send message without identity verification',
360
+ pattern: /server\.tool\s*\([^)]+(?:send|email|message)[^)]+,\s*async\s*\(\s*\{[^}]*(?:from|sender)[^}]*\}/gi,
361
+ category: 'confused_deputy',
362
+ baseSeverity: 'high',
363
+ description: 'Tool sends messages with a \'from\' parameter. Should use authenticated user identity.',
364
+ suggestedFix: 'Use context.user for sender identity. Do not allow arbitrary \'from\' values.',
365
+ },
366
+ // Cross-tenant data access
367
+ {
368
+ name: 'Organization/tenant data without scope',
369
+ pattern: /server\.tool\s*\([^)]+(?:org|organization|tenant|workspace)[^)]+,\s*async\s*\(\s*\{[^}]*(?:Id|id)\s*\}/gi,
370
+ category: 'confused_deputy',
371
+ baseSeverity: 'high',
372
+ description: 'Tool accesses organization data by ID without tenant context verification.',
373
+ suggestedFix: 'Verify tenant membership: if (org.id !== context.user.tenantId) throw new Error("Unauthorized")',
374
+ },
375
+ // Python tool without context
376
+ {
377
+ name: 'Python tool data operation without user',
378
+ pattern: /@server\.tool[^)]*\)\s*(?:async\s+)?def\s+(?:delete|update|remove|create)_\w+\s*\(\s*(?:\w+_)?id\s*:/gi,
379
+ category: 'confused_deputy',
380
+ baseSeverity: 'medium',
381
+ description: 'Python MCP tool performs data operation with only an ID parameter.',
382
+ suggestedFix: 'Add user context parameter and validate authorization.',
383
+ },
384
+ ];
385
+ /**
386
+ * Tool Description Injection Patterns
387
+ * Detect prompt injection risks in MCP tool descriptions/metadata
388
+ */
389
+ const DESCRIPTION_INJECTION_PATTERNS = [
390
+ // Dynamic description from variable/input (JS template literals)
391
+ {
392
+ name: 'Dynamic tool description from variable',
393
+ pattern: /description\s*:\s*[`'"].*\$\{.*(?:user|req|input|param|config).*\}.*[`'"]/gi,
394
+ category: 'description_injection',
395
+ baseSeverity: 'high',
396
+ description: 'Tool description constructed from user input or external variables. Malicious content could manipulate AI behavior.',
397
+ suggestedFix: 'Use static descriptions only. Never include user input in tool descriptions.',
398
+ },
399
+ // Description concatenated with user input
400
+ {
401
+ name: 'Tool description with user input concatenation',
402
+ pattern: /description\s*:\s*(?:["'][^"']*["']\s*\+\s*)?(?:user|req|input|param|options)\./gi,
403
+ category: 'description_injection',
404
+ baseSeverity: 'high',
405
+ description: 'Tool description concatenated with user-controlled values. Could inject prompt manipulation instructions.',
406
+ suggestedFix: 'Use static descriptions. If dynamic content is needed, sanitize and validate strictly.',
407
+ },
408
+ // Injection keywords in tool descriptions
409
+ {
410
+ name: 'Injection keywords in tool description',
411
+ pattern: /description\s*:\s*["'`][^"'`]*(?:ignore\s*(?:previous|above|all)|bypass|override|system\s*prompt|disregard|forget)[^"'`]*["'`]/gi,
412
+ category: 'description_injection',
413
+ baseSeverity: 'critical',
414
+ description: 'Tool description contains prompt injection keywords. This could manipulate AI behavior.',
415
+ suggestedFix: 'Remove manipulation keywords from description. Use neutral, factual descriptions.',
416
+ },
417
+ // Tool name from untrusted source
418
+ {
419
+ name: 'Dynamic tool name from config/options',
420
+ pattern: /(?:registerTool|server\.tool|addTool)\s*\(\s*(?:config|options|params|settings)\s*\[?\s*['".]?\s*(?:name|tool)/gi,
421
+ category: 'description_injection',
422
+ baseSeverity: 'high',
423
+ description: 'Tool name derived from configuration or options. Attackers could shadow legitimate tools.',
424
+ suggestedFix: 'Use hardcoded tool names. Validate against an allowlist if dynamic names are required.',
425
+ },
426
+ // Python dynamic description
427
+ {
428
+ name: 'Python tool with dynamic description',
429
+ pattern: /@server\.tool\s*\(\s*name\s*=\s*(?:f["']|["'].*\{)/gi,
430
+ category: 'description_injection',
431
+ baseSeverity: 'high',
432
+ description: 'Python MCP tool with f-string or formatted description. Could include injected content.',
433
+ suggestedFix: 'Use static string literals for tool names and descriptions.',
434
+ },
435
+ // Description from database/storage
436
+ {
437
+ name: 'Tool description from storage',
438
+ pattern: /description\s*:\s*(?:await\s+)?(?:db|database|storage|cache|redis)\.(?:get|read|fetch)/gi,
439
+ category: 'description_injection',
440
+ baseSeverity: 'medium',
441
+ description: 'Tool description loaded from storage. Stored content could be poisoned.',
442
+ suggestedFix: 'Use static descriptions. If dynamic descriptions are required, validate and sanitize thoroughly.',
443
+ },
444
+ ];
445
+ /**
446
+ * Cross-Server Tool Shadowing Patterns
447
+ * Detect malicious MCP servers overriding legitimate tools
448
+ */
449
+ const SERVER_SHADOWING_PATTERNS = [
450
+ // Server config from environment/user input
451
+ {
452
+ name: 'MCP server config from environment',
453
+ pattern: /(?:MCP_SERVERS?|mcpServers?)\s*[=:]\s*(?:JSON\.parse\s*\(\s*)?process\.env/gi,
454
+ category: 'server_shadowing',
455
+ baseSeverity: 'medium',
456
+ description: 'MCP server configuration loaded from environment variables. Ensure proper validation.',
457
+ suggestedFix: 'Validate server URLs against an allowlist. Use explicit server configuration in code.',
458
+ },
459
+ // Server URLs from user input
460
+ {
461
+ name: 'MCP server URL from user input',
462
+ pattern: /(?:server(?:Url|URL|Uri)|endpoint)\s*:\s*(?:req\.|user\.|input\.|params\.|body\.)/gi,
463
+ category: 'server_shadowing',
464
+ baseSeverity: 'high',
465
+ description: 'MCP server URL derived from user input. Attackers could point to malicious servers.',
466
+ suggestedFix: 'Use hardcoded server URLs or validate against a strict allowlist.',
467
+ },
468
+ // Dynamic server registration from config
469
+ {
470
+ name: 'Dynamic MCP server registration',
471
+ pattern: /(?:for|forEach)\s*\([^)]*\)\s*(?:=>|\{)\s*[^}]*(?:register|connect|add)(?:Server|MCP)/gi,
472
+ category: 'server_shadowing',
473
+ baseSeverity: 'medium',
474
+ description: 'MCP servers registered dynamically from configuration. Tool shadowing risk.',
475
+ suggestedFix: 'Register servers explicitly. Implement tool name conflict detection.',
476
+ },
477
+ // Server list from JSON parse
478
+ {
479
+ name: 'MCP servers from parsed JSON',
480
+ pattern: /servers\s*=\s*JSON\.parse\s*\(\s*(?:req\.|user|input|localStorage|sessionStorage)/gi,
481
+ category: 'server_shadowing',
482
+ baseSeverity: 'high',
483
+ description: 'MCP server list parsed from user-controlled data. Could inject malicious servers.',
484
+ suggestedFix: 'Define servers in code. If dynamic loading is needed, validate against an allowlist.',
485
+ },
486
+ // Server config override
487
+ {
488
+ name: 'MCP server config override pattern',
489
+ pattern: /Object\.assign\s*\([^)]*(?:server|mcp)Config[^)]*,\s*(?:req\.|user\.|options\.)/gi,
490
+ category: 'server_shadowing',
491
+ baseSeverity: 'medium',
492
+ description: 'MCP server configuration being overridden with user-provided values.',
493
+ suggestedFix: 'Validate and sanitize configuration overrides. Use allowlist for permitted settings.',
494
+ },
495
+ ];
496
+ // ============================================================================
497
+ // Main Detection Function
498
+ // ============================================================================
499
+ /**
500
+ * Map internal category to vulnerability category
501
+ */
502
+ function mapCategory(internal) {
503
+ switch (internal) {
504
+ case 'tool_poisoning':
505
+ return 'ai_mcp_tool_poisoning';
506
+ case 'credential_issue':
507
+ return 'ai_mcp_credential_issue';
508
+ case 'confused_deputy':
509
+ return 'ai_mcp_confused_deputy';
510
+ case 'description_injection':
511
+ return 'ai_mcp_description_injection';
512
+ case 'server_shadowing':
513
+ return 'ai_mcp_server_shadowing';
514
+ }
515
+ }
516
+ /**
517
+ * Main detection function for MCP security issues
518
+ */
519
+ function detectMCPSecurity(content, filePath) {
520
+ const vulnerabilities = [];
521
+ // Skip non-applicable files
522
+ if ((0, context_helpers_1.isScannerOrFixtureFile)(filePath))
523
+ return vulnerabilities;
524
+ if ((0, context_helpers_1.isDocumentationFile)(filePath))
525
+ return vulnerabilities;
526
+ // Only scan MCP-related files
527
+ if (!isMCPFile(content, filePath)) {
528
+ return vulnerabilities;
529
+ }
530
+ const lines = content.split('\n');
531
+ const isTestFile = (0, context_helpers_1.isTestOrMockFile)(filePath);
532
+ const isExample = (0, context_helpers_1.isExampleDirectory)(filePath);
533
+ const isLibrary = (0, context_helpers_1.isLibraryCode)(filePath);
534
+ // Process all pattern categories
535
+ const allPatterns = [
536
+ ...TOOL_POISONING_PATTERNS,
537
+ ...CREDENTIAL_PATTERNS,
538
+ ...CONFUSED_DEPUTY_PATTERNS,
539
+ ...DESCRIPTION_INJECTION_PATTERNS,
540
+ ...SERVER_SHADOWING_PATTERNS,
541
+ ];
542
+ // Track findings to avoid duplicates
543
+ const seenFindings = new Set();
544
+ for (const pattern of allPatterns) {
545
+ const regex = new RegExp(pattern.pattern.source, pattern.pattern.flags);
546
+ let match;
547
+ while ((match = regex.exec(content)) !== null) {
548
+ const lineNumber = content.substring(0, match.index).split('\n').length;
549
+ const lineContent = lines[lineNumber - 1]?.trim() || '';
550
+ // Skip comments
551
+ if ((0, context_helpers_1.isComment)(lineContent))
552
+ continue;
553
+ // Create dedup key
554
+ const dedupKey = `${filePath}:${lineNumber}:${pattern.category}`;
555
+ if (seenFindings.has(dedupKey))
556
+ continue;
557
+ seenFindings.add(dedupKey);
558
+ // Get surrounding context for analysis
559
+ const context = getSurroundingContext(content, lineNumber - 1, 30);
560
+ // Calculate severity based on context
561
+ let severity = pattern.baseSeverity;
562
+ let description = pattern.description;
563
+ const notes = [];
564
+ // Apply context-aware severity adjustments
565
+ if (pattern.category === 'tool_poisoning') {
566
+ // Check for content sanitization
567
+ if (hasContentSanitization(context)) {
568
+ severity = 'info';
569
+ notes.push('Content sanitization detected');
570
+ }
571
+ // Check for safe data source
572
+ else if (isSafeDataSource(context)) {
573
+ severity = 'info';
574
+ notes.push('Safe/static data source detected');
575
+ }
576
+ // Check for user context (their own data)
577
+ else if (hasUserContext(context)) {
578
+ // Has user context - might be returning user's own data
579
+ if (severity === 'high')
580
+ severity = 'medium';
581
+ notes.push('User context present - may be returning user\'s own data');
582
+ }
583
+ }
584
+ if (pattern.category === 'confused_deputy') {
585
+ // Check for user context
586
+ if (hasUserContext(context)) {
587
+ // User context present - check for auth
588
+ if (hasAuthorizationCheck(context)) {
589
+ severity = 'info';
590
+ notes.push('Authorization check detected');
591
+ }
592
+ else {
593
+ // Has user but no auth check - lower severity
594
+ if (severity === 'high')
595
+ severity = 'medium';
596
+ if (severity === 'critical')
597
+ severity = 'high';
598
+ notes.push('User context present but no authorization check');
599
+ }
600
+ }
601
+ }
602
+ // Credential issues are always serious, but check context
603
+ if (pattern.category === 'credential_issue') {
604
+ // Check if it's returning the credential
605
+ if (pattern.name.includes('response') || pattern.name.includes('return')) {
606
+ // Returning credentials is always critical/high
607
+ }
608
+ else if (hasUserContext(context)) {
609
+ // Parameter with user context - still bad but slightly less severe
610
+ if (severity === 'high')
611
+ severity = 'medium';
612
+ notes.push('User context present but credentials still in parameters');
613
+ }
614
+ }
615
+ // Description injection - check for input sanitization
616
+ if (pattern.category === 'description_injection') {
617
+ // Check for sanitization or validation before description
618
+ if (/sanitize|validate|escape|filter|strip/i.test(context)) {
619
+ severity = 'low';
620
+ notes.push('Input sanitization detected nearby');
621
+ }
622
+ // Check for static/constant descriptions
623
+ if (/const\s+\w+\s*=\s*["'`][^"'`]+["'`]\s*;?\s*$/m.test(context)) {
624
+ // Likely a constant being used
625
+ severity = 'info';
626
+ notes.push('May be using constant description');
627
+ }
628
+ }
629
+ // Server shadowing - check for allowlist validation
630
+ if (pattern.category === 'server_shadowing') {
631
+ // Check for allowlist/whitelist validation
632
+ if (/allowlist|whitelist|ALLOWED_SERVERS|validServers|trustedServers/i.test(context)) {
633
+ severity = 'info';
634
+ notes.push('Server allowlist detected');
635
+ }
636
+ // Check for URL validation
637
+ if (/validate.*url|url.*validate|isValidUrl|checkUrl/i.test(context)) {
638
+ severity = 'low';
639
+ notes.push('URL validation detected');
640
+ }
641
+ }
642
+ // Downgrade test files
643
+ if (isTestFile) {
644
+ severity = 'info';
645
+ notes.push('in test file');
646
+ }
647
+ // Downgrade example/demo directories
648
+ if (isExample && severity !== 'info') {
649
+ severity = 'info';
650
+ notes.push('in example/demo directory');
651
+ }
652
+ // Downgrade library code
653
+ if (isLibrary && severity !== 'info') {
654
+ severity = 'info';
655
+ notes.push('library code');
656
+ }
657
+ // Build final description
658
+ if (notes.length > 0) {
659
+ description += ` (${notes.join('; ')})`;
660
+ }
661
+ vulnerabilities.push({
662
+ id: `ai-mcp-${filePath}-${lineNumber}-${pattern.name.replace(/\s+/g, '-')}`,
663
+ filePath,
664
+ lineNumber,
665
+ lineContent,
666
+ severity,
667
+ category: mapCategory(pattern.category),
668
+ title: pattern.name,
669
+ description,
670
+ suggestedFix: pattern.suggestedFix,
671
+ confidence: severity === 'info' ? 'low' : 'medium',
672
+ layer: 2,
673
+ requiresAIValidation: severity !== 'info' && severity !== 'low',
674
+ });
675
+ }
676
+ }
677
+ return vulnerabilities;
678
+ }
679
+ //# sourceMappingURL=ai-mcp-security.js.map