diff --git a/CHANGES/+cve-2026-84232.bugfix b/CHANGES/+cve-2026-84232.bugfix new file mode 100644 index 00000000000..dc1fb1b50ca --- /dev/null +++ b/CHANGES/+cve-2026-84232.bugfix @@ -0,0 +1,2 @@ +Insures that `Content-Disposition`, `Content-Security-Policy`, and `X-Content-Type-Options` +are set consistently in the content-app. This addresses CVE-2026-84232. diff --git a/pulpcore/content/handler.py b/pulpcore/content/handler.py index 9343cf29b0c..ba2f68228c6 100644 --- a/pulpcore/content/handler.py +++ b/pulpcore/content/handler.py @@ -792,6 +792,7 @@ async def _match_and_stream(self, path, request): ) ca = ContentArtifact(relative_path=original_rel_path) ra = RemoteArtifact(remote=remote, url=url, content_artifact=ca) + self._set_secure_headers(os.path.basename(ca.relative_path), headers) try: return await self._stream_remote_artifact( request, @@ -969,6 +970,12 @@ def _save_artifact(self, download_result, remote_artifact, request=None): content_artifact.save() return artifact + def _set_secure_headers(self, filename, headers): + content_disposition = f"attachment;filename={filename}" + headers["Content-Disposition"] = content_disposition + headers["Content-Security-Policy"] = "default-src 'none'; sandbox" + headers["X-Content-Type-Options"] = "nosniff" + async def _serve_content_artifact(self, content_artifact, headers, request): """ Handle response for a Content Artifact with the file present. @@ -1000,10 +1007,6 @@ def _set_params_from_headers(hdrs, storage_domain): return params def _build_url(**kwargs): - filename = os.path.basename(content_artifact.relative_path) - content_disposition = f"attachment;filename={filename}" - - headers["Content-Disposition"] = content_disposition parameters = _set_params_from_headers(headers, domain.storage_class) storage_url = storage.url(artifact_name, parameters=parameters, **kwargs) @@ -1015,7 +1018,7 @@ def _build_url(**kwargs): storage = domain.get_storage() content_length = artifact_file.size - + self._set_secure_headers(os.path.basename(content_artifact.relative_path), headers) try: range_start, range_stop = request.http_range.start, request.http_range.stop if range_start or range_stop: @@ -1088,6 +1091,9 @@ async def _stream_remote_artifact(self, request, response, remote_artifact, save size = remote_artifact.size or "*" raise HTTPRequestRangeNotSatisfiable(headers={"Content-Range": f"bytes */{size}"}) + self._set_secure_headers( + os.path.basename(remote_artifact.content_artifact.relative_path), response.headers + ) actual_content_length = None if range_start or range_stop: