Skip to content

Commit eadb37f

Browse files
committed
Prevent rewriting paths that are not descendent of upstream URL
1 parent eff9bb0 commit eadb37f

File tree

2 files changed

+78
-33
lines changed

2 files changed

+78
-33
lines changed

src/stac_auth_proxy/middleware/ProcessLinksMiddleware.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,16 +58,27 @@ def transform_json(self, data: dict[str, Any], request: Request) -> dict[str, An
5858
parsed_req_url.netloc,
5959
parsed_upstream_url.netloc,
6060
]:
61-
logger.warning(
61+
logger.debug(
6262
"Ignoring link %s because it is not for an endpoint behind this proxy (%s or %s)",
6363
href,
6464
parsed_req_url.netloc,
6565
parsed_upstream_url.netloc,
6666
)
6767
continue
6868

69+
# If the link path is not a descendant of the upstream path, don't transform it
70+
if parsed_upstream_url.path != "/" and not parsed_link.path.startswith(
71+
parsed_upstream_url.path
72+
):
73+
logger.debug(
74+
"Ignoring link %s because it is not descendant of upstream path (%s)",
75+
href,
76+
parsed_upstream_url.path,
77+
)
78+
continue
79+
80+
# Replace the upstream host with the client's host
6981
if parsed_link.netloc == parsed_upstream_url.netloc:
70-
# Replace the upstream host with the client's host
7182
parsed_link = parsed_link._replace(
7283
netloc=parsed_req_url.netloc
7384
)._replace(scheme=parsed_req_url.scheme)

tests/test_process_links.py

Lines changed: 65 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -347,12 +347,66 @@ def test_transform_without_prefix():
347347
assert transformed["links"][1]["href"] == "http://proxy.example.com/collections"
348348

349349

350-
def test_transform_mixed_links():
350+
@pytest.mark.parametrize(
351+
"upstream_url,root_path,input_links,expected_links",
352+
[
353+
# Upstream links with upstream path
354+
(
355+
"http://upstream.example.com/api",
356+
"/proxy",
357+
[
358+
{"rel": "self", "href": "http://upstream.example.com/api/collections"},
359+
{"rel": "root", "href": "http://upstream.example.com/api"},
360+
{
361+
"rel": "items",
362+
"href": "http://upstream.example.com/api/collections/test/items",
363+
},
364+
],
365+
[
366+
"http://proxy.example.com/proxy/collections",
367+
"http://proxy.example.com/proxy",
368+
"http://proxy.example.com/proxy/collections/test/items",
369+
],
370+
),
371+
# Upstream links without upstream path
372+
(
373+
"http://upstream.example.com",
374+
"/proxy",
375+
[
376+
{"rel": "self", "href": "http://upstream.example.com/collections"},
377+
{"rel": "root", "href": "http://upstream.example.com/"},
378+
{"rel": "root", "href": "http://upstream.example.com/other/path"},
379+
],
380+
[
381+
"http://proxy.example.com/proxy/collections",
382+
"http://proxy.example.com/proxy/",
383+
"http://proxy.example.com/proxy/other/path",
384+
],
385+
),
386+
# Upstream links without root path
387+
(
388+
"http://upstream.example.com/api",
389+
None,
390+
[
391+
{"rel": "self", "href": "http://upstream.example.com/api/collections"},
392+
{"rel": "root", "href": "http://upstream.example.com/api"},
393+
{"rel": "root", "href": "http://upstream.example.com/other/path"},
394+
],
395+
[
396+
"http://proxy.example.com/collections",
397+
"http://proxy.example.com",
398+
# Upstream links without matching root path should be ignored
399+
"http://upstream.example.com/other/path",
400+
],
401+
),
402+
],
403+
)
404+
def test_transform_mixed_links(upstream_url, root_path, input_links, expected_links):
351405
"""Test transforming a mix of proxy links and upstream links."""
352406
middleware = ProcessLinksMiddleware(
353407
app=None,
354-
upstream_url="http://upstream.example.com/api",
355-
root_path="/proxy",
408+
upstream_url=upstream_url,
409+
root_path=root_path,
356410
)
357411
request_scope = {
358412
"type": "http",
@@ -363,35 +417,15 @@ def test_transform_mixed_links():
363417
],
364418
}
365419

366-
data = {
367-
"links": [
368-
# Proxy links (should be processed as before)
369-
{"rel": "self", "href": "http://proxy.example.com/api/collections"},
370-
# Upstream links (should be rewritten to proxy)
371-
{"rel": "root", "href": "http://upstream.example.com/api"},
372-
{
373-
"rel": "items",
374-
"href": "http://upstream.example.com/api/collections/test/items",
375-
},
376-
# External links (should be ignored)
377-
{"rel": "external", "href": "http://other.example.com/api"},
378-
]
379-
}
380-
381-
transformed = middleware.transform_json(data, Request(request_scope))
382-
383-
# Proxy links should be processed as before
384-
assert (
385-
transformed["links"][0]["href"] == "http://proxy.example.com/proxy/collections"
386-
)
387-
# Upstream links should be rewritten to proxy
388-
assert transformed["links"][1]["href"] == "http://proxy.example.com/proxy"
389-
assert (
390-
transformed["links"][2]["href"]
391-
== "http://proxy.example.com/proxy/collections/test/items"
420+
transformed = middleware.transform_json(
421+
{
422+
"links": input_links,
423+
},
424+
Request(request_scope),
392425
)
393-
# External links should be ignored
394-
assert transformed["links"][3]["href"] == "http://other.example.com/api"
426+
427+
for i, expected in enumerate(expected_links):
428+
assert transformed["links"][i]["href"] == expected
395429

396430

397431
def test_transform_upstream_links_nested_objects():

0 commit comments

Comments
 (0)