mcp-proxy-adapter 6.1.1__py3-none-any.whl ā 6.2.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.
- mcp_proxy_adapter/__main__.py +27 -7
- mcp_proxy_adapter/api/app.py +18 -7
- mcp_proxy_adapter/commands/ssl_setup_command.py +234 -351
- mcp_proxy_adapter/core/app_factory.py +87 -3
- mcp_proxy_adapter/core/app_runner.py +272 -0
- mcp_proxy_adapter/core/certificate_utils.py +291 -73
- mcp_proxy_adapter/core/client.py +574 -0
- mcp_proxy_adapter/core/client_manager.py +284 -0
- mcp_proxy_adapter/core/server_adapter.py +17 -80
- mcp_proxy_adapter/core/server_engine.py +5 -99
- mcp_proxy_adapter/core/ssl_utils.py +13 -12
- mcp_proxy_adapter/core/transport_manager.py +5 -5
- mcp_proxy_adapter/examples/__init__.py +16 -0
- mcp_proxy_adapter/examples/basic_framework/__init__.py +7 -0
- mcp_proxy_adapter/examples/basic_framework/commands/__init__.py +4 -0
- mcp_proxy_adapter/examples/basic_framework/hooks/__init__.py +4 -0
- mcp_proxy_adapter/examples/basic_framework/main.py +21 -40
- mcp_proxy_adapter/examples/commands/__init__.py +5 -1
- mcp_proxy_adapter/examples/create_certificates_simple.py +260 -75
- mcp_proxy_adapter/examples/debug_request_state.py +4 -36
- mcp_proxy_adapter/examples/debug_role_chain.py +2 -49
- mcp_proxy_adapter/examples/demo_client.py +0 -66
- mcp_proxy_adapter/examples/full_application/__init__.py +11 -0
- mcp_proxy_adapter/examples/full_application/commands/__init__.py +7 -0
- mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +0 -19
- mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +0 -16
- mcp_proxy_adapter/examples/full_application/hooks/__init__.py +7 -0
- mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +0 -22
- mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +0 -24
- mcp_proxy_adapter/examples/full_application/main.py +65 -44
- mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +154 -0
- mcp_proxy_adapter/examples/generate_all_certificates.py +0 -67
- mcp_proxy_adapter/examples/generate_certificates.py +0 -15
- mcp_proxy_adapter/examples/generate_certificates_and_tokens.py +369 -0
- mcp_proxy_adapter/examples/generate_test_configs.py +204 -0
- mcp_proxy_adapter/examples/proxy_registration_example.py +3 -70
- mcp_proxy_adapter/examples/run_example.py +1 -23
- mcp_proxy_adapter/examples/run_security_tests.py +2 -60
- mcp_proxy_adapter/examples/run_security_tests_fixed.py +0 -53
- mcp_proxy_adapter/examples/security_test_client.py +18 -123
- mcp_proxy_adapter/examples/setup_test_environment.py +179 -0
- mcp_proxy_adapter/examples/test_config.py +148 -0
- mcp_proxy_adapter/examples/test_config_generator.py +1 -25
- mcp_proxy_adapter/examples/test_examples.py +4 -67
- mcp_proxy_adapter/examples/universal_client.py +154 -162
- mcp_proxy_adapter/main.py +51 -161
- mcp_proxy_adapter/version.py +1 -1
- mcp_proxy_adapter-6.2.1.dist-info/METADATA +676 -0
- mcp_proxy_adapter-6.2.1.dist-info/RECORD +119 -0
- mcp_proxy_adapter/docs/EN/TROUBLESHOOTING.md +0 -285
- mcp_proxy_adapter/docs/RU/TROUBLESHOOTING.md +0 -285
- mcp_proxy_adapter/examples/README.md +0 -257
- mcp_proxy_adapter/examples/README_EN.md +0 -258
- mcp_proxy_adapter/examples/SECURITY_TESTING.md +0 -455
- mcp_proxy_adapter/examples/basic_framework/configs/http_auth.json +0 -37
- mcp_proxy_adapter/examples/basic_framework/configs/http_simple.json +0 -23
- mcp_proxy_adapter/examples/basic_framework/configs/https_auth.json +0 -43
- mcp_proxy_adapter/examples/basic_framework/configs/https_no_protocol_middleware.json +0 -36
- mcp_proxy_adapter/examples/basic_framework/configs/https_simple.json +0 -29
- mcp_proxy_adapter/examples/basic_framework/configs/mtls_no_protocol_middleware.json +0 -34
- mcp_proxy_adapter/examples/basic_framework/configs/mtls_no_roles.json +0 -39
- mcp_proxy_adapter/examples/basic_framework/configs/mtls_simple.json +0 -35
- mcp_proxy_adapter/examples/basic_framework/configs/mtls_with_roles.json +0 -45
- mcp_proxy_adapter/examples/basic_framework/roles.json +0 -21
- mcp_proxy_adapter/examples/cert_config.json +0 -9
- mcp_proxy_adapter/examples/certs/admin.crt +0 -32
- mcp_proxy_adapter/examples/certs/admin.key +0 -52
- mcp_proxy_adapter/examples/certs/admin_cert.pem +0 -21
- mcp_proxy_adapter/examples/certs/admin_key.pem +0 -28
- mcp_proxy_adapter/examples/certs/ca_cert.pem +0 -23
- mcp_proxy_adapter/examples/certs/ca_cert.srl +0 -1
- mcp_proxy_adapter/examples/certs/ca_key.pem +0 -28
- mcp_proxy_adapter/examples/certs/cert_config.json +0 -9
- mcp_proxy_adapter/examples/certs/client.crt +0 -32
- mcp_proxy_adapter/examples/certs/client.key +0 -52
- mcp_proxy_adapter/examples/certs/client_admin.crt +0 -32
- mcp_proxy_adapter/examples/certs/client_admin.key +0 -52
- mcp_proxy_adapter/examples/certs/client_user.crt +0 -32
- mcp_proxy_adapter/examples/certs/client_user.key +0 -52
- mcp_proxy_adapter/examples/certs/guest_cert.pem +0 -21
- mcp_proxy_adapter/examples/certs/guest_key.pem +0 -28
- mcp_proxy_adapter/examples/certs/mcp_proxy_adapter_ca_ca.crt +0 -23
- mcp_proxy_adapter/examples/certs/proxy_cert.pem +0 -21
- mcp_proxy_adapter/examples/certs/proxy_key.pem +0 -28
- mcp_proxy_adapter/examples/certs/readonly.crt +0 -32
- mcp_proxy_adapter/examples/certs/readonly.key +0 -52
- mcp_proxy_adapter/examples/certs/readonly_cert.pem +0 -21
- mcp_proxy_adapter/examples/certs/readonly_key.pem +0 -28
- mcp_proxy_adapter/examples/certs/server.crt +0 -32
- mcp_proxy_adapter/examples/certs/server.key +0 -52
- mcp_proxy_adapter/examples/certs/server_cert.pem +0 -32
- mcp_proxy_adapter/examples/certs/server_key.pem +0 -52
- mcp_proxy_adapter/examples/certs/test_ca_ca.crt +0 -20
- mcp_proxy_adapter/examples/certs/user.crt +0 -32
- mcp_proxy_adapter/examples/certs/user.key +0 -52
- mcp_proxy_adapter/examples/certs/user_cert.pem +0 -21
- mcp_proxy_adapter/examples/certs/user_key.pem +0 -28
- mcp_proxy_adapter/examples/client_configs/api_key_client.json +0 -13
- mcp_proxy_adapter/examples/client_configs/basic_auth_client.json +0 -13
- mcp_proxy_adapter/examples/client_configs/certificate_client.json +0 -22
- mcp_proxy_adapter/examples/client_configs/jwt_client.json +0 -15
- mcp_proxy_adapter/examples/client_configs/no_auth_client.json +0 -9
- mcp_proxy_adapter/examples/full_application/configs/http_auth.json +0 -37
- mcp_proxy_adapter/examples/full_application/configs/http_simple.json +0 -23
- mcp_proxy_adapter/examples/full_application/configs/https_auth.json +0 -39
- mcp_proxy_adapter/examples/full_application/configs/https_simple.json +0 -25
- mcp_proxy_adapter/examples/full_application/configs/mtls_no_roles.json +0 -39
- mcp_proxy_adapter/examples/full_application/configs/mtls_with_roles.json +0 -45
- mcp_proxy_adapter/examples/full_application/roles.json +0 -21
- mcp_proxy_adapter/examples/keys/ca_key.pem +0 -28
- mcp_proxy_adapter/examples/keys/mcp_proxy_adapter_ca_ca.key +0 -28
- mcp_proxy_adapter/examples/keys/test_ca_ca.key +0 -28
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log +0 -220
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.1 +0 -1
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.2 +0 -1
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.3 +0 -1
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.4 +0 -1
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.5 +0 -1
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log +0 -220
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.1 +0 -1
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.2 +0 -1
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.3 +0 -1
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.4 +0 -1
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.5 +0 -1
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log +0 -2
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.1 +0 -1
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.2 +0 -1
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.3 +0 -1
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.4 +0 -1
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.5 +0 -1
- mcp_proxy_adapter/examples/roles.json +0 -38
- mcp_proxy_adapter/examples/server_configs/config_basic_http.json +0 -204
- mcp_proxy_adapter/examples/server_configs/config_http_token.json +0 -238
- mcp_proxy_adapter/examples/server_configs/config_https.json +0 -215
- mcp_proxy_adapter/examples/server_configs/config_https_token.json +0 -231
- mcp_proxy_adapter/examples/server_configs/config_mtls.json +0 -215
- mcp_proxy_adapter/examples/server_configs/config_proxy_registration.json +0 -250
- mcp_proxy_adapter/examples/server_configs/config_simple.json +0 -46
- mcp_proxy_adapter/examples/server_configs/roles.json +0 -38
- mcp_proxy_adapter/utils/config_generator.py +0 -727
- mcp_proxy_adapter-6.1.1.dist-info/METADATA +0 -205
- mcp_proxy_adapter-6.1.1.dist-info/RECORD +0 -197
- mcp_proxy_adapter-6.1.1.dist-info/entry_points.txt +0 -2
- mcp_proxy_adapter-6.1.1.dist-info/licenses/LICENSE +0 -21
- {mcp_proxy_adapter-6.1.1.dist-info ā mcp_proxy_adapter-6.2.1.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-6.1.1.dist-info ā mcp_proxy_adapter-6.2.1.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())
|