Skip to content

Commit 36ae843

Browse files
committed
URL-encode client_id in Azure IMDS token request
The `_get_azure_response()` function constructs the Azure IMDS URL by interpolating `client_id` via f-string without URL encoding. While `resource` is already encoded (via `quote()` at the call site in `auth_oidc_shared.py`), `client_id` is not, creating an inconsistency. Apply `urllib.parse.quote()` to `client_id` before interpolation, consistent with the handling of `resource` and with the Node.js driver's use of `url.searchParams.append()` for the same parameter. Add a test to verify special characters in `client_id` are properly percent-encoded and cannot introduce additional query parameters.
1 parent e67931d commit 36ae843

2 files changed

Lines changed: 16 additions & 1 deletion

File tree

pymongo/_azure_helpers.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import json
1919
from typing import Any, Optional
20+
from urllib.parse import quote
2021

2122

2223
def _get_azure_response(
@@ -29,7 +30,7 @@ def _get_azure_response(
2930
url += "?api-version=2018-02-01"
3031
url += f"&resource={resource}"
3132
if client_id:
32-
url += f"&client_id={client_id}"
33+
url += f"&client_id={quote(client_id)}"
3334
headers = {"Metadata": "true", "Accept": "application/json"}
3435
request = Request(url, headers=headers) # noqa: S310
3536
try:

test/test_azure_helpers.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,20 @@ def test_timeout_passed_to_urlopen(self):
150150
_, kwargs = mock_open.call_args
151151
self.assertEqual(kwargs["timeout"], 42)
152152

153+
def test_client_id_is_url_encoded(self):
154+
"""Ensure special characters in client_id are percent-encoded."""
155+
body = json.dumps({"access_token": "tok", "expires_in": "3600"})
156+
with _mock_urlopen(200, body) as mock_open:
157+
self._call(client_id="id with spaces&special=chars")
158+
159+
url = mock_open.call_args[0][0].full_url
160+
# '&' and '=' must be percent-encoded so they don't inject extra query params
161+
self.assertIn("client_id=id%20with%20spaces%26special%3Dchars", url)
162+
# The encoded client_id should not introduce a raw '&'
163+
# Count params: api-version, resource, client_id — exactly 3
164+
query_string = url.split("?", 1)[1]
165+
self.assertEqual(query_string.count("&"), 2)
166+
153167

154168
if __name__ == "__main__":
155169
unittest.main()

0 commit comments

Comments
 (0)