bizteamai-smcp 1.13.1__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.
Files changed (29) hide show
  1. bizteamai_smcp-1.13.1/PKG-INFO +117 -0
  2. bizteamai_smcp-1.13.1/README.md +89 -0
  3. bizteamai_smcp-1.13.1/bizteamai_smcp.egg-info/PKG-INFO +117 -0
  4. bizteamai_smcp-1.13.1/bizteamai_smcp.egg-info/SOURCES.txt +27 -0
  5. bizteamai_smcp-1.13.1/bizteamai_smcp.egg-info/dependency_links.txt +1 -0
  6. bizteamai_smcp-1.13.1/bizteamai_smcp.egg-info/entry_points.txt +3 -0
  7. bizteamai_smcp-1.13.1/bizteamai_smcp.egg-info/requires.txt +11 -0
  8. bizteamai_smcp-1.13.1/bizteamai_smcp.egg-info/top_level.txt +1 -0
  9. bizteamai_smcp-1.13.1/pyproject.toml +53 -0
  10. bizteamai_smcp-1.13.1/setup.cfg +4 -0
  11. bizteamai_smcp-1.13.1/smcp/__init__.py +29 -0
  12. bizteamai_smcp-1.13.1/smcp/allowlist.py +169 -0
  13. bizteamai_smcp-1.13.1/smcp/app_wrapper.py +216 -0
  14. bizteamai_smcp-1.13.1/smcp/cli/__init__.py +3 -0
  15. bizteamai_smcp-1.13.1/smcp/cli/approve.py +261 -0
  16. bizteamai_smcp-1.13.1/smcp/cli/gen_key.py +73 -0
  17. bizteamai_smcp-1.13.1/smcp/cli/mkcert.py +327 -0
  18. bizteamai_smcp-1.13.1/smcp/cli/revoke.py +73 -0
  19. bizteamai_smcp-1.13.1/smcp/confirm.py +262 -0
  20. bizteamai_smcp-1.13.1/smcp/cpu.py +67 -0
  21. bizteamai_smcp-1.13.1/smcp/decorators.py +97 -0
  22. bizteamai_smcp-1.13.1/smcp/enforce.py +100 -0
  23. bizteamai_smcp-1.13.1/smcp/filters.py +176 -0
  24. bizteamai_smcp-1.13.1/smcp/license.py +113 -0
  25. bizteamai_smcp-1.13.1/smcp/logchain.py +270 -0
  26. bizteamai_smcp-1.13.1/smcp/tls.py +160 -0
  27. bizteamai_smcp-1.13.1/tests/test_app_wrapper.py +230 -0
  28. bizteamai_smcp-1.13.1/tests/test_decorators.py +204 -0
  29. bizteamai_smcp-1.13.1/tests/test_integration.py +223 -0
