mcpsec 2.7.2__tar.gz → 2.7.3__tar.gz

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 (197) hide show
  1. {mcpsec-2.7.2 → mcpsec-2.7.3}/CHANGELOG.md +17 -11
  2. {mcpsec-2.7.2 → mcpsec-2.7.3}/PKG-INFO +1 -1
  3. mcpsec-2.7.3/mcpsec/__init__.py +3 -0
  4. mcpsec-2.7.3/mcpsec/__main__.py +4 -0
  5. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/client/mcp_client.py +37 -9
  6. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/exploit/evidence.py +15 -8
  7. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/fuzzer/chain/chain_engine.py +81 -32
  8. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/fuzzer/evolve/engine.py +21 -3
  9. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/fuzzer/evolve/mcp_mutators.py +2 -1
  10. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/fuzzer/evolve/mutators.py +2 -0
  11. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/fuzzer/fuzz_engine.py +184 -79
  12. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/fuzzer/generators/__init__.py +2 -1
  13. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/fuzzer/generators/base.py +1 -0
  14. mcpsec-2.7.3/mcpsec/fuzzer/generators/header_routing.py +185 -0
  15. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/fuzzer/generators/method_mutations.py +11 -0
  16. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/fuzzer/generators/protocol_state.py +139 -2
  17. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/fuzzer/generators/protocol_violation.py +52 -6
  18. mcpsec-2.7.3/mcpsec/fuzzer/generators/schema_fuzzer.py +549 -0
  19. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/fuzzer/generators/session_attacks.py +67 -8
  20. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/fuzzer/transport/http_fuzzer.py +29 -2
  21. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/rogue/server.py +17 -0
  22. {mcpsec-2.7.2 → mcpsec-2.7.3}/pyproject.toml +1 -1
  23. {mcpsec-2.7.2 → mcpsec-2.7.3}/shell.nix +1 -1
  24. {mcpsec-2.7.2 → mcpsec-2.7.3}/tests/servers/blind_test.py +16 -26
  25. mcpsec-2.7.3/tests/servers/compat.py +72 -0
  26. mcpsec-2.7.3/tests/servers/dangerous_server.py +74 -0
  27. {mcpsec-2.7.2 → mcpsec-2.7.3}/tests/servers/stress_test_server.py +18 -27
  28. mcpsec-2.7.3/tests/servers/vulnerable_db_server.py +59 -0
  29. mcpsec-2.7.3/tests/test_schema_fuzzer.py +212 -0
  30. mcpsec-2.7.2/mcpsec/__init__.py +0 -11
  31. mcpsec-2.7.2/tests/servers/dangerous_server.py +0 -80
  32. mcpsec-2.7.2/tests/servers/vulnerable_db_server.py +0 -67
  33. {mcpsec-2.7.2 → mcpsec-2.7.3}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  34. {mcpsec-2.7.2 → mcpsec-2.7.3}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  35. {mcpsec-2.7.2 → mcpsec-2.7.3}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  36. {mcpsec-2.7.2 → mcpsec-2.7.3}/.github/workflows/ci.yml +0 -0
  37. {mcpsec-2.7.2 → mcpsec-2.7.3}/.github/workflows/release.yml +0 -0
  38. {mcpsec-2.7.2 → mcpsec-2.7.3}/.gitignore +0 -0
  39. {mcpsec-2.7.2 → mcpsec-2.7.3}/CONTRIBUTING.md +0 -0
  40. {mcpsec-2.7.2 → mcpsec-2.7.3}/LICENSE +0 -0
  41. {mcpsec-2.7.2 → mcpsec-2.7.3}/README.md +0 -0
  42. {mcpsec-2.7.2 → mcpsec-2.7.3}/SECURITY.md +0 -0
  43. {mcpsec-2.7.2 → mcpsec-2.7.3}/assets/demo.gif +0 -0
  44. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/ai/__init__.py +0 -0
  45. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/ai/ai_payload_generator.py +0 -0
  46. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/ai/ai_taint_analyzer.py +0 -0
  47. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/ai/ai_validator.py +0 -0
  48. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/ai/finding_classifier.py +0 -0
  49. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/ai/llm_client.py +0 -0
  50. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/cli.py +0 -0
  51. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/client/__init__.py +0 -0
  52. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/config.py +0 -0
  53. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/discovery.py +0 -0
  54. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/engine.py +0 -0
  55. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/exploit/__init__.py +0 -0
  56. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/exploit/connection.py +0 -0
  57. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/exploit/formatters.py +0 -0
  58. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/exploit/mcp_repl.py +0 -0
  59. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/exploit/payload_engine.py +0 -0
  60. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/exploit/playbooks/__init__.py +0 -0
  61. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/exploit/playbooks/command_injection.py +0 -0
  62. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/exploit/playbooks/generic.py +0 -0
  63. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/exploit/playbooks/path_traversal.py +0 -0
  64. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/exploit/playbooks/prompt_injection.py +0 -0
  65. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/exploit/playbooks/sandbox_escape.py +0 -0
  66. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/exploit/playbooks/sqli.py +0 -0
  67. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/exploit/playbooks/ssrf.py +0 -0
  68. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/exploit/session.py +0 -0
  69. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/exploit/state_tracker.py +0 -0
  70. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/fuzzer/chain/__init__.py +0 -0
  71. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/fuzzer/chain/chain_analyzer.py +0 -0
  72. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/fuzzer/chain/chain_builder.py +0 -0
  73. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/fuzzer/chain/chain_reporter.py +0 -0
  74. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/fuzzer/chain/injection_points.py +0 -0
  75. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/fuzzer/chain/state_extractor.py +0 -0
  76. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/fuzzer/chain/state_manager.py +0 -0
  77. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/fuzzer/evolve/__init__.py +0 -0
  78. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/fuzzer/evolve/corpus.py +0 -0
  79. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/fuzzer/evolve/feedback.py +0 -0
  80. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/fuzzer/evolve/scheduler.py +0 -0
  81. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/fuzzer/generators/boundary_testing.py +0 -0
  82. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/fuzzer/generators/concurrency_attacks.py +0 -0
  83. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/fuzzer/generators/deserialization.py +0 -0
  84. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/fuzzer/generators/encoding_attacks.py +0 -0
  85. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/fuzzer/generators/header_mutations.py +0 -0
  86. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/fuzzer/generators/id_confusion.py +0 -0
  87. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/fuzzer/generators/injection_payloads.py +0 -0
  88. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/fuzzer/generators/integer_boundaries.py +0 -0
  89. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/fuzzer/generators/json_edge_cases.py +0 -0
  90. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/fuzzer/generators/malformed_json.py +0 -0
  91. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/fuzzer/generators/memory_exhaustion.py +0 -0
  92. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/fuzzer/generators/param_mutations.py +0 -0
  93. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/fuzzer/generators/regex_dos.py +0 -0
  94. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/fuzzer/generators/resource_exhaustion.py +0 -0
  95. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/fuzzer/generators/timing_attacks.py +0 -0
  96. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/fuzzer/generators/type_confusion.py +0 -0
  97. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/fuzzer/generators/unicode_attacks.py +0 -0
  98. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/fuzzer/transport/stdio_fuzzer.py +0 -0
  99. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/models.py +0 -0
  100. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/reporters/__init__.py +0 -0
  101. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/reporters/json_report.py +0 -0
  102. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/reporters/sarif_report.py +0 -0
  103. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/rogue/__init__.py +0 -0
  104. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/rogue/payloads.py +0 -0
  105. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/rules/exclusions.yml +0 -0
  106. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/rules/mcp-ast-sandbox-escape.yml +0 -0
  107. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/rules/mcp-broad-command-injection.yml +0 -0
  108. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/rules/mcp-broad-path-traversal.yml +0 -0
  109. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/rules/mcp-broad-sql-injection.yml +0 -0
  110. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/rules/mcp-capability-issues.yml +0 -0
  111. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/rules/mcp-code-smells.yml +0 -0
  112. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/rules/mcp-command-injection.yml +0 -0
  113. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/rules/mcp-dangerous-defaults.yml +0 -0
  114. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/rules/mcp-dangerous-eval.yml +0 -0
  115. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/rules/mcp-description-injection.yml +0 -0
  116. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/rules/mcp-deserialization.yml +0 -0
  117. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/rules/mcp-dotnet-injection.yml +0 -0
  118. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/rules/mcp-go-injection.yml +0 -0
  119. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/rules/mcp-indirect-injection.yml +0 -0
  120. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/rules/mcp-path-traversal.yml +0 -0
  121. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/rules/mcp-protocol-handler.yml +0 -0
  122. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/rules/mcp-python-async.yml +0 -0
  123. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/rules/mcp-python-broad.yml +0 -0
  124. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/rules/mcp-python-injection.yml +0 -0
  125. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/rules/mcp-resource-uri.yml +0 -0
  126. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/rules/mcp-rust-injection.yml +0 -0
  127. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/rules/mcp-secrets-exposure.yml +0 -0
  128. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/rules/mcp-secrets-hardcoded.yml +0 -0
  129. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/rules/mcp-server-patterns.yml +0 -0
  130. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/rules/mcp-sql-injection.yml +0 -0
  131. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/rules/mcp-ssrf-patterns.yml +0 -0
  132. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/rules/mcp-taint-sinks.yml +0 -0
  133. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/scanners/__init__.py +0 -0
  134. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/scanners/annotation_integrity.py +0 -0
  135. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/scanners/auth_audit.py +0 -0
  136. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/scanners/base.py +0 -0
  137. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/scanners/capability_escalation.py +0 -0
  138. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/scanners/code_execution.py +0 -0
  139. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/scanners/command_injection.py +0 -0
  140. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/scanners/description_injection.py +0 -0
  141. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/scanners/description_prompt_injection.py +0 -0
  142. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/scanners/deserialization.py +0 -0
  143. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/scanners/idor.py +0 -0
  144. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/scanners/info_leak.py +0 -0
  145. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/scanners/path_traversal.py +0 -0
  146. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/scanners/prompt_injection.py +0 -0
  147. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/scanners/rag_poisoning.py +0 -0
  148. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/scanners/resource_ssrf.py +0 -0
  149. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/scanners/response_classifier.py +0 -0
  150. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/scanners/secrets_exposure.py +0 -0
  151. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/scanners/sql_rce.py +0 -0
  152. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/scanners/ssrf.py +0 -0
  153. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/scanners/template_injection.py +0 -0
  154. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/scanners/toolchain_analyzer.py +0 -0
  155. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/sql_scanner/__init__.py +0 -0
  156. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/sql_scanner/confirmer.py +0 -0
  157. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/sql_scanner/detector.py +0 -0
  158. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/sql_scanner/fingerprint.py +0 -0
  159. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/sql_scanner/payloads.py +0 -0
  160. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/static/__init__.py +0 -0
  161. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/static/analysis/__init__.py +0 -0
  162. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/static/analysis/reachability.py +0 -0
  163. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/static/analysis/sink_scanner.py +0 -0
  164. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/static/audit_engine.py +0 -0
  165. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/static/framework/__init__.py +0 -0
  166. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/static/framework/detector.py +0 -0
  167. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/static/patterns/__init__.py +0 -0
  168. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/static/patterns/base.py +0 -0
  169. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/static/patterns/generator.py +0 -0
  170. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/static/patterns/registry.py +0 -0
  171. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/static/patterns/sanitizers/__init__.py +0 -0
  172. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/static/patterns/sanitizers/sanitizers.py +0 -0
  173. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/static/patterns/sinks/__init__.py +0 -0
  174. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/static/patterns/sinks/code_execution.py +0 -0
  175. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/static/patterns/sinks/command_injection.py +0 -0
  176. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/static/patterns/sinks/crypto.py +0 -0
  177. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/static/patterns/sinks/deserialization.py +0 -0
  178. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/static/patterns/sinks/misc.py +0 -0
  179. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/static/patterns/sinks/path_traversal.py +0 -0
  180. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/static/patterns/sinks/sql_injection.py +0 -0
  181. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/static/patterns/sinks/ssrf.py +0 -0
  182. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/static/patterns/sinks/xxe.py +0 -0
  183. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/static/patterns/sources/__init__.py +0 -0
  184. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/static/patterns/sources/mcp_frameworks.py +0 -0
  185. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/static/semgrep_engine.py +0 -0
  186. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/static/source_fetcher.py +0 -0
  187. {mcpsec-2.7.2 → mcpsec-2.7.3}/mcpsec/ui.py +0 -0
  188. {mcpsec-2.7.2 → mcpsec-2.7.3}/tests/__init__.py +0 -0
  189. {mcpsec-2.7.2 → mcpsec-2.7.3}/tests/servers/__init__.py +0 -0
  190. {mcpsec-2.7.2 → mcpsec-2.7.3}/tests/servers/noisy_test_server.py +0 -0
  191. {mcpsec-2.7.2 → mcpsec-2.7.3}/tests/servers/stateful_test_server.py +0 -0
  192. {mcpsec-2.7.2 → mcpsec-2.7.3}/tests/test_config.py +0 -0
  193. {mcpsec-2.7.2 → mcpsec-2.7.3}/tests/test_exploit.py +0 -0
  194. {mcpsec-2.7.2 → mcpsec-2.7.3}/tests/test_fuzzer.py +0 -0
  195. {mcpsec-2.7.2 → mcpsec-2.7.3}/tests/test_sarif.py +0 -0
  196. {mcpsec-2.7.2 → mcpsec-2.7.3}/tests/test_scanners.py +0 -0
  197. {mcpsec-2.7.2 → mcpsec-2.7.3}/tests/test_semgrep_rules.py +0 -0
