Merge branch 'QA_4_9' into QA_5_0

This commit is contained in:
William Desportes 2023-03-25 00:42:54 +01:00
commit 5f12e75bfd
No known key found for this signature in database
GPG Key ID: 90A0EF1B8251A889
61 changed files with 1121 additions and 344 deletions

1
.gitignore vendored
View File

@ -61,3 +61,4 @@ composer.lock
phpstan.neon
libraries/cache/
js/dist/
/setup/styles.css.map

View File

@ -214,6 +214,23 @@ phpMyAdmin - ChangeLog
- issue #15677 Fix show process-list triggers a php exception
- issue #15697 Fix uncaught php error: "Call to a member function get() on null" in db_export.php when exporting a table from the list
4.9.11 (2023-02-07)
- issue [security] Fix an XSS attack through the drag-and-drop upload feature
4.9.10 (2022-02-10)
- issue #17308 Fix broken pagination links in the navigation sidebar
4.9.9 (2022-01-22)
- issue #17305 Fix syntax error for PHP 5
- issue #17307 Fix hide_connection_errors being undefined when a controluser is set
4.9.8 (2022-01-20)
- issue #14321 Display a correct error page when "$cfg['Servers'][$i]['SignonURL']" is empty for auth_type=signon
- issue #14321 [security] Remove leaked HTML on signon page redirect before login for auth_type=signon
- issue [security] Add configuration directive $cfg['Servers'][$i]['hide_connection_errors'] to allow hiding host names and other error details when login fails
- issue [security] Add configuration directive $cfg['URLQueryEncryption'] to allow encrypting senstive information in the URL
- issue [security] Fix a scenario where an authenticated user can disable two factor authentication
4.9.7 (2020-10-15)
- issue #16397 Fix compatibility problems with older PHP versions (also issue #16399)
- issue #16396 Fix broken two-factor authentication

View File

@ -68,7 +68,7 @@
"samyoul/u2f-php-server": "<1.1"
},
"suggest": {
"ext-openssl": "Cookie encryption",
"ext-openssl": "For encryption performance",
"ext-curl": "Updates checking",
"ext-opcache": "Better performance",
"ext-zlib": "For gz import and export",
@ -78,7 +78,10 @@
"ext-mbstring": "For best performance",
"tecnickcom/tcpdf": "For PDF support",
"pragmarx/google2fa-qrcode": "For 2FA authentication",
"samyoul/u2f-php-server": "For FIDO U2F authentication"
"pragmarx/google2fa": "For 2FA authentication",
"bacon/bacon-qr-code": "For 2FA authentication",
"samyoul/u2f-php-server": "For FIDO U2F authentication",
"paragonie/sodium_compat": "For modern encryption support"
},
"require-dev": {
"php-webdriver/webdriver": "^1.7.1",
@ -88,7 +91,9 @@
"pragmarx/google2fa-qrcode": "^1.0.1",
"samyoul/u2f-php-server": "^1.1",
"squizlabs/php_codesniffer": "^3.0",
"tecnickcom/tcpdf": "^6.3"
"tecnickcom/tcpdf": "^6.3",
"paragonie/random_compat": ">=2",
"paragonie/sodium_compat": "^1.17"
},
"extra": {
"branch-alias": {
@ -107,6 +112,11 @@
]
},
"config":{
"sort-packages": true
"sort-packages": true,
"allow-plugins": {
"dealerdirect/phpcodesniffer-composer-installer": true,
"phpstan/extension-installer": true,
"composer/package-versions-deprecated": true
}
}
}

View File

@ -149,6 +149,13 @@ $cfg['SaveDir'] = '';
*/
//$cfg['SendErrorReports'] = 'always';
/**
* 'URLQueryEncryption' defines whether phpMyAdmin will encrypt sensitive data from the URL query string.
* 'URLQueryEncryptionSecretKey' is a 32 bytes long secret key used to encrypt/decrypt the URL query string.
*/
//$cfg['URLQueryEncryption'] = true;
//$cfg['URLQueryEncryptionSecretKey'] = '';
/**
* You can find more configuration options in the documentation
* in the doc/ folder or at <https://docs.phpmyadmin.net/>.

View File

@ -1485,6 +1485,20 @@ Server connection settings
after logout (doesn't affect config authentication method). Should be
absolute including protocol.
.. config:option:: $cfg['Servers'][$i]['hide_connection_errors']
:type: boolean
:default: false
.. versionadded:: 4.9.8
Whether to show or hide detailed MySQL/MariaDB connection errors on the login page.
.. note::
This error message can contain the target database server hostname or IP address,
which may reveal information about your network to an attacker.
Generic settings
----------------
@ -1753,6 +1767,27 @@ Generic settings
When enabled, a user can drag a file in to their browser and phpMyAdmin will
attempt to import the file.
.. config:option:: $cfg['URLQueryEncryption']
:type: boolean
:default: false
.. versionadded:: 4.9.8
Define whether phpMyAdmin will encrypt sensitive data (like database name
and table name) from the URL query string. Default is to not encrypt the URL
query string.
.. config:option:: $cfg['URLQueryEncryptionSecretKey']
:type: string
:default: ``''``
.. versionadded:: 4.9.8
A secret key used to encrypt/decrypt the URL query string.
Should be 32 bytes long.
Cookie authentication options
-----------------------------

View File

@ -132,7 +132,7 @@ var DragDropImport = {
var filename = $this.parent('span').attr('data-filename');
$('body').append('<div class="pma_drop_result"><h2>' +
Messages.dropImportImportResultHeader + ' - ' +
filename + '<span class="close">x</span></h2>' + value.message + '</div>');
Functions.escapeHtml(filename) + '<span class="close">x</span></h2>' + value.message + '</div>');
$('.pma_drop_result').draggable(); // to make this dialog draggable
}
});

View File

@ -161,7 +161,7 @@ Navigation.loadChildNodes = function (isNode, $expandElem, callback) {
}
var url = $('#pma_navigation').find('a.navigation_url').attr('href');
$.get(url, params, function (data) {
$.post(url, params, function (data) {
if (typeof data !== 'undefined' && data.success === true) {
$destination.find('div.list_container').remove(); // FIXME: Hack, there shouldn't be a list container there
if (isNode) {
@ -1070,14 +1070,12 @@ Navigation.selectCurrentDatabase = function () {
Navigation.treePagination = function ($this) {
var $msgbox = Functions.ajaxShowMessage();
var isDbSelector = $this.closest('div.pageselector').is('.dbselector');
var url;
var params;
var url = 'navigation.php';
var params = 'ajax_request=true';
if ($this[0].tagName === 'A') {
url = $this.attr('href');
params = 'ajax_request=true';
params += PMA_commonParams.get('arg_separator') + $this.getPostData();
} else { // tagName === 'SELECT'
url = 'navigation.php';
params = $this.closest('form').serialize() + CommonParams.get('arg_separator') + 'ajax_request=true';
params += PMA_commonParams.get('arg_separator') + $this.closest('form').serialize();
}
var searchClause = Navigation.FastFilter.getSearchClause();
if (searchClause) {

View File

@ -342,7 +342,10 @@ class StructureController extends AbstractController
// got to be eliminated in long run
$db = &$this->db;
$table = &$this->table;
$url_params = [];
$url_params = [
'db' => $db,
'table' => $table,
];
include_once ROOT_PATH . 'libraries/tbl_common.inc.php';
$this->_db_is_system_schema = $db_is_system_schema;
$this->_url_query = Url::getCommonRaw([
@ -1280,14 +1283,6 @@ class StructureController extends AbstractController
'DistinctValues' => Util::getIcon('b_browse', __('Distinct values')),
];
$edit_view_url = '';
if ($this->_tbl_is_view && ! $this->_db_is_system_schema) {
$edit_view_url = Url::getCommon([
'db' => $this->db,
'table' => $this->table,
]);
}
/**
* Displays Space usage and row statistics
*/
@ -1359,10 +1354,6 @@ class StructureController extends AbstractController
$engine = $this->table_obj->getStorageEngine();
return $this->template->render('table/structure/display_structure', [
'url_params' => [
'db' => $this->db,
'table' => $this->table,
],
'collations' => $collations,
'is_foreign_key_supported' => Util::isForeignKeySupported($engine),
'displayIndexesHtml' => Index::getHtmlForDisplayIndexes(),
@ -1374,11 +1365,11 @@ class StructureController extends AbstractController
'tbl_is_view' => $this->_tbl_is_view,
'mime_map' => $mime_map,
'url_query' => $this->_url_query,
'url_params' => $url_params,
'titles' => $titles,
'tbl_storage_engine' => $this->_tbl_storage_engine,
'primary' => $primary_index,
'columns_with_unique_index' => $columns_with_unique_index,
'edit_view_url' => $edit_view_url,
'columns_list' => $columns_list,
'table_stats' => isset($tablestats) ? $tablestats : null,
'fields' => $fields,

View File

@ -1304,4 +1304,36 @@ class Core
$hmac = hash_hmac('sha256', $sqlQuery, $secret . $cfg['blowfish_secret']);
return hash_equals($hmac, $signature);
}
/**
* @return void
*/
public static function populateRequestWithEncryptedQueryParams()
{
if (
(! isset($_GET['eq']) || ! is_string($_GET['eq']))
&& (! isset($_POST['eq']) || ! is_string($_POST['eq']))
) {
unset($_GET['eq'], $_POST['eq'], $_REQUEST['eq']);
return;
}
$isFromPost = isset($_POST['eq']);
$decryptedQuery = Url::decryptQuery($isFromPost ? $_POST['eq'] : $_GET['eq']);
unset($_GET['eq'], $_POST['eq'], $_REQUEST['eq']);
if ($decryptedQuery === null) {
return;
}
$urlQueryParams = (array) json_decode($decryptedQuery);
foreach ($urlQueryParams as $urlQueryParamKey => $urlQueryParamValue) {
if ($isFromPost) {
$_POST[$urlQueryParamKey] = $urlQueryParamValue;
} else {
$_GET[$urlQueryParamKey] = $urlQueryParamValue;
}
$_REQUEST[$urlQueryParamKey] = $urlQueryParamValue;
}
}
}

View File

