Skip to content

Commit 90a77d6

Browse files
committed
fix(stdlib): Move get_aws_sigv4_signed_headers to sentry_sdk.utils + support for SigV4 query/presigned authentication
Refs: #7031 & PY-2667
1 parent 14bad29 commit 90a77d6

2 files changed

Lines changed: 56 additions & 25 deletions

File tree

sentry_sdk/integrations/stdlib.py

Lines changed: 2 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
SENSITIVE_DATA_SUBSTITUTE,
2222
capture_internal_exceptions,
2323
ensure_integration_enabled,
24+
get_aws_sigv4_signed_headers,
2425
is_sentry_url,
2526
logger,
2627
parse_url,
@@ -61,30 +62,6 @@ def add_python_runtime_context(
6162
return event
6263

6364

64-
def _aws_sigv4_signed_headers(buffer: "Optional[List[bytes]]") -> "Set[str]":
65-
if buffer is None:
66-
return set()
67-
for line in buffer:
68-
name, separator, value = line.partition(b":")
69-
if not separator or name.lower() != b"authorization":
70-
continue
71-
72-
value = value.lstrip()
73-
if not value.startswith((b"AWS4-HMAC-SHA256", b"AWS4-ECDSA-P256-SHA256")):
74-
continue
75-
76-
for part in value.split(b","):
77-
part = part.strip()
78-
if part.startswith(b"SignedHeaders="):
79-
_, _, header_names = part.partition(b"=")
80-
return {
81-
header.decode("ascii", "ignore").lower()
82-
for header in header_names.split(b";")
83-
if header
84-
}
85-
return set()
86-
87-
8865
def _request_header_names(buffer: "Optional[List[bytes]]") -> "Set[str]":
8966
if buffer is None:
9067
return set()
@@ -216,7 +193,7 @@ def endheaders(self: "HTTPConnection", *args: "Any", **kwargs: "Any") -> "Any":
216193
if real_url is not None:
217194
request_buffer = getattr(self, "_buffer", None)
218195
existing_headers = _request_header_names(request_buffer)
219-
signed_headers = _aws_sigv4_signed_headers(request_buffer)
196+
signed_headers = get_aws_sigv4_signed_headers(request_buffer, real_url)
220197

221198
for (
222199
header_name,

sentry_sdk/utils.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1697,6 +1697,60 @@ def parse_url(url: str, sanitize: bool = True) -> "ParsedUrl":
16971697
)
16981698

16991699

1700+
def get_aws_sigv4_signed_headers(
1701+
headers: "Any", url: "Optional[str]" = None
1702+
) -> "Set[str]":
1703+
# httpConnection exposes buffer, aiohttp uses header mapping.
1704+
if isinstance(headers, (str, bytes)):
1705+
authorization = headers
1706+
elif headers is None:
1707+
authorization = ""
1708+
elif hasattr(headers, "get"):
1709+
authorization = headers.get("Authorization", "")
1710+
else:
1711+
authorization = ""
1712+
for line in headers:
1713+
name, separator, value = line.partition(b":")
1714+
if separator and name.lower() == b"authorization":
1715+
authorization = value
1716+
break
1717+
1718+
if isinstance(authorization, bytes):
1719+
authorization = authorization.decode("ascii", "ignore")
1720+
1721+
signed_headers = set()
1722+
if isinstance(authorization, str):
1723+
# only AWS SigV4 authorization has the SignedHeaders parameter.
1724+
value = authorization.lstrip()
1725+
if value.startswith(("AWS4-HMAC-SHA256", "AWS4-ECDSA-P256-SHA256")):
1726+
for part in value.split(","):
1727+
part = part.strip()
1728+
if part.startswith("SignedHeaders="):
1729+
_, _, header_names = part.partition("=")
1730+
signed_headers.update(
1731+
header.lower() for header in header_names.split(";") if header
1732+
)
1733+
break
1734+
1735+
if url is None:
1736+
return signed_headers
1737+
1738+
query = {
1739+
key.lower(): values for key, values in parse_qs(urlsplit(url).query).items()
1740+
}
1741+
algorithm = query.get("x-amz-algorithm", [""])[0]
1742+
if algorithm not in ("AWS4-HMAC-SHA256", "AWS4-ECDSA-P256-SHA256"):
1743+
return signed_headers
1744+
1745+
# presigned requests have SignedHeaders in the URL query.
1746+
signed_headers.update(
1747+
header.lower()
1748+
for header in query.get("x-amz-signedheaders", [""])[0].split(";")
1749+
if header
1750+
)
1751+
return signed_headers
1752+
1753+
17001754
def is_valid_sample_rate(rate: "Any", source: str) -> bool:
17011755
"""
17021756
Checks the given sample rate to make sure it is valid type and value (a

0 commit comments

Comments
 (0)