@@ -5,20 +5,26 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
- ## [2.7.2] - 2026-07-12
9
8
 
10
- ### Changed
11
- - **Codebase Cleanup & Deduplication**: Removed 156 duplicate regexes and consolidated 3,459 sink patterns into 9 organized files by vulnerability type.
12
- - **Generator Consolidation**: Merged protocol_state generators and renamed memory_exhaustion_v2 for better maintainability.
13
- - **Dead Code Removal**: Removed unused AI analyzer stubs ( aint_analyzer, py_analyzer) and SQL exploiter stubs.
14
- - **Test Organization**: Moved test server fixtures from ests/ to ests/servers/ for cleaner separation of tests and fixtures.
9
+ ## [2.7.3] - 2026-11-01
15
10
 
16
- ### Fixed
17
- - **Silent Exception Handling**: Replaced bare except: pass blocks across 130+ files with proper logger.debug statements.
18
- - **Unicode Errors on Windows**: Fixed UnicodeEncodeError crashes caused by rich console emojis by forcing UTF-8 stdout encoding.
19
- - **Unused Imports**: Cleaned up stale imports in cli.py.
11
+ ### Added
12
+ - **Schema-Aware Argument Fuzzer (schema_fuzzer)**: Per-parameter, type-targeted mutation fuzzing driven by tool inputSchema.
13
+ - Type-targeted mutation catalog for strings (path traversal, command injection, template injection, SSRF, length abuse, unicode, format and pattern violations), numbers and integers (numeric boundaries, floats, NaN/Infinity tokens, min/max bounds), booleans (type confusion), arrays (item recursion, min/max bounds, deep nesting), and objects (prototype pollution, unexpected keys, deep nesting).
14
+ - Out-of-enum value generation for enum properties.
15
+ - Baseline value generation for non-targeted required parameters to ensure valid payloads reach tool handlers instead of failing schema validation.
16
+ - Schema-less tool support (defaults to untyped string argument fuzzing).
17
+ - Configurable intensity scaling (low, medium, high, insane).
18
+ - Comprehensive unit test suite in ests/test_schema_fuzzer.py.
20
19
 
