mcp-security-framework 2.3.0__tar.gz → 2.3.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/PKG-INFO +5 -1
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/__init__.py +1 -1
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/core/cert_manager.py +23 -2
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/core/ssl_manager.py +52 -13
- mcp_security_framework-2.3.2/mcp_security_framework/examples/tls_certificate_regressions_example.py +1197 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/middleware/fastapi_middleware.py +1 -1
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/schemas/config.py +47 -13
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework.egg-info/PKG-INFO +5 -1
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework.egg-info/SOURCES.txt +1 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework.egg-info/requires.txt +4 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/pyproject.toml +6 -2
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/LICENSE +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/README.md +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/cli/__init__.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/cli/cert_cli.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/cli/security_cli.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/constants.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/core/__init__.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/core/adapter_wrapper.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/core/audit_logger.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/core/auth_manager.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/core/permission_manager.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/core/rate_limiter.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/core/security_adapter.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/core/security_manager.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/examples/__init__.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/examples/adapter_example.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/examples/auth_methods_example.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/examples/cert_inspection_example.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/examples/certificate_roles_example.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/examples/comprehensive_example.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/examples/crl_retrieval_example.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/examples/fastapi_example.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/examples/flask_example.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/examples/gateway_example.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/examples/microservice_example.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/examples/protected_keys_example.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/examples/standalone_example.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/middleware/__init__.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/middleware/auth_middleware.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/middleware/fastapi_auth_middleware.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/middleware/flask_auth_middleware.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/middleware/flask_middleware.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/middleware/mtls_middleware.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/middleware/rate_limit_middleware.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/middleware/security_middleware.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/schemas/__init__.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/schemas/models.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/schemas/operation_context.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/schemas/responses.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/utils/__init__.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/utils/cert_utils/__init__.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/utils/cert_utils/chain.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/utils/cert_utils/errors.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/utils/cert_utils/inspection.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/utils/cert_utils/parsing.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/utils/cert_utils/revocation.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/utils/cert_utils/roles.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/utils/crl_client.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/utils/crypto_utils.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/utils/datetime_compat.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework/utils/validation_utils.py +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework.egg-info/dependency_links.txt +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework.egg-info/entry_points.txt +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/mcp_security_framework.egg-info/top_level.txt +0 -0
- {mcp_security_framework-2.3.0 → mcp_security_framework-2.3.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mcp-security-framework
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.2
|
|
4
4
|
Summary: Universal security framework for microservices with SSL/TLS, authentication, authorization, and rate limiting. Added extensible security adapter system for custom operation validation, structured audit logging, and operation context support. Comprehensive test coverage with 1104+ tests. Fully backward compatible. Requires cryptography>=42.0.0 for certificate operations.
|
|
5
5
|
Author-email: Vasiliy Zdanovskiy <vasilyvz@gmail.com>
|
|
6
6
|
Maintainer-email: Vasiliy Zdanovskiy <vasilyvz@gmail.com>
|
|
@@ -60,6 +60,10 @@ Requires-Dist: pytest>=7.0.0; extra == "test"
|
|
|
60
60
|
Requires-Dist: pytest-cov>=4.0.0; extra == "test"
|
|
61
61
|
Requires-Dist: pytest-asyncio>=0.21.0; extra == "test"
|
|
62
62
|
Requires-Dist: pytest-mock>=3.8.0; extra == "test"
|
|
63
|
+
Requires-Dist: fastapi>=0.68.0; extra == "test"
|
|
64
|
+
Requires-Dist: uvicorn[standard]>=0.15.0; extra == "test"
|
|
65
|
+
Requires-Dist: flask>=2.0.0; extra == "test"
|
|
66
|
+
Requires-Dist: flask-cors>=3.0.0; extra == "test"
|
|
63
67
|
Requires-Dist: httpx>=0.24.0; extra == "test"
|
|
64
68
|
Dynamic: license-file
|
|
65
69
|
|
|
@@ -27,6 +27,7 @@ Version: 1.0.0
|
|
|
27
27
|
License: MIT
|
|
28
28
|
"""
|
|
29
29
|
|
|
30
|
+
import ipaddress
|
|
30
31
|
import logging
|
|
31
32
|
import os
|
|
32
33
|
from datetime import datetime, timedelta, timezone
|
|
@@ -248,6 +249,14 @@ def _certificate_type_of(certificate) -> CertificateType:
|
|
|
248
249
|
return CertificateType.CLIENT
|
|
249
250
|
|
|
250
251
|
|
|
252
|
+
def _subject_alt_name(value: str) -> x509.GeneralName:
|
|
253
|
+
"""Return the correct SAN type for a DNS name or IP literal string."""
|
|
254
|
+
try:
|
|
255
|
+
return x509.IPAddress(ipaddress.ip_address(value))
|
|
256
|
+
except ValueError:
|
|
257
|
+
return x509.DNSName(value)
|
|
258
|
+
|
|
259
|
+
|
|
251
260
|
class CertificateManager:
|
|
252
261
|
"""
|
|
253
262
|
Certificate Management Class
|
|
@@ -1023,6 +1032,10 @@ class CertificateManager:
|
|
|
1023
1032
|
x509.SubjectKeyIdentifier.from_public_key(private_key.public_key()),
|
|
1024
1033
|
critical=False,
|
|
1025
1034
|
)
|
|
1035
|
+
builder = builder.add_extension(
|
|
1036
|
+
x509.AuthorityKeyIdentifier.from_issuer_public_key(ca_key.public_key()),
|
|
1037
|
+
critical=False,
|
|
1038
|
+
)
|
|
1026
1039
|
|
|
1027
1040
|
# Add roles and permissions to certificate extensions
|
|
1028
1041
|
# Validate and normalize roles using CertificateRole enum
|
|
@@ -1333,11 +1346,15 @@ class CertificateManager:
|
|
|
1333
1346
|
x509.SubjectKeyIdentifier.from_public_key(private_key.public_key()),
|
|
1334
1347
|
critical=False,
|
|
1335
1348
|
)
|
|
1349
|
+
builder = builder.add_extension(
|
|
1350
|
+
x509.AuthorityKeyIdentifier.from_issuer_public_key(ca_key.public_key()),
|
|
1351
|
+
critical=False,
|
|
1352
|
+
)
|
|
1336
1353
|
|
|
1337
1354
|
# Add Subject Alternative Name (SAN) if provided
|
|
1338
1355
|
if server_config.subject_alt_names:
|
|
1339
1356
|
san_names = [
|
|
1340
|
-
|
|
1357
|
+
_subject_alt_name(name) for name in server_config.subject_alt_names
|
|
1341
1358
|
]
|
|
1342
1359
|
|
|
1343
1360
|
builder = builder.add_extension(
|
|
@@ -2112,8 +2129,12 @@ class CertificateManager:
|
|
|
2112
2129
|
)
|
|
2113
2130
|
|
|
2114
2131
|
# Add Subject Alternative Name extension if common name looks like a domain
|
|
2132
|
+
# or an IP literal. Reuse _subject_alt_name so an IP-literal common_name
|
|
2133
|
+
# (e.g. "127.0.0.1") is encoded as x509.IPAddress, never x509.DNSName.
|
|
2115
2134
|
if "." in common_name and not common_name.startswith("*"):
|
|
2116
|
-
san_extension = x509.SubjectAlternativeName(
|
|
2135
|
+
san_extension = x509.SubjectAlternativeName(
|
|
2136
|
+
[_subject_alt_name(common_name)]
|
|
2137
|
+
)
|
|
2117
2138
|
csr_builder = csr_builder.add_extension(san_extension, critical=False)
|
|
2118
2139
|
|
|
2119
2140
|
# Build and sign CSR
|
|
@@ -432,9 +432,27 @@ class SSLManager:
|
|
|
432
432
|
if key_data is None:
|
|
433
433
|
client_key_file = client_key_file or self.config.client_key_file
|
|
434
434
|
ca_cert_file = ca_cert_file or self.config.ca_cert_file
|
|
435
|
-
verify_mode = verify_mode or self.config.verify_mode
|
|
436
435
|
min_version = min_version or self.config.min_tls_version
|
|
437
436
|
|
|
437
|
+
# Validate client_cert_file/client_key_file compatibility (bug B6),
|
|
438
|
+
# on the EFFECTIVE values resolved just above (call-site argument or
|
|
439
|
+
# config fallback). Skipped when cert_data/key_data supply the pair
|
|
440
|
+
# instead: cert_data/key_data are already validated all-or-none
|
|
441
|
+
# above, and both loading branches below try cert_data/key_data
|
|
442
|
+
# FIRST, falling back to client_cert_file/client_key_file only when
|
|
443
|
+
# cert_data/key_data are absent — so a lone client_cert_file/
|
|
444
|
+
# client_key_file value is never actually used together with
|
|
445
|
+
# cert_data/key_data. Raised here, once, before the cache lookup
|
|
446
|
+
# and before any context is built, so it covers both the CERT_NONE
|
|
447
|
+
# branch and the verifying branch below and no partially
|
|
448
|
+
# configured context is ever cached.
|
|
449
|
+
if cert_data is None and key_data is None:
|
|
450
|
+
if bool(client_cert_file) != bool(client_key_file):
|
|
451
|
+
raise SSLConfigurationError(
|
|
452
|
+
"client_cert_file and client_key_file must be provided "
|
|
453
|
+
"together; got one without the other"
|
|
454
|
+
)
|
|
455
|
+
|
|
438
456
|
# Validate that material_from_memory promises are kept
|
|
439
457
|
if cert_data is None and self.config.material_from_memory:
|
|
440
458
|
raise SSLConfigurationError(
|
|
@@ -447,17 +465,13 @@ class SSLManager:
|
|
|
447
465
|
"provide key_data as PEM bytes or set material_from_memory=False"
|
|
448
466
|
)
|
|
449
467
|
|
|
450
|
-
#
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
if cache_key in self._contexts:
|
|
458
|
-
return self._contexts[cache_key]
|
|
459
|
-
|
|
460
|
-
# Determine actual verify mode (explicit parameter takes precedence)
|
|
468
|
+
# Determine actual verify mode. Precedence (bug B5):
|
|
469
|
+
# 1) an explicit call-site verify_mode argument
|
|
470
|
+
# 2) self.config.verify is False -> CERT_NONE
|
|
471
|
+
# 3) self.config.verify_mode
|
|
472
|
+
# NOTE: verify_mode must NOT be defaulted from self.config.verify_mode
|
|
473
|
+
# before this check runs, otherwise it is never falsy here and
|
|
474
|
+
# self.config.verify is never consulted (bug B5's root cause).
|
|
461
475
|
actual_verify_mode = (
|
|
462
476
|
verify_mode
|
|
463
477
|
if verify_mode
|
|
@@ -466,6 +480,30 @@ class SSLManager:
|
|
|
466
480
|
)
|
|
467
481
|
)
|
|
468
482
|
|
|
483
|
+
# Effective check_hostname actually applied to the context: always
|
|
484
|
+
# False when verification is disabled (ssl refuses check_hostname=True
|
|
485
|
+
# with CERT_NONE), otherwise the configured value (bug B1).
|
|
486
|
+
effective_check_hostname = (
|
|
487
|
+
False
|
|
488
|
+
if actual_verify_mode == "CERT_NONE"
|
|
489
|
+
else bool(self.config.check_hostname)
|
|
490
|
+
)
|
|
491
|
+
|
|
492
|
+
# Check cache (only for file-based contexts). The key must reflect
|
|
493
|
+
# the RESOLVED verify mode and the check_hostname actually applied
|
|
494
|
+
# to the context (bugs B1, B5c) so that mutating
|
|
495
|
+
# manager.config.check_hostname or manager.config.verify between
|
|
496
|
+
# two calls on the same instance, with the raw inputs otherwise
|
|
497
|
+
# unchanged, never returns a context built for different values.
|
|
498
|
+
cache_key = None
|
|
499
|
+
if cert_data is None and key_data is None:
|
|
500
|
+
cache_key = (
|
|
501
|
+
f"client_{ca_cert_file}_{client_cert_file}_{client_key_file}"
|
|
502
|
+
f"_{actual_verify_mode}_{effective_check_hostname}_{min_version}"
|
|
503
|
+
)
|
|
504
|
+
if cache_key in self._contexts:
|
|
505
|
+
return self._contexts[cache_key]
|
|
506
|
+
|
|
469
507
|
# Check if verification is disabled
|
|
470
508
|
if actual_verify_mode == "CERT_NONE":
|
|
471
509
|
# For disabled verification or CERT_NONE, create a basic context
|
|
@@ -518,6 +556,7 @@ class SSLManager:
|
|
|
518
556
|
else:
|
|
519
557
|
# For other modes, use default context with verification
|
|
520
558
|
context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
|
|
559
|
+
context.check_hostname = effective_check_hostname
|
|
521
560
|
context.verify_mode = self._get_verify_mode(actual_verify_mode)
|
|
522
561
|
|
|
523
562
|
# Set minimum TLS version
|
|
@@ -582,7 +621,7 @@ class SSLManager:
|
|
|
582
621
|
"ca_cert_file": ca_cert_file,
|
|
583
622
|
"client_cert_file": client_cert_file,
|
|
584
623
|
"cert_data_provided": cert_data is not None,
|
|
585
|
-
"verify_mode":
|
|
624
|
+
"verify_mode": actual_verify_mode,
|
|
586
625
|
"min_version": min_version,
|
|
587
626
|
},
|
|
588
627
|
)
|