{{ link_or_button(
- url('/sql', url_params|merge({
+ url('/sql'),
+ url_params|merge({
'sql_query': 'DROP VIEW ' ~ backquote(table),
'goto': url('/table/structure'),
'reload': true,
'purge': true,
'message_to_show': 'View %s has been dropped.'|trans|format(table)|e,
'table': table
- })),
+ }),
'Delete the view (DROP)'|trans,
{
'id': 'drop_view_anchor',
diff --git a/templates/table/relation/foreign_key_row.twig b/templates/table/relation/foreign_key_row.twig
index c73004cddc..a011255f5a 100644
--- a/templates/table/relation/foreign_key_row.twig
+++ b/templates/table/relation/foreign_key_row.twig
@@ -22,9 +22,8 @@
{% if one_key['constraint'] is defined %}
- {% set drop_url = url('/sql', 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(url('/sql'), this_params, drop_str, {'class': 'drop_foreign_key_anchor ajax'}) }}
{% endif %}
diff --git a/templates/table/search/input_box.twig b/templates/table/search/input_box.twig
index e5ac9624d2..7915cc77ae 100644
--- a/templates/table/search/input_box.twig
+++ b/templates/table/search/input_box.twig
@@ -21,7 +21,7 @@
value="{{ criteria_values[column_index] }}"
{% endif %}>
{{ get_icon('b_browse', 'Browse foreign values'|trans) }}
@@ -36,7 +36,7 @@
{% if in_fbs %}
{% set edit_str = get_icon('b_edit', 'Edit/Insert'|trans) %}
- {{ link_or_button(url('/gis-data-editor'), edit_str, [], '_blank') }}
+ {{ link_or_button(url('/gis-data-editor'), [], edit_str, [], '_blank') }}
{% endif %}
{% elseif column_type starts with 'enum'
diff --git a/templates/table/structure/display_partitions.twig b/templates/table/structure/display_partitions.twig
index 2f9a6683a1..826c363262 100644
--- a/templates/table/structure/display_partitions.twig
+++ b/templates/table/structure/display_partitions.twig
@@ -80,7 +80,7 @@
'db': db,
'table': table,
'partition_name': partition.getName(),
- }) }}">
+ }, '', false) }}">
{{ get_icon('b_search', 'Analyze'|trans) }}
@@ -90,7 +90,7 @@
'db': db,
'table': table,
'partition_name': partition.getName(),
- }) }}">
+ }, '', false) }}">
{{ get_icon('eye', 'Check'|trans) }}
@@ -100,7 +100,7 @@
'db': db,
'table': table,
'partition_name': partition.getName(),
- }) }}">
+ }, '', false) }}">
{{ get_icon('normalize', 'Optimize'|trans) }}
@@ -110,7 +110,7 @@
'db': db,
'table': table,
'partition_name': partition.getName(),
- }) }}">
+ }, '', false) }}">
{{ get_icon('s_tbl', 'Rebuild'|trans) }}
@@ -120,7 +120,7 @@
'db': db,
'table': table,
'partition_name': partition.getName(),
- }) }}">
+ }, '', false) }}">
{{ get_icon('b_tblops', 'Repair'|trans) }}
@@ -130,7 +130,7 @@
'db': db,
'table': table,
'partition_name': partition.getName(),
- }) }}">
+ }, '', false) }}">
{{ get_icon('b_empty', 'Truncate'|trans) }}
@@ -141,7 +141,7 @@
'db': db,
'table': table,
'partition_name': partition.getName(),
- }) }}">
+ }, '', false) }}">
{{ get_icon('b_drop', 'Drop'|trans) }}
@@ -194,11 +194,12 @@
{% else %}
{{ link_or_button(
- url('/sql', {
+ url('/sql'),
+ {
'db': db,
'table': table,
'sql_query': 'ALTER TABLE ' ~ backquote(table) ~ ' REMOVE PARTITIONING'
- }),
+ },
'Remove partitioning'|trans, {
'class': 'btn btn-secondary ajax',
'id': 'remove_partitioning'
diff --git a/templates/table/structure/display_structure.twig b/templates/table/structure/display_structure.twig
index 8d089f75e3..492ec788f5 100644
--- a/templates/table/structure/display_structure.twig
+++ b/templates/table/structure/display_structure.twig
@@ -117,7 +117,7 @@
'dropped_column': row['Field'],
'purge': true,
'message_to_show': 'Column %s has been dropped.'|trans|format(row['Field']|e)
- }) }}">
+ }, '', false) }}">
{{ get_icon('b_drop', 'Drop'|trans) }}
@@ -142,7 +142,7 @@
'table': table,
'sql_query': 'ALTER TABLE ' ~ backquote(table) ~ (primary ? ' DROP PRIMARY KEY,') ~ ' ADD PRIMARY KEY(' ~ backquote(row['Field']) ~ ');',
'message_to_show': 'A primary key has been added on %s.'|trans|format(row['Field']|e)
- }) }}">
+ }, '', false) }}">
{{ get_icon('b_primary', 'Primary'|trans) }}
{% endif %}
@@ -157,7 +157,7 @@
'table': table,
'sql_query': 'ALTER TABLE ' ~ backquote(table) ~ ' ADD UNIQUE(' ~ backquote(row['Field']) ~ ');',
'message_to_show': 'An index has been added on %s.'|trans|format(row['Field']|e)
- }) }}">
+ }, '', false) }}">
{{ get_icon('b_unique', 'Unique'|trans) }}
{% endif %}
@@ -172,7 +172,7 @@
'table': table,
'sql_query': 'ALTER TABLE ' ~ backquote(table) ~ ' ADD INDEX(' ~ backquote(row['Field']) ~ ');',
'message_to_show': 'An index has been added on %s.'|trans|format(row['Field']|e)
- }) }}">
+ }, '', false) }}">
{{ get_icon('b_index', 'Index'|trans) }}
{% endif %}
@@ -197,7 +197,7 @@
'table': table,
'sql_query': 'ALTER TABLE ' ~ backquote(table) ~ ' ADD SPATIAL(' ~ backquote(row['Field']) ~ ');',
'message_to_show': 'An index has been added on %s.'|trans|format(row['Field']|e)
- }) }}">
+ }, '', false) }}">
{{ get_icon('b_spatial', 'Spatial'|trans) }}
{% endif %}
@@ -216,7 +216,7 @@
'table': table,
'sql_query': 'ALTER TABLE ' ~ backquote(table) ~ ' ADD FULLTEXT(' ~ backquote(row['Field']) ~ ');',
'message_to_show': 'An index has been added on %s.'|trans|format(row['Field']|e)
- }) }}">
+ }, '', false) }}">
{{ get_icon('b_ftext', 'Fulltext'|trans) }}
{% else %}
@@ -235,7 +235,7 @@
~ ' GROUP BY ' ~ backquote(row['Field'])
~ ' ORDER BY ' ~ backquote(row['Field']),
'is_browse_distinct': true
- }) }}">
+ }, '', false) }}">
{{ get_icon('b_browse', 'Distinct values'|trans) }}
@@ -326,7 +326,8 @@
{% if tbl_is_view and not db_is_system_schema %}
{{ link_or_button(
- url('/view/create', {'db': db, 'table': table}),
+ url('/view/create'),
+ {'db': db, 'table': table},
get_icon('b_edit', 'Edit view'|trans, true)
) }}
{% endif %}
@@ -339,7 +340,7 @@
'table': table,
'sql_query': 'SELECT * FROM ' ~ backquote(table) ~ ' PROCEDURE ANALYSE()',
'session_max_rows': 'all'
- }) }}">
+ }, '', false) }}">
{{ get_icon(
'b_tblanalyse',
'Propose table structure'|trans,
@@ -432,7 +433,7 @@
'db': db,
'table': table,
'index': index.getName()
- }, '') }}">
+ }, '', false) }}">
{{ get_icon('b_edit', 'Edit'|trans) }}
@@ -441,7 +442,7 @@
'db': db,
'table': table,
'index': index.getName()
- }, '') }}">
+ }, '', false) }}">
{{ get_icon('b_rename', 'Rename'|trans) }}
@@ -460,7 +461,8 @@
{{ link_or_button(
- url('/sql', index_params|merge({'db': db, 'table': table})),
+ url('/sql'),
+ index_params|merge({'db': db, 'table': table}),
get_icon('b_drop', 'Drop'|trans),
{'class': 'drop_primary_key_index_anchor ajax'}
) }}
diff --git a/templates/table/tracking/main.twig b/templates/table/tracking/main.twig
index ae58cbe208..e7000990b4 100644
--- a/templates/table/tracking/main.twig
+++ b/templates/table/tracking/main.twig
@@ -60,7 +60,7 @@
{{- get_common(url_params|merge({
'version': version['version'],
'submit_delete_version': true
- }), '') }}">
+ }), '', false) }}">
{{ get_icon('b_drop', 'Delete version'|trans) }}
@@ -69,14 +69,14 @@
{{- get_common(url_params|merge({
'version': version['version'],
'report': 'true'
- }), '') }}">
+ }), '', false) }}">
{{ get_icon('b_report', 'Tracking report'|trans) }}
+ }), '', false) }}">
{{ get_icon('b_props', 'Structure snapshot'|trans) }}
diff --git a/test/classes/AbstractTestCase.php b/test/classes/AbstractTestCase.php
index 72eb43b0e9..93a0212b97 100644
--- a/test/classes/AbstractTestCase.php
+++ b/test/classes/AbstractTestCase.php
@@ -200,6 +200,7 @@ abstract class AbstractTestCase extends TestCase
global $PMA_Config;
$PMA_Config = new Config();
$PMA_Config->set('environment', 'development');
+ $PMA_Config->set('URLQueryEncryption', false);
}
protected function setTheme(): void
diff --git a/test/classes/CoreTest.php b/test/classes/CoreTest.php
index 5c77e72c96..ce535571c1 100644
--- a/test/classes/CoreTest.php
+++ b/test/classes/CoreTest.php
@@ -6,6 +6,7 @@ namespace PhpMyAdmin\Tests;
use PhpMyAdmin\Core;
use PhpMyAdmin\Sanitize;
+use PhpMyAdmin\Url;
use stdClass;
use function hash;
use function htmlspecialchars;
@@ -15,6 +16,7 @@ use function ob_get_contents;
use function ob_start;
use function preg_quote;
use function serialize;
+use function str_repeat;
class CoreTest extends AbstractNetworkTestCase
{
@@ -33,6 +35,7 @@ class CoreTest extends AbstractNetworkTestCase
$GLOBALS['db'] = '';
$GLOBALS['table'] = '';
$GLOBALS['PMA_PHP_SELF'] = 'http://example.net/';
+ $GLOBALS['PMA_Config']->set('URLQueryEncryption', false);
}
/**
@@ -1437,4 +1440,61 @@ class CoreTest extends AbstractNetworkTestCase
$this->assertArrayHasKey('test', $_POST);
$this->assertEquals('test', $_POST['test']);
}
+
+ public function testPopulateRequestWithEncryptedQueryParams(): void
+ {
+ 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);
+ }
+
+ /**
+ * @param string[] $encrypted
+ * @param string[] $decrypted
+ *
+ * @dataProvider providerForTestPopulateRequestWithEncryptedQueryParamsWithInvalidParam
+ */
+ public function testPopulateRequestWithEncryptedQueryParamsWithInvalidParam(
+ array $encrypted,
+ array $decrypted
+ ): void {
+ 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(): array
+ {
+ return [
+ [[], []],
+ [['eq' => []], []],
+ [['eq' => ''], []],
+ [['eq' => 'invalid'], []],
+ ];
+ }
}
diff --git a/test/classes/Crypto/CryptoTest.php b/test/classes/Crypto/CryptoTest.php
new file mode 100644
index 0000000000..fe7964d7c1
--- /dev/null
+++ b/test/classes/Crypto/CryptoTest.php
@@ -0,0 +1,106 @@
+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);
+ }
+
+ public function testWithValidKeyFromSession(): void
+ {
+ 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);
+ }
+
+ public function testWithNewSessionKey(): void
+ {
+ 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'));
+ }
+
+ public function testDecryptWithInvalidKey(): void
+ {
+ 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));
+ }
+}
diff --git a/test/classes/Html/GeneratorTest.php b/test/classes/Html/GeneratorTest.php
index 23c7d5d30c..ea0c66ae91 100644
--- a/test/classes/Html/GeneratorTest.php
+++ b/test/classes/Html/GeneratorTest.php
@@ -221,6 +221,7 @@ class GeneratorTest extends AbstractTestCase
[
[
'index.php',
+ null,
'text',
],
1000,
@@ -228,15 +229,17 @@ class GeneratorTest extends AbstractTestCase
],
[
[
- 'index.php?some=parameter',
+ 'index.php',
+ ['some' => 'parameter'],
'text',
],
20,
- '
text ',
+ '
text ',
],
[
[
'index.php',
+ null,
'text',
[],
'target',
@@ -247,6 +250,7 @@ class GeneratorTest extends AbstractTestCase
[
[
'https://mariadb.org/explain_analyzer/analyze/?client=phpMyAdmin&raw_explain=%2B---%2B',
+ null,
'text',
[],
'target',
@@ -259,6 +263,7 @@ class GeneratorTest extends AbstractTestCase
[
[
'https://mariadb.org/explain_analyzer/analyze/?client=phpMyAdmin&raw_explain=%2B---%2B',
+ null,
'text',
[],
'target',
@@ -271,6 +276,7 @@ class GeneratorTest extends AbstractTestCase
[
[
'url.php?url=http://phpmyadmin.net/',
+ null,
'text',
[],
'_blank',
diff --git a/test/classes/Navigation/NavigationTreeTest.php b/test/classes/Navigation/NavigationTreeTest.php
index 6a0ddd715d..703065fdb8 100644
--- a/test/classes/Navigation/NavigationTreeTest.php
+++ b/test/classes/Navigation/NavigationTreeTest.php
@@ -7,6 +7,12 @@ namespace PhpMyAdmin\Tests\Navigation;
use PhpMyAdmin\Navigation\NavigationTree;
use PhpMyAdmin\Template;
use PhpMyAdmin\Tests\AbstractTestCase;
+use PhpMyAdmin\Url;
+use ReflectionMethod;
+use function str_repeat;
+use function parse_url;
+use function parse_str;
+use function htmlspecialchars_decode;
class NavigationTreeTest extends AbstractTestCase
{
@@ -80,4 +86,37 @@ class NavigationTreeTest extends AbstractTestCase
$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&db=test_db&table=test_table&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&pos=0&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);
+ }
}
diff --git a/test/classes/Server/UserGroupsTest.php b/test/classes/Server/UserGroupsTest.php
index e75886dc01..1fc8ab4560 100644
--- a/test/classes/Server/UserGroupsTest.php
+++ b/test/classes/Server/UserGroupsTest.php
@@ -118,7 +118,8 @@ class UserGroupsTest extends AbstractTestCase
'viewUsers' => 1,
'userGroup' => htmlspecialchars('usergroup'),
],
- ''
+ '',
+ false
);
$this->assertStringContainsString(
$url_tag,
@@ -130,7 +131,8 @@ class UserGroupsTest extends AbstractTestCase
'editUserGroup' => 1,
'userGroup' => htmlspecialchars('usergroup'),
],
- ''
+ '',
+ false
);
$this->assertStringContainsString(
$url_tag,
@@ -142,7 +144,8 @@ class UserGroupsTest extends AbstractTestCase
'deleteUserGroup' => 1,
'userGroup' => htmlspecialchars('usergroup'),
],
- ''
+ '',
+ false
);
$this->assertStringContainsString(
$url_tag,
diff --git a/test/classes/UrlTest.php b/test/classes/UrlTest.php
index 5b375df2c7..2d3b5693a1 100644
--- a/test/classes/UrlTest.php
+++ b/test/classes/UrlTest.php
@@ -6,6 +6,9 @@ namespace PhpMyAdmin\Tests;
use PhpMyAdmin\Url;
use function urldecode;
+use function str_repeat;
+use function parse_str;
+use function is_string;
class UrlTest extends AbstractTestCase
{
@@ -20,6 +23,7 @@ class UrlTest extends AbstractTestCase
parent::setUp();
parent::setLanguage();
unset($_COOKIE['pma_lang']);
+ $GLOBALS['PMA_Config']->set('URLQueryEncryption', false);
}
/**
@@ -179,4 +183,61 @@ class UrlTest extends AbstractTestCase
Url::getHiddenFields([])
);
}
+
+ /**
+ * @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);
+ }
}