20
+ ### Improved
21
+ - **Fuzz Engine Wiring**: Automatically discovers server tools on startup and executes targeted schema fuzzing.
22
+ - Replaced naive single-value spray in uzz_engine.py with schema-aware parameter targeting and baseline parameter population.
23
+
24
+ ## [2.7.2] - 2026-07-28
21
25
 
26
+ ### Added
27
+ - Support for MCP 2026-07-28 stateless protocol lifecycle (server/discover).
22
28
  ## [2.7.1] - 2026-04-15
23
29
 
24
30
  ### Improved
@@ -315,4 +321,4 @@ mcpsec evolve --stdio "server" --corpus ./corpus --crashes ./crashes
315
321
  - Protocol fuzzer with 500+ test cases
316
322
  - AI-powered payload generation
317
323
  - Semgrep-based static analysis
318
- - Support for OpenAI, Anthropic, Google, Groq, DeepSeek, Ollama
324
+ - Support for OpenAI, Anthropic, Google, Groq, DeepSeek, Ollama
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: mcpsec
3
- Version: 2.7.2
3
+ Version: 2.7.3
4
4
  Summary: Security scanner for MCP (Model Context Protocol) servers - pentest your AI agent's tool connections
5
5
  Project-URL: Homepage, https://github.com/manthanghasadiya/mcpsec
