Merge branch 'QA_5_1' into QA_5_2

Signed-off-by: Maurício Meneghini Fauth <mauricio@fauth.dev>
This commit is contained in:
Maurício Meneghini Fauth 2022-03-11 13:02:15 -03:00
commit 2653b2942b
No known key found for this signature in database
GPG Key ID: 6A16FD38AFC89CC8
21 changed files with 1704 additions and 1410 deletions

2
.gitignore vendored
View File

@ -62,3 +62,5 @@ phpstan.neon
# Infection
infection.json
infection.log
/js/vendor/openlayers/OpenLayers.js.LICENSE.txt
/setup/styles.rtl.css

View File

@ -36,6 +36,9 @@ phpMyAdmin - ChangeLog
- issue #16769 Fix create index form accepting too many columns
- issue #16816 Disable editing to system schemas
- issue #16853 Add better error handling when IndexedDB is not working
- issue Fixed incorrect escaping of special MySQL characters on some pages
- issue #17188 Fix GIS visualization with an edited query
- issue #17418 Remove the use of the deprecated `strftime` function in OpenDocument exports
5.1.3 (2022-02-10)
- issue #17308 Fix broken pagination links in the navigation sidebar

View File

@ -93,7 +93,7 @@
"php-webdriver/webdriver": "^1.11",
"phpmyadmin/coding-standard": "^3.0.0",
"phpstan/extension-installer": "^1.1",
"phpstan/phpstan": "^1.4.5",
"phpstan/phpstan": "^1.4.8",
"phpstan/phpstan-phpunit": "^1.0",
"phpstan/phpstan-webmozart-assert": "^1.0",
"phpunit/phpunit": "^8.5 || ^9.5",
@ -102,7 +102,7 @@
"roave/security-advisories": "dev-latest",
"symfony/console": "^5.2.3",
"tecnickcom/tcpdf": "^6.4.4",
"vimeo/psalm": "^4.19"
"vimeo/psalm": "^4.22"
},
"extra": {
"branch-alias": {
@ -110,19 +110,22 @@
}
},
"scripts": {
"phpcbf": "phpcbf",
"phpcs": "phpcs",
"phpstan": "phpstan analyse",
"psalm": "psalm --no-diff",
"phpunit": "phpunit --color=always",
"phpcbf": "@php phpcbf",
"phpcs": "@php phpcs",
"phpstan": "@php phpstan analyse",
"psalm": "@php psalm --no-diff",
"phpunit": "@php phpunit --color=always",
"test": [
"@phpcs",
"@phpstan",
"@psalm",
"@phpunit"
],
"update:baselines": "phpstan analyse --generate-baseline && psalm --set-baseline=psalm-baseline.xml",
"twig-lint": "php scripts/console lint:twig --ansi --show-deprecations"
"update:baselines": [
"@php phpstan analyse --generate-baseline",
"@php psalm --set-baseline=psalm-baseline.xml"
],
"twig-lint": "@php scripts/console lint:twig --ansi --show-deprecations"
},
"config":{
"sort-packages": true,

View File

@ -1,5 +1,5 @@
/*!
* OpenLayers v6.9.0 (https://openlayers.org/)
* OpenLayers v6.13.0 (https://openlayers.org/)
* Copyright 2005-present, OpenLayers Contributors All rights reserved.
* Licensed under BSD 2-Clause License (https://github.com/openlayers/openlayers/blob/main/LICENSE.md)
*

170
js/vendor/zxcvbn-ts.js vendored
View File

@ -424,7 +424,7 @@ this.zxcvbnts.core = (function (exports) {
this.matchers = {};
this.l33tTable = l33tTable;
this.dictionary = {
userInput: []
userInputs: []
};
this.rankedDictionaries = {};
this.translations = translationKeys;
@ -480,31 +480,45 @@ this.zxcvbnts.core = (function (exports) {
setRankedDictionaries() {
const rankedDictionaries = {};
Object.keys(this.dictionary).forEach(name => {
const list = this.dictionary[name];
if (name === 'userInputs') {
const sanitizedInputs = [];
list.forEach(input => {
const inputType = typeof input;
if (inputType === 'string' || inputType === 'number' || inputType === 'boolean') {
sanitizedInputs.push(input.toString().toLowerCase());
}
});
rankedDictionaries[name] = buildRankedDictionary(sanitizedInputs);
} else {
rankedDictionaries[name] = buildRankedDictionary(list);
}
rankedDictionaries[name] = this.getRankedDictionary(name);
});
this.rankedDictionaries = rankedDictionaries;
}
addMatcher(name, matcher) {
if (this.matchers[name]) {
throw new Error('Matcher already exists');
getRankedDictionary(name) {
const list = this.dictionary[name];
if (name === 'userInputs') {
const sanitizedInputs = [];
list.forEach(input => {
const inputType = typeof input;
if (inputType === 'string' || inputType === 'number' || inputType === 'boolean') {
sanitizedInputs.push(input.toString().toLowerCase());
}
});
return buildRankedDictionary(sanitizedInputs);
}
this.matchers[name] = matcher;
return buildRankedDictionary(list);
}
extendUserInputsDictionary(dictionary) {
if (this.dictionary.userInputs) {
this.dictionary.userInputs = [...this.dictionary.userInputs, ...dictionary];
} else {
this.dictionary.userInputs = dictionary;
}
this.rankedDictionaries.userInputs = this.getRankedDictionary('userInputs');
}
addMatcher(name, matcher) {
if (this.matchers[name]) {
console.info('Matcher already exists');
} else {
this.matchers[name] = matcher;
}
}
}
@ -1283,7 +1297,8 @@ this.zxcvbnts.core = (function (exports) {
const optimalMatchSequence = [];
let k = passwordLength - 1; // find the final best sequence length and score
let sequenceLength = 0;
let sequenceLength = 0; // eslint-disable-next-line no-loss-of-precision
let g = 2e308;
const temp = this.optimal.g[k]; // safety check for empty passwords
@ -1414,6 +1429,7 @@ this.zxcvbnts.core = (function (exports) {
*/
class MatchRepeat {
// eslint-disable-next-line max-statements
match({
password,
omniMatch
@ -1437,20 +1453,45 @@ this.zxcvbnts.core = (function (exports) {
if (match) {
const j = match.index + match[0].length - 1;
const baseGuesses = this.getBaseGuesses(baseToken, omniMatch);
matches.push({
pattern: 'repeat',
i: match.index,
j,
token: match[0],
baseToken,
baseGuesses,
repeatCount: match[0].length / baseToken.length
});
matches.push(this.normalizeMatch(baseToken, j, match, baseGuesses));
lastIndex = j + 1;
}
}
const hasPromises = matches.some(match => {
return match instanceof Promise;
});
if (hasPromises) {
return Promise.all(matches);
}
return matches;
} // eslint-disable-next-line max-params
normalizeMatch(baseToken, j, match, baseGuesses) {
const baseMatch = {
pattern: 'repeat',
i: match.index,
j,
token: match[0],
baseToken,
baseGuesses: 0,
repeatCount: match[0].length / baseToken.length
};
if (baseGuesses instanceof Promise) {
return baseGuesses.then(resolvedBaseGuesses => {
return { ...baseMatch,
baseGuesses: resolvedBaseGuesses
};
});
}
return { ...baseMatch,
baseGuesses
};
}
getGreedyMatch(password, lastIndex) {
@ -1502,7 +1543,16 @@ this.zxcvbnts.core = (function (exports) {
}
getBaseGuesses(baseToken, omniMatch) {
const baseAnalysis = scoring.mostGuessableMatchSequence(baseToken, omniMatch.match(baseToken));
const matches = omniMatch.match(baseToken);
if (matches instanceof Promise) {
return matches.then(resolvedMatches => {
const baseAnalysis = scoring.mostGuessableMatchSequence(baseToken, resolvedMatches);
return baseAnalysis.guesses;
});
}
const baseAnalysis = scoring.mostGuessableMatchSequence(baseToken, matches);
return baseAnalysis.guesses;
}
@ -1758,6 +1808,7 @@ this.zxcvbnts.core = (function (exports) {
date: MatchDate,
dictionary: MatchDictionary,
regex: MatchRegex,
// @ts-ignore => TODO resolve this type issue. This is because it is possible to be async
repeat: MatchRepeat,
sequence: MatchSequence,
spatial: MatchSpatial
@ -1766,6 +1817,7 @@ this.zxcvbnts.core = (function (exports) {
match(password) {
const matches = [];
const promises = [];
const matchers = [...Object.keys(this.matchers), ...Object.keys(Options$1.matchers)];
matchers.forEach(key => {
if (!this.matchers[key] && !Options$1.matchers[key]) {
@ -1774,11 +1826,29 @@ this.zxcvbnts.core = (function (exports) {
const Matcher = this.matchers[key] ? this.matchers[key] : Options$1.matchers[key].Matching;
const usedMatcher = new Matcher();
extend(matches, usedMatcher.match({
const result = usedMatcher.match({
password,
omniMatch: this
}));
});
if (result instanceof Promise) {
result.then(response => {
extend(matches, response);
});
promises.push(result);
} else {
extend(matches, result);
}
});
if (promises.length > 0) {
return new Promise(resolve => {
Promise.all(promises).then(() => {
resolve(sorted(matches));
});
});
}
return sorted(matches);
}
@ -1827,7 +1897,12 @@ this.zxcvbnts.core = (function (exports) {
offlineSlowHashing1e4PerSecond: guesses / 1e4,
offlineFastHashing1e10PerSecond: guesses / 1e10
};
const crackTimesDisplay = {};
const crackTimesDisplay = {
onlineThrottling100PerHour: '',
onlineNoThrottling10PerSecond: '',
offlineSlowHashing1e4PerSecond: '',
offlineFastHashing1e10PerSecond: ''
};
Object.keys(crackTimesSeconds).forEach(scenario => {
const seconds = crackTimesSeconds[scenario];
crackTimesDisplay[scenario] = this.displayTime(seconds);
@ -2113,13 +2188,10 @@ this.zxcvbnts.core = (function (exports) {
const time = () => new Date().getTime();
const zxcvbn = password => {
const createReturnValue = (resolvedMatches, password, start) => {
const feedback = new Feedback();
const matching = new Matching();
const timeEstimates = new TimeEstimates();
const start = time();
const matches = matching.match(password);
const matchSequence = scoring.mostGuessableMatchSequence(password, matches);
const matchSequence = scoring.mostGuessableMatchSequence(password, resolvedMatches);
const calcTime = time() - start;
const attackTimes = timeEstimates.estimateAttackTimes(matchSequence.guesses);
return {
@ -2130,6 +2202,24 @@ this.zxcvbnts.core = (function (exports) {
};
};
const zxcvbn = (password, userInputs) => {
if (userInputs) {
Options$1.extendUserInputsDictionary(userInputs);
}
const matching = new Matching();
const start = time();
const matches = matching.match(password);
if (matches instanceof Promise) {
return matches.then(resolvedMatches => {
return createReturnValue(resolvedMatches, password, start);
});
}
return createReturnValue(matches, password, start);
};
exports.ZxcvbnOptions = Options$1;
exports.zxcvbn = zxcvbn;
@ -2137,5 +2227,5 @@ this.zxcvbnts.core = (function (exports) {
return exports;
}({}));
})({});
//# sourceMappingURL=zxcvbn-ts.js.map

File diff suppressed because one or more lines are too long

View File

@ -54,6 +54,7 @@ use function stripos;
use function strlen;
use function strtolower;
use function strtoupper;
use function strtr;
use function substr;
use function syslog;
use function trigger_error;
@ -505,9 +506,7 @@ class DatabaseInterface implements DbalInterface
) . '\')';
} else {
$sql .= " `Name` LIKE '"
. Util::escapeMysqlWildcards(
$this->escapeString($table, $link)
)
. $this->escapeMysqlLikeString($table, $link)
. "%'";
}
@ -905,7 +904,7 @@ class DatabaseInterface implements DbalInterface
$sql = QueryGenerator::getColumnsSql(
$database,
$table,
Util::escapeMysqlWildcards($this->escapeString($column)),
$this->escapeMysqlLikeString($column),
$full
);
/** @var array<string, array> $fields */
@ -2137,6 +2136,19 @@ class DatabaseInterface implements DbalInterface
return $this->extension->escapeString($this->links[$link], $str);
}
/**
* returns properly escaped string for use in MySQL LIKE clauses
*
* @param string $str string to be escaped
* @param int $link optional database link to use
*
* @return string a MySQL escaped LIKE string
*/
public function escapeMysqlLikeString(string $str, int $link = self::CONNECT_USER)
{
return $this->escapeString(strtr($str, ['\\' => '\\\\', '_' => '\\_', '%' => '\\%']), $link);
}
/**
* Checks if this database server is running on Amazon RDS.
*/

View File

@ -635,6 +635,16 @@ interface DbalInterface
*/
public function escapeString(string $str, $link = DatabaseInterface::CONNECT_USER);
/**
* returns properly escaped string for use in MySQL LIKE clauses
*
* @param string $str string to be escaped
* @param int $link optional database link to use
*
* @return string a MySQL escaped LIKE string
*/
public function escapeMysqlLikeString(string $str, int $link = DatabaseInterface::CONNECT_USER);
/**
* Checks if this database server is running on Amazon RDS.
*/

View File

@ -25,6 +25,7 @@ use function mb_strtolower;
use function mb_substr;
use function ob_get_clean;
use function ob_start;
use function rtrim;
use const PNG_ALL_FILTERS;
@ -220,7 +221,7 @@ class GisVisualization
. ') AS ' . Util::backquote('srid') . ' ';
// Append the original query as the inner query
$modified_query .= 'FROM (' . $sql_query . ') AS '
$modified_query .= 'FROM (' . rtrim($sql_query, ';') . ') AS '
. Util::backquote('temp_gis');
// LIMIT clause

View File

@ -7,10 +7,12 @@ declare(strict_types=1);
namespace PhpMyAdmin;
use function date;
use DateTime;
use const DATE_W3C;
/**
* Simplfied OpenDocument creator class
* Simplified OpenDocument creator class
*/
class OpenDocument
{
@ -33,6 +35,9 @@ EOT;
*/
public static function create($mime, $data)
{
// Use the same date method as other PHP libs
// https://github.com/PHPOffice/PhpSpreadsheet/blob/1.22.0/src/PhpSpreadsheet/Writer/Ods/Meta.php#L49
$dateTimeCreation = (new DateTime())->format(DATE_W3C);
$data = [
$mime,
$data,
@ -45,7 +50,7 @@ EOT;
. '<meta:generator>phpMyAdmin ' . Version::VERSION . '</meta:generator>'
. '<meta:initial-creator>phpMyAdmin ' . Version::VERSION
. '</meta:initial-creator>'
. '<meta:creation-date>' . date('Y-m-d\TH:i:s')
. '<meta:creation-date>' . $dateTimeCreation
. '</meta:creation-date>'
. '</office:meta>'
. '</office:document-meta>',

View File

@ -3349,9 +3349,7 @@ class Privileges
if ($createDb1) {
// Create database with same name and grant all privileges
$query = 'CREATE DATABASE IF NOT EXISTS '
. Util::backquote(
$this->dbi->escapeString($username)
) . ';';
. Util::backquote($username) . ';';
$sqlQuery .= $query;
if (! $this->dbi->tryQuery($query)) {
$message = Message::rawError($this->dbi->getError());
@ -3365,9 +3363,7 @@ class Privileges
$query = 'GRANT ALL PRIVILEGES ON '
. Util::backquote(
Util::escapeMysqlWildcards(
$this->dbi->escapeString($username)
)
Util::escapeMysqlWildcards($username)
) . '.* TO \''
. $this->dbi->escapeString($username)
. '\'@\'' . $this->dbi->escapeString($hostname) . '\';';
@ -3381,9 +3377,7 @@ class Privileges
// Grant all privileges on wildcard name (username\_%)
$query = 'GRANT ALL PRIVILEGES ON '
. Util::backquote(
Util::escapeMysqlWildcards(
$this->dbi->escapeString($username)
) . '\_%'
Util::escapeMysqlWildcards($username) . '\_%'
) . '.* TO \''
. $this->dbi->escapeString($username)
. '\'@\'' . $this->dbi->escapeString($hostname) . '\';';

View File

@ -2342,8 +2342,8 @@ class Util
&& is_scalar($_REQUEST['tbl_group'])
&& strlen((string) $_REQUEST['tbl_group']) > 0
) {
$group = self::escapeMysqlWildcards((string) $_REQUEST['tbl_group']);
$groupWithSeparator = self::escapeMysqlWildcards(
$group = $dbi->escapeMysqlLikeString((string) $_REQUEST['tbl_group']);
$groupWithSeparator = $dbi->escapeMysqlLikeString(
$_REQUEST['tbl_group']
. $GLOBALS['cfg']['NavigationTreeTableSeparator']
);

View File

@ -18,6 +18,7 @@
<exclude-pattern>*/tmp/*</exclude-pattern>
<exclude-pattern>*/twig-templates/*</exclude-pattern>
<exclude-pattern>*/vendor/*</exclude-pattern>
<exclude-pattern>*/.git/*</exclude-pattern>
<rule ref="PhpMyAdmin"/>

View File

@ -120,6 +120,21 @@ parameters:
count: 1
path: libraries/classes/CheckUserPrivileges.php
-
message: "#^Cannot call method getExtension\\(\\) on mixed\\.$#"
count: 1
path: libraries/classes/Command/CacheWarmupCommand.php
-
message: "#^Cannot call method getPathname\\(\\) on mixed\\.$#"
count: 2
path: libraries/classes/Command/CacheWarmupCommand.php
-
message: "#^Cannot call method isFile\\(\\) on mixed\\.$#"
count: 1
path: libraries/classes/Command/CacheWarmupCommand.php
-
message: "#^Method PhpMyAdmin\\\\Command\\\\TwigLintCommand\\:\\:display\\(\\) has parameter \\$filesInfo with no value type specified in iterable type array\\.$#"
count: 1
@ -151,7 +166,7 @@ parameters:
path: libraries/classes/Common.php
-
message: "#^Parameter \\#2 \\$value of method Symfony\\\\Component\\\\DependencyInjection\\\\Container\\:\\:setParameter\\(\\) expects array\\|bool\\|float\\|int\\|string\\|null, mixed given\\.$#"
message: "#^Parameter \\#2 \\$value of method Symfony\\\\Component\\\\DependencyInjection\\\\Container\\:\\:setParameter\\(\\) expects array\\|bool\\|float\\|int\\|string\\|UnitEnum\\|null, mixed given\\.$#"
count: 1
path: libraries/classes/Common.php
@ -1285,6 +1300,11 @@ parameters:
count: 1
path: libraries/classes/Controllers/Import/ImportController.php
-
message: "#^Left side of \\|\\| is always true\\.$#"
count: 1
path: libraries/classes/Controllers/Import/ImportController.php
-
message: "#^Negated boolean expression is always true\\.$#"
count: 1
@ -1770,6 +1790,11 @@ parameters:
count: 1
path: libraries/classes/Controllers/Transformation/WrapperController.php
-
message: "#^Variable \\$mime_type on left side of \\?\\? always exists and is not nullable\\.$#"
count: 2
path: libraries/classes/Controllers/Transformation/WrapperController.php
-
message: "#^Parameter \\#1 \\$string of function substr expects string, mixed given\\.$#"
count: 1
@ -1915,6 +1940,11 @@ parameters:
count: 1
path: libraries/classes/Database/CentralColumns.php
-
message: "#^Right side of \\|\\| is always false\\.$#"
count: 1
path: libraries/classes/Database/CentralColumns.php
-
message: "#^Method PhpMyAdmin\\\\Database\\\\Designer\\:\\:getDatabaseTables\\(\\) has parameter \\$tab_column with no value type specified in iterable type array\\.$#"
count: 1
@ -2700,6 +2730,11 @@ parameters:
count: 2
path: libraries/classes/DatabaseInterface.php
-
message: "#^Parameter \\#2 \\$link of method PhpMyAdmin\\\\DatabaseInterface\\:\\:escapeMysqlLikeString\\(\\) expects int, mixed given\\.$#"
count: 1
path: libraries/classes/DatabaseInterface.php
-
message: "#^Parameter \\#2 \\$table of method PhpMyAdmin\\\\Query\\\\Cache\\:\\:cacheTableData\\(\\) expects bool\\|string, array\\|string given\\.$#"
count: 1
@ -2845,19 +2880,9 @@ parameters:
count: 1
path: libraries/classes/Dbal/DbiMysqli.php
-
message: "#^Method PhpMyAdmin\\\\Dbal\\\\MysqliResult\\:\\:__construct\\(\\) has parameter \\$result with no value type specified in iterable type mysqli_result\\.$#"
count: 1
path: libraries/classes/Dbal/MysqliResult.php
-
message: "#^Method PhpMyAdmin\\\\Dbal\\\\MysqliResult\\:\\:fetchAllKeyPair\\(\\) should return array\\<string, string\\|null\\> but returns array\\<int\\|string, mixed\\>\\.$#"
count: 1
path: libraries/classes/Dbal/MysqliResult.php
-
message: "#^Property PhpMyAdmin\\\\Dbal\\\\MysqliResult\\:\\:\\$result type has no value type specified in iterable type mysqli_result\\.$#"
count: 1
count: 2
path: libraries/classes/Dbal/MysqliResult.php
-
@ -3215,11 +3240,6 @@ parameters:
count: 1
path: libraries/classes/Display/Results.php
-
message: "#^Method PhpMyAdmin\\\\Display\\\\Results\\:\\:setParamForLinkForeignKeyRelatedTables\\(\\) should return array\\<string, array\\<string\\>\\> but returns array\\<int\\|string, array\\>\\.$#"
count: 1
path: libraries/classes/Display/Results.php
-
message: "#^Offset 3 does not exist on array\\{string\\|null, string\\|null, string\\|null\\}\\.$#"
count: 1
@ -5325,6 +5345,11 @@ parameters:
count: 1
path: libraries/classes/Navigation/NavigationTree.php
-
message: "#^Right side of \\|\\| is always false\\.$#"
count: 1
path: libraries/classes/Navigation/NavigationTree.php
-
message: "#^Method PhpMyAdmin\\\\Navigation\\\\NodeFactory\\:\\:getInstance\\(\\) has parameter \\$name with no value type specified in iterable type array\\.$#"
count: 1
@ -5685,6 +5710,11 @@ parameters:
count: 1
path: libraries/classes/Pdf.php
-
message: "#^Left side of \\|\\| is always false\\.$#"
count: 1
path: libraries/classes/Plugins.php
-
message: "#^Method PhpMyAdmin\\\\Plugins\\:\\:getPlugins\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
@ -5695,6 +5725,11 @@ parameters:
count: 1
path: libraries/classes/Plugins.php
-
message: "#^Right side of \\|\\| is always false\\.$#"
count: 3
path: libraries/classes/Plugins.php
-
message: "#^Parameter \\#2 \\$remoteIp of method ReCaptcha\\\\ReCaptcha\\:\\:verify\\(\\) expects string\\|null, bool\\|string given\\.$#"
count: 1
@ -6390,11 +6425,6 @@ parameters:
count: 1
path: libraries/classes/Plugins/Schema/Dia/Dia.php
-
message: "#^Parameter \\#2 \\$masterField of method PhpMyAdmin\\\\Plugins\\\\Schema\\\\Dia\\\\DiaRelationSchema\\:\\:addRelation\\(\\) expects string, int\\|string given\\.$#"
count: 1
path: libraries/classes/Plugins/Schema/Dia/DiaRelationSchema.php
-
message: "#^Binary operation \"\\+\" between int\\|string\\|false and 12 results in an error\\.$#"
count: 4
@ -6415,11 +6445,6 @@ parameters:
count: 1
path: libraries/classes/Plugins/Schema/Eps/EpsRelationSchema.php
-
message: "#^Parameter \\#4 \\$masterField of method PhpMyAdmin\\\\Plugins\\\\Schema\\\\Eps\\\\EpsRelationSchema\\:\\:addRelation\\(\\) expects string, int\\|string given\\.$#"
count: 1
path: libraries/classes/Plugins/Schema/Eps/EpsRelationSchema.php
-
message: "#^Call to an undefined method object\\:\\:line\\(\\)\\.$#"
count: 7
@ -6475,11 +6500,6 @@ parameters:
count: 1
path: libraries/classes/Plugins/Schema/Pdf/PdfRelationSchema.php
-
message: "#^Parameter \\#2 \\$masterField of method PhpMyAdmin\\\\Plugins\\\\Schema\\\\Pdf\\\\PdfRelationSchema\\:\\:addRelation\\(\\) expects string, int\\|string given\\.$#"
count: 1
path: libraries/classes/Plugins/Schema/Pdf/PdfRelationSchema.php
-
message: "#^Parameter \\#2 \\$master_table of class PhpMyAdmin\\\\Plugins\\\\Schema\\\\Pdf\\\\RelationStatsPdf constructor expects string, PhpMyAdmin\\\\Plugins\\\\Schema\\\\Pdf\\\\TableStatsPdf given\\.$#"
count: 1
@ -6530,6 +6550,11 @@ parameters:
count: 1
path: libraries/classes/Plugins/Schema/Pdf/TableStatsPdf.php
-
message: "#^Binary operation \"\\+\" between int\\|string\\|false and 1\\.5 results in an error\\.$#"
count: 1
path: libraries/classes/Plugins/Schema/RelationStats.php
-
message: "#^Method PhpMyAdmin\\\\Plugins\\\\Schema\\\\RelationStats\\:\\:getXy\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
@ -6585,11 +6610,6 @@ parameters:
count: 1
path: libraries/classes/Plugins/Schema/Svg/SvgRelationSchema.php
-
message: "#^Parameter \\#4 \\$masterField of method PhpMyAdmin\\\\Plugins\\\\Schema\\\\Svg\\\\SvgRelationSchema\\:\\:addRelation\\(\\) expects string, int\\|string given\\.$#"
count: 1
path: libraries/classes/Plugins/Schema/Svg/SvgRelationSchema.php
-
message: "#^Parameter \\#4 \\$y of method PhpMyAdmin\\\\Plugins\\\\Schema\\\\Svg\\\\Svg\\:\\:startSvgDoc\\(\\) expects int, float\\|int given\\.$#"
count: 1
@ -8305,6 +8325,11 @@ parameters:
count: 1
path: libraries/classes/Table.php
-
message: "#^Right side of \\|\\| is always false\\.$#"
count: 1
path: libraries/classes/Table.php
-
message: "#^Method PhpMyAdmin\\\\Table\\\\ColumnsDefinition\\:\\:displayForm\\(\\) has parameter \\$fields_meta with no value type specified in iterable type array\\.$#"
count: 1

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<files psalm-version="4.21.0@d8bec4c7aaee111a532daec32fb09de5687053d1">
<files psalm-version="4.22.0@fc2c6ab4d5fa5d644d8617089f012f3bb84b8703">
<file src="index.php">
<InvalidGlobal occurrences="1">
<code>global $route, $containerBuilder, $request;</code>
@ -5396,7 +5396,7 @@
<InvalidReturnType occurrences="1">
<code>int|bool</code>
</InvalidReturnType>
<MixedArgument occurrences="50">
<MixedArgument occurrences="51">
<code>$_SERVER['SCRIPT_NAME']</code>
<code>$a</code>
<code>$arrayKeys</code>
@ -5419,6 +5419,7 @@
<code>$link</code>
<code>$link</code>
<code>$link</code>
<code>$link</code>
<code>$one_database_tables</code>
<code>$one_database_tables</code>
<code>$one_table_name</code>
@ -15016,12 +15017,16 @@
</NonInvariantDocblockPropertyType>
</file>
<file src="test/classes/Gis/GisVisualizationTest.php">
<MixedAssignment occurrences="5">
<MixedAssignment occurrences="9">
<code>$dataSet</code>
<code>$queryString</code>
<code>$queryString</code>
<code>$queryString</code>
<code>$queryString</code>
<code>$queryString</code>
<code>$queryString</code>
<code>$queryString</code>
<code>$queryString</code>
</MixedAssignment>
</file>
<file src="test/classes/GitTest.php">
@ -15330,6 +15335,14 @@
<code>assertIsArray</code>
</RedundantConditionGivenDocblockType>
</file>
<file src="test/classes/OpenDocumentTest.php">
<MixedArgument occurrences="1">
<code>$zipExtension-&gt;getContents($tmpFile, '/meta\.xml/')['data']</code>
</MixedArgument>
<RedundantConditionGivenDocblockType occurrences="1">
<code>assertNotFalse</code>
</RedundantConditionGivenDocblockType>
</file>
<file src="test/classes/PdfTest.php">
<MixedArgument occurrences="3">
<code>$arr-&gt;getPDFData()</code>

View File

@ -188,6 +188,7 @@ cleanup_composer_vendors() {
vendor/twig/twig/.editorconfig \
vendor/twig/twig/.php_cs.dist \
vendor/twig/twig/drupal_test.sh \
vendor/twig/twig/.php-cs-fixer.dist.php \
vendor/webmozart/assert/.editorconfig \
vendor/webmozart/assert/.github/ \
vendor/webmozart/assert/.php_cs \

View File

@ -71,11 +71,11 @@ echo 'Updating OpenLayers'
cp ./node_modules/ol/ol.css ./js/vendor/openlayers/theme/ol.css
npx webpack-cli --config ./js/config/ol/webpack.config.js
echo "/*!
* OpenLayers v$(yarn -s info ol version) (https://openlayers.org/)
* OpenLayers v$(yarn info -s ol version) (https://openlayers.org/)
* Copyright 2005-present, OpenLayers Contributors All rights reserved.
* Licensed under BSD 2-Clause License (https://github.com/openlayers/openlayers/blob/main/LICENSE.md)
*
* @license $(yarn -s info ol license)
* @license $(yarn info -s ol license)
*/
$(cat ./js/vendor/openlayers/OpenLayers.js)" > ./js/vendor/openlayers/OpenLayers.js
echo 'Updating sprintf'

View File

@ -131,6 +131,106 @@ class GisVisualizationTest extends AbstractTestCase
);
}
/**
* Modify the query for an MySQL 8.0 version and trim the SQL end character
*/
public function testModifyQueryTrimSqlEnd(): void
{
$queryString = $this->callFunction(
GisVisualization::getByData([], [
'mysqlVersion' => 80000,
'spatialColumn' => 'abc',
'isMariaDB' => false,
]),
GisVisualization::class,
'modifySqlQuery',
[
'SELECT 1 FROM foo;',
0,
0,
]
);
$this->assertEquals(
'SELECT ST_ASTEXT(`abc`) AS `abc`, ST_SRID(`abc`) AS `srid` FROM (SELECT 1 FROM foo) AS `temp_gis`',
$queryString
);
}
/**
* Modify the query for an MySQL 8.0 version using a label column
*/
public function testModifyQueryLabelColumn(): void
{
$queryString = $this->callFunction(
GisVisualization::getByData([], [
'mysqlVersion' => 80000,
'spatialColumn' => 'country_geom',
'labelColumn' => 'country name',
'isMariaDB' => false,
]),
GisVisualization::class,
'modifySqlQuery',
[
'',
0,
0,
]
);
$this->assertEquals(
'SELECT `country name`, ST_ASTEXT(`country_geom`) AS `country_geom`,'
. ' ST_SRID(`country_geom`) AS `srid` FROM () AS `temp_gis`',
$queryString
);
}
/**
* Modify the query for an MySQL 8.0 version adding a LIMIT statement
*/
public function testModifyQueryWithLimit(): void
{
$queryString = $this->callFunction(
GisVisualization::getByData([], [
'mysqlVersion' => 80000,
'spatialColumn' => 'abc',
'isMariaDB' => false,
]),
GisVisualization::class,
'modifySqlQuery',
[
'',
10,// 10 rows
0,
]
);
$this->assertEquals(
'SELECT ST_ASTEXT(`abc`) AS `abc`, ST_SRID(`abc`) AS `srid` FROM () AS `temp_gis` LIMIT 0, 10',
$queryString
);
$queryString = $this->callFunction(
GisVisualization::getByData([], [
'mysqlVersion' => 80000,
'spatialColumn' => 'abc',
'isMariaDB' => false,
]),
GisVisualization::class,
'modifySqlQuery',
[
'',
15,// 15 rows
10,// position 10
]
);
$this->assertEquals(
'SELECT ST_ASTEXT(`abc`) AS `abc`, ST_SRID(`abc`) AS `srid` FROM () AS `temp_gis` LIMIT 10, 15',
$queryString
);
}
/**
* Modify the query for an MySQL 8.0.1 version
*/

View File

@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Tests;
use DateTime;
use PhpMyAdmin\OpenDocument;
use PhpMyAdmin\ZipExtension;
use ZipArchive;
use function file_put_contents;
use function tempnam;
use function unlink;
/**
* @requires extension zip
*/
class OpenDocumentTest extends AbstractTestCase
{
public function testCreateDocument(): void
{
$document = OpenDocument::create(
'application/vnd.oasis.opendocument.text',
'<data>'
);
$this->assertNotFalse($document);
$tmpFile = (string) tempnam('./', 'open-document-test');
$this->assertNotFalse(file_put_contents($tmpFile, $document), 'The temp file should be written');
$zipExtension = new ZipExtension(new ZipArchive());
$this->assertSame([
'error' => '',
'data' => 'application/vnd.oasis.opendocument.text',
], $zipExtension->getContents($tmpFile));
$this->assertSame([
'error' => '',
'data' => '<data>',
], $zipExtension->getContents($tmpFile, '/content\.xml/'));
$dateTimeCreation = (new DateTime())->format('Y-m-d\TH:i');
$this->assertStringContainsString(
// Do not use a full version or seconds could be out of sync and cause flaky test failures
'<meta:creation-date>' . $dateTimeCreation,
$zipExtension->getContents($tmpFile, '/meta\.xml/')['data']
);
$this->assertSame(5, $zipExtension->getNumberOfFiles($tmpFile));
$this->assertTrue(unlink($tmpFile));
}
}

View File

@ -1768,7 +1768,7 @@ class DbiDummy implements DbiExtension
'result' => [],
],
[
'query' => "SHOW TABLE STATUS FROM `my_dataset` WHERE `Name` LIKE 'company\_users%'",
'query' => "SHOW TABLE STATUS FROM `my_dataset` WHERE `Name` LIKE 'company\\\\_users%'",
'result' => [],
],
[
@ -2389,7 +2389,7 @@ class DbiDummy implements DbiExtension
'result' => [['1']],
],
[
'query' => 'SHOW TABLE STATUS FROM `PMA_db` WHERE `Name` LIKE \'PMA\_table%\'',
'query' => 'SHOW TABLE STATUS FROM `PMA_db` WHERE `Name` LIKE \'PMA\\\\_table%\'',
'columns' => ['Name', 'Engine'],
'result' => [['PMA_table', 'InnoDB']],
],
@ -2443,7 +2443,7 @@ class DbiDummy implements DbiExtension
],
],
[
'query' => 'SHOW FULL COLUMNS FROM `testdb`.`mytable` LIKE \'\_id\'',
'query' => 'SHOW FULL COLUMNS FROM `testdb`.`mytable` LIKE \'\\\\_id\'',
'columns' => ['Field', 'Type', 'Collation', 'Null', 'Key', 'Default', 'Extra', 'Privileges', 'Comment'],
'result' => [
[
@ -2548,7 +2548,7 @@ class DbiDummy implements DbiExtension
],
],
[
'query' => 'SHOW TABLE STATUS FROM `test_db` WHERE `Name` LIKE \'test\_table%\'',
'query' => 'SHOW TABLE STATUS FROM `test_db` WHERE `Name` LIKE \'test\\\\_table%\'',
'columns' => ['Name', 'Engine', 'Rows'],
'result' => [['test_table', 'InnoDB', '3']],
],
@ -2713,7 +2713,7 @@ class DbiDummy implements DbiExtension
'result' => [],
],
[
'query' => 'SHOW TABLE STATUS FROM `my_db` WHERE `Name` LIKE \'test\_tbl%\'',
'query' => 'SHOW TABLE STATUS FROM `my_db` WHERE `Name` LIKE \'test\\\\_tbl%\'',
'result' => [],
],
[

2567
yarn.lock

File diff suppressed because it is too large Load Diff