mcp-proxy-adapter 6.0.0__py3-none-any.whl → 6.1.1__py3-none-any.whl

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 (264) hide show
  1. mcp_proxy_adapter/api/app.py +174 -80
  2. mcp_proxy_adapter/api/handlers.py +16 -5
  3. mcp_proxy_adapter/api/middleware/__init__.py +9 -4
  4. mcp_proxy_adapter/api/middleware/command_permission_middleware.py +148 -0
  5. mcp_proxy_adapter/api/middleware/factory.py +36 -12
  6. mcp_proxy_adapter/api/middleware/protocol_middleware.py +32 -13
  7. mcp_proxy_adapter/api/middleware/unified_security.py +160 -0
  8. mcp_proxy_adapter/api/middleware/user_info_middleware.py +83 -0
  9. mcp_proxy_adapter/commands/__init__.py +7 -1
  10. mcp_proxy_adapter/commands/base.py +7 -4
  11. mcp_proxy_adapter/commands/builtin_commands.py +8 -2
  12. mcp_proxy_adapter/commands/command_registry.py +8 -0
  13. mcp_proxy_adapter/commands/echo_command.py +81 -0
  14. mcp_proxy_adapter/commands/help_command.py +21 -14
  15. mcp_proxy_adapter/commands/proxy_registration_command.py +326 -185
  16. mcp_proxy_adapter/commands/role_test_command.py +141 -0
  17. mcp_proxy_adapter/commands/security_command.py +488 -0
  18. mcp_proxy_adapter/commands/ssl_setup_command.py +2 -2
  19. mcp_proxy_adapter/commands/token_management_command.py +1 -1
  20. mcp_proxy_adapter/config.py +81 -21
  21. mcp_proxy_adapter/core/app_factory.py +326 -0
  22. mcp_proxy_adapter/core/client_security.py +384 -0
  23. mcp_proxy_adapter/core/logging.py +8 -3
  24. mcp_proxy_adapter/core/mtls_asgi.py +156 -0
  25. mcp_proxy_adapter/core/mtls_asgi_app.py +187 -0
  26. mcp_proxy_adapter/core/protocol_manager.py +139 -8
  27. mcp_proxy_adapter/core/proxy_client.py +602 -0
  28. mcp_proxy_adapter/core/proxy_registration.py +299 -47
  29. mcp_proxy_adapter/core/security_adapter.py +12 -15
  30. mcp_proxy_adapter/core/security_integration.py +285 -0
  31. mcp_proxy_adapter/core/server_adapter.py +345 -0
  32. mcp_proxy_adapter/core/server_engine.py +364 -0
  33. mcp_proxy_adapter/core/unified_config_adapter.py +579 -0
  34. mcp_proxy_adapter/docs/EN/TROUBLESHOOTING.md +285 -0
  35. mcp_proxy_adapter/docs/RU/TROUBLESHOOTING.md +285 -0
  36. mcp_proxy_adapter/examples/README.md +230 -97
  37. mcp_proxy_adapter/examples/README_EN.md +258 -0
  38. mcp_proxy_adapter/examples/SECURITY_TESTING.md +455 -0
  39. mcp_proxy_adapter/examples/basic_framework/configs/http_auth.json +37 -0
  40. mcp_proxy_adapter/examples/basic_framework/configs/http_simple.json +23 -0
  41. mcp_proxy_adapter/examples/basic_framework/configs/https_auth.json +43 -0
  42. mcp_proxy_adapter/examples/basic_framework/configs/https_no_protocol_middleware.json +36 -0
  43. mcp_proxy_adapter/examples/basic_framework/configs/https_simple.json +29 -0
  44. mcp_proxy_adapter/examples/basic_framework/configs/mtls_no_protocol_middleware.json +34 -0
  45. mcp_proxy_adapter/examples/basic_framework/configs/mtls_no_roles.json +39 -0
  46. mcp_proxy_adapter/examples/basic_framework/configs/mtls_simple.json +35 -0
  47. mcp_proxy_adapter/examples/basic_framework/configs/mtls_with_roles.json +45 -0
  48. mcp_proxy_adapter/examples/basic_framework/main.py +63 -0
  49. mcp_proxy_adapter/examples/basic_framework/roles.json +21 -0
  50. mcp_proxy_adapter/examples/cert_config.json +9 -0
  51. mcp_proxy_adapter/examples/certs/admin.crt +32 -0
  52. mcp_proxy_adapter/examples/certs/admin.key +52 -0
  53. mcp_proxy_adapter/examples/certs/admin_cert.pem +21 -0
  54. mcp_proxy_adapter/examples/certs/admin_key.pem +28 -0
  55. mcp_proxy_adapter/examples/certs/ca_cert.pem +23 -0
  56. mcp_proxy_adapter/examples/certs/ca_cert.srl +1 -0
  57. mcp_proxy_adapter/examples/certs/ca_key.pem +28 -0
  58. mcp_proxy_adapter/examples/certs/cert_config.json +9 -0
  59. mcp_proxy_adapter/examples/certs/client.crt +32 -0
  60. mcp_proxy_adapter/examples/certs/client.key +52 -0
  61. mcp_proxy_adapter/examples/certs/client_admin.crt +32 -0
  62. mcp_proxy_adapter/examples/certs/client_admin.key +52 -0
  63. mcp_proxy_adapter/examples/certs/client_user.crt +32 -0
  64. mcp_proxy_adapter/examples/certs/client_user.key +52 -0
  65. mcp_proxy_adapter/examples/certs/guest_cert.pem +21 -0
  66. mcp_proxy_adapter/examples/certs/guest_key.pem +28 -0
  67. mcp_proxy_adapter/examples/certs/mcp_proxy_adapter_ca_ca.crt +23 -0
  68. mcp_proxy_adapter/examples/certs/proxy_cert.pem +21 -0
  69. mcp_proxy_adapter/examples/certs/proxy_key.pem +28 -0
  70. mcp_proxy_adapter/examples/certs/readonly.crt +32 -0
  71. mcp_proxy_adapter/examples/certs/readonly.key +52 -0
  72. mcp_proxy_adapter/examples/certs/readonly_cert.pem +21 -0
  73. mcp_proxy_adapter/examples/certs/readonly_key.pem +28 -0
  74. mcp_proxy_adapter/examples/certs/server.crt +32 -0
  75. mcp_proxy_adapter/examples/certs/server.key +52 -0
  76. mcp_proxy_adapter/examples/certs/server_cert.pem +32 -0
  77. mcp_proxy_adapter/examples/certs/server_key.pem +52 -0
  78. mcp_proxy_adapter/examples/certs/test_ca_ca.crt +20 -0
  79. mcp_proxy_adapter/examples/certs/user.crt +32 -0
  80. mcp_proxy_adapter/examples/certs/user.key +52 -0
  81. mcp_proxy_adapter/examples/certs/user_cert.pem +21 -0
  82. mcp_proxy_adapter/examples/certs/user_key.pem +28 -0
  83. mcp_proxy_adapter/examples/client_configs/api_key_client.json +13 -0
  84. mcp_proxy_adapter/examples/client_configs/basic_auth_client.json +13 -0
  85. mcp_proxy_adapter/examples/client_configs/certificate_client.json +22 -0
  86. mcp_proxy_adapter/examples/client_configs/jwt_client.json +15 -0
  87. mcp_proxy_adapter/examples/client_configs/no_auth_client.json +9 -0
  88. mcp_proxy_adapter/examples/commands/__init__.py +1 -0
  89. mcp_proxy_adapter/examples/create_certificates_simple.py +307 -0
  90. mcp_proxy_adapter/examples/debug_request_state.py +144 -0
  91. mcp_proxy_adapter/examples/debug_role_chain.py +205 -0
  92. mcp_proxy_adapter/examples/demo_client.py +341 -0
  93. mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +99 -0
  94. mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +106 -0
  95. mcp_proxy_adapter/examples/full_application/configs/http_auth.json +37 -0
  96. mcp_proxy_adapter/examples/full_application/configs/http_simple.json +23 -0
  97. mcp_proxy_adapter/examples/full_application/configs/https_auth.json +39 -0
  98. mcp_proxy_adapter/examples/full_application/configs/https_simple.json +25 -0
  99. mcp_proxy_adapter/examples/full_application/configs/mtls_no_roles.json +39 -0
  100. mcp_proxy_adapter/examples/full_application/configs/mtls_with_roles.json +45 -0
  101. mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +97 -0
  102. mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +95 -0
  103. mcp_proxy_adapter/examples/full_application/main.py +138 -0
  104. mcp_proxy_adapter/examples/full_application/roles.json +21 -0
  105. mcp_proxy_adapter/examples/generate_all_certificates.py +429 -0
  106. mcp_proxy_adapter/examples/generate_certificates.py +121 -0
  107. mcp_proxy_adapter/examples/keys/ca_key.pem +28 -0
  108. mcp_proxy_adapter/examples/keys/mcp_proxy_adapter_ca_ca.key +28 -0
  109. mcp_proxy_adapter/examples/keys/test_ca_ca.key +28 -0
  110. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log +220 -0
  111. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.1 +1 -0
  112. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.2 +1 -0
  113. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.3 +1 -0
  114. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.4 +1 -0
  115. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.5 +1 -0
  116. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log +220 -0
  117. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.1 +1 -0
  118. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.2 +1 -0
  119. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.3 +1 -0
  120. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.4 +1 -0
  121. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.5 +1 -0
  122. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log +2 -0
  123. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.1 +1 -0
  124. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.2 +1 -0
  125. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.3 +1 -0
  126. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.4 +1 -0
  127. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.5 +1 -0
  128. mcp_proxy_adapter/examples/proxy_registration_example.py +401 -0
  129. mcp_proxy_adapter/examples/roles.json +38 -0
  130. mcp_proxy_adapter/examples/run_example.py +81 -0
  131. mcp_proxy_adapter/examples/run_security_tests.py +326 -0
  132. mcp_proxy_adapter/examples/run_security_tests_fixed.py +300 -0
  133. mcp_proxy_adapter/examples/security_test_client.py +743 -0
  134. mcp_proxy_adapter/examples/server_configs/config_basic_http.json +204 -0
  135. mcp_proxy_adapter/examples/server_configs/config_http_token.json +238 -0
  136. mcp_proxy_adapter/examples/server_configs/config_https.json +215 -0
  137. mcp_proxy_adapter/examples/server_configs/config_https_token.json +231 -0
  138. mcp_proxy_adapter/examples/server_configs/config_mtls.json +215 -0
  139. mcp_proxy_adapter/examples/server_configs/config_proxy_registration.json +250 -0
  140. mcp_proxy_adapter/examples/server_configs/config_simple.json +46 -0
  141. mcp_proxy_adapter/examples/server_configs/roles.json +38 -0
  142. mcp_proxy_adapter/examples/test_config_generator.py +110 -0
  143. mcp_proxy_adapter/examples/test_examples.py +344 -0
  144. mcp_proxy_adapter/examples/universal_client.py +628 -0
  145. mcp_proxy_adapter/main.py +21 -10
  146. mcp_proxy_adapter/utils/config_generator.py +727 -0
  147. mcp_proxy_adapter/version.py +5 -2
  148. mcp_proxy_adapter-6.1.1.dist-info/METADATA +205 -0
  149. mcp_proxy_adapter-6.1.1.dist-info/RECORD +197 -0
  150. mcp_proxy_adapter-6.1.1.dist-info/entry_points.txt +2 -0
  151. {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.1.1.dist-info}/licenses/LICENSE +2 -2
  152. mcp_proxy_adapter/api/middleware/auth.py +0 -146
  153. mcp_proxy_adapter/api/middleware/auth_adapter.py +0 -235
  154. mcp_proxy_adapter/api/middleware/mtls_adapter.py +0 -305
  155. mcp_proxy_adapter/api/middleware/mtls_middleware.py +0 -296
  156. mcp_proxy_adapter/api/middleware/rate_limit.py +0 -152
  157. mcp_proxy_adapter/api/middleware/rate_limit_adapter.py +0 -241
  158. mcp_proxy_adapter/api/middleware/roles_adapter.py +0 -365
  159. mcp_proxy_adapter/api/middleware/roles_middleware.py +0 -381
  160. mcp_proxy_adapter/api/middleware/security.py +0 -376
  161. mcp_proxy_adapter/api/middleware/token_auth_middleware.py +0 -261
  162. mcp_proxy_adapter/examples/__init__.py +0 -7
  163. mcp_proxy_adapter/examples/basic_server/README.md +0 -60
  164. mcp_proxy_adapter/examples/basic_server/__init__.py +0 -7
  165. mcp_proxy_adapter/examples/basic_server/basic_custom_settings.json +0 -39
  166. mcp_proxy_adapter/examples/basic_server/config.json +0 -70
  167. mcp_proxy_adapter/examples/basic_server/config_all_protocols.json +0 -54
  168. mcp_proxy_adapter/examples/basic_server/config_http.json +0 -70
  169. mcp_proxy_adapter/examples/basic_server/config_http_only.json +0 -52
  170. mcp_proxy_adapter/examples/basic_server/config_https.json +0 -58
  171. mcp_proxy_adapter/examples/basic_server/config_mtls.json +0 -58
  172. mcp_proxy_adapter/examples/basic_server/config_ssl.json +0 -46
  173. mcp_proxy_adapter/examples/basic_server/custom_settings_example.py +0 -238
  174. mcp_proxy_adapter/examples/basic_server/server.py +0 -114
  175. mcp_proxy_adapter/examples/custom_commands/README.md +0 -127
  176. mcp_proxy_adapter/examples/custom_commands/__init__.py +0 -27
  177. mcp_proxy_adapter/examples/custom_commands/advanced_hooks.py +0 -566
  178. mcp_proxy_adapter/examples/custom_commands/auto_commands/__init__.py +0 -6
  179. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_echo_command.py +0 -103
  180. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_info_command.py +0 -111
  181. mcp_proxy_adapter/examples/custom_commands/auto_commands/test_command.py +0 -105
  182. mcp_proxy_adapter/examples/custom_commands/catalog/commands/test_command.py +0 -129
  183. mcp_proxy_adapter/examples/custom_commands/config.json +0 -118
  184. mcp_proxy_adapter/examples/custom_commands/config_all_protocols.json +0 -46
  185. mcp_proxy_adapter/examples/custom_commands/config_https_only.json +0 -46
  186. mcp_proxy_adapter/examples/custom_commands/config_https_transport.json +0 -33
  187. mcp_proxy_adapter/examples/custom_commands/config_mtls_only.json +0 -46
  188. mcp_proxy_adapter/examples/custom_commands/config_mtls_transport.json +0 -33
  189. mcp_proxy_adapter/examples/custom_commands/config_single_transport.json +0 -33
  190. mcp_proxy_adapter/examples/custom_commands/custom_health_command.py +0 -169
  191. mcp_proxy_adapter/examples/custom_commands/custom_help_command.py +0 -215
  192. mcp_proxy_adapter/examples/custom_commands/custom_openapi_generator.py +0 -76
  193. mcp_proxy_adapter/examples/custom_commands/custom_settings.json +0 -96
  194. mcp_proxy_adapter/examples/custom_commands/custom_settings_manager.py +0 -241
  195. mcp_proxy_adapter/examples/custom_commands/data_transform_command.py +0 -135
  196. mcp_proxy_adapter/examples/custom_commands/echo_command.py +0 -122
  197. mcp_proxy_adapter/examples/custom_commands/full_help_response.json +0 -1
  198. mcp_proxy_adapter/examples/custom_commands/generated_openapi.json +0 -629
  199. mcp_proxy_adapter/examples/custom_commands/get_openapi.py +0 -103
  200. mcp_proxy_adapter/examples/custom_commands/hooks.py +0 -230
  201. mcp_proxy_adapter/examples/custom_commands/intercept_command.py +0 -123
  202. mcp_proxy_adapter/examples/custom_commands/loadable_commands/test_ignored.py +0 -129
  203. mcp_proxy_adapter/examples/custom_commands/manual_echo_command.py +0 -103
  204. mcp_proxy_adapter/examples/custom_commands/proxy_connection_manager.py +0 -278
  205. mcp_proxy_adapter/examples/custom_commands/server.py +0 -252
  206. mcp_proxy_adapter/examples/custom_commands/simple_openapi_server.py +0 -75
  207. mcp_proxy_adapter/examples/custom_commands/start_server_with_proxy_manager.py +0 -299
  208. mcp_proxy_adapter/examples/custom_commands/start_server_with_registration.py +0 -278
  209. mcp_proxy_adapter/examples/custom_commands/test_hooks.py +0 -176
  210. mcp_proxy_adapter/examples/custom_commands/test_openapi.py +0 -27
  211. mcp_proxy_adapter/examples/custom_commands/test_registry.py +0 -23
  212. mcp_proxy_adapter/examples/custom_commands/test_simple.py +0 -19
  213. mcp_proxy_adapter/examples/custom_project_example/README.md +0 -103
  214. mcp_proxy_adapter/examples/custom_project_example/README_EN.md +0 -103
  215. mcp_proxy_adapter/examples/deployment/README.md +0 -49
  216. mcp_proxy_adapter/examples/deployment/__init__.py +0 -7
  217. mcp_proxy_adapter/examples/deployment/config.development.json +0 -8
  218. mcp_proxy_adapter/examples/deployment/config.json +0 -29
  219. mcp_proxy_adapter/examples/deployment/config.production.json +0 -12
  220. mcp_proxy_adapter/examples/deployment/config.staging.json +0 -11
  221. mcp_proxy_adapter/examples/deployment/docker-compose.yml +0 -31
  222. mcp_proxy_adapter/examples/deployment/run.sh +0 -43
  223. mcp_proxy_adapter/examples/deployment/run_docker.sh +0 -84
  224. mcp_proxy_adapter/examples/simple_custom_commands/README.md +0 -149
  225. mcp_proxy_adapter/examples/simple_custom_commands/README_EN.md +0 -149
  226. mcp_proxy_adapter/schemas/base_schema.json +0 -114
  227. mcp_proxy_adapter/schemas/openapi_schema.json +0 -314
  228. mcp_proxy_adapter/schemas/roles_schema.json +0 -162
  229. mcp_proxy_adapter/tests/__init__.py +0 -0
  230. mcp_proxy_adapter/tests/api/__init__.py +0 -3
  231. mcp_proxy_adapter/tests/api/test_cmd_endpoint.py +0 -115
  232. mcp_proxy_adapter/tests/api/test_custom_openapi.py +0 -617
  233. mcp_proxy_adapter/tests/api/test_handlers.py +0 -522
  234. mcp_proxy_adapter/tests/api/test_middleware.py +0 -340
  235. mcp_proxy_adapter/tests/api/test_schemas.py +0 -546
  236. mcp_proxy_adapter/tests/api/test_tool_integration.py +0 -531
  237. mcp_proxy_adapter/tests/commands/__init__.py +0 -3
  238. mcp_proxy_adapter/tests/commands/test_config_command.py +0 -211
  239. mcp_proxy_adapter/tests/commands/test_echo_command.py +0 -127
  240. mcp_proxy_adapter/tests/commands/test_help_command.py +0 -136
  241. mcp_proxy_adapter/tests/conftest.py +0 -131
  242. mcp_proxy_adapter/tests/functional/__init__.py +0 -3
  243. mcp_proxy_adapter/tests/functional/test_api.py +0 -253
  244. mcp_proxy_adapter/tests/integration/__init__.py +0 -3
  245. mcp_proxy_adapter/tests/integration/test_cmd_integration.py +0 -129
  246. mcp_proxy_adapter/tests/integration/test_integration.py +0 -255
  247. mcp_proxy_adapter/tests/performance/__init__.py +0 -3
  248. mcp_proxy_adapter/tests/performance/test_performance.py +0 -189
  249. mcp_proxy_adapter/tests/stubs/__init__.py +0 -10
  250. mcp_proxy_adapter/tests/stubs/echo_command.py +0 -104
  251. mcp_proxy_adapter/tests/test_api_endpoints.py +0 -271
  252. mcp_proxy_adapter/tests/test_api_handlers.py +0 -289
  253. mcp_proxy_adapter/tests/test_base_command.py +0 -123
  254. mcp_proxy_adapter/tests/test_batch_requests.py +0 -117
  255. mcp_proxy_adapter/tests/test_command_registry.py +0 -281
  256. mcp_proxy_adapter/tests/test_config.py +0 -127
  257. mcp_proxy_adapter/tests/test_utils.py +0 -65
  258. mcp_proxy_adapter/tests/unit/__init__.py +0 -3
  259. mcp_proxy_adapter/tests/unit/test_base_command.py +0 -436
  260. mcp_proxy_adapter/tests/unit/test_config.py +0 -270
  261. mcp_proxy_adapter-6.0.0.dist-info/METADATA +0 -201
  262. mcp_proxy_adapter-6.0.0.dist-info/RECORD +0 -179
  263. {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.1.1.dist-info}/WHEEL +0 -0
  264. {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.1.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,326 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Security Test Runner for MCP Proxy Adapter
4
+
5
+ This script runs comprehensive security tests against all server configurations:
6
+ - Basic HTTP
7
+ - HTTP + Token authentication
8
+ - HTTPS
9
+ - HTTPS + Token authentication
10
+ - mTLS
11
+
12
+ Author: Vasiliy Zdanovskiy
13
+ email: vasilyvz@gmail.com
14
+ """
15
+
16
+ import asyncio
17
+ import json
18
+ import os
19
+ import signal
20
+ import subprocess
21
+ import sys
22
+ import time
23
+ from pathlib import Path
24
+ from typing import Dict, List, Optional, Any
25
+
26
+ # Add project root to path
27
+ project_root = Path(__file__).parent.parent.parent
28
+ sys.path.insert(0, str(project_root))
29
+
30
+ from security_test_client import SecurityTestClient, TestResult
31
+
32
+
33
+ class SecurityTestRunner:
34
+ """Main test runner for security testing."""
35
+
36
+ def __init__(self):
37
+ """Initialize test runner."""
38
+ self.servers = {}
39
+ self.test_results = {}
40
+ self.configs = {
41
+ "basic_http": {
42
+ "config": "server_configs/config_basic_http.json",
43
+ "port": 8000,
44
+ "url": "http://localhost:8000",
45
+ "auth": "none"
46
+ },
47
+ "http_token": {
48
+ "config": "server_configs/config_http_token.json",
49
+ "port": 8001,
50
+ "url": "http://localhost:8001",
51
+ "auth": "api_key"
52
+ },
53
+ "https": {
54
+ "config": "server_configs/config_https.json",
55
+ "port": 8443,
56
+ "url": "https://localhost:8443",
57
+ "auth": "none"
58
+ },
59
+ "https_token": {
60
+ "config": "server_configs/config_https_token.json",
61
+ "port": 8444,
62
+ "url": "https://localhost:8444",
63
+ "auth": "api_key"
64
+ },
65
+ "mtls": {
66
+ "config": "server_configs/config_mtls.json",
67
+ "port": 8445,
68
+ "url": "https://localhost:8445",
69
+ "auth": "certificate"
70
+ }
71
+ }
72
+
73
+ def check_prerequisites(self) -> bool:
74
+ """Check if all prerequisites are met."""
75
+ print("šŸ” Checking prerequisites...")
76
+
77
+ # Check if we're in the right directory
78
+ if not Path("server_configs").exists():
79
+ print("āŒ server_configs directory not found. Please run from mcp_proxy_adapter/examples/")
80
+ return False
81
+
82
+ # Check if certificates exist
83
+ cert_files = [
84
+ "certs/ca_cert.pem",
85
+ "certs/server_cert.pem",
86
+ "certs/server_key.pem"
87
+ ]
88
+
89
+ missing_certs = []
90
+ for cert_file in cert_files:
91
+ if not Path(cert_file).exists():
92
+ missing_certs.append(cert_file)
93
+
94
+ if missing_certs:
95
+ print(f"āŒ Missing certificates: {missing_certs}")
96
+ print("šŸ’” Run: python generate_certificates.py")
97
+ return False
98
+
99
+ print("āœ… Prerequisites check passed")
100
+ return True
101
+
102
+ def start_server(self, name: str, config_path: str, port: int) -> Optional[subprocess.Popen]:
103
+ """Start a server in background."""
104
+ try:
105
+ print(f"šŸš€ Starting {name} server on port {port}...")
106
+
107
+ # Start server in background
108
+ process = subprocess.Popen([
109
+ sys.executable, "-m", "mcp_proxy_adapter.main",
110
+ "--config", config_path
111
+ ], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
112
+
113
+ # Wait a bit for server to start
114
+ time.sleep(3)
115
+
116
+ # Check if process is still running
117
+ if process.poll() is None:
118
+ print(f"āœ… {name} server started (PID: {process.pid})")
119
+ return process
120
+ else:
121
+ stdout, stderr = process.communicate()
122
+ print(f"āŒ Failed to start {name} server:")
123
+ print(f"STDOUT: {stdout.decode()}")
124
+ print(f"STDERR: {stderr.decode()}")
125
+ return None
126
+
127
+ except Exception as e:
128
+ print(f"āŒ Error starting {name} server: {e}")
129
+ return None
130
+
131
+ def stop_server(self, name: str, process: subprocess.Popen):
132
+ """Stop a server."""
133
+ try:
134
+ print(f"šŸ›‘ Stopping {name} server (PID: {process.pid})...")
135
+ process.terminate()
136
+
137
+ # Wait for graceful shutdown
138
+ try:
139
+ process.wait(timeout=5)
140
+ print(f"āœ… {name} server stopped")
141
+ except subprocess.TimeoutExpired:
142
+ print(f"āš ļø Force killing {name} server")
143
+ process.kill()
144
+ process.wait()
145
+
146
+ except Exception as e:
147
+ print(f"āŒ Error stopping {name} server: {e}")
148
+
149
+ async def test_server(self, name: str, config: Dict[str, Any]) -> List[TestResult]:
150
+ """Test a specific server configuration."""
151
+ print(f"\n🧪 Testing {name} server...")
152
+ print("=" * 50)
153
+
154
+ # Create client with appropriate SSL context
155
+ if config["auth"] == "certificate":
156
+ # For mTLS, create client with certificate-based SSL context
157
+ client = SecurityTestClient(config["url"])
158
+ # Override SSL context for mTLS
159
+ client.create_ssl_context = client.create_ssl_context_for_mtls
160
+ async with client as client_session:
161
+ results = await client_session.run_security_tests(
162
+ config["url"],
163
+ config["auth"]
164
+ )
165
+ else:
166
+ # For other auth types, use default SSL context
167
+ async with SecurityTestClient(config["url"]) as client:
168
+ results = await client.run_security_tests(
169
+ config["url"],
170
+ config["auth"]
171
+ )
172
+
173
+ # Print summary for this server
174
+ passed = sum(1 for r in results if r.success)
175
+ total = len(results)
176
+ print(f"\nšŸ“Š {name} Results: {passed}/{total} tests passed")
177
+
178
+ return results
179
+
180
+ async def run_all_tests(self) -> Dict[str, List[TestResult]]:
181
+ """Run tests against all server configurations."""
182
+ print("šŸš€ Starting comprehensive security testing")
183
+ print("=" * 60)
184
+
185
+ # Start all servers
186
+ for name, config in self.configs.items():
187
+ process = self.start_server(name, config["config"], config["port"])
188
+ if process:
189
+ self.servers[name] = process
190
+ else:
191
+ print(f"āš ļø Skipping tests for {name} due to startup failure")
192
+
193
+ # Wait for all servers to be ready
194
+ print("\nā³ Waiting for servers to be ready...")
195
+ time.sleep(5)
196
+
197
+ # Test each server
198
+ all_results = {}
199
+ for name, config in self.configs.items():
200
+ if name in self.servers:
201
+ try:
202
+ results = await self.test_server(name, config)
203
+ all_results[name] = results
204
+ except Exception as e:
205
+ print(f"āŒ Error testing {name}: {e}")
206
+ all_results[name] = []
207
+ else:
208
+ print(f"āš ļø Skipping {name} tests (server not running)")
209
+ all_results[name] = []
210
+
211
+ return all_results
212
+
213
+ def print_final_summary(self, all_results: Dict[str, List[TestResult]]):
214
+ """Print final test summary."""
215
+ print("\n" + "=" * 80)
216
+ print("šŸ“Š FINAL SECURITY TEST SUMMARY")
217
+ print("=" * 80)
218
+
219
+ total_tests = 0
220
+ total_passed = 0
221
+
222
+ for server_name, results in all_results.items():
223
+ if results:
224
+ passed = sum(1 for r in results if r.success)
225
+ total = len(results)
226
+ total_tests += total
227
+ total_passed += passed
228
+
229
+ status = "āœ… PASS" if passed == total else "āŒ FAIL"
230
+ print(f"{status} {server_name.upper()}: {passed}/{total} tests passed")
231
+
232
+ # Show failed tests
233
+ failed_tests = [r for r in results if not r.success]
234
+ for test in failed_tests:
235
+ print(f" āŒ {test.test_name}: {test.error_message}")
236
+ else:
237
+ print(f"āš ļø SKIP {server_name.upper()}: No tests run")
238
+
239
+ print("\n" + "-" * 80)
240
+ print(f"OVERALL: {total_passed}/{total_tests} tests passed")
241
+ if total_tests > 0:
242
+ success_rate = (total_passed / total_tests) * 100
243
+ print(f"SUCCESS RATE: {success_rate:.1f}%")
244
+
245
+ # Overall status
246
+ if total_passed == total_tests and total_tests > 0:
247
+ print("šŸŽ‰ ALL TESTS PASSED!")
248
+ elif total_passed > 0:
249
+ print("āš ļø SOME TESTS FAILED")
250
+ else:
251
+ print("āŒ ALL TESTS FAILED")
252
+
253
+ def cleanup(self):
254
+ """Cleanup all running servers."""
255
+ print("\n🧹 Cleaning up...")
256
+
257
+ for name, process in self.servers.items():
258
+ self.stop_server(name, process)
259
+
260
+ self.servers.clear()
261
+
262
+ def signal_handler(self, signum, frame):
263
+ """Handle interrupt signals."""
264
+ print(f"\nāš ļø Received signal {signum}, cleaning up...")
265
+ self.cleanup()
266
+ sys.exit(0)
267
+
268
+ async def run(self):
269
+ """Main run method."""
270
+ # Set up signal handlers
271
+ signal.signal(signal.SIGINT, self.signal_handler)
272
+ signal.signal(signal.SIGTERM, self.signal_handler)
273
+
274
+ try:
275
+ # Check prerequisites
276
+ if not self.check_prerequisites():
277
+ return False
278
+
279
+ # Run all tests
280
+ all_results = await self.run_all_tests()
281
+
282
+ # Print summary
283
+ self.print_final_summary(all_results)
284
+
285
+ return True
286
+
287
+ except Exception as e:
288
+ print(f"āŒ Test runner error: {e}")
289
+ return False
290
+
291
+ finally:
292
+ # Always cleanup
293
+ self.cleanup()
294
+
295
+
296
+ def main():
297
+ """Main function."""
298
+ import argparse
299
+
300
+ parser = argparse.ArgumentParser(description="Security Test Runner for MCP Proxy Adapter")
301
+ parser.add_argument("--config", help="Test specific configuration")
302
+ parser.add_argument("--no-cleanup", action="store_true", help="Don't cleanup servers after tests")
303
+ parser.add_argument("--verbose", "-v", action="store_true", help="Verbose output")
304
+
305
+ args = parser.parse_args()
306
+
307
+ # Change to examples directory
308
+ examples_dir = Path(__file__).parent
309
+ os.chdir(examples_dir)
310
+
311
+ # Create and run test runner
312
+ runner = SecurityTestRunner()
313
+
314
+ try:
315
+ success = asyncio.run(runner.run())
316
+ sys.exit(0 if success else 1)
317
+ except KeyboardInterrupt:
318
+ print("\nāš ļø Interrupted by user")
319
+ sys.exit(1)
320
+ except Exception as e:
321
+ print(f"āŒ Unexpected error: {e}")
322
+ sys.exit(1)
323
+
324
+
325
+ if __name__ == "__main__":
326
+ main()
@@ -0,0 +1,300 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Security Testing Script - Fixed Version
4
+
5
+ This script runs comprehensive security tests without fallback mode
6
+ and with proper port management.
7
+
8
+ Author: Vasiliy Zdanovskiy
9
+ email: vasilyvz@gmail.com
10
+ """
11
+
12
+ import asyncio
13
+ import json
14
+ import os
15
+ import signal
16
+ import subprocess
17
+ import sys
18
+ import time
19
+ from pathlib import Path
20
+ from typing import Dict, List, Optional, Tuple
21
+
22
+ # Add project root to path
23
+ project_root = Path(__file__).parent.parent.parent
24
+ sys.path.insert(0, str(project_root))
25
+
26
+ from security_test_client import SecurityTestClient, TestResult
27
+
28
+
29
+ class SecurityTestRunner:
30
+ """Security test runner with proper port management."""
31
+
32
+ def __init__(self):
33
+ self.project_root = Path(__file__).parent.parent.parent
34
+ self.configs_dir = self.project_root / "mcp_proxy_adapter" / "examples" / "server_configs"
35
+ self.server_processes = {}
36
+ self.test_results = []
37
+
38
+ def kill_process_on_port(self, port: int) -> bool:
39
+ """Kill process using specific port."""
40
+ try:
41
+ # Find process using the port
42
+ result = subprocess.run(
43
+ ["lsof", "-ti", f":{port}"],
44
+ capture_output=True,
45
+ text=True,
46
+ timeout=5
47
+ )
48
+
49
+ if result.returncode == 0 and result.stdout.strip():
50
+ pid = result.stdout.strip()
51
+ # Kill the process
52
+ subprocess.run(["kill", "-9", pid], check=True)
53
+ print(f"āœ… Killed process {pid} on port {port}")
54
+ time.sleep(1) # Wait for port to be released
55
+ return True
56
+ else:
57
+ print(f"ā„¹ļø No process found on port {port}")
58
+ return True
59
+ except subprocess.TimeoutExpired:
60
+ print(f"āš ļø Timeout checking port {port}")
61
+ return False
62
+ except Exception as e:
63
+ print(f"āŒ Error killing process on port {port}: {e}")
64
+ return False
65
+
66
+ def start_server(self, config_name: str, config_path: Path) -> Optional[subprocess.Popen]:
67
+ """Start server with proper error handling."""
68
+ try:
69
+ # Get port from config
70
+ with open(config_path) as f:
71
+ config = json.load(f)
72
+ port = config.get("server", {}).get("port", 8000)
73
+
74
+ # Kill any existing process on this port
75
+ self.kill_process_on_port(port)
76
+
77
+ # Start server
78
+ cmd = [
79
+ sys.executable, "-m", "mcp_proxy_adapter.main",
80
+ "--config", str(config_path)
81
+ ]
82
+
83
+ # For mTLS, start from examples directory
84
+ if config_name == "mtls":
85
+ cwd = self.project_root / "mcp_proxy_adapter" / "examples"
86
+ else:
87
+ cwd = self.project_root
88
+
89
+ print(f"šŸš€ Starting {config_name} on port {port}...")
90
+ process = subprocess.Popen(
91
+ cmd,
92
+ cwd=cwd,
93
+ stdout=subprocess.PIPE,
94
+ stderr=subprocess.PIPE,
95
+ text=True
96
+ )
97
+
98
+ # Wait a bit for server to start
99
+ time.sleep(3)
100
+
101
+ # Check if process is still running
102
+ if process.poll() is None:
103
+ print(f"āœ… {config_name} started successfully on port {port}")
104
+ return process
105
+ else:
106
+ stdout, stderr = process.communicate()
107
+ print(f"āŒ {config_name} failed to start:")
108
+ print(f"STDOUT: {stdout}")
109
+ print(f"STDERR: {stderr}")
110
+ return None
111
+
112
+ except Exception as e:
113
+ print(f"āŒ Error starting {config_name}: {e}")
114
+ return None
115
+
116
+ def stop_server(self, config_name: str, process: subprocess.Popen):
117
+ """Stop server gracefully."""
118
+ try:
119
+ print(f"šŸ›‘ Stopping {config_name}...")
120
+ process.terminate()
121
+ process.wait(timeout=5)
122
+ print(f"āœ… {config_name} stopped")
123
+ except subprocess.TimeoutExpired:
124
+ print(f"āš ļø Force killing {config_name}...")
125
+ process.kill()
126
+ process.wait()
127
+ except Exception as e:
128
+ print(f"āŒ Error stopping {config_name}: {e}")
129
+
130
+ async def test_server(self, config_name: str, config_path: Path) -> List[TestResult]:
131
+ """Test a single server configuration."""
132
+ results = []
133
+
134
+ # Start server
135
+ process = self.start_server(config_name, config_path)
136
+ if not process:
137
+ return [TestResult(
138
+ test_name=f"{config_name}_startup",
139
+ server_url=f"http://localhost:{port}",
140
+ auth_type="none",
141
+ success=False,
142
+ error_message="Server failed to start"
143
+ )]
144
+
145
+ try:
146
+ # Get config for client setup
147
+ with open(config_path) as f:
148
+ config = json.load(f)
149
+
150
+ port = config.get("server", {}).get("port", 8000)
151
+ auth_enabled = config.get("security", {}).get("auth", {}).get("enabled", False)
152
+ auth_methods = config.get("security", {}).get("auth", {}).get("methods", [])
153
+
154
+ # Create test client with correct protocol
155
+ protocol = "https" if config.get("ssl", {}).get("enabled", False) else "http"
156
+ client = SecurityTestClient(base_url=f"{protocol}://localhost:{port}")
157
+ client.auth_enabled = auth_enabled
158
+ client.auth_methods = auth_methods
159
+ client.api_keys = config.get("security", {}).get("auth", {}).get("api_keys", {})
160
+
161
+ # For mTLS, override SSL context creation and change working directory
162
+ if config_name == "mtls":
163
+ client.create_ssl_context = client.create_ssl_context_for_mtls
164
+ # Ensure mTLS uses certificate auth
165
+ client.auth_methods = ["certificate"]
166
+ # Change to examples directory for mTLS tests
167
+ import os
168
+ os.chdir(self.project_root / "mcp_proxy_adapter" / "examples")
169
+
170
+ # Run tests
171
+ async with client:
172
+ # Test 1: Health check
173
+ result = await client.test_health()
174
+ results.append(result)
175
+
176
+ # Test 2: Command execution
177
+ result = await client.test_command_execution()
178
+ results.append(result)
179
+
180
+ # Test 3: Authentication (if enabled)
181
+ if auth_enabled:
182
+ result = await client.test_authentication()
183
+ results.append(result)
184
+
185
+ # Test 4: Negative authentication
186
+ result = await client.test_negative_authentication()
187
+ results.append(result)
188
+
189
+ # Test 5: Role-based access
190
+ result = await client.test_role_based_access(client.base_url, "api_key")
191
+ results.append(result)
192
+
193
+ # Test 6: Role permissions
194
+ result = await client.test_role_permissions(client.base_url, "api_key")
195
+ results.append(result)
196
+
197
+ # Test 7: Multiple roles test
198
+ result = await client.test_multiple_roles(client.base_url, "api_key")
199
+ results.append(result)
200
+ else:
201
+ # Test 3: No authentication required
202
+ result = await client.test_no_auth_required()
203
+ results.append(result)
204
+
205
+ # Test 4: Negative auth (should fail)
206
+ result = await client.test_negative_authentication()
207
+ results.append(result)
208
+
209
+ except Exception as e:
210
+ results.append(TestResult(
211
+ test_name=f"{config_name}_client_error",
212
+ server_url=f"http://localhost:{port}",
213
+ auth_type="none",
214
+ success=False,
215
+ error_message=str(e)
216
+ ))
217
+
218
+ finally:
219
+ # Stop server
220
+ self.stop_server(config_name, process)
221
+
222
+ return results
223
+
224
+ async def run_all_tests(self):
225
+ """Run all security tests."""
226
+ print("šŸ”’ Starting Security Testing Suite")
227
+ print("=" * 50)
228
+
229
+ # Test configurations
230
+ configs = [
231
+ ("basic_http", "config_basic_http.json"),
232
+ ("http_token", "config_http_token.json"),
233
+ ("https", "config_https.json"),
234
+ ("https_token", "config_https_token.json"),
235
+ ("mtls", "config_mtls.json")
236
+ ]
237
+
238
+ total_tests = 0
239
+ passed_tests = 0
240
+
241
+ for config_name, config_file in configs:
242
+ config_path = self.configs_dir / config_file
243
+
244
+ if not config_path.exists():
245
+ print(f"āŒ Configuration not found: {config_path}")
246
+ continue
247
+
248
+ print(f"\nšŸ“‹ Testing {config_name.upper()} configuration")
249
+ print("-" * 30)
250
+
251
+ results = await self.test_server(config_name, config_path)
252
+
253
+ for result in results:
254
+ total_tests += 1
255
+ if result.success:
256
+ passed_tests += 1
257
+ print(f"āœ… {result.test_name}: PASS")
258
+ else:
259
+ print(f"āŒ {result.test_name}: FAIL - {result.error_message}")
260
+
261
+ self.test_results.extend(results)
262
+
263
+ # Print summary
264
+ print("\n" + "=" * 50)
265
+ print("šŸ“Š TEST SUMMARY")
266
+ print("=" * 50)
267
+ print(f"Total tests: {total_tests}")
268
+ print(f"Passed: {passed_tests}")
269
+ print(f"Failed: {total_tests - passed_tests}")
270
+ print(f"Success rate: {(passed_tests/total_tests*100):.1f}%" if total_tests > 0 else "N/A")
271
+
272
+ # Detailed results
273
+ print("\nšŸ“‹ DETAILED RESULTS")
274
+ print("-" * 30)
275
+ for result in self.test_results:
276
+ status = "āœ… PASS" if result.success else "āŒ FAIL"
277
+ print(f"{status} {result.test_name}")
278
+ if not result.success and result.error_message:
279
+ print(f" Error: {result.error_message}")
280
+
281
+ return passed_tests == total_tests
282
+
283
+
284
+ async def main():
285
+ """Main function."""
286
+ runner = SecurityTestRunner()
287
+
288
+ try:
289
+ success = await runner.run_all_tests()
290
+ sys.exit(0 if success else 1)
291
+ except KeyboardInterrupt:
292
+ print("\nāš ļø Testing interrupted by user")
293
+ sys.exit(1)
294
+ except Exception as e:
295
+ print(f"\nāŒ Testing failed: {e}")
296
+ sys.exit(1)
297
+
298
+
299
+ if __name__ == "__main__":
300
+ asyncio.run(main())