diff --git a/resources/templates/javascript/redirect.twig b/resources/templates/javascript/redirect.twig
index 7aebb65d06..7572cde105 100644
--- a/resources/templates/javascript/redirect.twig
+++ b/resources/templates/javascript/redirect.twig
@@ -3,3 +3,4 @@
window.location = '{{ url|escape('js') }}';
};
+{{ t('Taking you to the target site.') }}
diff --git a/src/Application.php b/src/Application.php
index d76589b6b3..720563d637 100644
--- a/src/Application.php
+++ b/src/Application.php
@@ -103,7 +103,7 @@ class Application
$requestHandler->add(new RequestProblemChecking($this->template, $this->responseFactory));
$requestHandler->add(new CurrentServerGlobalSetting($this->config));
$requestHandler->add(new ThemeInitialization());
- $requestHandler->add(new UrlRedirection($this->config));
+ $requestHandler->add(new UrlRedirection($this->config, $this->template, $this->responseFactory));
$requestHandler->add(new SetupPageRedirection($this->config, $this->responseFactory));
$requestHandler->add(new MinimumCommonRedirection($this->config, $this->responseFactory));
$requestHandler->add(new LanguageAndThemeCookieSaving($this->config));
diff --git a/src/Http/Middleware/UrlRedirection.php b/src/Http/Middleware/UrlRedirection.php
index 056a963d45..76a7e6c190 100644
--- a/src/Http/Middleware/UrlRedirection.php
+++ b/src/Http/Middleware/UrlRedirection.php
@@ -6,6 +6,9 @@ namespace PhpMyAdmin\Http\Middleware;
use PhpMyAdmin\Config;
use PhpMyAdmin\Container\ContainerBuilder;
+use PhpMyAdmin\Http\Factory\ResponseFactory;
+use PhpMyAdmin\ResponseRenderer;
+use PhpMyAdmin\Template;
use PhpMyAdmin\Theme\ThemeManager;
use PhpMyAdmin\UrlRedirector;
use Psr\Http\Message\ResponseInterface;
@@ -13,12 +16,13 @@ use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
-use function is_string;
-
final class UrlRedirection implements MiddlewareInterface
{
- public function __construct(private readonly Config $config)
- {
+ public function __construct(
+ private readonly Config $config,
+ private readonly Template $template,
+ private readonly ResponseFactory $responseFactory,
+ ) {
}
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
@@ -32,11 +36,8 @@ final class UrlRedirection implements MiddlewareInterface
$themeManager = $container->get(ThemeManager::class);
$this->config->loadUserPreferences($themeManager, true);
- return UrlRedirector::redirect($this->getUrlParam($request->getQueryParams()['url'] ?? null));
- }
+ $urlRedirector = new UrlRedirector(ResponseRenderer::getInstance(), $this->template, $this->responseFactory);
- private function getUrlParam(mixed $url): string
- {
- return is_string($url) ? $url : '';
+ return $urlRedirector->redirect($request->getQueryParams()['url'] ?? null);
}
}
diff --git a/src/ResponseRenderer.php b/src/ResponseRenderer.php
index 6231f5090d..7cf1903494 100644
--- a/src/ResponseRenderer.php
+++ b/src/ResponseRenderer.php
@@ -428,21 +428,26 @@ class ResponseRenderer
throw new ExitException($message);
}
+ /**
+ * Avoid relative path redirect problems in case user entered URL
+ * like /phpmyadmin/index.php/ which some web servers happily accept.
+ */
+ public function fixRelativeUrlForRedirect(string $url): string
+ {
+ if (! str_starts_with($url, '.')) {
+ return $url;
+ }
+
+ return $this->config->getRootPath() . substr($url, 2);
+ }
+
/**
* @psalm-param non-empty-string $url
* @psalm-param StatusCodeInterface::STATUS_* $statusCode
*/
public function redirect(string $url, int $statusCode = StatusCodeInterface::STATUS_FOUND): void
{
- /**
- * Avoid relative path redirect problems in case user entered URL
- * like /phpmyadmin/index.php/ which some web servers happily accept.
- */
- if (str_starts_with($url, '.')) {
- $url = $this->config->getRootPath() . substr($url, 2);
- }
-
- $this->addHeader('Location', $url);
+ $this->addHeader('Location', $this->fixRelativeUrlForRedirect($url));
$this->setStatusCode($statusCode);
}
diff --git a/src/UrlRedirector.php b/src/UrlRedirector.php
index 6fdf4c7a1d..96e17c2b4c 100644
--- a/src/UrlRedirector.php
+++ b/src/UrlRedirector.php
@@ -4,10 +4,11 @@ declare(strict_types=1);
namespace PhpMyAdmin;
-use PhpMyAdmin\Container\ContainerBuilder;
+use Fig\Http\Message\StatusCodeInterface;
+use PhpMyAdmin\Http\Factory\ResponseFactory;
use PhpMyAdmin\Http\Response;
-use function __;
+use function is_string;
use function preg_match;
/**
@@ -15,37 +16,36 @@ use function preg_match;
*/
final class UrlRedirector
{
- public static function redirect(string $url): Response
+ public function __construct(
+ private readonly ResponseRenderer $response,
+ private readonly Template $template,
+ private readonly ResponseFactory $responseFactory,
+ ) {
+ }
+
+ public function redirect(mixed $urlParam): Response
{
- $container = ContainerBuilder::getContainer();
-
- // Only output the http headers
- $response = ResponseRenderer::getInstance();
- $response->getHeader()->sendHttpHeaders();
- $response->disable();
+ $response = $this->responseFactory->createResponse();
+ foreach ($this->response->getHeader()->getHttpHeaders() as $name => $value) {
+ $response = $response->withHeader($name, $value);
+ }
+ $url = is_string($urlParam) ? $urlParam : '';
if (
$url === ''
|| ! preg_match('/^https:\/\/[^\n\r]*$/', $url)
|| ! Core::isAllowedDomain($url)
) {
- $response->redirect('./');
+ $response = $response->withHeader('Location', $this->response->fixRelativeUrlForRedirect('./'));
- return $response->response();
+ return $response->withStatus(StatusCodeInterface::STATUS_FOUND);
}
/**
* JavaScript redirection is necessary. Because if header() is used then web browser sometimes does not change
* the HTTP_REFERER field and so with old URL as Referer, token also goes to external site.
- *
- * @var Template $template
*/
- $template = $container->get('template');
- echo $template->render('javascript/redirect', ['url' => $url]);
- // Display redirecting msg on screen.
- // Do not display the value of $_GET['url'] to avoid showing injected content
- echo __('Taking you to the target site.');
- return $response->response();
+ return $response->write($this->template->render('javascript/redirect', ['url' => $url]));
}
}
diff --git a/tests/unit/UrlRedirectorTest.php b/tests/unit/UrlRedirectorTest.php
index 577261e86f..862673c510 100644
--- a/tests/unit/UrlRedirectorTest.php
+++ b/tests/unit/UrlRedirectorTest.php
@@ -6,43 +6,44 @@ namespace PhpMyAdmin\Tests;
use Fig\Http\Message\StatusCodeInterface;
use PhpMyAdmin\Config;
-use PhpMyAdmin\ResponseRenderer;
+use PhpMyAdmin\Http\Factory\ResponseFactory;
+use PhpMyAdmin\Template;
+use PhpMyAdmin\Tests\Stubs\ResponseRenderer;
use PhpMyAdmin\UrlRedirector;
use PHPUnit\Framework\Attributes\CoversClass;
-use ReflectionProperty;
#[CoversClass(UrlRedirector::class)]
final class UrlRedirectorTest extends AbstractTestCase
{
public function testRedirectWithDisallowedUrl(): void
{
- (new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, null);
- $GLOBALS['lang'] = 'en';
$config = Config::getInstance();
$config->settings['PmaAbsoluteUri'] = 'http://localhost/phpmyadmin';
- $response = UrlRedirector::redirect('https://user:pass@example.com/');
+ $urlRedirector = new UrlRedirector(new ResponseRenderer(), new Template(), ResponseFactory::create());
+
+ $response = $urlRedirector->redirect('https://user:pass@example.com/');
self::assertSame('/phpmyadmin/', $response->getHeaderLine('Location'));
self::assertSame(StatusCodeInterface::STATUS_FOUND, $response->getStatusCode());
}
public function testRedirectWithAllowedUrl(): void
{
- (new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, null);
- $GLOBALS['lang'] = 'en';
$_SERVER['SERVER_NAME'] = 'localhost';
- UrlRedirector::redirect('https://phpmyadmin.net/');
- $output = self::getActualOutputForAssertion();
- $expected = <<<'HTML'
-
-Taking you to the target site.
-HTML;
+ $urlRedirector = new UrlRedirector(new ResponseRenderer(), new Template(), ResponseFactory::create());
- self::assertSame($expected, $output);
+ $response = $urlRedirector->redirect('https://phpmyadmin.net/');
+ $expected = <<<'HTML'
+
+ Taking you to the target site.
+
+ HTML;
+
+ self::assertSame($expected, (string) $response->getBody());
}
}