Skip to content

Commit f50a789

Browse files
committed
Adds unit tests for URL header pattern matching
Ensures that when multiple URL patterns match a given URL, header permissions (allowance and sensitivity) are correctly consolidated.
1 parent e3fa8d6 commit f50a789

File tree

1 file changed

+155
-0
lines changed

1 file changed

+155
-0
lines changed
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
from galaxy.config.url_headers import UrlHeadersConfigFactory
2+
3+
4+
def create_overlapping_patterns_config():
5+
"""Create config with multiple overlapping patterns to test all-matches behavior."""
6+
config_dict = {
7+
"patterns": [
8+
{
9+
"url_pattern": r"^https://api\.github\.com/.*",
10+
"headers": [
11+
{"name": "Accept", "sensitive": False},
12+
{"name": "Content-Type", "sensitive": False},
13+
],
14+
},
15+
{
16+
"url_pattern": r"^https://api\.github\.com/repos/.*",
17+
"headers": [
18+
{"name": "Authorization", "sensitive": True},
19+
],
20+
},
21+
{
22+
"url_pattern": r"^https://.*",
23+
"headers": [
24+
{"name": "Accept-Encoding", "sensitive": False},
25+
],
26+
},
27+
]
28+
}
29+
30+
return UrlHeadersConfigFactory.from_dict(config_dict)
31+
32+
33+
class TestAllMatchesPatternBehavior:
34+
"""Test that all matching patterns are considered (union of permissions)."""
35+
36+
def test_find_all_matching_returns_all_patterns(self):
37+
"""Test that find_all_matching returns all patterns that match a URL."""
38+
config = create_overlapping_patterns_config()
39+
40+
# URL matches all three patterns
41+
url = "https://api.github.com/repos/owner/repo"
42+
matching = config.find_all_matching(url)
43+
44+
assert len(matching) == 3
45+
46+
def test_header_allowed_checks_all_matching_patterns(self):
47+
"""Test that a header is allowed if ANY matching pattern allows it."""
48+
config = create_overlapping_patterns_config()
49+
50+
url = "https://api.github.com/repos/owner/repo"
51+
52+
# Each header should be allowed if it's in ANY matching pattern
53+
assert config.is_header_allowed_for_url("Accept", url) # From github_basic
54+
assert config.is_header_allowed_for_url("Content-Type", url) # From github_basic
55+
assert config.is_header_allowed_for_url("Authorization", url) # From github_auth
56+
assert config.is_header_allowed_for_url("Accept-Encoding", url) # From https_generic
57+
58+
# Headers not in any pattern
59+
assert not config.is_header_allowed_for_url("X-Custom-Header", url)
60+
assert not config.is_header_allowed_for_url("Cookie", url)
61+
62+
def test_header_allowed_subset_of_patterns(self):
63+
"""Test headers allowed when only subset of patterns match."""
64+
config = create_overlapping_patterns_config()
65+
66+
# URL only matches github_basic and https_generic (not github_auth)
67+
url = "https://api.github.com/users/octocat"
68+
69+
assert config.is_header_allowed_for_url("Accept", url) # From github_basic
70+
assert config.is_header_allowed_for_url("Accept-Encoding", url) # From https_generic
71+
# Authorization not allowed because github_auth pattern doesn't match
72+
assert not config.is_header_allowed_for_url("Authorization", url)
73+
74+
def test_header_sensitive_secure_by_default(self):
75+
"""Test that if ANY pattern marks header sensitive, it's treated as sensitive."""
76+
config_dict = {
77+
"patterns": [
78+
{
79+
"url_pattern": r"^https://test1\.example\.com/.*",
80+
"headers": [{"name": "Authorization", "sensitive": False}],
81+
},
82+
{
83+
"url_pattern": r"^https://.*\.example\.com/.*",
84+
"headers": [{"name": "Authorization", "sensitive": True}],
85+
},
86+
]
87+
}
88+
config = UrlHeadersConfigFactory.from_dict(config_dict)
89+
90+
# URL matches both patterns
91+
url = "https://test1.example.com/api"
92+
93+
# Should be treated as sensitive (secure by default)
94+
assert config.is_header_sensitive_for_url("Authorization", url)
95+
96+
def test_multiple_overlapping_patterns_union(self):
97+
"""Test that union of headers from all matching patterns is allowed."""
98+
config_dict = {
99+
"patterns": [
100+
{
101+
"url_pattern": r"^https://example\.com/.*",
102+
"headers": [
103+
{"name": "Header-A", "sensitive": False},
104+
{"name": "Header-B", "sensitive": True},
105+
],
106+
},
107+
{
108+
"url_pattern": r"^https://example\.com/api/.*",
109+
"headers": [
110+
{"name": "Header-C", "sensitive": False},
111+
{"name": "Header-D", "sensitive": True},
112+
],
113+
},
114+
{
115+
"url_pattern": r"^https://example\.com/api/v1/.*",
116+
"headers": [
117+
{"name": "Header-E", "sensitive": False},
118+
],
119+
},
120+
]
121+
}
122+
config = UrlHeadersConfigFactory.from_dict(config_dict)
123+
124+
# URL matches all three patterns
125+
url = "https://example.com/api/v1/resource"
126+
127+
# All headers from all patterns should be allowed
128+
assert config.is_header_allowed_for_url("Header-A", url)
129+
assert config.is_header_allowed_for_url("Header-B", url)
130+
assert config.is_header_allowed_for_url("Header-C", url)
131+
assert config.is_header_allowed_for_url("Header-D", url)
132+
assert config.is_header_allowed_for_url("Header-E", url)
133+
134+
# Headers B and D should be sensitive
135+
assert config.is_header_sensitive_for_url("Header-B", url)
136+
assert config.is_header_sensitive_for_url("Header-D", url)
137+
138+
# Headers A, C, E should not be sensitive
139+
assert not config.is_header_sensitive_for_url("Header-A", url)
140+
assert not config.is_header_sensitive_for_url("Header-C", url)
141+
assert not config.is_header_sensitive_for_url("Header-E", url)
142+
143+
def test_no_matching_patterns(self):
144+
"""Test behavior when no patterns match."""
145+
config = create_overlapping_patterns_config()
146+
147+
# URL doesn't match any pattern
148+
url = "http://example.com/api" # http, not https
149+
150+
matching = config.find_all_matching(url)
151+
assert len(matching) == 0
152+
153+
# No headers should be allowed
154+
assert not config.is_header_allowed_for_url("Accept", url)
155+
assert not config.is_header_allowed_for_url("Authorization", url)

0 commit comments

Comments
 (0)