From f80b15a7f65e4b5977bb219bf1a6ab0cbffdf1d3 Mon Sep 17 00:00:00 2001 From: Joshua Li Date: Sat, 31 Oct 2026 11:14:07 -0700 Subject: [PATCH 1/2] fix(sourcemaps): ignore non-string sourcemap headers Artifact headers come from uploaded manifests. Treat a non-string sourcemap or x-sourcemap value as absent instead of passing it into force_bytes, and cover the accessor directly. --- src/sentry/api/endpoints/source_map_debug.py | 13 ++++++++++--- tests/sentry/api/endpoints/test_source_map_debug.py | 8 ++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/sentry/api/endpoints/source_map_debug.py b/src/sentry/api/endpoints/source_map_debug.py index ca012244cca0..ec35f6b2f47d 100644 --- a/src/sentry/api/endpoints/source_map_debug.py +++ b/src/sentry/api/endpoints/source_map_debug.py @@ -44,6 +44,13 @@ from sentry.utils import json, metrics from sentry.utils.javascript import find_sourcemap from sentry.utils.safe import get_path + + +def _sourcemap_header(headers: Mapping[str, object]) -> str | None: + value = headers.get("sourcemap", headers.get("x-sourcemap")) + return value if isinstance(value, str) else None + + from sentry.utils.urls import non_standard_url_join logger = logging.getLogger(__name__) @@ -436,7 +443,7 @@ def _find_source_file_in_basic_uploaded_files(self) -> None: headers = ArtifactBundleArchive.normalize_headers( possible_release_file.file.headers ) - sourcemap_header = headers.get("sourcemap", headers.get("x-sourcemap")) + sourcemap_header = _sourcemap_header(headers) sourcemap_header = ( force_bytes(sourcemap_header) if sourcemap_header is not None else None ) @@ -482,7 +489,7 @@ def _find_source_file_in_artifact_indexes(self) -> None: self.found_source_file_name ) headers = ArtifactBundleArchive.normalize_headers(headers) - sourcemap_header = headers.get("sourcemap", headers.get("x-sourcemap")) + sourcemap_header = _sourcemap_header(headers) sourcemap_header = ( force_bytes(sourcemap_header) if sourcemap_header is not None @@ -529,7 +536,7 @@ def _find_source_file_in_artifact_bundles(self) -> None: headers = ArtifactBundleArchive.normalize_headers(headers) self.source_file_lookup_result = "found" self.found_source_file_name = potential_source_file_name - sourcemap_header = headers.get("sourcemap", headers.get("x-sourcemap")) + sourcemap_header = _sourcemap_header(headers) sourcemap_header = ( force_bytes(sourcemap_header) if sourcemap_header is not None diff --git a/tests/sentry/api/endpoints/test_source_map_debug.py b/tests/sentry/api/endpoints/test_source_map_debug.py index 443ccbf55555..4230b0b81d2f 100644 --- a/tests/sentry/api/endpoints/test_source_map_debug.py +++ b/tests/sentry/api/endpoints/test_source_map_debug.py @@ -15,6 +15,7 @@ MIN_JS_SDK_VERSION_FOR_DEBUG_IDS, ReleaseBundleUrls, SourceMapReleaseProcessResult, + _sourcemap_header, get_release_bundle_urls, report_unsuccessful_url_matches, ) @@ -34,6 +35,13 @@ from sentry.testutils.helpers.options import override_options from sentry.testutils.skips import requires_snuba + +def test_sourcemap_header_ignores_non_strings() -> None: + assert _sourcemap_header({"sourcemap": "foo"}) == "foo" + assert _sourcemap_header({"x-sourcemap": "bar"}) == "bar" + assert _sourcemap_header({"sourcemap": 1}) is None + + pytestmark = [requires_snuba] From 3b6f1868258b7c393b94e295731afc91d4aa39b4 Mon Sep 17 00:00:00 2001 From: Joshua Li Date: Sat, 31 Oct 2026 11:31:22 -0700 Subject: [PATCH 2/2] fix(sourcemaps): keep sourcemap bytes separate The helper returns the raw string header; convert it to bytes in a separate variable so the variable keeps one type through the archive lookup paths. --- src/sentry/api/endpoints/source_map_debug.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sentry/api/endpoints/source_map_debug.py b/src/sentry/api/endpoints/source_map_debug.py index ec35f6b2f47d..7a649eeebb5e 100644 --- a/src/sentry/api/endpoints/source_map_debug.py +++ b/src/sentry/api/endpoints/source_map_debug.py @@ -438,18 +438,18 @@ def _find_source_file_in_basic_uploaded_files(self) -> None: ): self.source_file_lookup_result = "found" self.found_source_file_name = possible_release_file.name - sourcemap_header = None + sourcemap_bytes: bytes | None = None if possible_release_file.file.headers: headers = ArtifactBundleArchive.normalize_headers( possible_release_file.file.headers ) sourcemap_header = _sourcemap_header(headers) - sourcemap_header = ( + sourcemap_bytes = ( force_bytes(sourcemap_header) if sourcemap_header is not None else None ) try: source_map_reference = find_sourcemap( - sourcemap_header, possible_release_file.file.getfile().read() + sourcemap_bytes, possible_release_file.file.getfile().read() ) self.source_map_reference = ( force_str(source_map_reference) @@ -490,13 +490,13 @@ def _find_source_file_in_artifact_indexes(self) -> None: ) headers = ArtifactBundleArchive.normalize_headers(headers) sourcemap_header = _sourcemap_header(headers) - sourcemap_header = ( + sourcemap_bytes = ( force_bytes(sourcemap_header) if sourcemap_header is not None else None ) source_map_reference = find_sourcemap( - sourcemap_header, source_file.read() + sourcemap_bytes, source_file.read() ) self.source_map_reference = ( force_str(source_map_reference) @@ -537,14 +537,14 @@ def _find_source_file_in_artifact_bundles(self) -> None: self.source_file_lookup_result = "found" self.found_source_file_name = potential_source_file_name sourcemap_header = _sourcemap_header(headers) - sourcemap_header = ( + sourcemap_bytes = ( force_bytes(sourcemap_header) if sourcemap_header is not None else None ) try: source_map_reference = find_sourcemap( - sourcemap_header, matching_file.read() + sourcemap_bytes, matching_file.read() ) self.source_map_reference = ( force_str(source_map_reference)