Skip to content

Commit e9eca99

Browse files
brettlangdonZStriker19majorgreys
authored
fix: remove url parts from http.url tag [backport #4906 to 0.61] (#4912)
Backport of #4906 This fix removes unintended url parts in the `http.url` tag. Co-authored-by: Tahir H. Butt <[email protected]> Co-authored-by: Brett Langdon <[email protected]> Co-authored-by: Zachary Groves <[email protected]> Co-authored-by: Tahir H. Butt <[email protected]>
1 parent 5d0fdf4 commit e9eca99

File tree

6 files changed

+114
-1
lines changed

6 files changed

+114
-1
lines changed

ddtrace/contrib/trace_utils.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from ddtrace import Pin
1616
from ddtrace import config
1717
from ddtrace.ext import http
18+
from ddtrace.internal.compat import parse
1819
from ddtrace.internal.logger import get_logger
1920
from ddtrace.internal.utils.cache import cached
2021
from ddtrace.internal.utils.http import normalize_header_name
@@ -132,6 +133,29 @@ def _store_response_headers(headers, span, integration_config):
132133
_store_headers(headers, span, integration_config, RESPONSE)
133134

134135

136+
def _sanitized_url(url):
137+
# type: (str) -> str
138+
"""
139+
Sanitize url by removing parts with potential auth info
140+
"""
141+
if "@" in url:
142+
parsed = parse.urlparse(url)
143+
netloc = parsed.netloc
144+
netloc = netloc[netloc.index("@") + 1 :]
145+
return parse.urlunparse(
146+
(
147+
parsed.scheme,
148+
netloc,
149+
parsed.path,
150+
"",
151+
parsed.query,
152+
"",
153+
)
154+
)
155+
156+
return url
157+
158+
135159
def with_traced_module(func):
136160
"""Helper for providing tracing essentials (module and pin) for tracing
137161
wrappers.
@@ -246,6 +270,7 @@ def set_http_meta(
246270
span._set_str_tag(http.METHOD, method)
247271

248272
if url is not None:
273+
url = _sanitized_url(url)
249274
span._set_str_tag(http.URL, url if integration_config.trace_query_string else strip_query_string(url))
250275

251276
if status_code is not None:
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
fixes:
3+
- |
4+
This fix removes unintended url parts in the ``http.url`` tag.

tests/contrib/aiohttp/test_aiohttp_client.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
PORT = HTTPBIN_CONFIG["port"]
1717
SOCKET = "{}:{}".format(HOST, PORT)
1818
URL = "http://{}".format(SOCKET)
19+
URL_AUTH = "http://user:pass@{}".format(SOCKET)
1920
URL_200 = "{}/status/200".format(URL)
21+
URL_AUTH_200 = "{}/status/200".format(URL_AUTH)
2022
URL_500 = "{}/status/500".format(URL)
2123

2224

@@ -35,6 +37,14 @@ async def test_200_request(snapshot_context):
3537
assert resp.status == 200
3638

3739

40+
@pytest.mark.asyncio
41+
async def test_auth_200_request(snapshot_context):
42+
with snapshot_context():
43+
async with aiohttp.ClientSession() as session:
44+
async with session.get(URL_AUTH_200) as resp:
45+
assert resp.status == 200
46+
47+
3848
@pytest.mark.asyncio
3949
async def test_200_request_post(snapshot_context):
4050
with snapshot_context():

tests/contrib/requests/test_requests.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
SOCKET = "httpbin.org"
3131
URL_200 = "http://{}/status/200".format(SOCKET)
3232
URL_500 = "http://{}/status/500".format(SOCKET)
33+
URL_AUTH_200 = "http://user:pass@{}/status/200".format(SOCKET)
3334

3435

3536
class BaseRequestTestCase(object):
@@ -125,6 +126,13 @@ def test_200(self):
125126
assert s.span_type == "http"
126127
assert http.QUERY_STRING not in s.get_tags()
127128

129+
def test_auth_200(self):
130+
self.session.get(URL_AUTH_200)
131+
spans = self.pop_spans()
132+
assert len(spans) == 1
133+
s = spans[0]
134+
assert s.get_tag(http.URL) == URL_200
135+
128136
def test_200_send(self):
129137
# when calling send directly
130138
req = requests.Request(url=URL_200, method="GET")
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
[[
2+
{
3+
"name": "aiohttp.request",
4+
"service": null,
5+
"resource": "aiohttp.request",
6+
"trace_id": 0,
7+
"span_id": 1,
8+
"parent_id": 0,
9+
"type": "http",
10+
"meta": {
11+
"http.method": "GET",
12+
"http.status_code": "200",
13+
"http.status_msg": "OK",
14+
"http.url": "http://localhost:8001/status/200",
15+
"runtime-id": "7a569f0d94574038b2b11ee3579d0936"
16+
},
17+
"metrics": {
18+
"_dd.agent_psr": 1.0,
19+
"_dd.top_level": 1,
20+
"_dd.tracer_kr": 1.0,
21+
"_sampling_priority_v1": 1,
22+
"system.pid": 57194
23+
},
24+
"duration": 8933000,
25+
"start": 1673646330398679000
26+
},
27+
{
28+
"name": "TCPConnector.connect",
29+
"service": null,
30+
"resource": "TCPConnector.connect",
31+
"trace_id": 0,
32+
"span_id": 2,
33+
"parent_id": 1,
34+
"duration": 1426000,
35+
"start": 1673646330399204000
36+
}]]

tests/tracer/test_trace_utils.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,30 @@ def test_ext_service(int_config, pin, config_val, default, expected):
305305
(None, None, None, None, None, None),
306306
("GET", "http://localhost/", 200, "OK", None, {"my-header": "value1"}),
307307
("GET", "http://localhost/", 200, "OK", "search?q=test+query", {"my-header": "value1"}),
308+
(
309+
"GET",
310+
"http://user:pass@localhost/",
311+
0,
312+
None,
313+
None,
314+
None,
315+
),
316+
(
317+
"GET",
318+
"http://user@localhost/",
319+
0,
320+
None,
321+
None,
322+
None,
323+
),
324+
(
325+
"GET",
326+
"http://user:pass@localhost/api?q=test",
327+
0,
328+
None,
329+
None,
330+
None,
331+
),
308332
],
309333
)
310334
def test_set_http_meta(span, int_config, method, url, status_code, status_msg, query, request_headers):
@@ -326,7 +350,13 @@ def test_set_http_meta(span, int_config, method, url, status_code, status_msg, q
326350
assert http.METHOD not in span.get_tags()
327351

328352
if url is not None:
329-
assert span.get_tag(http.URL) == stringify(url)
353+
if url.startswith("http://user"):
354+
# Remove any userinfo that may be in the original url
355+
expected_url = url[: url.index(":")] + "://" + url[url.index("@") + 1 :]
356+
else:
357+
expected_url = url
358+
359+
assert span.get_tag(http.URL) == stringify(expected_url)
330360
else:
331361
assert http.URL not in span.get_tags()
332362

0 commit comments

Comments
 (0)