@ -0,0 +1,161 @@
<?php
namespace PhpMyAdmin\Crypto;
use Exception;
use phpseclib\Crypt\AES;
use phpseclib\Crypt\Random;
final class Crypto
{
/** @var bool */
private $hasRandomBytesSupport;
/** @var bool */
private $hasSodiumSupport;
/**
* @param bool $forceFallback Force the usage of the fallback functions.
*/
public function __construct($forceFallback = false)
{
$this->hasRandomBytesSupport = ! $forceFallback && is_callable('random_bytes');
$this->hasSodiumSupport = ! $forceFallback
&& $this->hasRandomBytesSupport
&& is_callable('sodium_crypto_secretbox')
&& is_callable('sodium_crypto_secretbox_open')
&& defined('SODIUM_CRYPTO_SECRETBOX_NONCEBYTES')
&& defined('SODIUM_CRYPTO_SECRETBOX_KEYBYTES');
}
/**
* @param string $plaintext
*
* @return string
*/
public function encrypt($plaintext)
{
if ($this->hasSodiumSupport) {
return $this->encryptWithSodium($plaintext);
}
return $this->encryptWithPhpseclib($plaintext);
}
/**
* @param string $ciphertext
*
* @return string
*/
public function decrypt($ciphertext)
{
if ($this->hasSodiumSupport) {
return $this->decryptWithSodium($ciphertext);
}
return $this->decryptWithPhpseclib($ciphertext);
}
/**
* @return string
*/
private function getEncryptionKey()
{
global $PMA_Config;
$keyLength = $this->hasSodiumSupport ? SODIUM_CRYPTO_SECRETBOX_KEYBYTES : 32;
$key = $PMA_Config->get('URLQueryEncryptionSecretKey');
if (is_string($key) && mb_strlen($key, '8bit') === $keyLength) {
return $key;
}
$key = isset($_SESSION['URLQueryEncryptionSecretKey']) ? $_SESSION['URLQueryEncryptionSecretKey'] : null;
if (is_string($key) && mb_strlen($key, '8bit') === $keyLength) {
return $key;
}
$key = $this->hasRandomBytesSupport ? random_bytes($keyLength) : Random::string($keyLength);
$_SESSION['URLQueryEncryptionSecretKey'] = $key;
return $key;
}
/**
* @param string $plaintext
*
* @return string
*/
private function encryptWithPhpseclib($plaintext)
{
$key = $this->getEncryptionKey();
$cipher = new AES(AES::MODE_CBC);
$iv = $this->hasRandomBytesSupport ? random_bytes(16) : Random::string(16);
$cipher->setIV($iv);
$cipher->setKey($key);
$ciphertext = $cipher->encrypt($plaintext);
$hmac = hash_hmac('sha256', $iv . $ciphertext, $key, true);
return $hmac . $iv . $ciphertext;
}
/**
* @param string $encrypted
*
* @return string|null
*/
private function decryptWithPhpseclib($encrypted)
{
$key = $this->getEncryptionKey();
$hmac = mb_substr($encrypted, 0, 32, '8bit');
$iv = mb_substr($encrypted, 32, 16, '8bit');
$ciphertext = mb_substr($encrypted, 48, null, '8bit');
$calculatedHmac = hash_hmac('sha256', $iv . $ciphertext, $key, true);
if (! hash_equals($hmac, $calculatedHmac)) {
return null;
}
$cipher = new AES(AES::MODE_CBC);
$cipher->setIV($iv);
$cipher->setKey($key);
return $cipher->decrypt($ciphertext);
}
/**
* @param string $plaintext
*
* @return string
*/
private function encryptWithSodium($plaintext)
{
$key = $this->getEncryptionKey();
$nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
$ciphertext = sodium_crypto_secretbox($plaintext, $nonce, $key);
return $nonce . $ciphertext;
}
/**
* @param string $encrypted
*
* @return string|null
*/
private function decryptWithSodium($encrypted)
{
$key = $this->getEncryptionKey();
$nonce = mb_substr($encrypted, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, '8bit');
$ciphertext = mb_substr($encrypted, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit');
try {
$decrypted = sodium_crypto_secretbox_open($ciphertext, $nonce, $key);
} catch (Exception $e) {
return null;
}
if ($decrypted === false) {
return null;
}
return $decrypted;
}
}

View File

@ -1630,17 +1630,22 @@ class DatabaseInterface
*/
public function initRelationParamsCache()
{
if (strlen($GLOBALS['db'])) {
$cfgRelation = $this->relation->getRelationsParam();
if (empty($cfgRelation['db'])) {
$this->relation->fixPmaTables($GLOBALS['db'], false);
}
}
$cfgRelation = $this->relation->getRelationsParam();
if (empty($cfgRelation['db']) && isset($GLOBALS['dblist'])) {
if ($GLOBALS['dblist']->databases->exists('phpmyadmin')) {
$this->relation->fixPmaTables('phpmyadmin', false);
}
$storageDbName = isset($GLOBALS['cfg']['Server']['pmadb']) ? $GLOBALS['cfg']['Server']['pmadb'] : '';
// Use "phpmyadmin" as a default database name to check to keep the behavior consistent
$storageDbName = $storageDbName !== null
&& is_string($storageDbName)
&& $storageDbName !== '' ? $storageDbName : 'phpmyadmin';
// This will make users not having explicitly listed databases
// have config values filled by the default phpMyAdmin storage table name values
$this->relation->fixPmaTables($storageDbName, false);
// This global will be changed if fixPmaTables did find one valid table
$storageDbName = isset($GLOBALS['cfg']['Server']['pmadb']) ? $GLOBALS['cfg']['Server']['pmadb'] : '';
// Empty means that until now no pmadb was found eligible
if (empty($storageDbName)) {
$this->relation->fixPmaTables($GLOBALS['db'], false);
}
}
@ -2522,6 +2527,8 @@ class DatabaseInterface
$server = [];
$server['hide_connection_errors'] = $cfg['Server']['hide_connection_errors'];
if (! empty($cfg['Server']['controlhost'])) {
$server['host'] = $cfg['Server']['controlhost'];
} else {
@ -2592,6 +2599,10 @@ class DatabaseInterface
$server['compress'] = false;
}
if (! isset($server['hide_connection_errors'])) {
$server['hide_connection_errors'] = false;
}
return [
$user,
$password,

View File

@ -121,15 +121,27 @@ class DbiMysqli implements DbiExtension
$host = $server['host'];
}
$return_value = $mysqli->real_connect(
$host,
$user,
$password,
'',
$server['port'],
(string) $server['socket'],
$client_flags
);
if ($server['hide_connection_errors']) {
$return_value = @$mysqli->real_connect(
$host,
$user,
$password,
'',
$server['port'],
(string) $server['socket'],
$client_flags
);
} else {
$return_value = $mysqli->real_connect(
$host,
$user,
$password,
'',
$server['port'],
(string) $server['socket'],
$client_flags
);
}
if ($return_value === false || $return_value === null) {
/*
@ -151,7 +163,20 @@ class DbiMysqli implements DbiExtension
);
$server['ssl'] = true;
return self::connect($user, $password, $server);
} elseif ($error_number === 1045 && $server['hide_connection_errors']) {
trigger_error(
sprintf(
__(
'Error 1045: Access denied for user. Additional error information'
. ' may be available, but is being hidden by the %s configuration directive.'
),
'[code][doc@cfg_Servers_hide_connection_errors]'
. '$cfg[\'Servers\'][$i][\'hide_connection_errors\'][/doc][/code]'
),
E_USER_ERROR
);
}
return false;
}

View File

@ -1609,10 +1609,9 @@ class Results
}
$tmp_image = '<img class="fulltext" src="' . $tmp_image_file . '" alt="'
. $tmp_txt . '" title="' . $tmp_txt . '">';
$tmp_url = 'sql.php' . Url::getCommon($url_params_full_text);
. $tmp_txt . '" title="' . $tmp_txt . '" />';
return Util::linkOrButton($tmp_url, $tmp_image);
return Util::linkOrButton('sql.php', $url_params_full_text, $tmp_image);
}
/**
@ -1730,16 +1729,14 @@ class Results
'session_max_rows' => $session_max_rows,
'is_browse_distinct' => $this->__get('is_browse_distinct'),
];
$single_order_url = 'sql.php' . Url::getCommon($_single_url_params);
$multi_order_url = 'sql.php' . Url::getCommon($_multi_url_params);
// Displays the sorting URL
// enable sort order swapping for image
$order_link = $this->_getSortOrderLink(
$order_img,
$fields_meta,
$single_order_url,
$multi_order_url
$_single_url_params,
$_multi_url_params
);
$sorted_header_html .= $this->_getDraggableClassForSortableColumns(
@ -2041,10 +2038,10 @@ class Results
/**
* Get sort order link
*
* @param string $order_img the sort order image
* @param stdClass $fields_meta set of field properties
* @param string $order_url the url for sort
* @param string $multi_order_url the url for sort
* @param string $order_img the sort order image
* @param array $fields_meta set of field properties
* @param array $order_url_params the url params for sort
* @param array $multi_order_url_params the url params for sort
*
* @return string the sort order link
*
@ -2055,8 +2052,8 @@ class Results
private function _getSortOrderLink(
$order_img,
$fields_meta,
$order_url,
$multi_order_url
$order_url_params,
$multi_order_url_params
) {
$order_link_params = [
'class' => 'sortlink',
@ -2064,10 +2061,13 @@ class Results
$order_link_content = htmlspecialchars($fields_meta->name);
$inner_link_content = $order_link_content . $order_img
. '<input type="hidden" value="' . $multi_order_url . '">';
. '<input type="hidden" value="sql.php'
. Url::getCommon($multi_order_url_params, '?', false)
. '" />';
return Util::linkOrButton(
$order_url,
'sql.php',
$order_url_params,
$inner_link_content,
$order_link_params
);
@ -2517,7 +2517,7 @@ class Results
// 1. Prepares the row
// In print view these variable needs to be initialized
$del_url = $del_str = $edit_anchor_class
$del_url = $del_str = $edit_anchor_class = $editCopyUrlParams = $delUrlParams
= $edit_str = $js_conf = $copy_url = $copy_str = $edit_url = null;
// 1.2 Defines the URLs for the modify/delete link(s)
@ -2550,7 +2550,7 @@ class Results
// 1.2.1 Modify link(s) - update row case
if ($displayParts['edit_lnk'] == self::UPDATE_ROW) {
list($edit_url, $copy_url, $edit_str, $copy_str,
$edit_anchor_class)
$edit_anchor_class, $editCopyUrlParams)
= $this->_getModifiedLinks(
$where_clause,
$clause_is_unique,
@ -2559,7 +2559,7 @@ class Results
} // end if (1.2.1)
// 1.2.2 Delete/Kill link(s)
list($del_url, $del_str, $js_conf)
list($del_url, $del_str, $js_conf, $delUrlParams)
= $this->_getDeleteAndKillLinks(
$where_clause,
$clause_is_unique,
@ -2586,7 +2586,9 @@ class Results
$edit_str,
$copy_str,
$del_str,
$js_conf
$js_conf,
$editCopyUrlParams,
$delUrlParams
);
} elseif ($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_NONE) {
$table_body_html .= $this->_getPlacedLinks(
@ -2603,7 +2605,9 @@ class Results
$edit_str,
$copy_str,
$del_str,
$js_conf
$js_conf,
$editCopyUrlParams,
$delUrlParams
);
} // end if (1.3)
} // end if (1)
@ -2645,7 +2649,9 @@ class Results
$edit_str,
$copy_str,
$del_str,
$js_conf
$js_conf,
$editCopyUrlParams,
$delUrlParams
);
}
} // end if (3)
@ -3247,15 +3253,9 @@ class Results
'goto' => 'sql.php',
];
$edit_url = 'tbl_change.php'
. Url::getCommon(
$_url_params + ['default_action' => 'update']
);
$edit_url = 'tbl_change.php';
$copy_url = 'tbl_change.php'
. Url::getCommon(
$_url_params + ['default_action' => 'insert']
);
$copy_url = 'tbl_change.php';
$edit_str = $this->_getActionLinkContent(
'b_edit',
@ -3278,6 +3278,7 @@ class Results
$edit_str,
$copy_str,
$edit_anchor_class,
$_url_params
];
}
@ -3330,7 +3331,7 @@ class Results
'message_to_show' => __('The row has been deleted.'),
'goto' => $lnk_goto,
];
$del_url = 'sql.php' . Url::getCommon($_url_params);
$del_url = 'sql.php';
$js_conf = 'DELETE FROM ' . Sanitize::jsFormat($this->__get('table'))
. ' WHERE ' . Sanitize::jsFormat($where_clause, false)
@ -3355,20 +3356,21 @@ class Results
'goto' => $lnk_goto,
];
$del_url = 'sql.php' . Url::getCommon($_url_params);
$del_url = 'sql.php';
$js_conf = $kill;
$del_str = Util::getIcon(
'b_drop',
__('Kill')
);
} else {
$del_url = $del_str = $js_conf = null;
$del_url = $del_str = $js_conf = $_url_params = null;
}
return [
$del_url,
$del_str,
$js_conf,
$_url_params
];
}
@ -3415,20 +3417,22 @@ class Results
/**
* Prepare placed links
*
* @param string $dir the direction of links should place
* @param string $del_url the url for delete row
* @param array $displayParts which elements to display
* @param integer $row_no the index of current row
* @param string $where_clause the where clause of the sql
* @param string $where_clause_html the html encoded where clause
* @param array $condition_array array of keys (primary, unique, condition)
* @param string $edit_url the url for edit row
* @param string $copy_url the url for copy row
* @param string $edit_anchor_class the class for html element for edit
* @param string $edit_str the label for edit row
* @param string $copy_str the label for copy row
* @param string $del_str the label for delete row
* @param string|null $js_conf text for the JS confirmation
* @param string $dir the direction of links should place
* @param string $del_url the url for delete row
* @param array $displayParts which elements to display
* @param integer $row_no the index of current row
* @param string $where_clause the where clause of the sql
* @param string $where_clause_html the html encoded where clause
* @param array $condition_array array of keys (primary, unique, condition)
* @param string $edit_url the url for edit row
* @param string $copy_url the url for copy row
* @param string $edit_anchor_class the class for html element for edit
* @param string $edit_str the label for edit row
* @param string $copy_str the label for copy row
* @param string $del_str the label for delete row
* @param string $js_conf text for the JS confirmation
* @param array $editCopyUrlParams URL parameters
* @param array $delUrlParams URL parameters
*
* @return string html content
*
@ -3450,7 +3454,9 @@ class Results
$edit_str,
$copy_str,
$del_str,
?string $js_conf
$js_conf,
$editCopyUrlParams,
$delUrlParams
) {
if (! isset($js_conf)) {
@ -3471,7 +3477,9 @@ class Results
$edit_str,
$copy_str,
$del_str,
$js_conf
$js_conf,
$editCopyUrlParams,
$delUrlParams
);
}
@ -4781,8 +4789,8 @@ class Results
/**
* Generates HTML to display the Create view in span tag
*
* @param array $analyzed_sql_results analyzed sql results
* @param string $url_query String with URL Parameters
* @param array $analyzed_sql_results analyzed sql results
* @param array $urlParams URL Parameters
*
* @return string
*
@ -4790,13 +4798,14 @@ class Results
*
* @see _getResultsOperations()
*/
private function _getLinkForCreateView(array $analyzed_sql_results, $url_query)
private function _getLinkForCreateView(array $analyzed_sql_results, $urlParams)
{
$results_operations_html = '';
if (empty($analyzed_sql_results['procedure'])) {
$results_operations_html .= '<span>'
. Util::linkOrButton(
'view_create.php' . $url_query,
'view_create.php',
$urlParams,
Util::getIcon(
'b_view_add',
__('Create view'),
@ -4843,6 +4852,7 @@ class Results
{
return Util::linkOrButton(
'#',
null,
Util::getIcon(
'b_insrow',
__('Copy to clipboard'),
@ -4866,6 +4876,7 @@ class Results
{
return Util::linkOrButton(
'#',
null,
Util::getIcon(
'b_print',
__('Print'),
@ -4911,7 +4922,6 @@ class Results
'printview' => '1',
'sql_query' => $this->__get('sql_query'),
];
$url_query = Url::getCommon($_url_params);
if (! $header_shown) {
$results_operations_html .= $header;
@ -4921,8 +4931,7 @@ class Results
// show only view and not other options
if ($only_view) {
$results_operations_html .= $this->_getLinkForCreateView(
$analyzed_sql_results,
$url_query
$analyzed_sql_results, $_url_params
);
if ($header_shown) {
@ -4976,7 +4985,8 @@ class Results
}
$results_operations_html .= Util::linkOrButton(
'tbl_export.php' . Url::getCommon($_url_params),
'tbl_export.php',
$_url_params,
Util::getIcon(
'b_tblexport',
__('Export'),
@ -4987,7 +4997,8 @@ class Results
// prepare chart
$results_operations_html .= Util::linkOrButton(
'tbl_chart.php' . Url::getCommon($_url_params),
'tbl_chart.php',
$_url_params,
Util::getIcon(
'b_chart',
__('Display chart'),
@ -5009,8 +5020,8 @@ class Results
if ($geometry_found) {
$results_operations_html
.= Util::linkOrButton(
'tbl_gis_visualization.php'
. Url::getCommon($_url_params),
'tbl_gis_visualization.php',
$_url_params,
Util::getIcon(
'b_globe',
__('Visualize GIS data'),
@ -5035,8 +5046,7 @@ class Results
}
$results_operations_html .= $this->_getLinkForCreateView(
$analyzed_sql_results,
$url_query
$analyzed_sql_results, $_url_params
);
if ($header_shown) {
@ -5364,7 +5374,8 @@ class Results
$tag_params['class'] = 'ajax';
}
$result .= Util::linkOrButton(
'sql.php' . Url::getCommon($_url_params),
'sql.php',
$_url_params,
$displayedData,
$tag_params
);
@ -5438,6 +5449,7 @@ class Results
* Prepares an Edit link
*
* @param string $edit_url edit url
* @param array $urlParams URL parameters
* @param string $class css classes for td element
* @param string $edit_str text for the edit link
* @param string $where_clause where clause
@ -5451,6 +5463,7 @@ class Results
*/
private function _getEditLink(
$edit_url,
$urlParams,
$class,
$edit_str,
$where_clause,
@ -5460,7 +5473,7 @@ class Results
if (! empty($edit_url)) {
$ret .= '<td class="' . $class . ' center print_ignore">'
. '<span class="nowrap">'
. Util::linkOrButton($edit_url, $edit_str);
. Util::linkOrButton($edit_url, $urlParams, $edit_str);
/*
* Where clause for selecting this row uniquely is provided as
* a hidden input. Used by jQuery scripts for handling grid editing
@ -5479,6 +5492,7 @@ class Results
* Prepares an Copy link
*
* @param string $copy_url copy url
* @param array $urlParams URL parameters
* @param string $copy_str text for the copy link
* @param string $where_clause where clause
* @param string $where_clause_html url encoded where clause
@ -5492,6 +5506,7 @@ class Results
*/
private function _getCopyLink(
$copy_url,
$urlParams,
$copy_str,
$where_clause,
$where_clause_html,
@ -5504,8 +5519,8 @@ class Results
$ret .= $class . ' ';
}
$ret .= 'center print_ignore"><span class="nowrap">'
. Util::linkOrButton($copy_url, $copy_str);
$ret .= 'center print_ignore" ' . ' ><span class="nowrap">'
. Util::linkOrButton($copy_url, $urlParams, $copy_str);
/*
* Where clause for selecting this row uniquely is provided as
@ -5524,10 +5539,11 @@ class Results
/**
* Prepares a Delete link
*
* @param string $del_url delete url
* @param string $del_str text for the delete link
* @param string $js_conf text for the JS confirmation
* @param string $class css classes for the td element
* @param string $del_url delete url
* @param array $delUrlParams URL parameters
* @param string $del_str text for the delete link
* @param string $js_conf text for the JS confirmation
* @param string $class css classes for the td element
*
* @return string the generated HTML
*
@ -5535,7 +5551,7 @@ class Results
*
* @see _getTableBody(), _getCheckboxAndLinks()
*/
private function _getDeleteLink($del_url, $del_str, $js_conf, $class)
private function _getDeleteLink($del_url, $delUrlParams, $del_str, $js_conf, $class)
{
$ret = '';
@ -5551,6 +5567,7 @@ class Results
$ret .= 'center print_ignore">'
. Util::linkOrButton(
$del_url,
$delUrlParams,
$del_str,
['class' => 'delete_row requireConfirm' . $ajax]
)
@ -5579,6 +5596,8 @@ class Results
* @param string $copy_str text for the copy link
* @param string $del_str text for the delete link
* @param string $js_conf text for the JS confirmation
* @param array $editCopyUrlParams URL parameters
* @param array $delUrlParams URL parameters
*
* @return string the generated HTML
*
@ -5600,13 +5619,17 @@ class Results
$edit_str,
$copy_str,
$del_str,
$js_conf
$js_conf,
$editCopyUrlParams,
$delUrlParams
) {
$ret = '';
$editUrlParams = $editCopyUrlParams + ['default_action' => 'update'];
$copyUrlParams = $editCopyUrlParams + ['default_action' => 'insert'];
if ($position == self::POSITION_LEFT) {
$ret .= $this->_getCheckboxForMultiRowSubmissions(
$del_url,
$del_url . Url::getCommon($delUrlParams),
$displayParts,
$row_no,
$where_clause_html,
@ -5617,6 +5640,7 @@ class Results
$ret .= $this->_getEditLink(
$edit_url,
$editUrlParams,
$class,
$edit_str,
$where_clause,
@ -5625,18 +5649,22 @@ class Results
$ret .= $this->_getCopyLink(
$copy_url,
$copyUrlParams,
$copy_str,
$where_clause,
$where_clause_html,
''
);
$ret .= $this->_getDeleteLink($del_url, $del_str, $js_conf, '');
$ret .= $this->_getDeleteLink($del_url, $delUrlParams, $del_str, $js_conf, '');
} elseif ($position == self::POSITION_RIGHT) {
$ret .= $this->_getDeleteLink($del_url, $del_str, $js_conf, '');
$ret .= $this->_getDeleteLink($del_url, $delUrlParams, $del_str, $js_conf, '');
$ret .= $this->_getCopyLink(
$copy_url,
$copyUrlParams,
$copy_str,
$where_clause,
$where_clause_html,
@ -5645,6 +5673,7 @@ class Results
$ret .= $this->_getEditLink(
$edit_url,
$editUrlParams,
$class,
$edit_str,
$where_clause,
@ -5652,7 +5681,7 @@ class Results
);
$ret .= $this->_getCheckboxForMultiRowSubmissions(
$del_url,
$del_url . Url::getCommon($delUrlParams),
$displayParts,
$row_no,
$where_clause_html,
@ -5662,7 +5691,7 @@ class Results
);
} else { // $position == self::POSITION_NONE
$ret .= $this->_getCheckboxForMultiRowSubmissions(
$del_url,
$del_url . Url::getCommon($delUrlParams),
$displayParts,
$row_no,
$where_clause_html,

View File

@ -218,6 +218,7 @@ class ErrorReport
unset($queryArray["table"]);
unset($queryArray["token"]);
unset($queryArray["server"]);
unset($queryArray["eq"]);
$query = http_build_query($queryArray);
} else {
$query = '';

View File

@ -499,16 +499,17 @@ class Export
*/
$back_button = '<p id="export_back_button">[ <a href="';
if ($export_type == 'server') {
$back_button .= 'server_export.php" data-post="' . Url::getCommon([], '');
$back_button .= 'server_export.php" data-post="' . Url::getCommon([], '', false);
} elseif ($export_type == 'database') {
$back_button .= 'db_export.php" data-post="' . Url::getCommon(['db' => $db], '');
$back_button .= 'db_export.php" data-post="' . Url::getCommon(['db' => $db], '', false);
} else {
$back_button .= 'tbl_export.php" data-post="' . Url::getCommon(
[
'db' => $db,
'table' => $table,
],
''
'',
false
);
}

View File

@ -738,7 +738,7 @@ class Index
$r .= '" ' . $row_span . '>'
. ' <a class="';
$r .= 'ajax';
$r .= '" href="tbl_indexes.php" data-post="' . Url::getCommon($this_params, '')
$r .= '" href="tbl_indexes.php" data-post="' . Url::getCommon($this_params, '', false)
. '">' . Util::getIcon('b_edit', __('Edit')) . '</a>'
. '</td>' . "\n";
$this_params = $GLOBALS['url_params'];
@ -764,7 +764,8 @@ class Index
$r .= '<input type="hidden" class="drop_primary_key_index_msg"'
. ' value="' . $js_msg . '">';
$r .= Util::linkOrButton(
'sql.php' . Url::getCommon($this_params),
'sql.php',
$this_params,
Util::getIcon('b_drop', __('Drop')),
['class' => 'drop_primary_key_index_anchor ajax']
);

View File

@ -295,12 +295,12 @@ class InsertEdit
if (! $is_show) {
return ' : <a href="tbl_change.php" data-post="'
. Url::getCommon($this_url_params, '') . '">'
. Url::getCommon($this_url_params, '', false) . '">'
. $this->showTypeOrFunctionLabel($which)
. '</a>';
}
return '<th><a href="tbl_change.php" data-post="'
. Url::getCommon($this_url_params, '')
. Url::getCommon($this_url_params, '', false)
. '" title="' . __('Hide') . '">'
. $this->showTypeOrFunctionLabel($which)
. '</a></th>';
@ -888,7 +888,7 @@ class InsertEdit
'rownumber' => $rownumber,
'data' => $data,
],
''
'', false
) . '">'
. str_replace("'", "\'", $titles['Browse']) . '</a>';
return $html_output;
@ -1740,6 +1740,7 @@ class InsertEdit
return '<span class="open_gis_editor">'
. Util::linkOrButton(
'#',
null,
$edit_str,
[],
'_blank'

View File

@ -92,7 +92,8 @@ class Navigation
$hasStartChar = strpos($logo['link'], '?');
$logo['link'] .= Url::getCommon(
[],
is_bool($hasStartChar) ? '?' : Url::getArgSeparator()
is_bool($hasStartChar) ? '?' : Url::getArgSeparator(),
false
);
}
$logo['attributes'] = '';

View File

@ -123,13 +123,13 @@ class NavigationTree
$this->pos = $this->getNavigationDbPos();
}
// Get the active node
if (isset($_REQUEST['aPath'])) {
$this->aPath[0] = $this->parsePath($_REQUEST['aPath']);
$this->pos2Name[0] = $_REQUEST['pos2_name'];
$this->pos2Value[0] = (int) $_REQUEST['pos2_value'];
if (isset($_REQUEST['pos3_name'])) {
$this->pos3Name[0] = $_REQUEST['pos3_name'];
$this->pos3Value[0] = (int) $_REQUEST['pos3_value'];
if (isset($_POST['aPath'])) {
$this->aPath[0] = $this->parsePath($_POST['aPath']);
$this->pos2Name[0] = $_POST['pos2_name'];
$this->pos2Value[0] = (int) $_POST['pos2_value'];
if (isset($_POST['pos3_name'])) {
$this->pos3Name[0] = $_POST['pos3_name'];
$this->pos3Value[0] = (int) $_POST['pos3_value'];
}
} else {
if (isset($_POST['n0_aPath'])) {
@ -150,8 +150,8 @@ class NavigationTree
}
}
}
if (isset($_REQUEST['vPath'])) {
$this->vPath[0] = $this->parsePath($_REQUEST['vPath']);
if (isset($_POST['vPath'])) {
$this->vPath[0] = $this->parsePath($_POST['vPath']);
} else {
if (isset($_POST['n0_vPath'])) {
$count = 0;
@ -163,11 +163,11 @@ class NavigationTree
}
}
}
if (isset($_REQUEST['searchClause'])) {
$this->searchClause = $_REQUEST['searchClause'];
if (isset($_POST['searchClause'])) {
$this->searchClause = $_POST['searchClause'];
}
if (isset($_REQUEST['searchClause2'])) {
$this->searchClause2 = $_REQUEST['searchClause2'];
if (isset($_POST['searchClause2'])) {
$this->searchClause2 = $_POST['searchClause2'];
}
// Initialise the tree by creating a root node
$node = NodeFactory::getInstance('NodeDatabaseContainer', 'root');
@ -1168,7 +1168,7 @@ class NavigationTree
}
foreach ($icons as $key => $icon) {
$link = vsprintf($iconLinks[$key], $args);
$link = $this->encryptQueryParams(vsprintf($iconLinks[$key], $args));
if ($linkClass != '') {
$retval .= "<a class='$linkClass' href='$link'>";
$retval .= "{$icon}</a>";
@ -1186,7 +1186,7 @@ class NavigationTree
foreach ($node->parents(true) as $parent) {
$args[] = urlencode($parent->realName);
}
$link = vsprintf($node->links['text'], $args);
$link = $this->encryptQueryParams(vsprintf($node->links['text'], $args));
$title = isset($node->links['title']) ? $node->links['title'] : $node->title ?? '';
if ($node->type == Node::CONTAINER) {
$retval .= "&nbsp;<a class='hover_show_full' href='$link'>";
@ -1572,4 +1572,23 @@ class NavigationTree
return $retval;
}
/**
* @param string $link
*
* @return string
*/
private function encryptQueryParams($link)
{
global $PMA_Config;
if (! $PMA_Config->get('URLQueryEncryption')) {
return $link;
}
$url = parse_url($link);
parse_str(htmlspecialchars_decode($url['query']), $query);
return $url['path'] . '?' . htmlspecialchars(Url::buildHttpQuery($query));
}
}

View File

@ -681,7 +681,7 @@ class NodeDatabase extends Node
];
$ret = '<span class="dbItemControls">'
. '<a href="navigation.php" data-post="'
. Url::getCommon($params, '') . '"'
. Url::getCommon($params, '', false) . '"'
. ' class="showUnhide ajax">'
. Util::getImage(
'show',

View File

@ -51,7 +51,7 @@ abstract class NodeDatabaseChild extends Node
$ret = '<span class="navItemControls">'
. '<a href="navigation.php" data-post="'
. Url::getCommon($params, '') . '"'
. Url::getCommon($params, '', false) . '"'
. ' class="hideNavItem ajax">'
. Util::getImage('hide', __('Hide'))
. '</a></span>';

View File

@ -1589,7 +1589,8 @@ class Operations
{
return '<li>'
. Util::linkOrButton(
'sql.php' . Url::getCommon(array_merge($url_params, $params)),
'sql.php',
array_merge($url_params, $params),
$action_message,
['class' => 'maintain_action ajax']
)
@ -1649,7 +1650,8 @@ class Operations
public function getDeleteDataOrTablelink(array $url_params, $syntax, $link, $htmlId)
{
return '<li>' . Util::linkOrButton(
'sql.php' . Url::getCommon($url_params),
'sql.php',
$url_params,
$link,
[
'id' => $htmlId,

View File

@ -12,6 +12,7 @@ namespace PhpMyAdmin\Plugins\Auth;
use PhpMyAdmin\Core;
use PhpMyAdmin\Plugins\AuthenticationPlugin;
use PhpMyAdmin\Response;
use PhpMyAdmin\Util;
/**
@ -28,6 +29,7 @@ class AuthenticationSignon extends AuthenticationPlugin
*/
public function showLoginForm()
{
Response::getInstance()->disable();
unset($_SESSION['LAST_SIGNON_URL']);
if (empty($GLOBALS['cfg']['Server']['SignonURL'])) {
Core::fatalError('You must set SignonURL!');

View File

@ -133,7 +133,7 @@ class ReplicationGui
}
$urlParams['sr_slave_control_parm'] = 'IO_THREAD';
$slaveControlIoLink = Url::getCommon($urlParams, '');
$slaveControlIoLink = Url::getCommon($urlParams, '', false);
if ($serverSlaveReplication[0]['Slave_SQL_Running'] == 'No') {
$urlParams['sr_slave_action'] = 'start';
@ -142,7 +142,7 @@ class ReplicationGui
}
$urlParams['sr_slave_control_parm'] = 'SQL_THREAD';
$slaveControlSqlLink = Url::getCommon($urlParams, '');
$slaveControlSqlLink = Url::getCommon($urlParams, '', false);
if ($serverSlaveReplication[0]['Slave_IO_Running'] == 'No'
|| $serverSlaveReplication[0]['Slave_SQL_Running'] == 'No'
@ -153,21 +153,21 @@ class ReplicationGui
}
$urlParams['sr_slave_control_parm'] = null;
$slaveControlFullLink = Url::getCommon($urlParams, '');
$slaveControlFullLink = Url::getCommon($urlParams, '', false);
$urlParams['sr_slave_action'] = 'reset';
$slaveControlResetLink = Url::getCommon($urlParams, '');
$slaveControlResetLink = Url::getCommon($urlParams, '', false);
$urlParams = $GLOBALS['url_params'];
$urlParams['sr_take_action'] = true;
$urlParams['sr_slave_skip_error'] = true;
$slaveSkipErrorLink = Url::getCommon($urlParams, '');
$slaveSkipErrorLink = Url::getCommon($urlParams, '', false);
$urlParams = $GLOBALS['url_params'];
$urlParams['sl_configure'] = true;
$urlParams['repl_clear_scr'] = true;
$reconfigureMasterLink = Url::getCommon($urlParams, '');
$reconfigureMasterLink = Url::getCommon($urlParams, '', false);
$slaveStatusTable = $this->getHtmlForReplicationStatusTable('slave', true, false);

View File

@ -221,7 +221,7 @@ class RteList
*/
public function getRoutineRow(array $routine, $rowclass = '')
{
global $url_query, $db, $titles;
global $url_query, $url_params, $db, $titles;
$sql_drop = sprintf(
'DROP %s IF EXISTS %s',
@ -348,7 +348,11 @@ class RteList
$retval .= " </td>\n";
$retval .= " <td>\n";
$retval .= Util::linkOrButton(
'sql.php' . $url_query . '&amp;sql_query=' . urlencode($sql_drop) . '&amp;goto=db_routines.php' . urlencode("?db={$db}"),
'sql.php',
array_merge(
$url_params,
['sql_query' => $sql_drop, 'goto' => 'db_routines.php' . Url::getCommon(['db' => $db])]
),
$titles['Drop'],
['class' => 'ajax drop_anchor']
);
@ -375,7 +379,7 @@ class RteList
*/
public function getTriggerRow(array $trigger, $rowclass = '')
{
global $url_query, $db, $table, $titles;
global $url_query, $url_params, $db, $table, $titles;
$retval = " <tr class='$rowclass'>\n";
$retval .= " <td>\n";
@ -392,8 +396,8 @@ class RteList
$retval .= " </td>\n";
if (empty($table)) {
$retval .= " <td>\n";
$retval .= "<a href='db_triggers.php{$url_query}"
. "&amp;table=" . urlencode($trigger['table']) . "'>"
$retval .= "<a href='db_triggers.php"
. Url::getCommon(array_merge($url_params, ['table' => $trigger['table']])) . "'>"
. htmlspecialchars($trigger['table']) . "</a>";
$retval .= " </td>\n";
}
@ -422,7 +426,11 @@ class RteList
$retval .= " <td>\n";
if (Util::currentUserHasPrivilege('TRIGGER', $db)) {
$retval .= Util::linkOrButton(
'sql.php' . $url_query . '&amp;sql_query=' . urlencode($trigger['drop']) . '&amp;goto=db_triggers.php' . urlencode("?db={$db}"),
'sql.php',
array_merge(
$url_params,
['sql_query' => $trigger['drop'], 'goto' => 'db_triggers.php' . Url::getCommon(['db' => $db])]
),
$titles['Drop'],
['class' => 'ajax drop_anchor']
);
@ -451,7 +459,7 @@ class RteList
*/
public function getEventRow(array $event, $rowclass = '')
{
global $url_query, $db, $titles;
global $url_query, $url_params, $db, $titles;
$sql_drop = sprintf(
'DROP EVENT IF EXISTS %s',
@ -500,7 +508,11 @@ class RteList
$retval .= " <td>\n";
if (Util::currentUserHasPrivilege('EVENT', $db)) {
$retval .= Util::linkOrButton(
'sql.php' . $url_query . '&amp;sql_query=' . urlencode($sql_drop) . '&amp;goto=db_events.php' . urlencode("?db={$db}"),
'sql.php',
array_merge(
$url_params,
['sql_query' => $sql_drop, 'goto' => 'db_events.php' . Url::getCommon(['db' => $db])]
),
$titles['Drop'],
['class' => 'ajax drop_anchor']
);

View File

@ -2985,7 +2985,7 @@ class Privileges
$html .= ' href="server_privileges.php';
if ($linktype == 'revoke') {
$html .= '" data-post="' . Url::getCommon($params, '');
$html .= '" data-post="' . Url::getCommon($params, '', false);
} else {
$html .= Url::getCommon($params);
}

View File

@ -118,7 +118,7 @@ class UserGroups
'viewUsers' => 1,
'userGroup' => $groupName,
],
''
'', false
)
. '">'
. Util::getIcon('b_usrlist', __('View users'))
@ -130,7 +130,7 @@ class UserGroups
'editUserGroup' => 1,
'userGroup' => $groupName,
],
''
'', false
)
. '">'
. Util::getIcon('b_edit', __('Edit')) . '</a>';
@ -142,7 +142,7 @@ class UserGroups
'deleteUserGroup' => 1,
'userGroup' => $groupName,
],
''
'', false
)
. '">'
. Util::getIcon('b_drop', __('Delete')) . '</a>';

View File

@ -649,7 +649,7 @@ class Tracking
'report' => 'true',
'version' => $_POST['version'],
$deleteParam => $lineNumber - $offset,
], '');
], '', false);
$entry['line_number'] = $lineNumber;
$entries[] = $entry;
}

View File

@ -9,6 +9,8 @@ declare(strict_types=1);
namespace PhpMyAdmin;
use PhpMyAdmin\Crypto\Crypto;
/**
* Static methods for URL/hidden inputs generating
*
@ -161,14 +163,15 @@ class Url
*
* @param mixed $params optional, Contains an associative array with url params
* @param string $divider optional character to use instead of '?'
* @param bool $encrypt whether to encrypt URL params
*
* @return string string with URL parameters
* @access public
*/
public static function getCommon($params = [], $divider = '?')
public static function getCommon($params = [], $divider = '?', $encrypt = true)
{
return htmlspecialchars(
Url::getCommonRaw($params, $divider)
Url::getCommonRaw($params, $divider, $encrypt)
);
}
@ -197,15 +200,15 @@ class Url
*
* @param mixed $params optional, Contains an associative array with url params
* @param string $divider optional character to use instead of '?'
* @param bool $encrypt whether to encrypt URL params
*
* @return string string with URL parameters
* @access public
*/
public static function getCommonRaw($params = [], $divider = '?')
public static function getCommonRaw($params = [], $divider = '?', $encrypt = true)
{
/** @var Config $PMA_Config */
global $PMA_Config;
$separator = Url::getArgSeparator();
// avoid overwriting when creating navi panel links to servers
if (isset($GLOBALS['server'])
@ -222,7 +225,7 @@ class Url
$params['lang'] = $GLOBALS['lang'];
}
$query = http_build_query($params, '', $separator);
$query = self::buildHttpQuery($params, $encrypt);
if ($divider != '?' || strlen($query) > 0) {
return $divider . $query;
@ -231,6 +234,81 @@ class Url
return '';
}
/**
* @param array<string, mixed> $params
* @param bool $encrypt whether to encrypt URL params
*
* @return string
*/
public static function buildHttpQuery($params, $encrypt = true)
{
global $PMA_Config;
$separator = self::getArgSeparator();
if (! $encrypt || ! $PMA_Config->get('URLQueryEncryption')) {
return http_build_query($params, '', $separator);
}
$data = $params;
$keys = [
'db',
'table',
'field',
'sql_query',
'sql_signature',
'where_clause',
'goto',
'back',
'message_to_show',
'username',
'hostname',
'dbname',
'tablename',
'checkprivsdb',
'checkprivstable',
];
$paramsToEncrypt = [];
foreach ($params as $paramKey => $paramValue) {
if (! in_array($paramKey, $keys)) {
continue;
}
$paramsToEncrypt[$paramKey] = $paramValue;
unset($data[$paramKey]);
}
if ($paramsToEncrypt !== []) {
$data['eq'] = self::encryptQuery(json_encode($paramsToEncrypt));
}
return http_build_query($data, '', $separator);
}
/**
* @param string $query
*
* @return string
*/
public static function encryptQuery($query)
{
$crypto = new Crypto();
return strtr(base64_encode($crypto->encrypt($query)), '+/', '-_');
}
/**
* @param string $query
*
* @return string|null
*/
public static function decryptQuery($query)
{
$crypto = new Crypto();
return $crypto->decrypt(base64_decode(strtr($query, '-_', '+/')));
}
/**
* Returns url separator
*

View File

@ -1096,7 +1096,8 @@ class Util
$explain_params['sql_query'] = 'EXPLAIN ' . $sql_query;
$explain_link = ' [&nbsp;'
. self::linkOrButton(
'import.php' . Url::getCommon($explain_params),
'import.php',
$explain_params,
__('Explain SQL')
) . '&nbsp;]';
} elseif (preg_match(
@ -1107,7 +1108,8 @@ class Util
= mb_substr($sql_query, 8);
$explain_link = ' [&nbsp;'
. self::linkOrButton(
'import.php' . Url::getCommon($explain_params),
'import.php',
$explain_params,
__('Skip Explain SQL')
) . ']';
$url = 'https://mariadb.org/explain_analyzer/analyze/'
@ -1116,6 +1118,7 @@ class Util
$explain_link .= ' ['
. self::linkOrButton(
htmlspecialchars('url.php?url=' . urlencode($url)),
null,
sprintf(__('Analyze Explain at %s'), 'mariadb.org'),
[],
'_blank'
@ -1131,9 +1134,8 @@ class Util
if (! empty($cfg['SQLQuery']['Edit'])
&& empty($GLOBALS['show_as_php'])
) {
$edit_link .= Url::getCommon($url_params);
$edit_link = ' [&nbsp;'
. self::linkOrButton($edit_link, __('Edit'))
. self::linkOrButton($edit_link, $url_params, __('Edit'))
. '&nbsp;]';
} else {
$edit_link = '';
@ -1145,14 +1147,16 @@ class Util
if (! empty($GLOBALS['show_as_php'])) {
$php_link = ' [&nbsp;'
. self::linkOrButton(
'import.php' . Url::getCommon($url_params),
'import.php',
$url_params,
__('Without PHP code')
)
. '&nbsp;]';
$php_link .= ' [&nbsp;'
. self::linkOrButton(
'import.php' . Url::getCommon($url_params),
'import.php',
$url_params,
__('Submit query')
)
. '&nbsp;]';
@ -1161,7 +1165,8 @@ class Util
$php_params['show_as_php'] = 1;
$php_link = ' [&nbsp;'
. self::linkOrButton(
'import.php' . Url::getCommon($php_params),
'import.php',
$php_params,
__('Create PHP code')
)
. '&nbsp;]';
@ -1177,7 +1182,7 @@ class Util
) {
$refresh_link = 'sql.php' . Url::getCommon($url_params);
$refresh_link = ' [&nbsp;'
. self::linkOrButton($refresh_link, __('Refresh')) . ']';
. self::linkOrButton('import.php', $url_params, __('Refresh')) . ']';
} else {
$refresh_link = '';
} //refresh
@ -1216,6 +1221,7 @@ class Util
$inline_edit_link = ' ['
. self::linkOrButton(
'#',
null,
_pgettext('Inline edit query', 'Edit inline'),
['class' => 'inline_edit_sql']
)
@ -1785,20 +1791,26 @@ class Util
* - URL components are over Suhosin limits
* - There is SQL query in the parameters
*
* @param string $url the URL
* @param string $message the link message
* @param mixed $tag_params string: js confirmation; array: additional tag
* params (f.e. style="")
* @param string $target target
* @param string $urlPath the URL
* @param array|null $urlParams URL parameters
* @param string $message the link message
* @param mixed $tag_params string: js confirmation; array: additional tag params (f.e. style="")
* @param string $target target
*
* @return string the results to be echoed or saved in an array
*/
public static function linkOrButton(
$url,
$urlPath,
$urlParams,
$message,
$tag_params = [],
$target = ''
) {
$url = $urlPath;
if (is_array($urlParams)) {
$url = $urlPath . Url::getCommon($urlParams, '?', false);
}
$url_length = strlen($url);
if (! is_array($tag_params)) {
@ -1857,6 +1869,11 @@ class Util
) {
$url .= '?' . explode('&', $parts[1], 2)[0];
}
} else {
$url = $urlPath;
if (is_array($urlParams)) {
$url = $urlPath . Url::getCommon($urlParams);
}
}
foreach ($tag_params as $par_name => $par_value) {
@ -2427,13 +2444,15 @@ class Util
$_url_params[$name] = 0;
$list_navigator_html .= '<a' . $class . $title1 . ' href="' . $script
. Url::getCommon($_url_params) . '">' . $caption1
. '</a>';
. '" data-post="'
. Url::getCommon($_url_params, '', false)
. '">' . $caption1 . '</a>';
$_url_params[$name] = $pos - $max_count;
$list_navigator_html .= ' <a' . $class . $title2
. ' href="' . $script . Url::getCommon($_url_params) . '">'
. $caption2 . '</a>';
$list_navigator_html .= ' <a' . $class . $title2 . ' href="' . $script
. '" data-post="'
. Url::getCommon($_url_params, '', false)
. '">' . $caption2 . '</a>';
}
$list_navigator_html .= '<form action="' . basename($script)
@ -2464,17 +2483,19 @@ class Util
$_url_params[$name] = $pos + $max_count;
$list_navigator_html .= '<a' . $class . $title3 . ' href="' . $script
. Url::getCommon($_url_params) . '" >' . $caption3
. '</a>';
. '" data-post="'
. Url::getCommon($_url_params, '', false)
. '" >' . $caption3 . '</a>';
$_url_params[$name] = floor($count / $max_count) * $max_count;
if ($_url_params[$name] == $count) {
$_url_params[$name] = $count - $max_count;
}
$list_navigator_html .= ' <a' . $class . $title4
. ' href="' . $script . Url::getCommon($_url_params) . '" >'
. $caption4 . '</a>';
$list_navigator_html .= ' <a' . $class . $title4 . ' href="' . $script
. '" data-post="'
. Url::getCommon($_url_params, '', false)
. '" >' . $caption4 . '</a>';
}
$list_navigator_html .= '</div>' . "\n";
}
@ -4969,9 +4990,7 @@ class Util
$urlParams['tbl_group'] = $_REQUEST['tbl_group'];
}
$url = 'db_structure.php' . Url::getCommon($urlParams);
return self::linkOrButton($url, $title . $orderImg, $orderLinkParams);
return self::linkOrButton('db_structure.php', $urlParams, $title . $orderImg, $orderLinkParams);
}
/**

View File

@ -137,6 +137,8 @@ if (! defined('PMA_NO_SESSION')) {
Session::setUp($GLOBALS['PMA_Config'], $GLOBALS['error_handler']);
}
Core::populateRequestWithEncryptedQueryParams();
/**
* init some variables LABEL_variables_init
*/

View File

@ -606,6 +606,13 @@ $cfg['Servers'][$i]['tracking_add_drop_table'] = true;
*/
$cfg['Servers'][$i]['tracking_add_drop_database'] = true;
/**
* Whether to show or hide detailed MySQL/MariaDB connection errors on the login page.
*
* @global bool $cfg['Servers'][$i]['hide_connection_errors']
*/
$cfg['Servers'][$i]['hide_connection_errors'] = false;
/**
* Default server (0 = no default server)
*
@ -820,6 +827,20 @@ $cfg['UseDbSearch'] = true;
*/
$cfg['IgnoreMultiSubmitErrors'] = false;
/**
* Define whether phpMyAdmin will encrypt sensitive data from the URL query string.
*
* @global bool $cfg['URLQueryEncryption']
*/
$cfg['URLQueryEncryption'] = false;
/**
* A secret key used to encrypt/decrypt the URL query string. Should be 32 bytes long.
*
* @global string $cfg['URLQueryEncryptionSecretKey']
*/
$cfg['URLQueryEncryptionSecretKey'] = '';
/**
* allow login to any user entered server in cookie based authentication
*

View File

@ -247,20 +247,7 @@ if [ ! -d libraries/tcpdf ] ; then
echo "* Running composer"
composer config platform.php "$PHP_REQ"
composer update --no-dev
# Parse the required versions from composer.json
PACKAGES_VERSIONS=''
case "$branch" in
QA_4*) PACKAGE_LIST="tecnickcom/tcpdf pragmarx/google2fa bacon/bacon-qr-code samyoul/u2f-php-server" ;;
*) PACKAGE_LIST="tecnickcom/tcpdf pragmarx/google2fa-qrcode samyoul/u2f-php-server" ;;
esac
for PACKAGES in $PACKAGE_LIST
do
PACKAGES_VERSIONS="$PACKAGES_VERSIONS $PACKAGES:`awk "/require-dev/ {printline = 1; print; next } printline" composer.json | grep "$PACKAGES" | awk -F [\\"] '{print $4}'`"
done
composer require --update-no-dev $PACKAGES_VERSIONS
composer require --update-no-dev tecnickcom/tcpdf:\^6.3 pragmarx/google2fa:\^3.0.0 bacon/bacon-qr-code:\^1.0.0 samyoul/u2f-php-server:\^1.1
mv composer.json.backup composer.json
echo "* Cleanup of composer packages"
rm -rf \
@ -290,8 +277,8 @@ if [ ! -d libraries/tcpdf ] ; then
vendor/twig/twig/test \
vendor/google/recaptcha/examples/ \
vendor/google/recaptcha/tests/
find vendor/phpseclib/phpseclib/phpseclib/Crypt/ -maxdepth 1 -type f -not -name AES.php -not -name Base.php -not -name Random.php -not -name Rijndael.php -print0 | xargs -0 rm
find vendor/tecnickcom/tcpdf/fonts/ -maxdepth 1 -type f -not -name 'dejavusans.*' -not -name 'dejavusansb.*' -not -name 'helvetica.php' -print0 | xargs -0 rm
find vendor/phpseclib/phpseclib/phpseclib/Crypt/ -maxdepth 1 -type f -not -name AES.php -not -name Base.php -not -name Random.php -not -name Rijndael.php -print0 | xargs -0 rm -f
find vendor/tecnickcom/tcpdf/fonts/ -maxdepth 1 -type f -not -name 'dejavusans.*' -not -name 'dejavusansb.*' -not -name 'helvetica.php' -print0 | xargs -0 rm -f
if [ $do_tag -eq 1 ] ; then
echo "* Commiting composer.lock"
git add --force composer.lock

View File

@ -52,7 +52,7 @@
'back': 'db_tracking.php',
'table': version.table_name,
'delete_tracking': true
}, '') }}">
}, '', false) }}">
{{ get_icon('b_drop', 'Delete tracking'|trans) }}
</a>
</td>
@ -63,7 +63,7 @@
'goto': 'tbl_tracking.php',
'back': 'db_tracking.php',
'table': version.table_name
}, '') }}">
}, '', false) }}">
{{ get_icon('b_versions', 'Versions'|trans) }}
</a>
<a href="tbl_tracking.php" data-post="
@ -74,7 +74,7 @@
'table': version.table_name,
'report': true,
'version': version.version
}, '') }}">
}, '', false) }}">
{{ get_icon('b_report', 'Tracking report'|trans) }}
</a>
<a href="tbl_tracking.php" data-post="
@ -85,7 +85,7 @@
'table': version.table_name,
'snapshot': true,
'version': version.version
}, '') }}">
}, '', false) }}">
{{ get_icon('b_props', 'Structure snapshot'|trans) }}
</a>
</td>

View File

@ -43,12 +43,12 @@
<td colspan="6" class="center">
{% if has_previous %}
{% if has_icons %}
<a href="server_binlog.php" data-post="{{ get_common(previous_params, '') }}" title="
<a href="server_binlog.php" data-post="{{ get_common(previous_params, '', false) }}" title="
{%- trans %}Previous{% context %}Previous page{% endtrans %}">
&laquo;
</a>
{% else %}
<a href="server_binlog.php" data-post="{{ get_common(previous_params, '') }}">
<a href="server_binlog.php" data-post="{{ get_common(previous_params, '', false) }}">
{% trans %}Previous{% context %}Previous page{% endtrans %} &laquo;
</a>
{% endif %}
@ -56,11 +56,11 @@
{% endif %}
{% if is_full_query %}
<a href="server_binlog.php" data-post="{{ get_common(full_queries_params, '') }}" title="{% trans 'Truncate shown queries' %}">
<a href="server_binlog.php" data-post="{{ get_common(full_queries_params, '', false) }}" title="{% trans 'Truncate shown queries' %}">
<img src="{{ image_path }}s_partialtext.png" alt="{% trans 'Truncate shown queries' %}">
</a>
{% else %}
<a href="server_binlog.php" data-post="{{ get_common(full_queries_params, '') }}" title="{% trans 'Show full queries' %}">
<a href="server_binlog.php" data-post="{{ get_common(full_queries_params, '', false) }}" title="{% trans 'Show full queries' %}">
<img src="{{ image_path }}s_fulltext.png" alt="{% trans 'Show full queries' %}">
</a>
{% endif %}
@ -68,12 +68,12 @@
{% if has_next %}
-
{% if has_icons %}
<a href="server_binlog.php" data-post="{{ get_common(next_params, '') }}" title="
<a href="server_binlog.php" data-post="{{ get_common(next_params, '', false) }}" title="
{%- trans %}Next{% context %}Next page{% endtrans %}">
&raquo;
</a>
{% else %}
<a href="server_binlog.php" data-post="{{ get_common(next_params, '') }}">
<a href="server_binlog.php" data-post="{{ get_common(next_params, '', false) }}">
{% trans %}Next{% context %}Next page{% endtrans %} &raquo;
</a>
{% endif %}

View File

@ -310,7 +310,7 @@
{{ 'Note: Enabling the database statistics here might cause heavy traffic between the web server and the MySQL server.'|trans|notice }}
<ul>
<li class="li_switch_dbstats">
<a href="server_databases.php" data-post="{{ get_common({'statistics': '1'}, '') }}" title="{% trans 'Enable statistics' %}">
<a href="server_databases.php" data-post="{{ get_common({'statistics': '1'}, '', false) }}" title="{% trans 'Enable statistics' %}">
<strong>{% trans 'Enable statistics' %}</strong>
</a>
</li>

View File

@ -39,7 +39,7 @@
</div>
</li>
<li>
<a href="server_replication.php" data-post="{{ get_common(url_params, '') }}" id="master_addslaveuser_href">
<a href="server_replication.php" data-post="{{ get_common(url_params, '', false) }}" id="master_addslaveuser_href">
{% trans 'Add slave replication user' %}
</a>
</li>

View File

@ -102,7 +102,7 @@
{% apply format('<a href="server_replication.php" data-post="' ~ get_common(url_params|merge({
'sl_configure': true,
'repl_clear_scr': true
})) ~ '">', '</a>')|raw %}
}, '', false)) ~ '">', '</a>')|raw %}
{% trans 'This server is not configured as slave in a replication process. Would you like to %sconfigure%s it?' %}
{% endapply %}
{% endif %}

View File

@ -5,7 +5,7 @@
<th>{% trans 'Processes' %}</th>
{% for column in columns %}
<th>
<a href="server_status_processes.php" data-post="{{ get_common(column.params) }}" class="sortlink">
<a href="server_status_processes.php" data-post="{{ get_common(column.params, '', false) }}" class="sortlink">
{{ column.name }}
{% if column.is_sorted %}
<img class="icon ic_s_desc soimg" alt="
@ -15,7 +15,7 @@
{% endif %}
</a>
{% if column.has_full_query %}
<a href="server_status_processes.php" data-post="{{ get_common(refresh_params, '') }}">
<a href="server_status_processes.php" data-post="{{ get_common(refresh_params, '', false) }}">
{% if column.is_full %}
{{ get_image(
's_partialtext',
@ -40,7 +40,7 @@
{% for row in rows %}
<tr>
<td>
<a class="ajax kill_process" href="server_status_processes.php" data-post="{{ get_common({'kill': row.id}, '') }}">
<a class="ajax kill_process" href="server_status_processes.php" data-post="{{ get_common({'kill': row.id}, '', false) }}">
{% trans 'Kill' %}
</a>
</td>

View File

@ -58,7 +58,7 @@
</a>
|
<a class="delete-server" href="{{ get_common(server.params.remove) }}" data-post="
{{- get_common({ token: server.params.token }, '') }}">
{{- get_common({ token: server.params.token }, '', false) }}">
{% trans 'Delete' %}
</a>
</small>

View File

@ -1,4 +1,4 @@
<span class="curr_value">{{ current_value }}</span>
<a href="browse_foreigners.php" data-post="{{ get_common(params, '') }}" class="ajax browse_foreign">
<a href="browse_foreigners.php" data-post="{{ get_common(params, '', false) }}" class="ajax browse_foreign">
{% trans 'Browse foreign values' %}
</a>

View File

@ -22,9 +22,8 @@
{% if one_key['constraint'] is defined %}
<input type="hidden" class="drop_foreign_key_msg" value="
{{- js_msg }}">
{% set drop_url = 'sql.php' ~ get_common(this_params) %}
{% set drop_str = get_icon('b_drop', 'Drop'|trans) %}
{{ link_or_button(drop_url, drop_str, {'class': 'drop_foreign_key_anchor ajax'}) }}
{{ link_or_button('sql.php', this_params, drop_str, {'class': 'drop_foreign_key_anchor ajax'}) }}
{% endif %}
</td>
<td>

View File

@ -21,7 +21,7 @@
value="{{ criteria_values[column_index] }}"
{% endif %}>
<a class="ajax browse_foreign" href="browse_foreigners.php" data-post="
{{- get_common({'db': db, 'table': table}, '') -}}
{{- get_common({'db': db, 'table': table}, '', false) -}}
&amp;field={{ column_name|url_encode }}&amp;fieldkey=
{{- column_index }}&amp;fromsearch=1">
{{ titles['Browse']|replace({"'": "\\'"})|raw }}
@ -34,10 +34,9 @@
class="textfield"
id="field_{{ column_index }}">
{% if in_fbs %}
{% set edit_url = 'gis_data_editor.php' ~ get_common() %}
{% set edit_str = get_icon('b_edit', 'Edit/Insert'|trans) %}
<span class="open_search_gis_editor">
{{ link_or_button(edit_url, edit_str, [], '_blank') }}
{{ link_or_button('gis_data_editor.php', [], edit_str, [], '_blank') }}
</span>
{% endif %}
{% elseif column_type starts with 'enum'

View File

@ -134,7 +134,7 @@
{% if partitions is empty %}
<input class="btn btn-secondary" type="submit" name="edit_partitioning" value="{% trans 'Partition table' %}">
{% else %}
{{ link_or_button(remove_url, 'Remove partitioning'|trans, {
{{ link_or_button('sql.php', remove_url_params, 'Remove partitioning'|trans, {
'class': 'button ajax',
'id': 'remove_partitioning'
}) }}

View File

@ -372,9 +372,9 @@
{# Work on the table #}
<div id="structure-action-links">
{% if tbl_is_view and not db_is_system_schema %}
{% set edit_view_url = 'view_create.php' ~ edit_view_url %}
{{ link_or_button(
edit_view_url,
'view_create.php',
{'db': db, 'table': table},
get_icon('b_edit', 'Edit view'|trans, true)
) }}
{% endif %}
@ -481,7 +481,6 @@
} only %}
{% set remove_sql = 'ALTER TABLE ' ~ backquote(table) ~ ' REMOVE PARTITIONING' %}
{% set remove_url = 'sql.php' ~ url_query ~ '&sql_query=' ~ remove_sql|url_encode %}
{% include 'table/structure/display_partitions.twig' with {
'db': db,
@ -496,7 +495,7 @@
'sub_partition_expression': has_sub_partitions ? first_sub_partition.getExpression(),
'action_icons': action_icons,
'range_or_list': range_or_list,
'remove_url': remove_url
'remove_url_params': url_params|merge({'sql_query': remove_sql})
} only %}
{% else %}
{% include 'table/structure/display_partitions.twig' with {

View File

@ -60,7 +60,7 @@
{{- get_common(url_params|merge({
'version': version['version'],
'submit_delete_version': true
}), '') }}">
}), '', false) }}">
{{ get_icon('b_drop', 'Delete version'|trans) }}
</a>
</td>
@ -69,14 +69,14 @@
{{- get_common(url_params|merge({
'version': version['version'],
'report': 'true'
}), '') }}">
}), '', false) }}">
{{ get_icon('b_report', 'Tracking report'|trans) }}
</a>
<a href="tbl_tracking.php" data-post="
{{- get_common(url_params|merge({
'version': version['version'],
'snapshot': 'true'
}), '') }}">
}), '', false) }}">
{{ get_icon('b_props', 'Structure snapshot'|trans) }}
</a>
</td>

View File

@ -29,6 +29,7 @@ class PageSettingsTest extends PmaTestCase
{
$GLOBALS['PMA_Config'] = new Config();
$GLOBALS['PMA_Config']->enableBc();
$GLOBALS['PMA_Config']->set('URLQueryEncryption', false);
$GLOBALS['server'] = 1;
$GLOBALS['db'] = 'db';
$GLOBALS['table'] = '';

View File

@ -250,7 +250,7 @@ class ProcessesControllerTest extends TestCase
]);
$killProcess = 'href="server_status_processes.php" data-post="'
. Url::getCommon(['kill' => $process['Id']], '') . '"';
. Url::getCommon(['kill' => $process['Id']], '', false) . '"';
$this->assertStringContainsString(
$killProcess,
$html

View File

@ -13,6 +13,7 @@ use PhpMyAdmin\Config;
use PhpMyAdmin\Core;
use PhpMyAdmin\Sanitize;
use PhpMyAdmin\Tests\PmaTestCase;
use PhpMyAdmin\Url;
use stdClass;
/**
@ -52,6 +53,7 @@ class CoreTest extends PmaTestCase
$GLOBALS['db'] = '';
$GLOBALS['table'] = '';
$GLOBALS['PMA_PHP_SELF'] = 'http://example.net/';
$GLOBALS['PMA_Config']->set('URLQueryEncryption', false);
}
/**
@ -1537,4 +1539,61 @@ class CoreTest extends PmaTestCase
// Must work now, (good secret and blowfish_secret)
$this->assertTrue(Core::checkSqlQuerySignature($sqlQuery, $hmac));
}
/**
* @return void
*/
public function testPopulateRequestWithEncryptedQueryParams()
{
global $PMA_Config;
$_SESSION = [];
$PMA_Config->set('URLQueryEncryption', true);
$PMA_Config->set('URLQueryEncryptionSecretKey', str_repeat('a', 32));
$_GET = ['pos' => '0', 'eq' => Url::encryptQuery('{"db":"test_db","table":"test_table"}')];
$_REQUEST = $_GET;
Core::populateRequestWithEncryptedQueryParams();
$expected = ['pos' => '0', 'db' => 'test_db', 'table' => 'test_table'];
$this->assertEquals($expected, $_GET);
$this->assertEquals($expected, $_REQUEST);
}
/**
* @return void
*
* @dataProvider providerForTestPopulateRequestWithEncryptedQueryParamsWithInvalidParam
*/
public function testPopulateRequestWithEncryptedQueryParamsWithInvalidParam($encrypted, $decrypted)
{
global $PMA_Config;
$_SESSION = [];
$PMA_Config->set('URLQueryEncryption', true);
$PMA_Config->set('URLQueryEncryptionSecretKey', str_repeat('a', 32));
$_GET = $encrypted;
$_REQUEST = $encrypted;
Core::populateRequestWithEncryptedQueryParams();
$this->assertEquals($decrypted, $_GET);
$this->assertEquals($decrypted, $_REQUEST);
}
/**
* @return string[][][]
*/
public function providerForTestPopulateRequestWithEncryptedQueryParamsWithInvalidParam()
{
return [
[[], []],
[['eq' => []], []],
[['eq' => ''], []],
[['eq' => 'invalid'], []],
];
}
}

View File

@ -0,0 +1,114 @@
<?php
namespace PhpMyAdmin\Tests\Crypto;
use PhpMyAdmin\Crypto\Crypto;
use PhpMyAdmin\Tests\PmaTestCase;
/**
* @covers \PhpMyAdmin\Crypto\Crypto
*/
class CryptoTest extends PmaTestCase
{
/**
* @return void
*/
public function testWithValidKeyFromConfig()
{
global $PMA_Config;
$_SESSION = [];
$PMA_Config->set('URLQueryEncryptionSecretKey', str_repeat('a', 32));
$cryptoWithSodium = new Crypto();
$encrypted = $cryptoWithSodium->encrypt('test');
$this->assertNotSame('test', $encrypted);
$this->assertSame('test', $cryptoWithSodium->decrypt($encrypted));
$this->assertArrayNotHasKey('URLQueryEncryptionSecretKey', $_SESSION);
$cryptoWithPhpseclib = new Crypto(true);
$encrypted = $cryptoWithPhpseclib->encrypt('test');
$this->assertNotSame('test', $encrypted);
$this->assertSame('test', $cryptoWithPhpseclib->decrypt($encrypted));
$this->assertArrayNotHasKey('URLQueryEncryptionSecretKey', $_SESSION);
}
/**
* @return void
*/
public function testWithValidKeyFromSession()
{
global $PMA_Config;
$_SESSION = ['URLQueryEncryptionSecretKey' => str_repeat('a', 32)];
$PMA_Config->set('URLQueryEncryptionSecretKey', '');
$cryptoWithSodium = new Crypto();
$encrypted = $cryptoWithSodium->encrypt('test');
$this->assertNotSame('test', $encrypted);
$this->assertSame('test', $cryptoWithSodium->decrypt($encrypted));
$this->assertArrayHasKey('URLQueryEncryptionSecretKey', $_SESSION);
$cryptoWithPhpseclib = new Crypto(true);
$encrypted = $cryptoWithPhpseclib->encrypt('test');
$this->assertNotSame('test', $encrypted);
$this->assertSame('test', $cryptoWithPhpseclib->decrypt($encrypted));
$this->assertArrayHasKey('URLQueryEncryptionSecretKey', $_SESSION);
}
/**
* @return void
*/
public function testWithNewSessionKey()
{
global $PMA_Config;
$_SESSION = [];
$PMA_Config->set('URLQueryEncryptionSecretKey', '');
$cryptoWithSodium = new Crypto();
$encrypted = $cryptoWithSodium->encrypt('test');
$this->assertNotSame('test', $encrypted);
$this->assertSame('test', $cryptoWithSodium->decrypt($encrypted));
$this->assertArrayHasKey('URLQueryEncryptionSecretKey', $_SESSION);
$this->assertEquals(32, mb_strlen($_SESSION['URLQueryEncryptionSecretKey'], '8bit'));
$cryptoWithPhpseclib = new Crypto(true);
$encrypted = $cryptoWithPhpseclib->encrypt('test');
$this->assertNotSame('test', $encrypted);
$this->assertSame('test', $cryptoWithPhpseclib->decrypt($encrypted));
$this->assertArrayHasKey('URLQueryEncryptionSecretKey', $_SESSION);
$this->assertEquals(32, mb_strlen($_SESSION['URLQueryEncryptionSecretKey'], '8bit'));
}
/**
* @return void
*/
public function testDecryptWithInvalidKey()
{
global $PMA_Config;
$_SESSION = [];
$PMA_Config->set('URLQueryEncryptionSecretKey', str_repeat('a', 32));
$cryptoWithSodium = new Crypto();
$encrypted = $cryptoWithSodium->encrypt('test');
$this->assertNotSame('test', $encrypted);
$this->assertSame('test', $cryptoWithSodium->decrypt($encrypted));
$PMA_Config->set('URLQueryEncryptionSecretKey', str_repeat('b', 32));
$cryptoWithSodium = new Crypto();
$this->assertNull($cryptoWithSodium->decrypt($encrypted));
$cryptoWithPhpseclib = new Crypto(true);
$encrypted = $cryptoWithPhpseclib->encrypt('test');
$this->assertNotSame('test', $encrypted);
$this->assertSame('test', $cryptoWithPhpseclib->decrypt($encrypted));
$PMA_Config->set('URLQueryEncryptionSecretKey', str_repeat('a', 32));
$cryptoWithPhpseclib = new Crypto(true);
$this->assertNull($cryptoWithPhpseclib->decrypt($encrypted));
}
}

View File

@ -266,6 +266,7 @@ class DatabaseInterfaceTest extends PmaTestCase
'host' => '',
'controluser' => 'u2',
'controlpass' => 'p2',
'hide_connection_errors' => false,
];
$cfg_ssl = [
'user' => 'u',
@ -274,6 +275,7 @@ class DatabaseInterfaceTest extends PmaTestCase
'ssl' => true,
'controluser' => 'u2',
'controlpass' => 'p2',
'hide_connection_errors' => false,
];
$cfg_control_ssl = [
'user' => 'u',
@ -282,6 +284,7 @@ class DatabaseInterfaceTest extends PmaTestCase
'control_ssl' => true,
'controluser' => 'u2',
'controlpass' => 'p2',
'hide_connection_errors' => false,
];
return [
[
@ -301,6 +304,7 @@ class DatabaseInterfaceTest extends PmaTestCase
'compress' => false,
'controluser' => 'u2',
'controlpass' => 'p2',
'hide_connection_errors' => false,
],
],
],
@ -317,6 +321,7 @@ class DatabaseInterfaceTest extends PmaTestCase
'port' => 0,
'ssl' => false,
'compress' => false,
'hide_connection_errors' => false,
],
],
],
@ -337,6 +342,7 @@ class DatabaseInterfaceTest extends PmaTestCase
'compress' => false,
'controluser' => 'u2',
'controlpass' => 'p2',
'hide_connection_errors' => false,
],
],
],
@ -353,6 +359,7 @@ class DatabaseInterfaceTest extends PmaTestCase
'port' => 0,
'ssl' => true,
'compress' => false,
'hide_connection_errors' => false,
],
],
],
@ -374,6 +381,7 @@ class DatabaseInterfaceTest extends PmaTestCase
'controluser' => 'u2',
'controlpass' => 'p2',
'control_ssl' => true,
'hide_connection_errors' => false,
],
],
],
@ -390,6 +398,7 @@ class DatabaseInterfaceTest extends PmaTestCase
'port' => 0,
'ssl' => true,
'compress' => false,
'hide_connection_errors' => false,
],
],
],

View File

@ -46,6 +46,7 @@ class ResultsTest extends PmaTestCase
$GLOBALS['PMA_PHP_SELF'] = 'index.php';
$this->object = new DisplayResults('as', '', 0, '', '');
$GLOBALS['PMA_Config'] = new Config();
$GLOBALS['PMA_Config']->set('URLQueryEncryption', false);
$GLOBALS['PMA_Config']->enableBc();
$GLOBALS['text_dir'] = 'ltr';
$_SESSION[' HMAC_secret '] = 'test';
@ -427,10 +428,16 @@ class ResultsTest extends PmaTestCase
{
return [
[
'tbl_change.php?db=Data&amp;table=customer&amp;where_clause=%60'
. 'customer%60.%60id%60+%3D+1&amp;clause_is_unique=1&amp;sql_query='
. 'SELECT+%2A+FROM+%60customer%60&amp;goto=sql.php&amp;default_'
. 'action=update',
'tbl_change.php',
[
'db' => 'Data',
'table' => 'customer',
'where_clause' => '`customer`.`id` = 1',
'clause_is_unique' => true,
'sql_query' => 'SELECT * FROM `customer`',
'goto' => 'sql.php',
'default_action' => 'update',
],
'klass edit_row_anchor',
'<span class="nowrap"><img src="themes/dot.gif" title="Edit" alt='
. '"Edit" class="icon ic_b_edit"> Edit</span>',
@ -441,7 +448,7 @@ class ResultsTest extends PmaTestCase
. '<a href="tbl_change.php" data-post="db=Data&amp;table=customer&amp;where_'
. 'clause=%60customer%60.%60id%60+%3D+1&amp;clause_is_unique=1&amp;'
. 'sql_query=SELECT+%2A+FROM+%60customer%60&amp;goto=sql.php&amp;'
. 'default_action=update"'
. 'default_action=update&amp;server=0&amp;lang=en"'
. '><span class="nowrap"><img src="themes/dot.gif" title="Edit" '
. 'alt="Edit" class="icon ic_b_edit"> Edit</span></a>'
. '<input type="hidden" class="where_clause" value ="%60customer'
@ -466,6 +473,7 @@ class ResultsTest extends PmaTestCase
*/
public function testGetEditLink(
$edit_url,
$urlParams,
$class,
$edit_str,
$where_clause,
@ -481,6 +489,7 @@ class ResultsTest extends PmaTestCase
'_getEditLink',
[
$edit_url,
$urlParams,
$class,
$edit_str,
$where_clause,
@ -499,21 +508,27 @@ class ResultsTest extends PmaTestCase
{
return [
[
'tbl_change.php?db=Data&amp;table=customer&amp;where_clause=%60cust'
. 'omer%60.%60id%60+%3D+1&amp;clause_is_unique=1&amp;sql_query='
. 'SELECT+%2A+FROM+%60customer%60&amp;goto=sql.php&amp;default_'
. 'action=insert',
'tbl_change.php',
[
'db' => 'Data',
'table' => 'customer',
'where_clause' => '`customer`.`id` = 1',
'clause_is_unique' => true,
'sql_query' => 'SELECT * FROM `customer`',
'goto' => 'sql.php',
'default_action' => 'insert',
],
'<span class="nowrap"><img src="themes/dot.gif" title="Copy" alt'
. '="Copy" class="icon ic_b_insrow"> Copy</span>',
'`customer`.`id` = 1',
'%60customer%60.%60id%60+%3D+1',
'klass',
'<td class="klass center print_ignore"><span class='
'<td class="klass center print_ignore" ><span class='
. '"nowrap">'
. '<a href="tbl_change.php" data-post="db=Data&amp;table=customer&amp;where_'
. 'clause=%60customer%60.%60id%60+%3D+1&amp;clause_is_unique=1&amp;'
. 'sql_query=SELECT+%2A+FROM+%60customer%60&amp;goto=sql.php&amp;'
. 'default_action=insert"'
. 'default_action=insert&amp;server=0&amp;lang=en"'
. '><span class="nowrap"><img src="themes/dot.gif" title="Copy" '
. 'alt="Copy" class="icon ic_b_insrow"> Copy</span></a>'
. '<input type="hidden" class="where_clause" value="%60customer%60'
@ -538,6 +553,7 @@ class ResultsTest extends PmaTestCase
*/
public function testGetCopyLink(
$copy_url,
$urlParams,
$copy_str,
$where_clause,
$where_clause_html,
@ -553,6 +569,7 @@ class ResultsTest extends PmaTestCase
'_getCopyLink',
[
$copy_url,
$urlParams,
$copy_str,
$where_clause,
$where_clause_html,
@ -571,12 +588,14 @@ class ResultsTest extends PmaTestCase
{
return [
[
'sql.php?db=Data&amp;table=customer&amp;sql_query=DELETE+FROM+%60'
. 'Data%60.%60customer%60+WHERE+%60customer%60.%60id%60+%3D+1&amp;'
. 'message_to_show=The+row+has+been+deleted&amp;goto=sql.php%3Fdb'
. '%3DData%26table%3Dcustomer%26sql_query%3DSELECT%2B%252A%2BFROM'
. '%2B%2560customer%2560%26message_to_show%3DThe%2Brow%2Bhas%2Bbeen'
. '%2Bdeleted%26goto%3Dtbl_structure.php',
'sql.php',
[
'db' => 'Data',
'table' => 'customer',
'sql_query' => 'DELETE FROM `Data`.`customer` WHERE `customer`.`id` = 1',
'message_to_show' => 'The row has been deleted.',
'goto' => 'tbl_sql.php',
],
'<span class="nowrap"><img src="themes/dot.gif" title="Delete" '
. 'alt="Delete" class="icon ic_b_drop"> Delete</span>',
'DELETE FROM `Data`.`customer` WHERE `customer`.`id` = 1',
@ -584,10 +603,8 @@ class ResultsTest extends PmaTestCase
'<td class="klass center print_ignore">'
. '<a href="sql.php" data-post="db=Data&amp;table=customer&amp;sql_query=DELETE'
. '+FROM+%60Data%60.%60customer%60+WHERE+%60customer%60.%60id%60+%3D'
. '+1&amp;message_to_show=The+row+has+been+deleted&amp;goto=sql.php'
. '%3Fdb%3DData%26table%3Dcustomer%26sql_query%3DSELECT%2B%252A%2B'
. 'FROM%2B%2560customer%2560%26message_to_show%3DThe%2Brow%2Bhas%2B'
. 'been%2Bdeleted%26goto%3Dtbl_structure.php" '
. '+1&amp;message_to_show=The+row+has+been+deleted.'
. '&amp;goto=tbl_sql.php&amp;server=0&amp;lang=en" '
. 'class="delete_row requireConfirm"><span class="nowrap"><img src="themes/dot.'
. 'gif" title="Delete" alt="Delete" class="icon ic_b_drop"> '
. 'Delete</span></a>'
@ -612,6 +629,7 @@ class ResultsTest extends PmaTestCase
*/
public function testGetDeleteLink(
$del_url,
$delUrlParams,
$del_str,
$js_conf,
$class,
@ -626,6 +644,7 @@ class ResultsTest extends PmaTestCase
'_getDeleteLink',
[
$del_url,
$delUrlParams,
$del_str,
$js_conf,
$class,
@ -644,12 +663,7 @@ class ResultsTest extends PmaTestCase
return [
[
DisplayResults::POSITION_LEFT,
'sql.php?db=data&amp;table=new&amp;sql_query=DELETE+FROM+%60data'
. '%60.%60new%60+WHERE+%60new%60.%60id%60+%3D+1&amp;message_to_show='
. 'The+row+has+been+deleted&amp;goto=sql.php%3Fdb%3Ddata%26table%3D'
. 'new%26sql_query%3DSELECT%2B%252A%2BFROM%2B%2560new%2560%26'
. 'message_to_show%3DThe%2Brow%2Bhas%2Bbeen%2Bdeleted%26goto%3D'
. 'tbl_structure.php',
'sql.php',
[
'edit_lnk' => 'ur',
'del_lnk' => 'dr',
@ -665,12 +679,8 @@ class ResultsTest extends PmaTestCase
[
'`new`.`id`' => '= 1',
],
'tbl_change.php?db=data&amp;table=new&amp;where_clause=%60new%60.'
. '%60id%60+%3D+1&amp;clause_is_unique=1&amp;sql_query=SELECT+%2A+'
. 'FROM+%60new%60&amp;goto=sql.php&amp;default_action=update',
'tbl_change.php?db=data&amp;table=new&amp;where_clause=%60new%60.'
. '%60id%60+%3D+1&amp;clause_is_unique=1&amp;sql_query=SELECT+%2A+'
. 'FROM+%60new%60&amp;goto=sql.php&amp;default_action=insert',
'tbl_change.php',
'tbl_change.php',
'edit_row_anchor',
'<span class="nowrap"><img src="themes/dot.gif" title="Edit" '
. 'alt="Edit" class="icon ic_b_edit"> Edit</span>',
@ -679,6 +689,21 @@ class ResultsTest extends PmaTestCase
'<span class="nowrap"><img src="themes/dot.gif" title="Delete" '
. 'alt="Delete" class="icon ic_b_drop"> Delete</span>',
'DELETE FROM `data`.`new` WHERE `new`.`id` = 1',
[
'db' => 'data',
'table' => 'new',
'where_clause' => '`new`.`id` = 1',
'clause_is_unique' => true,
'sql_query' => 'SELECT * FROM `new`',
'goto' => 'sql.php',
],
[
'db' => 'data',
'table' => 'new',
'sql_query' => 'DELETE FROM `data`.`new` WHERE `new`.`id` = 1',
'message_to_show' => 'The row has been deleted.',
'goto' => 'tbl_sql.php',
],
'<td class="center print_ignore"><input type="checkbox" id="id_rows_to_delete0_'
. 'left" name="rows_to_delete[0]" class="multi_checkbox checkall" '
. 'value="%60new%60.%60id%60+%3D+1"><input type="hidden" class='
@ -688,26 +713,24 @@ class ResultsTest extends PmaTestCase
. '<a href="tbl_change.php" data-post="db=data&amp;table=new&amp;where_'
. 'clause=%60new%60.%60id%60+%3D+1&amp;clause_is_unique=1&amp;'
. 'sql_query=SELECT+%2A+FROM+%60new%60&amp;goto=sql.php&amp;default'
. '_action=update">'
. '_action=update&amp;server=0&amp;lang=en">'
. '<span class="nowrap"><img src="themes/dot.gif" title="Edit" '
. 'alt="Edit" class="icon ic_b_edit"> Edit</span></a>'
. '<input type="hidden" class="where_clause" value ="%60new%60.%60'
. 'id%60+%3D+1"></span></td><td class="center print_ignore"><span class'
. 'id%60+%3D+1"></span></td><td class="center print_ignore" ><span class'
. '="nowrap">'
. '<a href="tbl_change.php" data-post="db=data&amp;table=new&amp;where_clause'
. '=%60new%60.%60id%60+%3D+1&amp;clause_is_unique=1&amp;sql_query='
. 'SELECT+%2A+FROM+%60new%60&amp;goto=sql.php&amp;default_action='
. 'insert"><span class'
. 'insert&amp;server=0&amp;lang=en"><span class'
. '="nowrap"><img src="themes/dot.gif" title="Copy" alt="Copy" '
. 'class="icon ic_b_insrow"> Copy</span></a>'
. '<input type="hidden" class="where_clause" value="%60new%60.%60id'
. '%60+%3D+1"></span></td><td class="center print_ignore">'
. '<a href="sql.php" data-post="db=data&amp;table=new&amp;sql_query=DELETE+'
. 'FROM+%60data%60.%60new%60+WHERE+%60new%60.%60id%60+%3D+1&amp;'
. 'message_to_show=The+row+has+been+deleted&amp;goto=sql.php%3F'
. 'db%3Ddata%26table%3Dnew%26sql_query%3DSELECT%2B%252A%2BFROM%2B'
. '%2560new%2560%26message_to_show%3DThe%2Brow%2Bhas%2Bbeen%2B'
. 'deleted%26goto%3Dtbl_structure.php" '
. 'message_to_show=The+row+has+been+deleted.'
. '&amp;goto=tbl_sql.php&amp;server=0&amp;lang=en" '
. 'class="delete_row requireConfirm"><span class="nowrap"><img src="themes/dot.'
. 'gif" title="Delete" alt="Delete" class="icon ic_b_drop"> '
. 'Delete</span></a>'
@ -716,12 +739,7 @@ class ResultsTest extends PmaTestCase
],
[
DisplayResults::POSITION_RIGHT,
'sql.php?db=data&amp;table=new&amp;sql_query=DELETE+FROM+%60data%60'
. '.%60new%60+WHERE+%60new%60.%60id%60+%3D+1&amp;message_to_show='
. 'The+row+has+been+deleted&amp;goto=sql.php%3Fdb%3Ddata%26table%3D'
. 'new%26sql_query%3DSELECT%2B%252A%2BFROM%2B%2560new%2560%26message'
. '_to_show%3DThe%2Brow%2Bhas%2Bbeen%2Bdeleted%26goto%3Dtbl_'
. 'structure.php',
'sql.php',
[
'edit_lnk' => 'ur',
'del_lnk' => 'dr',
@ -737,12 +755,8 @@ class ResultsTest extends PmaTestCase
[
'`new`.`id`' => '= 1',
],
'tbl_change.php?db=data&amp;table=new&amp;where_clause=%60new%60.'
. '%60id%60+%3D+1&amp;clause_is_unique=1&amp;sql_query=SELECT+%2A+'
. 'FROM+%60new%60&amp;goto=sql.php&amp;default_action=update',
'tbl_change.php?db=data&amp;table=new&amp;where_clause=%60new%60.'
. '%60id%60+%3D+1&amp;clause_is_unique=1&amp;sql_query=SELECT+%2A+'
. 'FROM+%60new%60&amp;goto=sql.php&amp;default_action=insert',
'tbl_change.php',
'tbl_change.php',
'edit_row_anchor',
'<span class="nowrap"><img src="themes/dot.gif" title="Edit" '
. 'alt="Edit" class="icon ic_b_edit"> Edit</span>',
@ -751,21 +765,34 @@ class ResultsTest extends PmaTestCase
'<span class="nowrap"><img src="themes/dot.gif" title="Delete" '
. 'alt="Delete" class="icon ic_b_drop"> Delete</span>',
'DELETE FROM `data`.`new` WHERE `new`.`id` = 1',
[
'db' => 'data',
'table' => 'new',
'where_clause' => '`new`.`id` = 1',
'clause_is_unique' => true,
'sql_query' => 'SELECT * FROM `new`',
'goto' => 'sql.php',
],
[
'db' => 'data',
'table' => 'new',
'sql_query' => 'DELETE FROM `data`.`new` WHERE `new`.`id` = 1',
'message_to_show' => 'The row has been deleted.',
'goto' => 'tbl_sql.php',
],
'<td class="center print_ignore">'
. '<a href="sql.php" data-post="db=data&amp;table=new&amp;sql_query=DELETE+'
. 'FROM+%60data%60.%60new%60+WHERE+%60new%60.%60id%60+%3D+1&amp;'
. 'message_to_show=The+row+has+been+deleted&amp;goto=sql.php%3Fdb'
. '%3Ddata%26table%3Dnew%26sql_query%3DSELECT%2B%252A%2BFROM%2B%25'
. '60new%2560%26message_to_show%3DThe%2Brow%2Bhas%2Bbeen%2Bdeleted'
. '%26goto%3Dtbl_structure.php" class="delete'
. 'message_to_show=The+row+has+been+deleted.&amp;goto=tbl_sql.php'
. '&amp;server=0&amp;lang=en" class="delete'
. '_row requireConfirm"><span class="nowrap"><img src="themes/dot.gif" title='
. '"Delete" alt="Delete" class="icon ic_b_drop"> Delete</span></a>'
. '<div class="hide">DELETE FROM `data`.`new` WHERE `new`.'
. '`id` = 1</div></td><td class="center print_ignore"><span class="nowrap">'
. '`id` = 1</div></td><td class="center print_ignore" ><span class="nowrap">'
. '<a href="tbl_change.php" data-post="db=data&amp;table=new&amp;where_'
. 'clause=%60new%60.%60id%60+%3D+1&amp;clause_is_unique=1&amp;sql_'
. 'query=SELECT+%2A+FROM+%60new%60&amp;goto=sql.php&amp;default_'
. 'action=insert"><span '
. 'action=insert&amp;server=0&amp;lang=en"><span '
. 'class="nowrap"><img src="themes/dot.gif" title="Copy" alt="Copy" '
. 'class="icon ic_b_insrow"> Copy</span></a>'
. '<input type="hidden" class="where_clause" value="%60new%60.%60id'
@ -774,7 +801,7 @@ class ResultsTest extends PmaTestCase
. '<a href="tbl_change.php" data-post="db=data&amp;table=new&amp;where_clause'
. '=%60new%60.%60id%60+%3D+1&amp;clause_is_unique=1&amp;sql_query='
. 'SELECT+%2A+FROM+%60new%60&amp;goto=sql.php&amp;default_action='
. 'update"><span class='
. 'update&amp;server=0&amp;lang=en"><span class='
. '"nowrap"><img src="themes/dot.gif" title="Edit" alt="Edit" class'
. '="icon ic_b_edit"> Edit</span></a>'
. '<input type="hidden" class="where_clause" value ="%60new%60.%60'
@ -786,12 +813,7 @@ class ResultsTest extends PmaTestCase
],
[
DisplayResults::POSITION_NONE,
'sql.php?db=data&amp;table=new&amp;sql_query=DELETE+FROM+%60data%60.'
. '%60new%60+WHERE+%60new%60.%60id%60+%3D+1&amp;message_to_show=The+'
. 'row+has+been+deleted&amp;goto=sql.php%3Fdb%3Ddata%26table%3Dnew'
. '%26sql_query%3DSELECT%2B%252A%2BFROM%2B%2560new%2560%26message_'
. 'to_show%3DThe%2Brow%2Bhas%2Bbeen%2Bdeleted%26goto%3Dtbl_structure'
. '.php',
'sql.php',
[
'edit_lnk' => 'ur',
'del_lnk' => 'dr',
@ -807,12 +829,8 @@ class ResultsTest extends PmaTestCase
[
'`new`.`id`' => '= 1',
],
'tbl_change.php?db=data&amp;table=new&amp;where_clause=%60new%60.%60'
. 'id%60+%3D+1&amp;clause_is_unique=1&amp;sql_query=SELECT+%2A+FROM+'
. '%60new%60&amp;goto=sql.php&amp;default_action=update',
'tbl_change.php?db=data&amp;table=new&amp;where_clause=%60new%60.%60'
. 'id%60+%3D+1&amp;clause_is_unique=1&amp;sql_query=SELECT+%2A+FROM+'
. '%60new%60&amp;goto=sql.php&amp;default_action=insert',
'tbl_change.php',
'tbl_change.php',
'edit_row_anchor',
'<span class="nowrap"><img src="themes/dot.gif" title="Edit" '
. 'alt="Edit" class="icon ic_b_edit"> Edit</span>',
@ -821,6 +839,21 @@ class ResultsTest extends PmaTestCase
'<span class="nowrap"><img src="themes/dot.gif" title="Delete" '
. 'alt="Delete" class="icon ic_b_drop"> Delete</span>',
'DELETE FROM `data`.`new` WHERE `new`.`id` = 1',
[
'db' => 'data',
'table' => 'new',
'where_clause' => '`new`.`id` = 1',
'clause_is_unique' => true,
'sql_query' => 'SELECT * FROM `new`',
'goto' => 'sql.php',
],
[
'db' => 'data',
'table' => 'new',
'sql_query' => 'DELETE FROM `data`.`new` WHERE `new`.`id` = 1',
'message_to_show' => 'The row has been deleted.',
'goto' => 'tbl_sql.php',
],
'<td class="center print_ignore"><input type="checkbox" id="id_rows_to_'
. 'delete0_left" name="rows_to_delete[0]" class="multi_checkbox '
. 'checkall" value="%60new%60.%60id%60+%3D+1"><input type='
@ -869,6 +902,8 @@ class ResultsTest extends PmaTestCase
$copy_str,
$del_str,
$js_conf,
$editCopyUrlParams,
$delUrlParams,
$output
) {
$this->assertEquals(
@ -890,6 +925,8 @@ class ResultsTest extends PmaTestCase
$copy_str,
$del_str,
$js_conf,
$editCopyUrlParams,
$delUrlParams,
]
)
);
@ -940,6 +977,8 @@ class ResultsTest extends PmaTestCase
'<span class="nowrap"><img src="themes/dot.gif" title="Delete" '
. 'alt="Delete" class="icon ic_b_drop"> Delete</span>',
null,
[],
[],
'<td class="center print_ignore"><input type="checkbox" id="id_rows_to_'
. 'delete0_left" name="rows_to_delete[0]" class="multi_checkbox '
. 'checkall" value="%60new%60.%60id%60+%3D+1"><input type='
@ -987,6 +1026,8 @@ class ResultsTest extends PmaTestCase
$copy_str,
$del_str,
$js_conf,
$editCopyUrlParams,
$delUrlParams,
$output
) {
$this->assertEquals(
@ -1008,6 +1049,8 @@ class ResultsTest extends PmaTestCase
$copy_str,
$del_str,
$js_conf,
$editCopyUrlParams,
$delUrlParams,
]
)
);

View File

@ -96,6 +96,13 @@ class EncodingTest extends TestCase
$this->markTestSkipped('iconv extension missing');
}
// Set PHP native locale
if (function_exists('setlocale') && setlocale(0, 'POSIX') === false) {
$this->markTestSkipped('native setlocale failed');
}
_setlocale(LC_ALL, 'POSIX');
if (PHP_INT_SIZE === 8) {
$GLOBALS['cfg']['IconvExtraParams'] = '//TRANSLIT';
Encoding::setEngine(Encoding::ENGINE_ICONV);

View File

@ -48,6 +48,7 @@ class FooterTest extends PmaTestCase
$GLOBALS['table'] = '';
$GLOBALS['text_dir'] = 'ltr';
$GLOBALS['PMA_Config'] = new Config();
$GLOBALS['PMA_Config']->set('URLQueryEncryption', false);
$GLOBALS['PMA_Config']->enableBc();
$GLOBALS['cfg']['Server']['DisableIS'] = false;
$GLOBALS['cfg']['Server']['verbose'] = 'verbose host';

View File

@ -65,6 +65,7 @@ class InsertEditTest extends TestCase
$GLOBALS['cfg']['LoginCookieValidity'] = 1440;
$GLOBALS['cfg']['enable_drag_drop_import'] = true;
$GLOBALS['PMA_Config'] = new Config();
$GLOBALS['PMA_Config']->set('URLQueryEncryption', false);
$this->insertEdit = new InsertEdit($GLOBALS['dbi']);
}

View File

@ -13,6 +13,9 @@ use PhpMyAdmin\Config;
use PhpMyAdmin\Navigation\NavigationTree;
use PhpMyAdmin\Template;
use PhpMyAdmin\Tests\PmaTestCase;
use PhpMyAdmin\Theme;
use PhpMyAdmin\Url;
use ReflectionMethod;
/**
* Tests for PhpMyAdmin\Navigation\NavigationTree class
@ -36,6 +39,7 @@ class NavigationTreeTest extends PmaTestCase
{
$GLOBALS['server'] = 1;
$GLOBALS['PMA_Config'] = new Config();
$GLOBALS['PMA_Config']->set('URLQueryEncryption', false);
$GLOBALS['PMA_Config']->enableBc();
$GLOBALS['cfg']['Server']['host'] = 'localhost';
$GLOBALS['cfg']['Server']['user'] = 'user';
@ -96,4 +100,37 @@ class NavigationTreeTest extends PmaTestCase
$result = $this->object->renderDbSelect();
$this->assertStringContainsString('pma_navigation_select_database', $result);
}
/**
* @return void
*/
public function testEncryptQueryParams()
{
global $PMA_Config;
$_SESSION = [];
$PMA_Config->set('URLQueryEncryption', false);
$PMA_Config->set('URLQueryEncryptionSecretKey', str_repeat('a', 32));
$method = new ReflectionMethod($this->object, 'encryptQueryParams');
$method->setAccessible(true);
$link = 'tbl_structure.php?server=1&amp;db=test_db&amp;table=test_table&amp;pos=0';
$actual = $method->invoke($this->object, $link);
$this->assertEquals($link, $actual);
$PMA_Config->set('URLQueryEncryption', true);
$actual = $method->invoke($this->object, $link);
$this->assertStringStartsWith('tbl_structure.php?server=1&amp;pos=0&amp;eq=', $actual);
$url = parse_url($actual);
parse_str(htmlspecialchars_decode($url['query']), $query);
$this->assertRegExp('/^[a-zA-Z0-9-_=]+$/', $query['eq']);
$decrypted = Url::decryptQuery($query['eq']);
$this->assertJson($decrypted);
$this->assertSame('{"db":"test_db","table":"test_table"}', $decrypted);
}
}

View File

@ -40,6 +40,7 @@ class AuthenticationCookieTest extends PmaTestCase
protected function setUp(): void
{
$GLOBALS['PMA_Config'] = new Config();
$GLOBALS['PMA_Config']->set('URLQueryEncryption', false);
$GLOBALS['PMA_Config']->enableBc();
$GLOBALS['server'] = 0;
$GLOBALS['text_dir'] = 'ltr';

View File

@ -40,6 +40,7 @@ class PmaTestCase extends TestCase
{
require ROOT_PATH . 'libraries/config.default.php';
$GLOBALS['cfg'] = $cfg;
$GLOBALS['PMA_Config']->set('URLQueryEncryption', false);
}
/**

View File

@ -131,7 +131,7 @@ class UserGroupsTest extends TestCase
'viewUsers' => 1,
'userGroup' => htmlspecialchars('usergroup'),
],
''
'', false
);
$this->assertStringContainsString(
$url_tag,
@ -143,7 +143,7 @@ class UserGroupsTest extends TestCase
'editUserGroup' => 1,
'userGroup' => htmlspecialchars('usergroup'),
],
''
'', false
);
$this->assertStringContainsString(
$url_tag,
@ -155,7 +155,7 @@ class UserGroupsTest extends TestCase
'deleteUserGroup' => 1,
'userGroup' => htmlspecialchars('usergroup'),
],
''
'', false
);
$this->assertStringContainsString(
$url_tag,

View File

@ -29,6 +29,7 @@ class UrlTest extends TestCase
protected function setUp(): void
{
unset($_COOKIE['pma_lang']);
$GLOBALS['PMA_Config']->set('URLQueryEncryption', false);
}
/**
@ -114,4 +115,61 @@ class UrlTest extends TestCase
$expected = '?server=x' . htmlentities($separator) . 'lang=en' ;
$this->assertEquals($expected, Url::getCommon());
}
/**
* @return void
*/
public function testBuildHttpQueryWithUrlQueryEncryptionDisabled()
{
global $PMA_Config;
$PMA_Config->set('URLQueryEncryption', false);
$params = ['db' => 'test_db', 'table' => 'test_table', 'pos' => 0];
$this->assertEquals('db=test_db&table=test_table&pos=0', Url::buildHttpQuery($params));
}
/**
* @return void
*/
public function testBuildHttpQueryWithUrlQueryEncryptionEnabled()
{
global $PMA_Config;
$_SESSION = [];
$PMA_Config->set('URLQueryEncryption', true);
$PMA_Config->set('URLQueryEncryptionSecretKey', str_repeat('a', 32));
$params = ['db' => 'test_db', 'table' => 'test_table', 'pos' => 0];
$query = Url::buildHttpQuery($params);
$this->assertStringStartsWith('pos=0&eq=', $query);
parse_str($query, $queryParams);
$this->assertCount(2, $queryParams);
$this->assertSame('0', $queryParams['pos']);
$this->assertTrue(is_string($queryParams['eq']));
$this->assertNotSame('', $queryParams['eq']);
$this->assertRegExp('/^[a-zA-Z0-9-_=]+$/', $queryParams['eq']);
$decrypted = Url::decryptQuery($queryParams['eq']);
$this->assertJson($decrypted);
$this->assertSame('{"db":"test_db","table":"test_table"}', $decrypted);
}
/**
* @return void
*/
public function testQueryEncryption()
{
global $PMA_Config;
$_SESSION = [];
$PMA_Config->set('URLQueryEncryption', true);
$PMA_Config->set('URLQueryEncryptionSecretKey', str_repeat('a', 32));
$query = '{"db":"test_db","table":"test_table"}';
$encrypted = Url::encryptQuery($query);
$this->assertNotSame($query, $encrypted);
$this->assertNotSame('', $encrypted);
$this->assertRegExp('/^[a-zA-Z0-9-_=]+$/', $encrypted);
$decrypted = Url::decryptQuery($encrypted);
$this->assertSame($query, $decrypted);
}
}

View File

@ -2700,38 +2700,22 @@ class UtilTest extends PmaTestCase
{
return [
[
[
'index.php',
'text',
],
['index.php', null, 'text'],
1000,
'<a href="index.php" >text</a>',
],
[
[
'index.php?some=parameter',
'text',
],
['index.php', ['some' => 'parameter'], 'text'],
20,
'<a href="index.php" data-post="some=parameter">text</a>',
'<a href="index.php" data-post="some=parameter&amp;lang=en">text</a>',
],
[
[
'index.php',
'text',
[],
'target',
],
['index.php', null, 'text', [], 'target'],
1000,
'<a href="index.php" target="target">text</a>',
],
[
[
'url.php?url=http://phpmyadmin.net/',
'text',
[],
'_blank',
],
['url.php?url=http://phpmyadmin.net/', null, 'text', [], '_blank'],
1000,
'<a href="url.php?url=http://phpmyadmin.net/" target="_blank" rel="noopener noreferrer">text</a>',
],