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