From f60ffe88dd3839609683df6a7e967b00a8d77cd3 Mon Sep 17 00:00:00 2001 From: David Blain Date: Wed, 26 Aug 2026 09:22:49 +0200 Subject: [PATCH] Add wildcard host matching to AllowedHostsValidator Support wildcard patterns in the allowlist so trusted host groups can be managed without listing every subdomain explicitly. Keep strict host normalization and validation to preserve safe trust decisions. --- .../authentication/allowed_hosts_validator.py | 10 ++++++- .../test_allowed_hosts_validator.py | 26 ++++++++++++++++++- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/packages/abstractions/kiota_abstractions/authentication/allowed_hosts_validator.py b/packages/abstractions/kiota_abstractions/authentication/allowed_hosts_validator.py index 577faebf..201055ea 100644 --- a/packages/abstractions/kiota_abstractions/authentication/allowed_hosts_validator.py +++ b/packages/abstractions/kiota_abstractions/authentication/allowed_hosts_validator.py @@ -67,4 +67,12 @@ def is_url_host_valid(self, url: str) -> bool: return False if not o.hostname: return False - return o.hostname.lower() in self.allowed_hosts + hostname = o.hostname.lower() + # Existing exact match + if hostname in self.allowed_hosts: + return True + # Suffix match for entries starting with "." + return any( + suffix.startswith(".") and hostname.endswith(suffix) + for suffix in self.allowed_hosts + ) diff --git a/packages/abstractions/tests/authentication/test_allowed_hosts_validator.py b/packages/abstractions/tests/authentication/test_allowed_hosts_validator.py index 1f4d6d41..d07942a7 100644 --- a/packages/abstractions/tests/authentication/test_allowed_hosts_validator.py +++ b/packages/abstractions/tests/authentication/test_allowed_hosts_validator.py @@ -62,11 +62,28 @@ def test_validates_subdomain_separately(self): validator = AllowedHostsValidator(["example.com"]) assert validator.is_url_host_valid("https://sub.example.com/path") is False + def test_returns_true_for_subdomain_matching_allowed_suffix(self): + validator = AllowedHostsValidator([".fabric.microsoft.com"]) + assert validator.is_url_host_valid( + "https://abc.123.graphql.fabric.microsoft.com/path" + ) is True + + def test_returns_false_for_bare_domain_when_allowed_as_suffix(self): + validator = AllowedHostsValidator([".fabric.microsoft.com"]) + assert validator.is_url_host_valid("https://fabric.microsoft.com/path") is False + + def test_suffix_host_matching_is_case_insensitive(self): + validator = AllowedHostsValidator([".Fabric.Microsoft.COM"]) + assert validator.is_url_host_valid("https://ABC.z2c.graphql.fabric.microsoft.com/path") is True + def test_allows_multiple_valid_hosts(self): - validator = AllowedHostsValidator(["example.com", "api.example.com"]) + validator = AllowedHostsValidator(["example.com", "api.example.com", ".fabric.microsoft.com"]) assert validator.is_url_host_valid("https://example.com/path") is True assert validator.is_url_host_valid("https://api.example.com/path") is True assert validator.is_url_host_valid("https://other.com/path") is False + assert validator.is_url_host_valid( + "https://abc.123.graphql.fabric.microsoft.com/path" + ) is True def test_handles_url_with_port(self): validator = AllowedHostsValidator(["example.com"]) @@ -93,3 +110,10 @@ def test_raises_on_http_prefix(self): validator = AllowedHostsValidator(["example.com"]) with pytest.raises(ValueError): validator.set_allowed_hosts(["http://example.com"]) + + def test_allows_suffix_based_hosts_after_update(self): + validator = AllowedHostsValidator(["example.com"]) + validator.set_allowed_hosts([".fabric.microsoft.com"]) + assert validator.is_url_host_valid( + "https://abc.123.graphql.fabric.microsoft.com/path" + ) is True