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.
This commit is contained in:
Qi Deng 2026-04-29 13:55:08 -07:00
parent e67931dff7
commit 36ae843bde
2 changed files with 16 additions and 1 deletions

View File

@ -17,6 +17,7 @@ from __future__ import annotations
import json
from typing import Any, Optional
from urllib.parse import quote
def _get_azure_response(
@ -29,7 +30,7 @@ def _get_azure_response(
url += "?api-version=2018-02-01"
url += f"&resource={resource}"
if client_id:
url += f"&client_id={client_id}"
url += f"&client_id={quote(client_id)}"
headers = {"Metadata": "true", "Accept": "application/json"}
request = Request(url, headers=headers) # noqa: S310
try:

View File

@ -150,6 +150,20 @@ class TestGetAzureResponse(unittest.TestCase):
_, kwargs = mock_open.call_args
self.assertEqual(kwargs["timeout"], 42)
def test_client_id_is_url_encoded(self):
"""Ensure special characters in client_id are percent-encoded."""
body = json.dumps({"access_token": "tok", "expires_in": "3600"})
with _mock_urlopen(200, body) as mock_open:
self._call(client_id="id with spaces&special=chars")
url = mock_open.call_args[0][0].full_url
# '&' and '=' must be percent-encoded so they don't inject extra query params
self.assertIn("client_id=id%20with%20spaces%26special%3Dchars", url)
# The encoded client_id should not introduce a raw '&'
# Count params: api-version, resource, client_id — exactly 3
query_string = url.split("?", 1)[1]
self.assertEqual(query_string.count("&"), 2)
if __name__ == "__main__":
unittest.main()