From 842dab4942113480045aaf8df56ad52ae8d48da8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Taavi=20V=C3=A4=C3=A4n=C3=A4nen?= <taavi@wikimedia.org>
Date: Fri, 6 Mar 2026 12:41:52 +0200
Subject: [PATCH] SECURITY: Actions: Make headers set after redirect actually
 apply

If a special page redirect is done, the Request object in the context is
set to a DerivativeRequest, which extends from FauxRequest and thus
returns a FauxResponse from WebRequest::response().

This patch updates the actions entry point to copy any headers set in
that FauxResponse object to be applied to the real response object, to
make sure that e.g. the correct Content-Type is set for redirected
action=raw requests.

Bug: T419192
Change-Id: I04ec931bc8997383bf837738a3968bfd8a044858
---
 includes/Actions/ActionEntryPoint.php | 13 +++++++++++++
 includes/Request/FauxResponse.php     |  5 +++++
 2 files changed, 18 insertions(+)

diff --git a/includes/Actions/ActionEntryPoint.php b/includes/Actions/ActionEntryPoint.php
index 67b85c6258c..11ec8bc2588 100644
--- a/includes/Actions/ActionEntryPoint.php
+++ b/includes/Actions/ActionEntryPoint.php
@@ -20,6 +20,7 @@ use MediaWiki\Permissions\PermissionStatus;
 use MediaWiki\Profiler\Profiler;
 use MediaWiki\Profiler\ProfilingContext;
 use MediaWiki\Request\DerivativeRequest;
+use MediaWiki\Request\FauxRequest;
 use MediaWiki\Request\WebRequest;
 use MediaWiki\SpecialPage\RedirectSpecialPage;
 use MediaWiki\SpecialPage\SpecialPage;
@@ -515,6 +516,18 @@ class ActionEntryPoint extends MediaWikiEntryPoint {
 						. " returned neither an object nor a URL" );
 				}
 			}
+
+			if ( $context->getRequest() instanceof FauxRequest ) {
+				$fauxResponse = $context->getRequest()->response();
+				if ( $fauxResponse->getStatusCode() ) {
+					$request->response()->statusHeader( $fauxResponse->getStatusCode() );
+				}
+
+				foreach ( $fauxResponse->getHeaders() as $key => $value ) {
+					$request->response()->header( "$key: $value" );
+				}
+			}
+
 			$output->considerCacheSettingsFinal();
 		}
 	}
diff --git a/includes/Request/FauxResponse.php b/includes/Request/FauxResponse.php
index 006354a8454..11ec5241089 100644
--- a/includes/Request/FauxResponse.php
+++ b/includes/Request/FauxResponse.php
@@ -80,6 +80,11 @@ class FauxResponse extends WebResponse {
 		return $this->headers[$key] ?? null;
 	}

+	/** @return string[] All set headers */
+	public function getHeaders(): array {
+		return $this->headers ?? [];
+	}
+
 	/**
 	 * Get the HTTP response code, null if not set
 	 *
--
2.51.0

