mcp-security-framework 1.1.0__py3-none-any.whl → 1.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.
- mcp_security_framework/__init__.py +26 -15
- mcp_security_framework/cli/__init__.py +1 -1
- mcp_security_framework/cli/cert_cli.py +233 -197
- mcp_security_framework/cli/security_cli.py +324 -234
- mcp_security_framework/constants.py +21 -27
- mcp_security_framework/core/auth_manager.py +41 -22
- mcp_security_framework/core/cert_manager.py +210 -147
- mcp_security_framework/core/permission_manager.py +9 -9
- mcp_security_framework/core/rate_limiter.py +2 -2
- mcp_security_framework/core/security_manager.py +284 -229
- mcp_security_framework/examples/__init__.py +6 -0
- mcp_security_framework/examples/comprehensive_example.py +349 -279
- mcp_security_framework/examples/django_example.py +247 -206
- mcp_security_framework/examples/fastapi_example.py +315 -283
- mcp_security_framework/examples/flask_example.py +274 -203
- mcp_security_framework/examples/gateway_example.py +304 -237
- mcp_security_framework/examples/microservice_example.py +258 -189
- mcp_security_framework/examples/standalone_example.py +255 -230
- mcp_security_framework/examples/test_all_examples.py +151 -135
- mcp_security_framework/middleware/__init__.py +46 -55
- mcp_security_framework/middleware/auth_middleware.py +62 -63
- mcp_security_framework/middleware/fastapi_auth_middleware.py +119 -118
- mcp_security_framework/middleware/fastapi_middleware.py +156 -148
- mcp_security_framework/middleware/flask_auth_middleware.py +160 -147
- mcp_security_framework/middleware/flask_middleware.py +183 -157
- mcp_security_framework/middleware/mtls_middleware.py +106 -117
- mcp_security_framework/middleware/rate_limit_middleware.py +105 -101
- mcp_security_framework/middleware/security_middleware.py +109 -124
- mcp_security_framework/schemas/config.py +2 -1
- mcp_security_framework/schemas/models.py +18 -6
- mcp_security_framework/utils/cert_utils.py +14 -8
- mcp_security_framework/utils/datetime_compat.py +116 -0
- {mcp_security_framework-1.1.0.dist-info → mcp_security_framework-1.1.1.dist-info}/METADATA +2 -1
- mcp_security_framework-1.1.1.dist-info/RECORD +84 -0
- tests/conftest.py +63 -66
- tests/test_cli/test_cert_cli.py +184 -146
- tests/test_cli/test_security_cli.py +274 -247
- tests/test_core/test_cert_manager.py +24 -10
- tests/test_core/test_security_manager.py +2 -2
- tests/test_examples/test_comprehensive_example.py +190 -137
- tests/test_examples/test_fastapi_example.py +124 -101
- tests/test_examples/test_flask_example.py +124 -101
- tests/test_examples/test_standalone_example.py +73 -80
- tests/test_integration/test_auth_flow.py +213 -197
- tests/test_integration/test_certificate_flow.py +180 -149
- tests/test_integration/test_fastapi_integration.py +108 -111
- tests/test_integration/test_flask_integration.py +141 -140
- tests/test_integration/test_standalone_integration.py +290 -259
- tests/test_middleware/test_fastapi_auth_middleware.py +195 -174
- tests/test_middleware/test_fastapi_middleware.py +147 -132
- tests/test_middleware/test_flask_auth_middleware.py +260 -202
- tests/test_middleware/test_flask_middleware.py +201 -179
- tests/test_middleware/test_security_middleware.py +145 -130
- tests/test_utils/test_datetime_compat.py +147 -0
- mcp_security_framework-1.1.0.dist-info/RECORD +0 -82
- {mcp_security_framework-1.1.0.dist-info → mcp_security_framework-1.1.1.dist-info}/WHEEL +0 -0
- {mcp_security_framework-1.1.0.dist-info → mcp_security_framework-1.1.1.dist-info}/entry_points.txt +0 -0
- {mcp_security_framework-1.1.0.dist-info → mcp_security_framework-1.1.1.dist-info}/top_level.txt +0 -0
@@ -14,25 +14,25 @@ Version: 1.0.0
|
|
14
14
|
License: MIT
|
15
15
|
"""
|
16
16
|
|
17
|
-
import os
|
18
|
-
import sys
|
19
17
|
import json
|
18
|
+
import os
|
20
19
|
import shutil
|
21
20
|
import subprocess
|
21
|
+
import sys
|
22
22
|
import tempfile
|
23
|
-
from pathlib import Path
|
24
|
-
from typing import Dict, List, Tuple, Optional
|
25
|
-
from datetime import datetime
|
26
23
|
import traceback
|
24
|
+
from datetime import datetime
|
25
|
+
from pathlib import Path
|
26
|
+
from typing import Dict, List, Optional, Tuple
|
27
27
|
|
28
28
|
|
29
29
|
class ExampleTester:
|
30
30
|
"""Test runner for all MCP Security Framework examples."""
|
31
|
-
|
31
|
+
|
32
32
|
def __init__(self, test_dir: Optional[str] = None, install_deps: bool = True):
|
33
33
|
"""
|
34
34
|
Initialize the example tester.
|
35
|
-
|
35
|
+
|
36
36
|
Args:
|
37
37
|
test_dir: Directory for test environment. If None, creates temporary directory.
|
38
38
|
install_deps: Whether to install missing dependencies automatically.
|
@@ -42,79 +42,74 @@ class ExampleTester:
|
|
42
42
|
self.results: Dict[str, Dict] = {}
|
43
43
|
self.errors: List[str] = []
|
44
44
|
self.warnings: List[str] = []
|
45
|
-
|
45
|
+
|
46
46
|
# Example files to test
|
47
47
|
self.examples = [
|
48
48
|
"standalone_example.py",
|
49
|
-
"fastapi_example.py",
|
49
|
+
"fastapi_example.py",
|
50
50
|
"flask_example.py",
|
51
51
|
"django_example.py",
|
52
52
|
"gateway_example.py",
|
53
53
|
"microservice_example.py",
|
54
|
-
"comprehensive_example.py"
|
54
|
+
"comprehensive_example.py",
|
55
55
|
]
|
56
|
-
|
56
|
+
|
57
57
|
# Required dependencies for examples
|
58
58
|
self.required_deps = {
|
59
59
|
"django_example.py": ["django"],
|
60
60
|
"gateway_example.py": ["aiohttp"],
|
61
|
-
"microservice_example.py": ["aiohttp"]
|
61
|
+
"microservice_example.py": ["aiohttp"],
|
62
62
|
}
|
63
|
-
|
63
|
+
|
64
64
|
print(f"🔧 Test environment: {self.test_dir}")
|
65
65
|
if not self.install_deps:
|
66
66
|
print("⚠️ Automatic dependency installation disabled")
|
67
|
-
|
67
|
+
|
68
68
|
def setup_environment(self) -> bool:
|
69
69
|
"""
|
70
70
|
Set up the test environment with all required files and dependencies.
|
71
|
-
|
71
|
+
|
72
72
|
Returns:
|
73
73
|
bool: True if setup was successful
|
74
74
|
"""
|
75
75
|
print("\n📁 Setting up test environment...")
|
76
|
-
|
76
|
+
|
77
77
|
try:
|
78
78
|
# Create required directories
|
79
79
|
self._create_directories()
|
80
|
-
|
80
|
+
|
81
81
|
# Create configuration files
|
82
82
|
self._create_config_files()
|
83
|
-
|
83
|
+
|
84
84
|
# Create dummy certificates
|
85
85
|
self._create_certificates()
|
86
|
-
|
86
|
+
|
87
87
|
# Check dependencies
|
88
88
|
self._check_dependencies()
|
89
|
-
|
89
|
+
|
90
90
|
# Install missing dependencies if enabled
|
91
91
|
if self.install_deps:
|
92
92
|
if not self._install_missing_dependencies():
|
93
93
|
print("❌ Failed to install missing dependencies")
|
94
94
|
return False
|
95
|
-
|
95
|
+
|
96
96
|
print("✅ Environment setup completed successfully")
|
97
97
|
return True
|
98
|
-
|
98
|
+
|
99
99
|
except Exception as e:
|
100
100
|
self.errors.append(f"Environment setup failed: {str(e)}")
|
101
101
|
print(f"❌ Environment setup failed: {str(e)}")
|
102
102
|
return False
|
103
|
-
|
103
|
+
|
104
104
|
def _create_directories(self):
|
105
105
|
"""Create required directories for examples."""
|
106
|
-
directories = [
|
107
|
-
|
108
|
-
"config",
|
109
|
-
"logs",
|
110
|
-
"keys"
|
111
|
-
]
|
112
|
-
|
106
|
+
directories = ["certs", "config", "logs", "keys"]
|
107
|
+
|
113
108
|
for directory in directories:
|
114
109
|
dir_path = os.path.join(self.test_dir, directory)
|
115
110
|
os.makedirs(dir_path, exist_ok=True)
|
116
111
|
print(f" 📂 Created directory: {directory}")
|
117
|
-
|
112
|
+
|
118
113
|
def _create_config_files(self):
|
119
114
|
"""Create configuration files for examples."""
|
120
115
|
# Create roles configuration
|
@@ -123,58 +118,58 @@ class ExampleTester:
|
|
123
118
|
"admin": {
|
124
119
|
"description": "Administrator role",
|
125
120
|
"permissions": ["*"],
|
126
|
-
"parent_roles": []
|
121
|
+
"parent_roles": [],
|
127
122
|
},
|
128
123
|
"user": {
|
129
|
-
"description": "User role",
|
124
|
+
"description": "User role",
|
130
125
|
"permissions": ["read", "write"],
|
131
|
-
"parent_roles": []
|
126
|
+
"parent_roles": [],
|
132
127
|
},
|
133
128
|
"readonly": {
|
134
129
|
"description": "Read Only role",
|
135
130
|
"permissions": ["read"],
|
136
|
-
"parent_roles": []
|
137
|
-
}
|
131
|
+
"parent_roles": [],
|
132
|
+
},
|
138
133
|
}
|
139
134
|
}
|
140
|
-
|
135
|
+
|
141
136
|
roles_file = os.path.join(self.test_dir, "config", "roles.json")
|
142
|
-
with open(roles_file,
|
137
|
+
with open(roles_file, "w") as f:
|
143
138
|
json.dump(roles_config, f, indent=2)
|
144
|
-
|
139
|
+
|
145
140
|
print(f" 📄 Created roles configuration: config/roles.json")
|
146
|
-
|
141
|
+
|
147
142
|
def _create_certificates(self):
|
148
143
|
"""Create dummy SSL certificates for testing."""
|
149
144
|
certs_dir = os.path.join(self.test_dir, "certs")
|
150
|
-
|
145
|
+
|
151
146
|
# Server certificate
|
152
147
|
server_cert = os.path.join(certs_dir, "server.crt")
|
153
|
-
with open(server_cert,
|
148
|
+
with open(server_cert, "w") as f:
|
154
149
|
f.write("-----BEGIN CERTIFICATE-----\n")
|
155
150
|
f.write("DUMMY SERVER CERTIFICATE FOR TESTING\n")
|
156
151
|
f.write("-----END CERTIFICATE-----\n")
|
157
|
-
|
152
|
+
|
158
153
|
# Server private key
|
159
154
|
server_key = os.path.join(certs_dir, "server.key")
|
160
|
-
with open(server_key,
|
155
|
+
with open(server_key, "w") as f:
|
161
156
|
f.write("-----BEGIN PRIVATE KEY-----\n")
|
162
157
|
f.write("DUMMY SERVER PRIVATE KEY FOR TESTING\n")
|
163
158
|
f.write("-----END PRIVATE KEY-----\n")
|
164
|
-
|
159
|
+
|
165
160
|
# CA certificate
|
166
161
|
ca_cert = os.path.join(certs_dir, "ca.crt")
|
167
|
-
with open(ca_cert,
|
162
|
+
with open(ca_cert, "w") as f:
|
168
163
|
f.write("-----BEGIN CERTIFICATE-----\n")
|
169
164
|
f.write("DUMMY CA CERTIFICATE FOR TESTING\n")
|
170
165
|
f.write("-----END CERTIFICATE-----\n")
|
171
|
-
|
166
|
+
|
172
167
|
print(f" 🔐 Created dummy SSL certificates")
|
173
|
-
|
168
|
+
|
174
169
|
def _check_dependencies(self):
|
175
170
|
"""Check if required dependencies are available."""
|
176
171
|
print("\n🔍 Checking dependencies...")
|
177
|
-
|
172
|
+
|
178
173
|
for example, deps in self.required_deps.items():
|
179
174
|
missing_deps = []
|
180
175
|
for dep in deps:
|
@@ -182,22 +177,24 @@ class ExampleTester:
|
|
182
177
|
__import__(dep)
|
183
178
|
except ImportError:
|
184
179
|
missing_deps.append(dep)
|
185
|
-
|
180
|
+
|
186
181
|
if missing_deps:
|
187
|
-
self.warnings.append(
|
182
|
+
self.warnings.append(
|
183
|
+
f"Missing dependencies for {example}: {', '.join(missing_deps)}"
|
184
|
+
)
|
188
185
|
print(f" ⚠️ {example}: Missing {', '.join(missing_deps)}")
|
189
186
|
else:
|
190
187
|
print(f" ✅ {example}: All dependencies available")
|
191
|
-
|
188
|
+
|
192
189
|
def _install_missing_dependencies(self) -> bool:
|
193
190
|
"""
|
194
191
|
Install missing dependencies for examples.
|
195
|
-
|
192
|
+
|
196
193
|
Returns:
|
197
194
|
bool: True if installation was successful
|
198
195
|
"""
|
199
196
|
print("\n📦 Installing missing dependencies...")
|
200
|
-
|
197
|
+
|
201
198
|
# Collect all missing dependencies
|
202
199
|
all_missing_deps = set()
|
203
200
|
for example, deps in self.required_deps.items():
|
@@ -206,13 +203,13 @@ class ExampleTester:
|
|
206
203
|
__import__(dep)
|
207
204
|
except ImportError:
|
208
205
|
all_missing_deps.add(dep)
|
209
|
-
|
206
|
+
|
210
207
|
if not all_missing_deps:
|
211
208
|
print(" ✅ All dependencies are already available")
|
212
209
|
return True
|
213
|
-
|
210
|
+
|
214
211
|
print(f" 📥 Installing: {', '.join(all_missing_deps)}")
|
215
|
-
|
212
|
+
|
216
213
|
try:
|
217
214
|
# Install missing dependencies
|
218
215
|
for dep in all_missing_deps:
|
@@ -221,16 +218,16 @@ class ExampleTester:
|
|
221
218
|
[sys.executable, "-m", "pip", "install", "--user", dep],
|
222
219
|
capture_output=True,
|
223
220
|
text=True,
|
224
|
-
timeout=120
|
221
|
+
timeout=120,
|
225
222
|
)
|
226
|
-
|
223
|
+
|
227
224
|
if result.returncode == 0:
|
228
225
|
print(f" ✅ {dep} installed successfully")
|
229
226
|
else:
|
230
227
|
print(f" ❌ Failed to install {dep}: {result.stderr}")
|
231
228
|
self.errors.append(f"Failed to install {dep}: {result.stderr}")
|
232
229
|
return False
|
233
|
-
|
230
|
+
|
234
231
|
# Verify installation by importing in a new process
|
235
232
|
print("\n🔍 Verifying installations...")
|
236
233
|
for dep in all_missing_deps:
|
@@ -241,23 +238,23 @@ class ExampleTester:
|
|
241
238
|
[sys.executable, "-c", test_script],
|
242
239
|
capture_output=True,
|
243
240
|
text=True,
|
244
|
-
timeout=10
|
241
|
+
timeout=10,
|
245
242
|
)
|
246
|
-
|
243
|
+
|
247
244
|
if result.returncode == 0:
|
248
245
|
print(f" ✅ {dep} is now available")
|
249
246
|
else:
|
250
247
|
print(f" ❌ {dep} is still not available after installation")
|
251
248
|
self.errors.append(f"{dep} installation verification failed")
|
252
249
|
return False
|
253
|
-
|
250
|
+
|
254
251
|
except Exception as e:
|
255
252
|
print(f" ❌ {dep} verification failed: {str(e)}")
|
256
253
|
self.errors.append(f"{dep} verification failed: {str(e)}")
|
257
254
|
return False
|
258
|
-
|
255
|
+
|
259
256
|
return True
|
260
|
-
|
257
|
+
|
261
258
|
except subprocess.TimeoutExpired:
|
262
259
|
self.errors.append("Dependency installation timed out")
|
263
260
|
print(" ❌ Installation timed out")
|
@@ -266,19 +263,19 @@ class ExampleTester:
|
|
266
263
|
self.errors.append(f"Dependency installation failed: {str(e)}")
|
267
264
|
print(f" ❌ Installation failed: {str(e)}")
|
268
265
|
return False
|
269
|
-
|
266
|
+
|
270
267
|
def run_example(self, example_file: str) -> Dict:
|
271
268
|
"""
|
272
269
|
Run a single example and capture results.
|
273
|
-
|
270
|
+
|
274
271
|
Args:
|
275
272
|
example_file: Name of the example file to run
|
276
|
-
|
273
|
+
|
277
274
|
Returns:
|
278
275
|
Dict: Test results for the example
|
279
276
|
"""
|
280
277
|
print(f"\n🚀 Running {example_file}...")
|
281
|
-
|
278
|
+
|
282
279
|
result = {
|
283
280
|
"file": example_file,
|
284
281
|
"status": "unknown",
|
@@ -286,36 +283,36 @@ class ExampleTester:
|
|
286
283
|
"end_time": None,
|
287
284
|
"error": None,
|
288
285
|
"output": "",
|
289
|
-
"warnings": []
|
286
|
+
"warnings": [],
|
290
287
|
}
|
291
|
-
|
288
|
+
|
292
289
|
try:
|
293
290
|
# Change to test directory
|
294
291
|
original_cwd = os.getcwd()
|
295
292
|
os.chdir(self.test_dir)
|
296
|
-
|
293
|
+
|
297
294
|
# Get the full path to the example file
|
298
295
|
examples_dir = Path(__file__).parent
|
299
296
|
example_path = examples_dir / example_file
|
300
|
-
|
297
|
+
|
301
298
|
if not example_path.exists():
|
302
299
|
result["status"] = "error"
|
303
300
|
result["error"] = f"Example file not found: {example_path}"
|
304
301
|
return result
|
305
|
-
|
302
|
+
|
306
303
|
# Run the example
|
307
304
|
start_time = datetime.now()
|
308
305
|
process = subprocess.run(
|
309
306
|
[sys.executable, str(example_path)],
|
310
307
|
capture_output=True,
|
311
308
|
text=True,
|
312
|
-
timeout=30 # 30 second timeout
|
309
|
+
timeout=30, # 30 second timeout
|
313
310
|
)
|
314
311
|
end_time = datetime.now()
|
315
|
-
|
312
|
+
|
316
313
|
result["end_time"] = end_time
|
317
314
|
result["output"] = process.stdout + process.stderr
|
318
|
-
|
315
|
+
|
319
316
|
# Analyze results
|
320
317
|
if process.returncode == 0:
|
321
318
|
result["status"] = "success"
|
@@ -323,11 +320,11 @@ class ExampleTester:
|
|
323
320
|
else:
|
324
321
|
# Check if the example actually worked despite test failures
|
325
322
|
output_lower = result["output"].lower()
|
326
|
-
|
323
|
+
|
327
324
|
# Check for successful initialization and basic functionality
|
328
325
|
success_indicators = [
|
329
326
|
"security manager initialized successfully",
|
330
|
-
"api key authentication successful",
|
327
|
+
"api key authentication successful",
|
331
328
|
"rate limiting test completed",
|
332
329
|
"middleware setup",
|
333
330
|
"fastapi example",
|
@@ -335,35 +332,46 @@ class ExampleTester:
|
|
335
332
|
"django example",
|
336
333
|
"gateway example",
|
337
334
|
"microservice example",
|
338
|
-
"standalone example"
|
335
|
+
"standalone example",
|
339
336
|
]
|
340
|
-
|
341
|
-
has_success_indicators = any(
|
342
|
-
|
337
|
+
|
338
|
+
has_success_indicators = any(
|
339
|
+
indicator in output_lower for indicator in success_indicators
|
340
|
+
)
|
341
|
+
|
343
342
|
# Check if it's just a test assertion failure (not a real error)
|
344
|
-
is_test_failure =
|
345
|
-
|
343
|
+
is_test_failure = (
|
344
|
+
"assertionerror" in output_lower
|
345
|
+
or "test assertion failed" in output_lower
|
346
|
+
)
|
347
|
+
|
346
348
|
if has_success_indicators and is_test_failure:
|
347
349
|
result["status"] = "success"
|
348
|
-
result["warnings"].append(
|
349
|
-
|
350
|
+
result["warnings"].append(
|
351
|
+
"Test assertion failed but core functionality works"
|
352
|
+
)
|
353
|
+
print(
|
354
|
+
f" ✅ {example_file}: Success (core functionality works, test assertions failed)"
|
355
|
+
)
|
350
356
|
else:
|
351
357
|
result["status"] = "error"
|
352
358
|
result["error"] = f"Exit code: {process.returncode}"
|
353
|
-
print(
|
354
|
-
|
359
|
+
print(
|
360
|
+
f" ❌ {example_file}: Failed (exit code: {process.returncode})"
|
361
|
+
)
|
362
|
+
|
355
363
|
# Check for specific patterns in output
|
356
364
|
if "AssertionError" in result["output"]:
|
357
365
|
result["warnings"].append("Test assertion failed")
|
358
366
|
print(f" ⚠️ {example_file}: Test assertion failed")
|
359
|
-
|
367
|
+
|
360
368
|
if "ModuleNotFoundError" in result["output"]:
|
361
369
|
result["warnings"].append("Missing dependencies")
|
362
370
|
print(f" ⚠️ {example_file}: Missing dependencies")
|
363
|
-
|
371
|
+
|
364
372
|
# Restore original directory
|
365
373
|
os.chdir(original_cwd)
|
366
|
-
|
374
|
+
|
367
375
|
except subprocess.TimeoutExpired:
|
368
376
|
result["status"] = "timeout"
|
369
377
|
result["error"] = "Test timed out after 30 seconds"
|
@@ -372,28 +380,28 @@ class ExampleTester:
|
|
372
380
|
result["status"] = "error"
|
373
381
|
result["error"] = str(e)
|
374
382
|
print(f" ❌ {example_file}: Exception - {str(e)}")
|
375
|
-
|
383
|
+
|
376
384
|
return result
|
377
|
-
|
385
|
+
|
378
386
|
def run_all_examples(self) -> Dict:
|
379
387
|
"""
|
380
388
|
Run all examples and collect results.
|
381
|
-
|
389
|
+
|
382
390
|
Returns:
|
383
391
|
Dict: Summary of all test results
|
384
392
|
"""
|
385
393
|
print("\n🎯 Starting test run for all examples...")
|
386
|
-
|
394
|
+
|
387
395
|
for example in self.examples:
|
388
396
|
result = self.run_example(example)
|
389
397
|
self.results[example] = result
|
390
|
-
|
398
|
+
|
391
399
|
return self._generate_summary()
|
392
|
-
|
400
|
+
|
393
401
|
def _generate_summary(self) -> Dict:
|
394
402
|
"""
|
395
403
|
Generate a comprehensive test summary.
|
396
|
-
|
404
|
+
|
397
405
|
Returns:
|
398
406
|
Dict: Test summary with statistics
|
399
407
|
"""
|
@@ -401,7 +409,7 @@ class ExampleTester:
|
|
401
409
|
successful = sum(1 for r in self.results.values() if r["status"] == "success")
|
402
410
|
failed = sum(1 for r in self.results.values() if r["status"] == "error")
|
403
411
|
timeout = sum(1 for r in self.results.values() if r["status"] == "timeout")
|
404
|
-
|
412
|
+
|
405
413
|
summary = {
|
406
414
|
"total_examples": total,
|
407
415
|
"successful": successful,
|
@@ -410,22 +418,22 @@ class ExampleTester:
|
|
410
418
|
"success_rate": (successful / total * 100) if total > 0 else 0,
|
411
419
|
"results": self.results,
|
412
420
|
"errors": self.errors,
|
413
|
-
"warnings": self.warnings
|
421
|
+
"warnings": self.warnings,
|
414
422
|
}
|
415
|
-
|
423
|
+
|
416
424
|
return summary
|
417
|
-
|
425
|
+
|
418
426
|
def print_report(self, summary: Dict):
|
419
427
|
"""
|
420
428
|
Print a comprehensive test report.
|
421
|
-
|
429
|
+
|
422
430
|
Args:
|
423
431
|
summary: Test summary dictionary
|
424
432
|
"""
|
425
|
-
print("\n" + "="*80)
|
433
|
+
print("\n" + "=" * 80)
|
426
434
|
print("📊 MCP SECURITY FRAMEWORK EXAMPLES TEST REPORT")
|
427
|
-
print("="*80)
|
428
|
-
|
435
|
+
print("=" * 80)
|
436
|
+
|
429
437
|
# Overall statistics
|
430
438
|
print(f"\n📈 OVERALL STATISTICS:")
|
431
439
|
print(f" Total examples tested: {summary['total_examples']}")
|
@@ -433,53 +441,61 @@ class ExampleTester:
|
|
433
441
|
print(f" Failed: {summary['failed']} ❌")
|
434
442
|
print(f" Timeout: {summary['timeout']} ⏰")
|
435
443
|
print(f" Success rate: {summary['success_rate']:.1f}%")
|
436
|
-
|
444
|
+
|
437
445
|
# Detailed results
|
438
446
|
print(f"\n📋 DETAILED RESULTS:")
|
439
447
|
for example, result in summary["results"].items():
|
440
448
|
status_icon = {
|
441
449
|
"success": "✅",
|
442
|
-
"error": "❌",
|
450
|
+
"error": "❌",
|
443
451
|
"timeout": "⏰",
|
444
|
-
"unknown": "❓"
|
452
|
+
"unknown": "❓",
|
445
453
|
}.get(result["status"], "❓")
|
446
|
-
|
454
|
+
|
447
455
|
print(f" {status_icon} {example}")
|
448
|
-
|
456
|
+
|
449
457
|
if result["status"] == "error" and result["error"]:
|
450
458
|
print(f" Error: {result['error']}")
|
451
|
-
|
459
|
+
|
452
460
|
if result["warnings"]:
|
453
461
|
for warning in result["warnings"]:
|
454
462
|
print(f" ⚠️ {warning}")
|
455
|
-
|
463
|
+
|
456
464
|
# Errors and warnings
|
457
465
|
if summary["errors"]:
|
458
466
|
print(f"\n❌ ERRORS:")
|
459
467
|
for error in summary["errors"]:
|
460
468
|
print(f" • {error}")
|
461
|
-
|
469
|
+
|
462
470
|
if summary["warnings"]:
|
463
471
|
print(f"\n⚠️ WARNINGS:")
|
464
472
|
for warning in summary["warnings"]:
|
465
473
|
print(f" • {warning}")
|
466
|
-
|
474
|
+
|
467
475
|
# Recommendations
|
468
476
|
print(f"\n💡 RECOMMENDATIONS:")
|
469
477
|
if summary["failed"] > 0:
|
470
|
-
print(
|
471
|
-
|
478
|
+
print(
|
479
|
+
f" • {summary['failed']} examples failed - check dependencies and configuration"
|
480
|
+
)
|
481
|
+
|
472
482
|
if summary["timeout"] > 0:
|
473
|
-
print(
|
474
|
-
|
483
|
+
print(
|
484
|
+
f" • {summary['timeout']} examples timed out - consider increasing timeout"
|
485
|
+
)
|
486
|
+
|
475
487
|
if summary["success_rate"] < 50:
|
476
|
-
print(
|
488
|
+
print(
|
489
|
+
f" • Low success rate ({summary['success_rate']:.1f}%) - review setup and dependencies"
|
490
|
+
)
|
477
491
|
elif summary["success_rate"] >= 80:
|
478
|
-
print(
|
479
|
-
|
492
|
+
print(
|
493
|
+
f" • Excellent success rate ({summary['success_rate']:.1f}%) - examples are working well"
|
494
|
+
)
|
495
|
+
|
480
496
|
print(f"\n🔧 Test environment: {self.test_dir}")
|
481
|
-
print("="*80)
|
482
|
-
|
497
|
+
print("=" * 80)
|
498
|
+
|
483
499
|
def cleanup(self):
|
484
500
|
"""Clean up the test environment."""
|
485
501
|
if self.test_dir and os.path.exists(self.test_dir):
|
@@ -494,12 +510,12 @@ def main():
|
|
494
510
|
"""Main function to run all example tests."""
|
495
511
|
print("🚀 MCP Security Framework Examples Test Runner")
|
496
512
|
print("=" * 60)
|
497
|
-
|
513
|
+
|
498
514
|
# Parse command line arguments
|
499
515
|
keep_env = "--keep-env" in sys.argv
|
500
516
|
test_dir = None
|
501
517
|
install_deps = True
|
502
|
-
|
518
|
+
|
503
519
|
if "--test-dir" in sys.argv:
|
504
520
|
try:
|
505
521
|
idx = sys.argv.index("--test-dir")
|
@@ -507,30 +523,30 @@ def main():
|
|
507
523
|
except (IndexError, ValueError):
|
508
524
|
print("❌ Invalid --test-dir argument")
|
509
525
|
sys.exit(1)
|
510
|
-
|
526
|
+
|
511
527
|
if "--no-install-deps" in sys.argv:
|
512
528
|
install_deps = False
|
513
529
|
print("⚠️ Skipping automatic dependency installation.")
|
514
|
-
|
530
|
+
|
515
531
|
# Create tester
|
516
532
|
tester = ExampleTester(test_dir, install_deps)
|
517
|
-
|
533
|
+
|
518
534
|
try:
|
519
535
|
# Setup environment
|
520
536
|
if not tester.setup_environment():
|
521
537
|
print("❌ Failed to setup test environment")
|
522
538
|
sys.exit(1)
|
523
|
-
|
539
|
+
|
524
540
|
# Run all examples
|
525
541
|
summary = tester.run_all_examples()
|
526
|
-
|
542
|
+
|
527
543
|
# Print report
|
528
544
|
tester.print_report(summary)
|
529
|
-
|
545
|
+
|
530
546
|
# Cleanup
|
531
547
|
if not keep_env:
|
532
548
|
tester.cleanup()
|
533
|
-
|
549
|
+
|
534
550
|
# Exit with appropriate code
|
535
551
|
if summary["failed"] > 0:
|
536
552
|
print(f"\n❌ Test run completed with {summary['failed']} failures")
|
@@ -538,7 +554,7 @@ def main():
|
|
538
554
|
else:
|
539
555
|
print(f"\n✅ Test run completed successfully!")
|
540
556
|
sys.exit(0)
|
541
|
-
|
557
|
+
|
542
558
|
except KeyboardInterrupt:
|
543
559
|
print("\n⏹️ Test run interrupted by user")
|
544
560
|
if not keep_env:
|