@@ -0,0 +1,117 @@
1
+ Metadata-Version: 2.4
2
+ Name: bizteamai-smcp
3
+ Version: 1.13.1
4
+ Summary: Secure Model Context Protocol - Security layers for MCP servers
5
+ Author: SMCP Contributors
6
+ License: MIT
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Intended Audience :: Developers
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.8
12
+ Classifier: Programming Language :: Python :: 3.9
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Requires-Python: >=3.8
17
+ Description-Content-Type: text/markdown
18
+ Requires-Dist: mcp
19
+ Requires-Dist: fastmcp
20
+ Requires-Dist: cryptography
21
+ Requires-Dist: pyyaml
22
+ Provides-Extra: dev
23
+ Requires-Dist: pytest; extra == "dev"
24
+ Requires-Dist: pytest-asyncio; extra == "dev"
25
+ Requires-Dist: black; extra == "dev"
26
+ Requires-Dist: isort; extra == "dev"
27
+ Requires-Dist: mypy; extra == "dev"
28
+
29
+ # SMCP - Secure Model Context Protocol
30
+
31
+ A security-focused wrapper library for MCP (Model Context Protocol) servers, providing multiple layers of protection through conditional guards that activate only when needed.
32
+
33
+ ## Features
34
+
35
+ - **Conditional Security Guards**: Each security layer activates only when its required configuration is present
36
+ - **Mutual TLS Support**: Automatic certificate-based authentication
37
+ - **Host Allowlisting**: Outbound connection validation
38
+ - **Input Sanitization**: Prompt and parameter filtering
39
+ - **Destructive Action Confirmation**: Queue-based approval system for dangerous operations
40
+ - **Tamper-proof Logging**: SHA-chained append-only audit logs
41
+ - **Universal Coverage**: Same decorator factory works for tools, prompts, and retrieval
42
+
43
+ ## Quick Start
44
+
45
+ ```python
46
+ from smcp import FastSMCP as FastMCP
47
+ from smcp import tool, prompt
48
+
49
+ # Configure security features (all optional)
50
+ cfg = {
51
+ "ca_path": "ca.pem",
52
+ "cert_path": "server.pem",
53
+ "key_path": "server.key",
54
+ "ALLOWED_HOSTS": ["api.internal.local", "10.0.0.5"],
55
+ "SAFE_RE": r"^[\w\s.,:;!?-]{1,2048}$",
56
+ "LOG_PATH": "/var/log/smcp.log"
57
+ }
58
+
59
+ app = FastMCP("myserver", smcp_cfg=cfg)
60
+
61
+ @tool(confirm=True) # Requires approval
62
+ def delete_user(uid: str):
63
+ ...
64
+
65
+ @prompt() # Auto-filtered if SAFE_RE present
66
+ def chat(prompt: str):
67
+ ...
68
+ ```
69
+
70
+ ## Security Guards
71
+
72
+ | Feature | Activation Trigger | Purpose |
73
+ |---------|-------------------|---------|
74
+ | Mutual TLS | `ca_path`, `cert_path`, `key_path` in config | Certificate-based authentication |
75
+ | Host Allowlist | Non-empty `ALLOWED_HOSTS` | Outbound connection validation |
76
+ | Input Filtering | `SAFE_RE` or `MAX_LEN` defined | Sanitize prompts and parameters |
77
+ | Action Confirmation | `confirm=True` on decorator | Queue destructive operations for approval |
78
+ | Audit Logging | `LOG_PATH` set | Tamper-proof operation logging |
79
+
80
+ ## CLI Tools
81
+
82
+ ```bash
83
+ # Generate certificates
84
+ smcp-mkcert --ca-name "MyCA" --server-name "myserver.local"
85
+
86
+ # Approve queued actions
87
+ smcp-approve <action-id>
88
+ ```
89
+
90
+ ## Installation
91
+
92
+ ### From PyPI.org (Public)
93
+
94
+ ```bash
95
+ pip install bizteam-smcp
96
+ ```
97
+
98
+ ### From Private PyPI Server
99
+
100
+ ```bash
101
+ # Using private PyPI server
102
+ pip install --extra-index-url https://bizteamai.com/pypi/simple/ bizteam-smcp
103
+ ```
104
+
105
+ ### Upgrading to Business Edition
106
+
107
+ For additional features and enterprise support, a business edition is available:
108
+
109
+ ```bash
110
+ pip install --extra-index-url https://bizteamai.com/pypi/simple/ bizteam-smcp-biz
111
+ ```
112
+
113
+ **Contact**: [business@bizteamai.com](mailto:business@bizteamai.com) for more information.
114
+
115
+ ## License
116
+
117
+ MIT License
@@ -0,0 +1,89 @@
1
+ # SMCP - Secure Model Context Protocol
2
+
3
+ A security-focused wrapper library for MCP (Model Context Protocol) servers, providing multiple layers of protection through conditional guards that activate only when needed.
4
+
5
+ ## Features
6
+
7
+ - **Conditional Security Guards**: Each security layer activates only when its required configuration is present
8
+ - **Mutual TLS Support**: Automatic certificate-based authentication
9
+ - **Host Allowlisting**: Outbound connection validation
10
+ - **Input Sanitization**: Prompt and parameter filtering
11
+ - **Destructive Action Confirmation**: Queue-based approval system for dangerous operations
12
+ - **Tamper-proof Logging**: SHA-chained append-only audit logs
13
+ - **Universal Coverage**: Same decorator factory works for tools, prompts, and retrieval
14
+
15
+ ## Quick Start
16
+
17
+ ```python
18
+ from smcp import FastSMCP as FastMCP
19
+ from smcp import tool, prompt
20
+
21
+ # Configure security features (all optional)
22
+ cfg = {
23
+ "ca_path": "ca.pem",
24
+ "cert_path": "server.pem",
25
+ "key_path": "server.key",
26
+ "ALLOWED_HOSTS": ["api.internal.local", "10.0.0.5"],
27
+ "SAFE_RE": r"^[\w\s.,:;!?-]{1,2048}$",
28
+ "LOG_PATH": "/var/log/smcp.log"
29
+ }
30
+
31
+ app = FastMCP("myserver", smcp_cfg=cfg)
32
+
33
+ @tool(confirm=True) # Requires approval
34
+ def delete_user(uid: str):
35
+ ...
36
+
37
+ @prompt() # Auto-filtered if SAFE_RE present
38
+ def chat(prompt: str):
39
+ ...
40
+ ```
41
+
42
+ ## Security Guards
43
+
44
+ | Feature | Activation Trigger | Purpose |
45
+ |---------|-------------------|---------|
46
+ | Mutual TLS | `ca_path`, `cert_path`, `key_path` in config | Certificate-based authentication |
47
+ | Host Allowlist | Non-empty `ALLOWED_HOSTS` | Outbound connection validation |
48
+ | Input Filtering | `SAFE_RE` or `MAX_LEN` defined | Sanitize prompts and parameters |
49
+ | Action Confirmation | `confirm=True` on decorator | Queue destructive operations for approval |
50
+ | Audit Logging | `LOG_PATH` set | Tamper-proof operation logging |
51
+
52
+ ## CLI Tools
53
+
54
+ ```bash
55
+ # Generate certificates
56
+ smcp-mkcert --ca-name "MyCA" --server-name "myserver.local"
57
+
58
+ # Approve queued actions
59
+ smcp-approve <action-id>
60
+ ```
61
+
62
+ ## Installation
63
+
64
+ ### From PyPI.org (Public)
65
+
66
+ ```bash
67
+ pip install bizteam-smcp
68
+ ```
69
+
70
+ ### From Private PyPI Server
71
+
72
+ ```bash
73
+ # Using private PyPI server
74
+ pip install --extra-index-url https://bizteamai.com/pypi/simple/ bizteam-smcp
75
+ ```
76
+
77
+ ### Upgrading to Business Edition
78
+
79
+ For additional features and enterprise support, a business edition is available:
80
+
81
+ ```bash
82
+ pip install --extra-index-url https://bizteamai.com/pypi/simple/ bizteam-smcp-biz
83
+ ```
84
+
85
+ **Contact**: [business@bizteamai.com](mailto:business@bizteamai.com) for more information.
86
+
87
+ ## License
88
+
89
+ MIT License
@@ -0,0 +1,117 @@
1
+ Metadata-Version: 2.4
2
+ Name: bizteamai-smcp
3
+ Version: 1.13.1
4
+ Summary: Secure Model Context Protocol - Security layers for MCP servers
5
+ Author: SMCP Contributors
6
+ License: MIT
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Intended Audience :: Developers
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.8
12
+ Classifier: Programming Language :: Python :: 3.9
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Requires-Python: >=3.8
17
+ Description-Content-Type: text/markdown
18
+ Requires-Dist: mcp
19
+ Requires-Dist: fastmcp
20
+ Requires-Dist: cryptography
21
+ Requires-Dist: pyyaml
22
+ Provides-Extra: dev
23
+ Requires-Dist: pytest; extra == "dev"
24
+ Requires-Dist: pytest-asyncio; extra == "dev"
25
+ Requires-Dist: black; extra == "dev"
26
+ Requires-Dist: isort; extra == "dev"
27
+ Requires-Dist: mypy; extra == "dev"
28
+
29
+ # SMCP - Secure Model Context Protocol
30
+
31
+ A security-focused wrapper library for MCP (Model Context Protocol) servers, providing multiple layers of protection through conditional guards that activate only when needed.
32
+
33
+ ## Features
34
+
35
+ - **Conditional Security Guards**: Each security layer activates only when its required configuration is present
36
+ - **Mutual TLS Support**: Automatic certificate-based authentication
37
+ - **Host Allowlisting**: Outbound connection validation
38
+ - **Input Sanitization**: Prompt and parameter filtering
39
+ - **Destructive Action Confirmation**: Queue-based approval system for dangerous operations
40
+ - **Tamper-proof Logging**: SHA-chained append-only audit logs
41
+ - **Universal Coverage**: Same decorator factory works for tools, prompts, and retrieval
42
+
43
+ ## Quick Start
44
+
45
+ ```python
46
+ from smcp import FastSMCP as FastMCP
47
+ from smcp import tool, prompt
48
+
49
+ # Configure security features (all optional)
50
+ cfg = {
51
+ "ca_path": "ca.pem",
52
+ "cert_path": "server.pem",
53
+ "key_path": "server.key",
54
+ "ALLOWED_HOSTS": ["api.internal.local", "10.0.0.5"],
55
+ "SAFE_RE": r"^[\w\s.,:;!?-]{1,2048}$",
56
+ "LOG_PATH": "/var/log/smcp.log"
57
+ }
58
+
59
+ app = FastMCP("myserver", smcp_cfg=cfg)
60
+
61
+ @tool(confirm=True) # Requires approval
62
+ def delete_user(uid: str):
63
+ ...
64
+
65
+ @prompt() # Auto-filtered if SAFE_RE present
66
+ def chat(prompt: str):
67
+ ...
68
+ ```
69
+
70
+ ## Security Guards
71
+
72
+ | Feature | Activation Trigger | Purpose |
73
+ |---------|-------------------|---------|
74
+ | Mutual TLS | `ca_path`, `cert_path`, `key_path` in config | Certificate-based authentication |
75
+ | Host Allowlist | Non-empty `ALLOWED_HOSTS` | Outbound connection validation |
76
+ | Input Filtering | `SAFE_RE` or `MAX_LEN` defined | Sanitize prompts and parameters |
77
+ | Action Confirmation | `confirm=True` on decorator | Queue destructive operations for approval |
78
+ | Audit Logging | `LOG_PATH` set | Tamper-proof operation logging |
79
+
80
+ ## CLI Tools
81
+
82
+ ```bash
83
+ # Generate certificates
84
+ smcp-mkcert --ca-name "MyCA" --server-name "myserver.local"
85
+
86
+ # Approve queued actions
87
+ smcp-approve <action-id>
88
+ ```
89
+
90
+ ## Installation
91
+
92
+ ### From PyPI.org (Public)
93
+
94
+ ```bash
95
+ pip install bizteam-smcp
96
+ ```
97
+
98
+ ### From Private PyPI Server
99
+
100
+ ```bash
101
+ # Using private PyPI server
102
+ pip install --extra-index-url https://bizteamai.com/pypi/simple/ bizteam-smcp
103
+ ```
104
+
105
+ ### Upgrading to Business Edition
106
+
107
+ For additional features and enterprise support, a business edition is available:
108
+
109
+ ```bash
110
+ pip install --extra-index-url https://bizteamai.com/pypi/simple/ bizteam-smcp-biz
111
+ ```
112
+
113
+ **Contact**: [business@bizteamai.com](mailto:business@bizteamai.com) for more information.
114
+
115
+ ## License
116
+
117
+ MIT License
@@ -0,0 +1,27 @@
1
+ README.md
2
+ pyproject.toml
3
+ bizteamai_smcp.egg-info/PKG-INFO
4
+ bizteamai_smcp.egg-info/SOURCES.txt
5
+ bizteamai_smcp.egg-info/dependency_links.txt
6
+ bizteamai_smcp.egg-info/entry_points.txt
7
+ bizteamai_smcp.egg-info/requires.txt
8
+ bizteamai_smcp.egg-info/top_level.txt
9
+ smcp/__init__.py
10
+ smcp/allowlist.py
11
+ smcp/app_wrapper.py
12
+ smcp/confirm.py
13
+ smcp/cpu.py
14
+ smcp/decorators.py
15
+ smcp/enforce.py
16
+ smcp/filters.py
17
+ smcp/license.py
18
+ smcp/logchain.py
19
+ smcp/tls.py
20
+ smcp/cli/__init__.py
21
+ smcp/cli/approve.py
22
+ smcp/cli/gen_key.py
23
+ smcp/cli/mkcert.py
24
+ smcp/cli/revoke.py
25
+ tests/test_app_wrapper.py
26
+ tests/test_decorators.py
27
+ tests/test_integration.py
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ smcp-approve = smcp.cli.approve:main
3
+ smcp-mkcert = smcp.cli.mkcert:main
@@ -0,0 +1,11 @@
1
+ mcp
2
+ fastmcp
3
+ cryptography
4
+ pyyaml
5
+
6
+ [dev]
7
+ pytest
8
+ pytest-asyncio
9
+ black
10
+ isort
11
+ mypy
@@ -0,0 +1,53 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "bizteamai-smcp"
7
+ version = "1.13.1"
8
+ description = "Secure Model Context Protocol - Security layers for MCP servers"
9
+ authors = [{name = "SMCP Contributors"}]
10
+ license = {text = "MIT"}
11
+ readme = "README.md"
12
+ requires-python = ">=3.8"
13
+ classifiers = [
14
+ "Development Status :: 3 - Alpha",
15
+ "Intended Audience :: Developers",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Programming Language :: Python :: 3",
18
+ "Programming Language :: Python :: 3.8",
19
+ "Programming Language :: Python :: 3.9",
20
+ "Programming Language :: Python :: 3.10",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Programming Language :: Python :: 3.12",
23
+ ]
24
+ dependencies = [
25
+ "mcp",
26
+ "fastmcp",
27
+ "cryptography",
28
+ "pyyaml",
29
+ ]
30
+
31
+ [project.optional-dependencies]
32
+ dev = [
33
+ "pytest",
34
+ "pytest-asyncio",
35
+ "black",
36
+ "isort",
37
+ "mypy",
38
+ ]
39
+
40
+ [project.scripts]
41
+ smcp-mkcert = "smcp.cli.mkcert:main"
42
+ smcp-approve = "smcp.cli.approve:main"
43
+
44
+ [tool.setuptools.packages.find]
45
+ where = ["."]
46
+ include = ["smcp*"]
47
+
48
+ [tool.black]
49
+ line-length = 88
50
+ target-version = ['py38']
51
+
52
+ [tool.isort]
53
+ profile = "black"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,29 @@
1
+ """
2
+ SMCP - Secure Model Context Protocol
3
+
4
+ A security-focused wrapper library for MCP servers providing conditional
5
+ security guards that activate only when their required configuration is present.
6
+ """
7
+
8
+ import logging
9
+
10
+ from .app_wrapper import FastSMCP
11
+ from .decorators import tool, prompt, retrieval
12
+
13
+ __version__ = "0.1.0"
14
+ __all__ = ["FastSMCP", "tool", "prompt", "retrieval"]
15
+
16
+ # Non-intrusive watermark for community edition
17
+ def _show_watermark():
18
+ """Display a subtle watermark message for the community edition."""
19
+ logger = logging.getLogger(__name__)
20
+ logger.info("SMCP Community Edition - For commercial licensing visit: https://smcp.dev/business")
21
+
22
+ # Show watermark on import (only once)
23
+ try:
24
+ if not hasattr(_show_watermark, '_shown'):
25
+ _show_watermark()
26
+ _show_watermark._shown = True
27
+ except Exception:
28
+ # Silently fail if logging isn't configured
29
+ pass
@@ -0,0 +1,169 @@
1
+ """
2
+ Host allowlist validation for outbound connections.
3
+ """
4
+
5
+ import ipaddress
6
+ import re
7
+ from typing import Dict, List, Union
8
+ from urllib.parse import urlparse
9
+
10
+
11
+ class HostValidationError(Exception):
12
+ """Raised when a host fails allowlist validation."""
13
+ pass
14
+
15
+
16
+ def validate_host(target: str, cfg: Dict[str, Union[str, List[str]]]) -> None:
17
+ """
18
+ Validate that a target host is in the allowlist.
19
+
20
+ Args:
21
+ target: Target host, URL, or IP address to validate
22
+ cfg: Configuration dictionary containing ALLOWED_HOSTS
23
+
24
+ Raises:
25
+ HostValidationError: If the host is not in the allowlist
26
+ """
27
+ allowed_hosts = cfg.get("ALLOWED_HOSTS", [])
28
+ if not allowed_hosts:
29
+ return # No allowlist configured, allow all
30
+
31
+ # Extract hostname from URL if needed
32
+ hostname = _extract_hostname(target)
33
+
34
+ # Check against allowlist
35
+ if not _is_host_allowed(hostname, allowed_hosts):
36
+ raise HostValidationError(f"Host '{hostname}' not in allowlist")
37
+
38
+
39
+ def _extract_hostname(target: str) -> str:
40
+ """
41
+ Extract hostname from a target string (URL, hostname, or IP).
42
+
43
+ Args:
44
+ target: Target string to parse
45
+
46
+ Returns:
47
+ Extracted hostname or IP address
48
+ """
49
+ # If it looks like a URL, parse it
50
+ if "://" in target:
51
+ parsed = urlparse(target)
52
+ return parsed.hostname or parsed.netloc
53
+
54
+ # If it contains a port, strip it
55
+ if ":" in target and not _is_ipv6(target):
56
+ return target.split(":")[0]
57
+
58
+ return target
59
+
60
+
61
+ def _is_ipv6(address: str) -> bool:
62
+ """Check if a string is an IPv6 address."""
63
+ try:
64
+ ipaddress.IPv6Address(address)
65
+ return True
66
+ except ipaddress.AddressValueError:
67
+ return False
68
+
69
+
70
+ def _is_host_allowed(hostname: str, allowed_hosts: List[str]) -> bool:
71
+ """
72
+ Check if a hostname is in the allowlist.
73
+
74
+ Args:
75
+ hostname: Hostname to check
76
+ allowed_hosts: List of allowed hosts (can include patterns)
77
+
78
+ Returns:
79
+ True if the hostname is allowed
80
+ """
81
+ for allowed in allowed_hosts:
82
+ if _host_matches(hostname, allowed):
83
+ return True
84
+ return False
85
+
86
+
87
+ def _host_matches(hostname: str, pattern: str) -> bool:
88
+ """
89
+ Check if a hostname matches an allowlist pattern.
90
+
91
+ Supports:
92
+ - Exact matches: "api.example.com"
93
+ - Wildcard subdomains: "*.example.com"
94
+ - IP addresses: "192.168.1.1"
95
+ - IP ranges: "192.168.1.0/24"
96
+
97
+ Args:
98
+ hostname: Hostname to check
99
+ pattern: Pattern to match against
100
+
101
+ Returns:
102
+ True if the hostname matches the pattern
103
+ """
104
+ # Exact match
105
+ if hostname == pattern:
106
+ return True
107
+
108
+ # Wildcard subdomain match
109
+ if pattern.startswith("*."):
110
+ domain = pattern[2:]
111
+ return hostname.endswith(f".{domain}") or hostname == domain
112
+
113
+ # IP range match
114
+ if "/" in pattern:
115
+ try:
116
+ network = ipaddress.ip_network(pattern, strict=False)
117
+ address = ipaddress.ip_address(hostname)
118
+ return address in network
119
+ except (ipaddress.AddressValueError, ValueError):
120
+ pass
121
+
122
+ # Regex pattern match (if pattern contains regex characters)
123
+ if any(char in pattern for char in r"[](){}+?^$|\\"):
124
+ try:
125
+ return bool(re.match(pattern, hostname))
126
+ except re.error:
127
+ pass
128
+
129
+ return False
130
+
131
+
132
+ def add_host_to_allowlist(cfg: Dict[str, List[str]], host: str) -> None:
133
+ """
134
+ Add a host to the allowlist configuration.
135
+
136
+ Args:
137
+ cfg: Configuration dictionary to modify
138
+ host: Host to add to the allowlist
139
+ """
140
+ if "ALLOWED_HOSTS" not in cfg:
141
+ cfg["ALLOWED_HOSTS"] = []
142
+
143
+ if host not in cfg["ALLOWED_HOSTS"]:
144
+ cfg["ALLOWED_HOSTS"].append(host)
145
+
146
+
147
+ def remove_host_from_allowlist(cfg: Dict[str, List[str]], host: str) -> None:
148
+ """
149
+ Remove a host from the allowlist configuration.
150
+
151
+ Args:
152
+ cfg: Configuration dictionary to modify
153
+ host: Host to remove from the allowlist
154
+ """
155
+ if "ALLOWED_HOSTS" in cfg and host in cfg["ALLOWED_HOSTS"]:
156
+ cfg["ALLOWED_HOSTS"].remove(host)
157
+
158
+
159
+ def get_allowed_hosts(cfg: Dict[str, List[str]]) -> List[str]:
160
+ """
161
+ Get the current allowlist.
162
+
163
+ Args:
164
+ cfg: Configuration dictionary
165
+
166
+ Returns:
167
+ List of allowed hosts
168
+ """
169
+ return cfg.get("ALLOWED_HOSTS", [])