Skip to content

Commit 1df741c

Browse files
Use regex for domain validation (#7)
* Use regex for domain validation * Escape slashes
1 parent fcbb065 commit 1df741c

File tree

2 files changed

+36
-10
lines changed

2 files changed

+36
-10
lines changed

src/saleor_mcp/config.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,24 @@
11
import os
2+
import re
23
from dataclasses import dataclass
3-
from fnmatch import fnmatch
44

55
from fastmcp.exceptions import ToolError
66
from fastmcp.server.dependencies import get_http_headers
77

88

99
def validate_api_url(url, pattern):
10-
"""Validate if the given URL matches the allowed domain pattern."""
11-
return fnmatch(url, pattern)
10+
"""Validate if the given URL matches the allowed domain pattern.
11+
12+
Pattern should be a properly escaped regular expression.
13+
"""
14+
# Add anchors if not present
15+
if not pattern.startswith("^"):
16+
pattern = "^" + pattern
17+
18+
if not pattern.endswith("$"):
19+
pattern = pattern + "$"
20+
21+
return bool(re.match(pattern, url))
1222

1323

1424
@dataclass

src/saleor_mcp/tests/test_config.py

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,27 @@
77
@pytest.mark.parametrize(
88
("url", "pattern", "expected"),
99
[
10-
("https://exactmatch.saleor.cloud", "https://exactmatch.saleor.cloud", True),
11-
("https://example.saleor.cloud", "https://*.saleor.cloud", True),
12-
("https://sub.domain.saleor.cloud", "https://*.saleor.cloud", True),
13-
("https://other.saleor.cloud", "https://exact.saleor.cloud", False),
14-
("https://malicious-saleor.cloud", "https://*.saleor.cloud", False),
10+
(
11+
"https://exactmatch.saleor.cloud",
12+
r"https:\/\/exactmatch\.saleor\.cloud",
13+
True,
14+
),
15+
("https://exactmatch.example.com", r"https:\/\/exactmatch\.example\.com", True),
16+
("https://example-domain.saleor.cloud", r"https:\/\/.*\.saleor\.cloud", True),
17+
("https://example.saleor.cloud", r"https:\/\/.*\.saleor\.cloud", True),
18+
(
19+
"https://example.saleor.cloud/graphql/",
20+
r"https:\/\/.*\.saleor\.cloud\/graphql\/",
21+
True,
22+
),
23+
("https://sub.domain.saleor.cloud", r"https:\/\/.*\.saleor\.cloud", True),
24+
("https://other.saleor.cloud", r"https:\/\/exact\.saleor\.cloud", False),
25+
("https://malicious-saleor.cloud", r"https:\/\/.*\.saleor\.cloud", False),
26+
(
27+
"https://example.com?url=https://a.saleor.cloud/",
28+
r"https:\/\/.*\.saleor\.cloud",
29+
False,
30+
),
1531
],
1632
)
1733
def test_validate_api_url(url, pattern, expected):
@@ -39,7 +55,7 @@ def mock_get_http_headers():
3955

4056

4157
def test_get_config_from_headers_with_allowed_domain_pattern(monkeypatch):
42-
monkeypatch.setenv("ALLOWED_DOMAIN_PATTERN", "https://*.saleor.cloud")
58+
monkeypatch.setenv("ALLOWED_DOMAIN_PATTERN", r"https://.*\.saleor\.cloud")
4359
headers = {
4460
"x-saleor-api-url": "https://my.saleor.cloud",
4561
"x-saleor-auth-token": "mytoken",
@@ -59,7 +75,7 @@ def mock_get_http_headers():
5975

6076

6177
def test_get_config_from_headers_invalid_domain_pattern(monkeypatch):
62-
monkeypatch.setenv("ALLOWED_DOMAIN_PATTERN", "https://*.saleor.cloud")
78+
monkeypatch.setenv("ALLOWED_DOMAIN_PATTERN", r"https://.*\.saleor\.cloud")
6379

6480
from saleor_mcp.config import get_config_from_headers
6581

0 commit comments

Comments
 (0)