diff --git a/libraries/classes/Application.php b/libraries/classes/Application.php
index d132c97cfb..35204cbdcf 100644
--- a/libraries/classes/Application.php
+++ b/libraries/classes/Application.php
@@ -13,8 +13,6 @@ use PhpMyAdmin\Http\Handler\ApplicationHandler;
use PhpMyAdmin\Http\Handler\QueueRequestHandler;
use PhpMyAdmin\Http\Response;
use PhpMyAdmin\Http\ServerRequest;
-use PhpMyAdmin\Identifiers\DatabaseName;
-use PhpMyAdmin\Identifiers\TableName;
use PhpMyAdmin\Middleware\Authentication;
use PhpMyAdmin\Middleware\ConfigErrorAndPermissionChecking;
use PhpMyAdmin\Middleware\ConfigLoading;
@@ -53,20 +51,9 @@ use PhpMyAdmin\Middleware\ZeroConfPostConnection;
use PhpMyAdmin\Routing\Routing;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
-use Symfony\Component\DependencyInjection\ContainerInterface;
use Throwable;
-use function __;
-use function function_exists;
-use function hash_equals;
-use function is_array;
-use function is_scalar;
-use function session_id;
use function sprintf;
-use function strlen;
-use function trigger_error;
-
-use const E_USER_ERROR;
class Application
{
@@ -93,7 +80,7 @@ class Application
$requestHandler = new QueueRequestHandler(new ApplicationHandler($this));
$requestHandler->add(new ErrorHandling($this->errorHandler));
$requestHandler->add(new OutputBuffering());
- $requestHandler->add(new PhpExtensionsChecking($this, $this->template, $this->responseFactory));
+ $requestHandler->add(new PhpExtensionsChecking($this->template, $this->responseFactory));
$requestHandler->add(new ServerConfigurationChecking($this->template, $this->responseFactory));
$requestHandler->add(new PhpSettingsConfiguration());
$requestHandler->add(new RouteParsing());
@@ -107,8 +94,8 @@ class Application
));
$requestHandler->add(new EncryptedQueryParamsHandling());
$requestHandler->add(new UrlParamsSetting($this->config));
- $requestHandler->add(new TokenRequestParamChecking($this));
- $requestHandler->add(new DatabaseAndTableSetting($this));
+ $requestHandler->add(new TokenRequestParamChecking());
+ $requestHandler->add(new DatabaseAndTableSetting());
$requestHandler->add(new SqlQueryGlobalSetting());
$requestHandler->add(new LanguageLoading());
$requestHandler->add(new ConfigErrorAndPermissionChecking(
@@ -162,119 +149,4 @@ class Application
$this->responseFactory,
);
}
-
- /**
- * Checks that required PHP extensions are there.
- */
- public function checkRequiredPhpExtensions(): void
- {
- /**
- * Warning about mbstring.
- */
- if (! function_exists('mb_detect_encoding')) {
- Core::warnMissingExtension('mbstring');
- }
-
- /**
- * We really need this one!
- */
- if (! function_exists('preg_replace')) {
- Core::warnMissingExtension('pcre', true);
- }
-
- /**
- * JSON is required in several places.
- */
- if (! function_exists('json_encode')) {
- Core::warnMissingExtension('json', true);
- }
-
- /**
- * ctype is required for Twig.
- */
- if (! function_exists('ctype_alpha')) {
- Core::warnMissingExtension('ctype', true);
- }
-
- if (! function_exists('mysqli_connect')) {
- $moreInfo = sprintf(__('See %sour documentation%s for more information.'), '[doc@faqmysql]', '[/doc]');
- Core::warnMissingExtension('mysqli', true, $moreInfo);
- }
-
- if (! function_exists('session_name')) {
- Core::warnMissingExtension('session', true);
- }
-
- /**
- * hash is required for cookie authentication.
- */
- if (function_exists('hash_hmac')) {
- return;
- }
-
- Core::warnMissingExtension('hash', true);
- }
-
- /**
- * Check whether user supplied token is valid, if not remove any possibly
- * dangerous stuff from request.
- *
- * Check for token mismatch only if the Request method is POST.
- * GET Requests would never have token and therefore checking
- * mis-match does not make sense.
- */
- public function checkTokenRequestParam(): void
- {
- $GLOBALS['token_mismatch'] = true;
- $GLOBALS['token_provided'] = false;
-
- if (($_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'POST') {
- return;
- }
-
- if (isset($_POST['token']) && is_scalar($_POST['token']) && strlen((string) $_POST['token']) > 0) {
- $GLOBALS['token_provided'] = true;
- $GLOBALS['token_mismatch'] = ! @hash_equals($_SESSION[' PMA_token '], (string) $_POST['token']);
- }
-
- if (! $GLOBALS['token_mismatch']) {
- return;
- }
-
- // Warn in case the mismatch is result of failed setting of session cookie
- if (isset($_POST['set_session']) && $_POST['set_session'] !== session_id()) {
- trigger_error(
- __(
- 'Failed to set session cookie. Maybe you are using HTTP instead of HTTPS to access phpMyAdmin.',
- ),
- E_USER_ERROR,
- );
- }
-
- /**
- * We don't allow any POST operation parameters if the token is mismatched
- * or is not provided.
- */
- $allowList = ['ajax_request'];
- Sanitize::removeRequestVars($allowList);
- }
-
- public function setDatabaseAndTableFromRequest(ContainerInterface $container, ServerRequest $request): void
- {
- $GLOBALS['urlParams'] ??= null;
-
- $db = DatabaseName::tryFrom($request->getParam('db'));
- $table = TableName::tryFrom($request->getParam('table'));
-
- $GLOBALS['db'] = $db?->getName() ?? '';
- $GLOBALS['table'] = $table?->getName() ?? '';
-
- if (! is_array($GLOBALS['urlParams'])) {
- $GLOBALS['urlParams'] = [];
- }
-
- $GLOBALS['urlParams']['db'] = $GLOBALS['db'];
- $GLOBALS['urlParams']['table'] = $GLOBALS['table'];
- $container->setParameter('url_params', $GLOBALS['urlParams']);
- }
}
diff --git a/libraries/classes/Middleware/DatabaseAndTableSetting.php b/libraries/classes/Middleware/DatabaseAndTableSetting.php
index 7733d815ed..31f25ff45b 100644
--- a/libraries/classes/Middleware/DatabaseAndTableSetting.php
+++ b/libraries/classes/Middleware/DatabaseAndTableSetting.php
@@ -4,28 +4,46 @@ declare(strict_types=1);
namespace PhpMyAdmin\Middleware;
-use PhpMyAdmin\Application;
use PhpMyAdmin\Core;
use PhpMyAdmin\Http\ServerRequest;
+use PhpMyAdmin\Identifiers\DatabaseName;
+use PhpMyAdmin\Identifiers\TableName;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
use function assert;
+use function is_array;
final class DatabaseAndTableSetting implements MiddlewareInterface
{
- public function __construct(private readonly Application $application)
- {
- }
-
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
assert($request instanceof ServerRequest);
$container = Core::getContainerBuilder();
- $this->application->setDatabaseAndTableFromRequest($container, $request);
+ $this->setDatabaseAndTableFromRequest($container, $request);
return $handler->handle($request);
}
+
+ private function setDatabaseAndTableFromRequest(ContainerInterface $container, ServerRequest $request): void
+ {
+ $GLOBALS['urlParams'] ??= null;
+
+ $db = DatabaseName::tryFrom($request->getParam('db'));
+ $table = TableName::tryFrom($request->getParam('table'));
+
+ $GLOBALS['db'] = $db?->getName() ?? '';
+ $GLOBALS['table'] = $table?->getName() ?? '';
+
+ if (! is_array($GLOBALS['urlParams'])) {
+ $GLOBALS['urlParams'] = [];
+ }
+
+ $GLOBALS['urlParams']['db'] = $GLOBALS['db'];
+ $GLOBALS['urlParams']['table'] = $GLOBALS['table'];
+ $container->setParameter('url_params', $GLOBALS['urlParams']);
+ }
}
diff --git a/libraries/classes/Middleware/PhpExtensionsChecking.php b/libraries/classes/Middleware/PhpExtensionsChecking.php
index d450427ffe..767afa156e 100644
--- a/libraries/classes/Middleware/PhpExtensionsChecking.php
+++ b/libraries/classes/Middleware/PhpExtensionsChecking.php
@@ -5,7 +5,7 @@ declare(strict_types=1);
namespace PhpMyAdmin\Middleware;
use Fig\Http\Message\StatusCodeInterface;
-use PhpMyAdmin\Application;
+use PhpMyAdmin\Core;
use PhpMyAdmin\Exceptions\MissingExtensionException;
use PhpMyAdmin\Http\Factory\ResponseFactory;
use PhpMyAdmin\Template;
@@ -14,19 +14,20 @@ use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
+use function __;
+use function function_exists;
+use function sprintf;
+
final class PhpExtensionsChecking implements MiddlewareInterface
{
- public function __construct(
- private readonly Application $application,
- private readonly Template $template,
- private readonly ResponseFactory $responseFactory,
- ) {
+ public function __construct(private readonly Template $template, private readonly ResponseFactory $responseFactory)
+ {
}
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
try {
- $this->application->checkRequiredPhpExtensions();
+ $this->checkRequiredPhpExtensions();
} catch (MissingExtensionException $exception) {
// Disables template caching because the cache directory is not known yet.
$this->template->disableCache();
@@ -43,4 +44,56 @@ final class PhpExtensionsChecking implements MiddlewareInterface
return $handler->handle($request);
}
+
+ /**
+ * Checks that required PHP extensions are there.
+ */
+ private function checkRequiredPhpExtensions(): void
+ {
+ /**
+ * Warning about mbstring.
+ */
+ if (! function_exists('mb_detect_encoding')) {
+ Core::warnMissingExtension('mbstring');
+ }
+
+ /**
+ * We really need this one!
+ */
+ if (! function_exists('preg_replace')) {
+ Core::warnMissingExtension('pcre', true);
+ }
+
+ /**
+ * JSON is required in several places.
+ */
+ if (! function_exists('json_encode')) {
+ Core::warnMissingExtension('json', true);
+ }
+
+ /**
+ * ctype is required for Twig.
+ */
+ if (! function_exists('ctype_alpha')) {
+ Core::warnMissingExtension('ctype', true);
+ }
+
+ if (! function_exists('mysqli_connect')) {
+ $moreInfo = sprintf(__('See %sour documentation%s for more information.'), '[doc@faqmysql]', '[/doc]');
+ Core::warnMissingExtension('mysqli', true, $moreInfo);
+ }
+
+ if (! function_exists('session_name')) {
+ Core::warnMissingExtension('session', true);
+ }
+
+ /**
+ * hash is required for cookie authentication.
+ */
+ if (function_exists('hash_hmac')) {
+ return;
+ }
+
+ Core::warnMissingExtension('hash', true);
+ }
}
diff --git a/libraries/classes/Middleware/TokenRequestParamChecking.php b/libraries/classes/Middleware/TokenRequestParamChecking.php
index ad20b13023..7a482ee3bc 100644
--- a/libraries/classes/Middleware/TokenRequestParamChecking.php
+++ b/libraries/classes/Middleware/TokenRequestParamChecking.php
@@ -4,22 +4,71 @@ declare(strict_types=1);
namespace PhpMyAdmin\Middleware;
-use PhpMyAdmin\Application;
+use PhpMyAdmin\Sanitize;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
+use function __;
+use function hash_equals;
+use function is_scalar;
+use function session_id;
+use function strlen;
+use function trigger_error;
+
+use const E_USER_ERROR;
+
+/**
+ * Check whether user supplied token is valid, if not remove any possibly
+ * dangerous stuff from request.
+ *
+ * Check for token mismatch only if the Request method is POST.
+ * GET Requests would never have token and therefore checking
+ * mismatch does not make sense.
+ */
final class TokenRequestParamChecking implements MiddlewareInterface
{
- public function __construct(private readonly Application $application)
- {
- }
-
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
- $this->application->checkTokenRequestParam();
+ $this->checkTokenRequestParam();
return $handler->handle($request);
}
+
+ public function checkTokenRequestParam(): void
+ {
+ $GLOBALS['token_mismatch'] = true;
+ $GLOBALS['token_provided'] = false;
+
+ if (($_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'POST') {
+ return;
+ }
+
+ if (isset($_POST['token']) && is_scalar($_POST['token']) && strlen((string) $_POST['token']) > 0) {
+ $GLOBALS['token_provided'] = true;
+ $GLOBALS['token_mismatch'] = ! @hash_equals($_SESSION[' PMA_token '], (string) $_POST['token']);
+ }
+
+ if (! $GLOBALS['token_mismatch']) {
+ return;
+ }
+
+ // Warn in case the mismatch is result of failed setting of session cookie
+ if (isset($_POST['set_session']) && $_POST['set_session'] !== session_id()) {
+ trigger_error(
+ __(
+ 'Failed to set session cookie. Maybe you are using HTTP instead of HTTPS to access phpMyAdmin.',
+ ),
+ E_USER_ERROR,
+ );
+ }
+
+ /**
+ * We don't allow any POST operation parameters if the token is mismatched
+ * or is not provided.
+ */
+ $allowList = ['ajax_request'];
+ Sanitize::removeRequestVars($allowList);
+ }
}
diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon
index a1694aaa44..9d647d2ef5 100644
--- a/phpstan-baseline.neon
+++ b/phpstan-baseline.neon
@@ -45,21 +45,6 @@ parameters:
count: 1
path: libraries/classes/Advisory/Advisor.php
- -
- message: "#^Cannot access offset 'table' on mixed\\.$#"
- count: 1
- path: libraries/classes/Application.php
-
- -
- message: "#^Parameter \\#1 \\$known_string of function hash_equals expects string, mixed given\\.$#"
- count: 1
- path: libraries/classes/Application.php
-
- -
- message: "#^Parameter \\#2 \\$value of method Symfony\\\\Component\\\\DependencyInjection\\\\ContainerInterface\\:\\:setParameter\\(\\) expects array\\|bool\\|float\\|int\\|string\\|UnitEnum\\|null, mixed given\\.$#"
- count: 1
- path: libraries/classes/Application.php
-
-
message: "#^Cannot access offset 'AllowSharedBookmarks' on mixed\\.$#"
count: 3
@@ -15810,6 +15795,16 @@ parameters:
count: 1
path: libraries/classes/Middleware/CurrentServerGlobalSetting.php
+ -
+ message: "#^Cannot access offset 'table' on mixed\\.$#"
+ count: 1
+ path: libraries/classes/Middleware/DatabaseAndTableSetting.php
+
+ -
+ message: "#^Parameter \\#2 \\$value of method Symfony\\\\Component\\\\DependencyInjection\\\\ContainerInterface\\:\\:setParameter\\(\\) expects array\\|bool\\|float\\|int\\|string\\|UnitEnum\\|null, mixed given\\.$#"
+ count: 1
+ path: libraries/classes/Middleware/DatabaseAndTableSetting.php
+
-
message: "#^Cannot call method getVersion\\(\\) on mixed\\.$#"
count: 1
@@ -15835,6 +15830,11 @@ parameters:
count: 1
path: libraries/classes/Middleware/SetupPageRedirection.php
+ -
+ message: "#^Parameter \\#1 \\$known_string of function hash_equals expects string, mixed given\\.$#"
+ count: 1
+ path: libraries/classes/Middleware/TokenRequestParamChecking.php
+
-
message: "#^Cannot access offset 'goto' on mixed\\.$#"
count: 1
diff --git a/psalm-baseline.xml b/psalm-baseline.xml
index 976a63e642..ae3c12d174 100644
--- a/psalm-baseline.xml
+++ b/psalm-baseline.xml
@@ -16,15 +16,6 @@
$value
-
-
-
-
-
-
-
-
-
escapeString
@@ -7159,6 +7150,15 @@
+
+
+
+
+
+
+
+
+
@@ -12688,14 +12688,6 @@
providerForTestRules
-
-
- assertFalse
- assertTrue
- assertTrue
- assertTrue
-
-
$result
diff --git a/test/classes/ApplicationTest.php b/test/classes/ApplicationTest.php
index 0578a30fda..e6488bdc77 100644
--- a/test/classes/ApplicationTest.php
+++ b/test/classes/ApplicationTest.php
@@ -57,45 +57,4 @@ final class ApplicationTest extends AbstractTestCase
$this->assertSame($expected, $output);
$this->assertSame($errorHandler, $GLOBALS['errorHandler']);
}
-
- public function testCheckTokenRequestParam(): void
- {
- $application = new Application(
- $this->createStub(ErrorHandler::class),
- $this->createStub(Config::class),
- $this->createStub(Template::class),
- new ResponseFactory($this->createStub(ResponseFactoryInterface::class)),
- );
-
- $_SERVER['REQUEST_METHOD'] = 'GET';
- $application->checkTokenRequestParam();
- $this->assertTrue($GLOBALS['token_mismatch']);
- $this->assertFalse($GLOBALS['token_provided']);
-
- $_SERVER['REQUEST_METHOD'] = 'POST';
- $_POST['test'] = 'test';
- $application->checkTokenRequestParam();
- $this->assertTrue($GLOBALS['token_mismatch']);
- $this->assertFalse($GLOBALS['token_provided']);
- $this->assertArrayNotHasKey('test', $_POST);
-
- $_SERVER['REQUEST_METHOD'] = 'POST';
- $_POST['token'] = 'token';
- $_POST['test'] = 'test';
- $_SESSION[' PMA_token '] = 'mismatch';
- $application->checkTokenRequestParam();
- $this->assertTrue($GLOBALS['token_mismatch']);
- $this->assertTrue($GLOBALS['token_provided']);
- $this->assertArrayNotHasKey('test', $_POST);
-
- $_SERVER['REQUEST_METHOD'] = 'POST';
- $_POST['token'] = 'token';
- $_POST['test'] = 'test';
- $_SESSION[' PMA_token '] = 'token';
- $application->checkTokenRequestParam();
- $this->assertFalse($GLOBALS['token_mismatch']);
- $this->assertTrue($GLOBALS['token_provided']);
- $this->assertArrayHasKey('test', $_POST);
- $this->assertEquals('test', $_POST['test']);
- }
}
diff --git a/test/classes/Middleware/TokenRequestParamCheckingTest.php b/test/classes/Middleware/TokenRequestParamCheckingTest.php
new file mode 100644
index 0000000000..6c5004cbfb
--- /dev/null
+++ b/test/classes/Middleware/TokenRequestParamCheckingTest.php
@@ -0,0 +1,84 @@
+checkTokenRequestParam();
+ $this->assertFalse($GLOBALS['token_mismatch']);
+ $this->assertTrue($GLOBALS['token_provided']);
+ $this->assertArrayHasKey('test', $_POST);
+ $this->assertEquals('test', $_POST['test']);
+ }
+
+ public function testCheckTokenRequestParamWithGetMethod(): void
+ {
+ $_REQUEST = [];
+ $_GET = [];
+ $_POST = [];
+ $_COOKIE = [];
+
+ $middleware = new TokenRequestParamChecking();
+
+ $_SERVER['REQUEST_METHOD'] = 'GET';
+ $middleware->checkTokenRequestParam();
+ $this->assertTrue($GLOBALS['token_mismatch']);
+ $this->assertFalse($GLOBALS['token_provided']);
+ }
+
+ public function testCheckTokenRequestParamWithoutToken(): void
+ {
+ $_REQUEST = [];
+ $_GET = [];
+ $_POST = [];
+ $_COOKIE = [];
+
+ $middleware = new TokenRequestParamChecking();
+
+ $_SERVER['REQUEST_METHOD'] = 'POST';
+ $_POST['test'] = 'test';
+ $middleware->checkTokenRequestParam();
+ $this->assertTrue($GLOBALS['token_mismatch']);
+ $this->assertFalse($GLOBALS['token_provided']);
+ $this->assertArrayNotHasKey('test', $_POST);
+ }
+
+ public function testCheckTokenRequestParamWithTokenMismatch(): void
+ {
+ $_REQUEST = [];
+ $_GET = [];
+ $_POST = [];
+ $_COOKIE = [];
+
+ $middleware = new TokenRequestParamChecking();
+
+ $_SERVER['REQUEST_METHOD'] = 'POST';
+ $_POST['token'] = 'token';
+ $_POST['test'] = 'test';
+ $_SESSION[' PMA_token '] = 'mismatch';
+ $middleware->checkTokenRequestParam();
+ $this->assertTrue($GLOBALS['token_mismatch']);
+ $this->assertTrue($GLOBALS['token_provided']);
+ $this->assertArrayNotHasKey('test', $_POST);
+ }
+}