6
6
  Project-URL: Documentation, https://github.com/manthanghasadiya/mcpsec#readme
@@ -0,0 +1,3 @@
1
+ """mcpsec - Security scanner for MCP server implementations."""
2
+
3
+ __version__ = "2.7.3"
@@ -0,0 +1,4 @@
1
+ from mcpsec.cli import app
2
+
3
+ if __name__ == "__main__":
4
+ app()
@@ -14,7 +14,11 @@ from typing import Any
14
14
 
15
15
  from mcp import ClientSession, StdioServerParameters
16
16
  from mcp.client.stdio import stdio_client
17
- from mcp.client.streamable_http import streamablehttp_client
17
+
18
+ try:
19
+ from mcp.client.streamable_http import streamable_http_client
20
+ except ImportError: # mcp < 2.0.0
21
+ from mcp.client.streamable_http import streamablehttp_client as streamable_http_client
18
22
 
19
23
  from mcpsec.models import (
20
24
  PromptInfo,
@@ -77,21 +81,42 @@ class MCPSecClient:
77
81
  self.session = await self._exit_stack.enter_async_context(
78
82
  ClientSession(read_stream, write_stream)
79
83
  )
80
- await self.session.initialize()
84
+ await self._initialize()
81
85
  return await self._enumerate()
82
86
 
83
87
  async def connect_http(self, url: str, headers: dict | None = None) -> ServerProfile:
84
88
  """Connect to an MCP server via streamable HTTP transport."""
85
- http_transport = await self._exit_stack.enter_async_context(
86
- streamablehttp_client(url=url, headers=headers)
87
- )
89
+ try:
90
+ from mcp.client.streamable_http import create_mcp_http_client
91
+
92
+ http_client = create_mcp_http_client(headers=headers or {})
93
+ except ImportError: # mcp < 2.0.0
94
+ http_client = None
95
+
96
+ if http_client is not None:
97
+ http_transport = await self._exit_stack.enter_async_context(
98
+ streamable_http_client(url=url, http_client=http_client)
99
+ )
100
+ else:
101
+ http_transport = await self._exit_stack.enter_async_context(
102
+ streamable_http_client(url=url, headers=headers)
103
+ )
88
104
  read_stream, write_stream = http_transport[0], http_transport[1]
89
105
  self.session = await self._exit_stack.enter_async_context(
90
106
  ClientSession(read_stream, write_stream)
91
107
  )
92
- await self.session.initialize()
108
+ await self._initialize()
93
109
  return await self._enumerate()
94
110
 
111
+ async def _initialize(self):
112
+ """Initialize the session, preferring the stateless server/discover probe."""
113
+ if not self.session:
114
+ raise RuntimeError("Not connected")
115
+ try:
116
+ await self.session.discover()
117
+ except Exception:
118
+ await self.session.initialize()
119
+
95
120
  async def call_tool(
96
121
  self, tool_name: str, arguments: dict[str, Any], timeout: float = 30.0
97
122
  ) -> Any:
@@ -124,9 +149,12 @@ class MCPSecClient:
124
149
  for tool in tools_result.tools:
125
150
  params = {}
126
151
  raw_schema = {}
127
- if tool.inputSchema and "properties" in tool.inputSchema:
128
- raw_schema = tool.inputSchema
129
- for param_name, param_def in tool.inputSchema["properties"].items():
152
+ input_schema = getattr(tool, "inputSchema", None) or getattr(
153
+ tool, "input_schema", None
154
+ )
155
+ if input_schema and "properties" in input_schema:
156
+ raw_schema = input_schema
157
+ for param_name, param_def in input_schema["properties"].items():
130
158
  param_type = param_def.get("type", "any")
131
159
  params[param_name] = param_type
132
160
 
@@ -89,16 +89,23 @@ def main():
89
89
  proc = subprocess.Popen(SERVER_CMD.split(), stdin=subprocess.PIPE,
90
90
  stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
91
91
 
92
- # Initialize Handshake
93
- print("[*] Sending initialization...")
94
- send_jsonrpc(proc, {{
95
- "jsonrpc": "2.0", "id": 1, "method": "initialize",
96
- "params": {{"protocolVersion": "2024-11-05", "capabilities": {{}},
92
+ # Probe: server/discover (2026-07-28 stateless) with legacy initialize fallback
93
+ print("[*] Probing server capabilities...")
94
+ discover = send_jsonrpc(proc, {{
95
+ "jsonrpc": "2.0", "id": 1, "method": "server/discover",
96
+ "params": {{"protocolVersion": "2026-07-28", "capabilities": {{}},
97
97
  "clientInfo": {{"name": "mcpsec-poc", "version": "1.0"}}}}
98
98
  }})
99
- proc.stdin.write(json.dumps({{"jsonrpc":"2.0","method":"notifications/initialized"}}) + "\\n")
100
- proc.stdin.flush()
101
- time.sleep(0.5)
99
+ if not discover or "result" not in discover:
100
+ print("[*] server/discover unsupported, falling back to legacy initialize...")
101
+ send_jsonrpc(proc, {{
102
+ "jsonrpc": "2.0", "id": 1, "method": "initialize",
103
+ "params": {{"protocolVersion": "2024-11-05", "capabilities": {{}},
104
+ "clientInfo": {{"name": "mcpsec-poc", "version": "1.0"}}}}
105
+ }})
106
+ proc.stdin.write(json.dumps({{"jsonrpc":"2.0","method":"notifications/initialized"}}) + "\\n")
107
+ proc.stdin.flush()
108
+ time.sleep(0.5)
102
109
 
103
110
  # Exploit Request
104
111
  print("[*] Sending exploit payload to {tool_name}...")
@@ -264,18 +264,18 @@ class ChainEngine:
264
264
  if hasattr(self._transport, "framing"):
265
265
  self._transport.framing = "jsonl"
266
266
 
267
- init_msg = {
268
- "jsonrpc": "2.0",
269
- "id": 1,
270
- "method": "initialize",
271
- "params": {
272
- "protocolVersion": "2024-11-05",
273
- "capabilities": {},
274
- "clientInfo": {"name": "mcpsec-chained", "version": "1.0.0"},
275
- },
276
- }
277
-
278
- init_response = await self._transport.send_request_async(init_msg)
267
+ init_response = await self._transport.send_request_async(
268
+ {
269
+ "jsonrpc": "2.0",
270
+ "id": 1,
271
+ "method": "server/discover",
272
+ "params": {
273
+ "protocolVersion": "2026-07-28",
274
+ "capabilities": {},
275
+ "clientInfo": {"name": "mcpsec-chained", "version": "1.0.0"},
276
+ },
277
+ }
278
+ )
279
279
 
280
280
  # If JSONL didn't work, try CRLF framing (Node servers)
281
281
  if not init_response or "result" not in init_response:
@@ -295,8 +295,54 @@ class ChainEngine:
295
295
 
296
296
  if hasattr(self._transport, "framing"):
297
297
  self._transport.framing = "crlf"
298
+ init_response = await self._transport.send_request_async(
299
+ {
300
+ "jsonrpc": "2.0",
301
+ "id": 1,
302
+ "method": "server/discover",
303
+ "params": {
304
+ "protocolVersion": "2026-07-28",
305
+ "capabilities": {},
306
+ "clientInfo": {"name": "mcpsec-chained", "version": "1.0.0"},
307
+ },
308
+ }
309
+ )
310
+
311
+ # Legacy fallback: initialize handshake
312
+ if not init_response or "result" not in init_response:
313
+ if self.config.verbose:
314
+ console.print(" [dim]server/discover failed, trying legacy initialize...[/dim]")
315
+
316
+ if hasattr(self._transport, "restart_async"):
317
+ await self._transport.restart_async()
318
+ else:
319
+ restart_fn = getattr(self._transport, "restart", None)
320
+ if restart_fn:
321
+ await asyncio.to_thread(restart_fn)
322
+ await asyncio.sleep(1.0)
323
+
324
+ init_msg = {
325
+ "jsonrpc": "2.0",
326
+ "id": 1,
327
+ "method": "initialize",
328
+ "params": {
329
+ "protocolVersion": "2024-11-05",
330
+ "capabilities": {},
331
+ "clientInfo": {"name": "mcpsec-chained", "version": "1.0.0"},
332
+ },
333
+ }
298
334
  init_response = await self._transport.send_request_async(init_msg)
299
335
 
336
+ if init_response and "result" in init_response:
337
+ # Send initialized notification (no ID!)
338
+ await self._transport.send_notification_async(
339
+ {
340
+ "jsonrpc": "2.0",
341
+ "method": "notifications/initialized",
342
+ }
343
+ )
344
+ await asyncio.sleep(0.5)
345
+
300
346
  if not init_response or "result" not in init_response:
301
347
  console.print("[yellow] Warning: Server did not respond to initialize[/yellow]")
302
348
  return
@@ -305,16 +351,6 @@ class ChainEngine:
305
351
  framing = getattr(self._transport, "framing", "unknown")
306
352
  console.print(f" [dim]Using framing: {framing}[/dim]")
307
353
 
308
- # Send initialized notification (no ID!)
309
- await self._transport.send_notification_async(
310
- {
311
- "jsonrpc": "2.0",
312
- "method": "notifications/initialized",
313
- }
314
- )
315
-
316
- await asyncio.sleep(0.5) # Give server time to process
317
-
318
354
  # Get tools list
319
355
  tools_response = await self._transport.send_request_async(
320
356
  {"jsonrpc": "2.0", "id": 2, "method": "tools/list", "params": {}}
@@ -803,25 +839,38 @@ class ChainEngine:
803
839
  await self._transport.restart()
804
840
  await asyncio.sleep(1.0)
805
841
 
806
- # Re-initialize
807
- await self._transport.send_request_async(
842
+ # Re-initialize (discover first, legacy fallback)
843
+ init_response = await self._transport.send_request_async(
808
844
  {
809
845
  "jsonrpc": "2.0",
810
846
  "id": 1,
811
- "method": "initialize",
847
+ "method": "server/discover",
812
848
  "params": {
813
- "protocolVersion": "2024-11-05",
849
+ "protocolVersion": "2026-07-28",
814
850
  "capabilities": {},
815
851
  "clientInfo": {"name": "mcpsec-chained", "version": "1.0.0"},
816
852
  },
817
853
  }
818
854
  )
819
- await self._transport.send_notification_async(
820
- {
821
- "jsonrpc": "2.0",
822
- "method": "notifications/initialized",
823
- }
824
- )
855
+ if not init_response or "result" not in init_response:
856
+ await self._transport.send_request_async(
857
+ {
858
+ "jsonrpc": "2.0",
859
+ "id": 1,
860
+ "method": "initialize",
861
+ "params": {
862
+ "protocolVersion": "2024-11-05",
863
+ "capabilities": {},
864
+ "clientInfo": {"name": "mcpsec-chained", "version": "1.0.0"},
865
+ },
866
+ }
867
+ )
868
+ await self._transport.send_notification_async(
869
+ {
870
+ "jsonrpc": "2.0",
871
+ "method": "notifications/initialized",
872
+ }
873
+ )
825
874
  await asyncio.sleep(0.5)
826
875
 
827
876
  def _print_dependency_graph(self, graph: dict) -> None:
@@ -182,8 +182,8 @@ class EvolveFuzzEngine:
182
182
  def _create_default_seeds(self):
183
183
  """Create initial seed inputs."""
184
184
  seeds = [
185
- {"jsonrpc": "2.0", "id": 1, "method": "initialize", "params": {
186
- "protocolVersion": "2024-11-05",
185
+ {"jsonrpc": "2.0", "id": 1, "method": "server/discover", "params": {
186
+ "protocolVersion": "2026-07-28",
187
187
  "capabilities": {},
188
188
  "clientInfo": {"name": "mcpsec-evolve", "version": "1.0"}
189
189
  }},
@@ -194,6 +194,9 @@ class EvolveFuzzEngine:
194
194
  {"jsonrpc": "2.0", "id": 6, "method": "tools/call", "params": {
195
195
  "name": "test", "arguments": {}
196
196
  }},
197
+ {"jsonrpc": "2.0", "id": 7, "method": "tasks/get", "params": {
198
+ "taskId": "task-1"
199
+ }},
197
200
  ]
198
201
 
199
202
  for seed in seeds:
@@ -212,7 +215,22 @@ class EvolveFuzzEngine:
212
215
  try:
213
216
  self.process = await self._start_server()
214
217
 
215
- # Send initialize
218
+ # Send server/discover (2026-07-28 stateless), fall back to initialize
219
+ discover_req = json.dumps({
220
+ "jsonrpc": "2.0", "id": 1, "method": "server/discover",
221
+ "params": {
222
+ "protocolVersion": "2026-07-28",
223
+ "capabilities": {},
224
+ "clientInfo": {"name": "mcpsec-evolve", "version": "1.0"}
225
+ }
226
+ }).encode() + b"\n"
227
+
228
+ self.process.stdin.write(discover_req)
229
+ self.process.stdin.flush()
230
+
231
+ await asyncio.sleep(0.5)
232
+
233
+ # Legacy fallback: initialize handshake
216
234
  init_req = json.dumps({
217
235
  "jsonrpc": "2.0", "id": 1, "method": "initialize",
218
236
  "params": {
@@ -69,7 +69,8 @@ class MCPStructureMutator(Mutator):
69
69
  """Change the method to something unexpected."""
70
70
  methods = [
71
71
  "tools/call", "tools/list", "resources/read", "resources/list",
72
- "prompts/get", "initialize", "ping", "shutdown",
72
+ "prompts/get", "initialize", "server/discover", "tasks/get",
73
+ "tasks/update", "subscriptions/listen", "ping", "shutdown",
73
74
  "../../../etc/passwd", "'; DROP TABLE tools;--",
74
75
  "AAAA" * 100, "", None, 12345, {"nested": "method"},
75
76
  "tools/call\x00hidden", "tools/../../call",
@@ -213,6 +213,8 @@ class DictionaryMutator(Mutator):
213
213
  b'"initialize"', b'"tools/list"', b'"tools/call"',
214
214
  b'"resources/read"', b'"resources/list"', b'"prompts/list"',
215
215
  b'"notifications/initialized"', b'"ping"',
216
+ b'"server/discover"', b'"tasks/get"', b'"tasks/update"',
217
+ b'"subscriptions/listen"', b'"inputResponses"', b'"input_required"',
216
218
  b'"protocolVersion"', b'"capabilities"', b'"clientInfo"',
217
219
  b'"name"', b'"version"', b'"arguments"',
218
220
  b'null', b'true', b'false', b'{}', b'[]',