From b556b8febb622e2e64becd69cfb72c546e589d39 Mon Sep 17 00:00:00 2001 From: Shubham Padkonde Date: Thu, 24 Sep 2026 04:24:27 +0000 Subject: [PATCH] Fix remove_url_auth leaving username-only credentials in URLs remove_url_auth removed the literal "{username}:{password}@" string, so a URL with only a username (e.g. "mqtt://TOKEN@broker:1883", a common way to pass a token) became "TOKEN:None@" and nothing was removed. The broker URL is published in the AsyncAPI document and in broker_safe_url, so the token was exposed. Rebuild the netloc without the userinfo instead of doing a string replace. --- pygeoapi/util.py | 5 +++-- tests/other/test_util.py | 12 ++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/pygeoapi/util.py b/pygeoapi/util.py index b60e187a08..5b90a7c3e7 100644 --- a/pygeoapi/util.py +++ b/pygeoapi/util.py @@ -791,8 +791,9 @@ def remove_url_auth(url: str) -> str: """ u = urlparse(url) - auth = f'{u.username}:{u.password}@' - return url.replace(auth, '') + if '@' not in u.netloc: + return url + return u._replace(netloc=u.netloc.rpartition('@')[2]).geturl() def is_request_allowed(url: str, allow_internal: bool = False) -> bool: diff --git a/tests/other/test_util.py b/tests/other/test_util.py index 7cb321019d..81a7a371ae 100644 --- a/tests/other/test_util.py +++ b/tests/other/test_util.py @@ -170,6 +170,18 @@ def test_json_serial(): util.json_serial('foo') +@pytest.mark.parametrize('url,expected', [ + ['https://user:pw@example.org/x', 'https://example.org/x'], + ['https://token@example.org/x', 'https://example.org/x'], + ['mqtt://user:@broker:1883', 'mqtt://broker:1883'], + ['postgresql://u:p@ss@db:5432/x?a=b', 'postgresql://db:5432/x?a=b'], + ['https://example.org/x?email=a@b.c', 'https://example.org/x?email=a@b.c'], + ['https://example.org/x', 'https://example.org/x'], +]) +def test_remove_url_auth(url, expected): + assert util.remove_url_auth(url) == expected + + def test_mimetype(): assert util.get_mimetype('file.xml') == 'application/xml' assert util.get_mimetype('file.yml') == 'text/plain'