diff --git a/libraries/classes/Util.php b/libraries/classes/Util.php index 53ca6496c6..3c66198d1e 100644 --- a/libraries/classes/Util.php +++ b/libraries/classes/Util.php @@ -71,6 +71,7 @@ use function set_time_limit; use function sort; use function sprintf; use function str_contains; +use function str_getcsv; use function str_pad; use function str_replace; use function strcasecmp; @@ -1906,11 +1907,16 @@ class Util // If you are fixing something here, // you need to also update the JS port. - preg_match('|\((.*)\)|', $definition, $matches); + // This should really be delegated to MySQL but since we also want to HTML encode it, + // it is easier this way. + // It future replace str_getcsv with $dbi->fetchSingleRow('SELECT '.$expressionInBrackets[1]); + + preg_match('/\((.*)\)/', $definition, $expressionInBrackets); + $matches = str_getcsv($expressionInBrackets[1], ',', "'"); $values = []; - foreach (explode(',', $matches[1]) as $value) { - $value = strtr(substr($value, 1, -1), ["''" => "'", "\\'" => "'", '\\\\' => '\\']); + foreach ($matches as $value) { + $value = strtr($value, ['\\\\' => '\\']); // str_getcsv doesn't unescape backslashes so we do it ourselves $values[] = $escapeHtml ? htmlspecialchars($value, ENT_QUOTES, 'UTF-8') : $value; } diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 0a87f82ad3..b9e5ad0278 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1635,6 +1635,16 @@ parameters: count: 1 path: libraries/classes/Controllers/Table/Structure/SaveController.php + - + message: "#^Comparison operation \"\\>\" between int\\<1, max\\> and 0 is always true\\.$#" + count: 1 + path: libraries/classes/Controllers/Table/Structure/SaveController.php + + - + message: "#^Else branch is unreachable because ternary operator condition is always true\\.$#" + count: 1 + path: libraries/classes/Controllers/Table/Structure/SaveController.php + - message: "#^Method PhpMyAdmin\\\\Controllers\\\\Table\\\\Structure\\\\SaveController\\:\\:adjustColumnPrivileges\\(\\) has parameter \\$adjust_privileges with no value type specified in iterable type array\\.$#" count: 1 @@ -4520,11 +4530,6 @@ parameters: count: 2 path: libraries/classes/Import.php - - - message: "#^Parameter \\#1 \\$importRunBuffer of method PhpMyAdmin\\\\Import\\:\\:runQueryPost\\(\\) expects array\\|null, array\\|float\\|int\\|string\\|false\\|null given\\.$#" - count: 1 - path: libraries/classes/Import.php - - message: "#^Parameter \\#1 \\$lastCumulativeSize of method PhpMyAdmin\\\\Import\\:\\:getDecimalPrecision\\(\\) expects string, int\\|string given\\.$#" count: 3 @@ -5615,9 +5620,14 @@ parameters: count: 1 path: libraries/classes/ParseAnalyze.php + - + message: "#^Offset 'select_tables' does not exist on array\\{distinct\\?\\: bool, drop_database\\?\\: bool, group\\?\\: bool, having\\?\\: bool, is_affected\\?\\: bool, is_analyse\\?\\: bool, is_count\\?\\: bool, is_delete\\?\\: bool, \\.\\.\\.\\}\\.$#" + count: 3 + path: libraries/classes/ParseAnalyze.php + - message: "#^Offset 'select_tables' on array\\{distinct\\?\\: bool, drop_database\\?\\: bool, group\\?\\: bool, having\\?\\: bool, is_affected\\?\\: bool, is_analyse\\?\\: bool, is_count\\?\\: bool, is_delete\\?\\: bool, \\.\\.\\.\\} in empty\\(\\) does not exist\\.$#" - count: 1 + count: 2 path: libraries/classes/ParseAnalyze.php - @@ -9030,6 +9040,11 @@ parameters: count: 1 path: libraries/classes/Util.php + - + message: "#^Parameter \\#1 \\$str of function strtr expects string, string\\|null given\\.$#" + count: 1 + path: libraries/classes/Util.php + - message: "#^Cannot cast mixed to int\\.$#" count: 1 diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 620a3009b7..666815a6ac 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -14248,19 +14248,12 @@ $value $values['token'] - - string - get get getCookie getCookie - - $html_separator - $separator - (string) $db (string) $table @@ -14484,9 +14477,10 @@ $sep $tableGroup - + $maxSize $maxUnit + $value $maxSize diff --git a/test/classes/Controllers/Sql/EnumValuesControllerTest.php b/test/classes/Controllers/Sql/EnumValuesControllerTest.php index 0363ef30ef..57a373e838 100644 --- a/test/classes/Controllers/Sql/EnumValuesControllerTest.php +++ b/test/classes/Controllers/Sql/EnumValuesControllerTest.php @@ -63,7 +63,7 @@ class EnumValuesControllerTest extends AbstractTestCase [ [ 'set', - "set('','a&b','b&c','vrai&','','漢字','''','\\','\\\"\\\\''')", + "set('','a&b','b&c','vrai&','','漢字','''','\\\\','\"\\\\''')", 'No', '', 'NULL', @@ -110,7 +110,7 @@ class EnumValuesControllerTest extends AbstractTestCase . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" - . ' ' . "\n" + . ' ' . "\n" . ' ' . "\n", ], $this->getResponseJsonResult() diff --git a/test/classes/UtilTest.php b/test/classes/UtilTest.php index 0c5cdb27cc..772bf70e2a 100644 --- a/test/classes/UtilTest.php +++ b/test/classes/UtilTest.php @@ -782,7 +782,7 @@ class UtilTest extends AbstractTestCase 'zerofill' => false, 'spec_in_brackets' => "'\'a','b'", 'enum_set_values' => [ - "'a", + "\'a", 'b', ], 'attribute' => ' ', @@ -819,7 +819,7 @@ class UtilTest extends AbstractTestCase 'spec_in_brackets' => "'a&b','b''c\\'d','e\\\\f'", 'enum_set_values' => [ 'a&b', - 'b\'c\'d', + 'b\'c\\\'d', 'e\\f', ], 'attribute' => ' ', @@ -890,6 +890,126 @@ class UtilTest extends AbstractTestCase ]; } + /** + * Test case for parsing ENUM values + * + * @param string[] $out + * + * @dataProvider providerParseEnumSetValues + */ + public function testParseEnumSetValues(string $in, bool $escapeHTML, array $out): void + { + $this->assertEquals( + $out, + Util::parseEnumSetValues($in, $escapeHTML) + ); + } + + /** + * Data provider for testParseEnumSetValues + * + * @return iterable + */ + public function providerParseEnumSetValues(): iterable + { + $enumSpec = "enum('a&b','b''c''d','e\\f')"; + + yield [ + $enumSpec, + false, + [ + 'a&b', + 'b\'c\'d', + 'e\\f', + ], + ]; + + yield [ + $enumSpec, + true, + [ + 'a&b', + 'b'c'd', + 'e\\f', + ], + ]; + + $enumSpec = "set('','a&b','b&c','vrai&','','漢字','''','\\\\','\"\\\\''')"; + + yield [ + $enumSpec, + false, + [ + '', + 'a&b', + 'b&c', + 'vrai&', + '', + '漢字', + "'", + '\\', + '"\\\'', + ], + ]; + + yield [ + $enumSpec, + true, + [ + '<script>alert("ok")</script>', + 'a&b', + 'b&c', + 'vrai&amp', + '', + '漢字', + ''', + '\\', + '"\'', + ], + ]; + + $enumSpec = "enum('1','2,','3''','''4')"; + + yield [ + $enumSpec, + false, + [ + '1', + '2,', + '3\'', + '\'4', + ], + ]; + + yield [ + $enumSpec, + true, + [ + '1', + '2,', + '3'', + ''4', + ], + ]; + + $enumSpec = "enum('''','''''','\"','\\\\','\\\\''','\\\\\"',',','()')"; + + yield [ + $enumSpec, + false, + [ + "'", + "''", + '"', + '\\', + "\\'", + '\\"', + ',', + '()', + ], + ]; + } + /** * Test for Util::extractValueFromFormattedSize *