Skip to content

Commit 7395692

Browse files
committed
Remove user credentials in URLs when converting to a string
1 parent e70d0b0 commit 7395692

File tree

6 files changed

+30
-29
lines changed

6 files changed

+30
-29
lines changed

httpx/_urlparse.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -166,9 +166,9 @@ class ParseResult(typing.NamedTuple):
166166

167167
@property
168168
def authority(self) -> str:
169+
# Do not include the userinfo here, to avoid leaking credentials
169170
return "".join(
170171
[
171-
f"{self.userinfo}@" if self.userinfo else "",
172172
f"[{self.host}]" if ":" in self.host else self.host,
173173
f":{self.port}" if self.port is not None else "",
174174
]

httpx/_urls.py

+1-24
Original file line numberDiff line numberDiff line change
@@ -375,30 +375,7 @@ def __str__(self) -> str:
375375
return str(self._uri_reference)
376376

377377
def __repr__(self) -> str:
378-
scheme, userinfo, host, port, path, query, fragment = self._uri_reference
379-
380-
if ":" in userinfo:
381-
# Mask any password component.
382-
userinfo = f'{userinfo.split(":")[0]}:[secure]'
383-
384-
authority = "".join(
385-
[
386-
f"{userinfo}@" if userinfo else "",
387-
f"[{host}]" if ":" in host else host,
388-
f":{port}" if port is not None else "",
389-
]
390-
)
391-
url = "".join(
392-
[
393-
f"{self.scheme}:" if scheme else "",
394-
f"//{authority}" if authority else "",
395-
path,
396-
f"?{query}" if query is not None else "",
397-
f"#{fragment}" if fragment is not None else "",
398-
]
399-
)
400-
401-
return f"{self.__class__.__name__}({url!r})"
378+
return f"{self.__class__.__name__}({str(self._uri_reference)!r})"
402379

403380
@property
404381
def raw(self) -> tuple[bytes, bytes, int, bytes]: # pragma: nocover

tests/client/test_auth.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ async def test_auth_disable_per_request() -> None:
302302

303303
def test_auth_hidden_url() -> None:
304304
url = "http://example-username:[email protected]/"
305-
expected = "URL('http://example-username:[secure]@example.org/')"
305+
expected = "URL('http://example.org/')"
306306
assert url == httpx.URL(url)
307307
assert expected == repr(httpx.URL(url))
308308

tests/models/test_responses.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,8 @@ def test_response_json():
8989

9090

9191
def test_raise_for_status():
92-
request = httpx.Request("GET", "https://example.org")
92+
# The credentials here should be removed from all exception strings below
93+
request = httpx.Request("GET", "https://user:[email protected]")
9394

9495
# 2xx status codes are not an error.
9596
response = httpx.Response(200, request=request)

tests/models/test_url.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -591,7 +591,10 @@ def test_url_copywith_authority_subcomponents():
591591
}
592592
url = httpx.URL("https://example.org")
593593
new = url.copy_with(**copy_with_kwargs)
594-
assert str(new) == "https://username:[email protected]:444"
594+
assert str(new) == "https://example.net:444"
595+
assert new.username == "username"
596+
assert new.password == "password"
597+
assert new.userinfo == b"username:password"
595598

596599

597600
def test_url_copywith_netloc():
@@ -610,7 +613,7 @@ def test_url_copywith_userinfo_subcomponents():
610613
}
611614
url = httpx.URL("https://example.org")
612615
new = url.copy_with(**copy_with_kwargs)
613-
assert str(new) == "https://tom%40example.org:abc123%40%20%@example.org"
616+
assert str(new) == "https://example.org"
614617
assert new.username == "[email protected]"
615618
assert new.password == "abc123@ %"
616619
assert new.userinfo == b"tom%40example.org:abc123%40%20%"

tests/test_utils.py

+20
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,26 @@ def test_logging_request(server, caplog):
6565
]
6666

6767

68+
def test_logging_request_with_auth(server, caplog):
69+
caplog.set_level(logging.INFO)
70+
with httpx.Client() as client:
71+
credentials = {
72+
"username": "user",
73+
"password": "abc123%",
74+
}
75+
url = server.url.copy_with(**credentials)
76+
response = client.get(url)
77+
assert response.status_code == 200
78+
79+
assert caplog.record_tuples == [
80+
(
81+
"httpx",
82+
logging.INFO,
83+
'HTTP Request: GET http://127.0.0.1:8000/ "HTTP/1.1 200 OK"',
84+
)
85+
]
86+
87+
6888
def test_logging_redirect_chain(server, caplog):
6989
caplog.set_level(logging.INFO)
7090
with httpx.Client(follow_redirects=True) as client:

0 commit comments

Comments
 (0)