diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index 232868cfab2..1414852d443 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -1,35 +1,61 @@
-Drupal 6.47 LTS, 2019-01-02
+Drupal 6.52, 2019-05-08 - Long term support
+---------------------------------------
+- Fixed security issues (cross site scripting), backport. See SA-CORE-2019-007.
+- Fixed db_version_compare() does not handle if $db_url is an array
+
+Drupal 6.51, 2019-04-29 - Long term support
+---------------------------------------
+- Add support for MySQL 8
+
+Drupal 6.50, 2019-04-17 - Long term support
+---------------------------------------
+- Fixed security issues (cross site scripting), backport. See SA-CORE-2019-006.
+
+Drupal 6.49, 2019-01-16 - Long term support
+---------------------------------------
+- Fixes issues with some Drush commands when using the PHAR file. See
+ https://www.drupal.org/project/drupal/issues/3026386
+
+Drupal 6.48, 2019-01-16 - Long term support
+---------------------------------------
+- Fixed security issues (arbitrary PHP code execution), backport. See
+ SA-CORE-2019-002.
+
+Drupal 6.47, 2019-01-02 - Long term support
---------------------------------------
- Improved support for PHP 7.2.
-Drupal 6.46 LTS, 2018-10-17
+Drupal 6.46, 2018-10-17 - Long term support
---------------------------------------
- Fixed security issues (open redirect), backport. See SA-CORE-2018-006.
-Drupal 6.45 LTS, 2018-10-04
+Drupal 6.45, 2018-10-04 - Long term support
---------------------------------------
- Initial support for PHP 7.2.
-Drupal 6.44 LTS, 2018-04-25
+Drupal 6.44, 2018-04-25 - Long term support
---------------------------------------
- Fixed security issues (remote code execution), backport. See SA-CORE-2018-004.
-Drupal 6.43 LTS, 2018-03-29
------------------------
-- Fixes bug from SA-CORE-2018-002 changes, update version.
+Drupal 6.43, 2018-03-28 - Long term support
+---------------------------------------
+- Bug fixes to changes in 6.42 that affects the OG module.
-Drupal 6.41, Drupal 6.42
------------------------
-Skipped to bring version number in line with
-https://github.com/d6lts/drupal
+Drupal 6.42, 2018-03-28 - Long term support
+---------------------------------------
+- Fixed security issues (remote code execution), backport. See SA-CORE-2018-002.
-Drupal 6.40 Pressflow, 2018-03-28
------------------------
-- Fixed security issues (multiple vulnerabilities). See SA-CORE-2018-002.
+Drupal 6.40, 2018-02-22 - Long term support
+---------------------------------------
+- Bug fixes to changes in 6.39
-Drupal 6.39 Pressflow, 2018-02-21
------------------------
-- Fixed security issues (multiple vulnerabilities). See SA-CORE-2018-001.
+Drupal 6.40, 2018-02-22 - Long term support
+---------------------------------------
+- Bug fixes to changes in 6.39
+
+Drupal 6.39, 2018-02-21 - Long term support
+---------------------------------------
+- Fixed security issues (multiple vulnerabilities), backport. See SA-CORE-2018-001.
Drupal 6.38, 2016-02-24 - Final release
---------------------------------------
diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc
index be8b4d02324..e910a0429a8 100644
--- a/includes/bootstrap.inc
+++ b/includes/bootstrap.inc
@@ -1479,6 +1479,18 @@ function _drupal_bootstrap($phase) {
case DRUPAL_BOOTSTRAP_CONFIGURATION:
drupal_unset_globals();
+ // PHP's built-in phar:// stream wrapper is not sufficiently secure. Override
+ // it with a more secure one, which requires PHP 5.3.3. For lower versions,
+ // unregister the built-in one without replacing it. Sites needing phar
+ // support for lower PHP versions must implement hook_stream_wrappers() to
+ // register their desired implementation.
+ if (in_array('phar', stream_get_wrappers(), TRUE)) {
+ stream_wrapper_unregister('phar');
+ if (version_compare(PHP_VERSION, '5.3.3', '>=')) {
+ include_once './includes/file.phar.inc';
+ file_register_phar_wrapper();
+ }
+ }
// Start a page timer:
timer_start('page');
// Initialize the configuration
diff --git a/includes/common.inc b/includes/common.inc
index de895897edd..f70e0e1ae18 100644
--- a/includes/common.inc
+++ b/includes/common.inc
@@ -2243,6 +2243,8 @@ function drupal_add_js($data = NULL, $type = 'module', $scope = 'header', $defer
$javascript['header'] = array(
'core' => array(
'misc/jquery.js' => array('cache' => TRUE, 'defer' => FALSE, 'preprocess' => TRUE),
+ 'misc/jquery-extend-3.4.0.js' => array('cache' => TRUE, 'defer' => FALSE, 'preprocess' => TRUE),
+ 'misc/jquery-html-prefilter-3.5.0-backport.js' => array('cache' => TRUE, 'defer' => FALSE, 'preprocess' => TRUE),
'misc/drupal.js' => array('cache' => TRUE, 'defer' => FALSE, 'preprocess' => TRUE),
),
'module' => array(),
@@ -3583,6 +3585,17 @@ function drupal_write_record($table, &$object, $update = array()) {
}
}
+ // Need to escape reserved keywords for MySQL 8.
+ if (db_version_compare('mysql', '8.0.0')) {
+ global $db_mysql8_reserved_keywords;
+
+ foreach ($fields as $key => $field) {
+ if (in_array($field, $db_mysql8_reserved_keywords)) {
+ $fields[$key] = '`' . $field . '`';
+ }
+ }
+ }
+
// Build the SQL.
$query = '';
if (!count($update)) {
diff --git a/includes/database.inc b/includes/database.inc
index efb4a90fa25..70737da2003 100644
--- a/includes/database.inc
+++ b/includes/database.inc
@@ -65,6 +65,312 @@ function update_sql($sql) {
return array('success' => $result !== FALSE, 'query' => check_plain($sql));
}
+/**
+ * MySQL 8 reserved keywords list.
+ *
+ * @see https://dev.mysql.com/doc/refman/8.0/en/keywords.html
+ */
+global $db_mysql8_reserved_keywords;
+$db_mysql8_reserved_keywords = array(
+ 'accessible',
+ 'add',
+ 'admin',
+ 'all',
+ 'alter',
+ 'analyze',
+ 'and',
+ 'as',
+ 'asc',
+ 'asensitive',
+ 'before',
+ 'between',
+ 'bigint',
+ 'binary',
+ 'blob',
+ 'both',
+ 'by',
+ 'call',
+ 'cascade',
+ 'case',
+ 'change',
+ 'char',
+ 'character',
+ 'check',
+ 'collate',
+ 'column',
+ 'condition',
+ 'constraint',
+ 'continue',
+ 'convert',
+ 'create',
+ 'cross',
+ 'cube',
+ 'cume_dist',
+ 'current_date',
+ 'current_time',
+ 'current_timestamp',
+ 'current_user',
+ 'cursor',
+ 'database',
+ 'databases',
+ 'day_hour',
+ 'day_microsecond',
+ 'day_minute',
+ 'day_second',
+ 'dec',
+ 'decimal',
+ 'declare',
+ 'default',
+ 'delayed',
+ 'delete',
+ 'dense_rank',
+ 'desc',
+ 'describe',
+ 'deterministic',
+ 'distinct',
+ 'distinctrow',
+ 'div',
+ 'double',
+ 'drop',
+ 'dual',
+ 'each',
+ 'else',
+ 'elseif',
+ 'empty',
+ 'enclosed',
+ 'escaped',
+ 'except',
+ 'exists',
+ 'exit',
+ 'explain',
+ 'false',
+ 'fetch',
+ 'first_value',
+ 'float',
+ 'float4',
+ 'float8',
+ 'for',
+ 'force',
+ 'foreign',
+ 'from',
+ 'fulltext',
+ 'function',
+ 'generated',
+ 'get',
+ 'grant',
+ 'group',
+ 'grouping',
+ 'groups',
+ 'having',
+ 'high_priority',
+ 'hour_microsecond',
+ 'hour_minute',
+ 'hour_second',
+ 'if',
+ 'ignore',
+ 'in',
+ 'index',
+ 'infile',
+ 'inner',
+ 'inout',
+ 'insensitive',
+ 'insert',
+ 'int',
+ 'int1',
+ 'int2',
+ 'int3',
+ 'int4',
+ 'int8',
+ 'integer',
+ 'interval',
+ 'into',
+ 'io_after_gtids',
+ 'io_before_gtids',
+ 'is',
+ 'iterate',
+ 'join',
+ 'json_table',
+ 'key',
+ 'keys',
+ 'kill',
+ 'lag',
+ 'last_value',
+ 'lead',
+ 'leading',
+ 'leave',
+ 'left',
+ 'like',
+ 'limit',
+ 'linear',
+ 'lines',
+ 'load',
+ 'localtime',
+ 'localtimestamp',
+ 'lock',
+ 'long',
+ 'longblob',
+ 'longtext',
+ 'loop',
+ 'low_priority',
+ 'master_bind',
+ 'master_ssl_verify_server_cert',
+ 'match',
+ 'maxvalue',
+ 'mediumblob',
+ 'mediumint',
+ 'mediumtext',
+ 'middleint',
+ 'minute_microsecond',
+ 'minute_second',
+ 'mod',
+ 'modifies',
+ 'natural',
+ 'not',
+ 'no_write_to_binlog',
+ 'nth_value',
+ 'ntile',
+ 'null',
+ 'numeric',
+ 'of',
+ 'on',
+ 'optimize',
+ 'optimizer_costs',
+ 'option',
+ 'optionally',
+ 'or',
+ 'order',
+ 'out',
+ 'outer',
+ 'outfile',
+ 'over',
+ 'partition',
+ 'percent_rank',
+ 'persist',
+ 'persist_only',
+ 'precision',
+ 'primary',
+ 'procedure',
+ 'purge',
+ 'range',
+ 'rank',
+ 'read',
+ 'reads',
+ 'read_write',
+ 'real',
+ 'recursive',
+ 'references',
+ 'regexp',
+ 'release',
+ 'rename',
+ 'repeat',
+ 'replace',
+ 'require',
+ 'resignal',
+ 'restrict',
+ 'return',
+ 'revoke',
+ 'right',
+ 'rlike',
+ 'row',
+ 'rows',
+ 'row_number',
+ 'schema',
+ 'schemas',
+ 'second_microsecond',
+ 'select',
+ 'sensitive',
+ 'separator',
+ 'set',
+ 'show',
+ 'signal',
+ 'smallint',
+ 'spatial',
+ 'specific',
+ 'sql',
+ 'sqlexception',
+ 'sqlstate',
+ 'sqlwarning',
+ 'sql_big_result',
+ 'sql_calc_found_rows',
+ 'sql_small_result',
+ 'ssl',
+ 'starting',
+ 'stored',
+ 'straight_join',
+ 'system',
+ 'table',
+ 'terminated',
+ 'then',
+ 'tinyblob',
+ 'tinyint',
+ 'tinytext',
+ 'to',
+ 'trailing',
+ 'trigger',
+ 'true',
+ 'undo',
+ 'union',
+ 'unique',
+ 'unlock',
+ 'unsigned',
+ 'update',
+ 'usage',
+ 'use',
+ 'using',
+ 'utc_date',
+ 'utc_time',
+ 'utc_timestamp',
+ 'values',
+ 'varbinary',
+ 'varchar',
+ 'varcharacter',
+ 'varying',
+ 'virtual',
+ 'when',
+ 'where',
+ 'while',
+ 'window',
+ 'with',
+ 'write',
+ 'xor',
+ 'year_month',
+ 'zerofill',
+);
+
+/**
+ * Checks if we are under certain database driver and database version.
+ *
+ * For example, to check if we are under MySQL 8.0 or higher use
+ * db_version_compare('mysql', '8.0.0').
+ *
+ * @param string $driver
+ * Database driver name from $db_url setting. For example 'mysql' (which covers both 'mysql:' and 'mysqli:' drivers.
+ * @param string $version
+ * Database version returned by db_version() function call.
+ * @param string $operator
+ * Operator to compare version. '>=' by default.
+ * @return bool
+ * TRUE if version check passes; otherwise FALSE.
+*/
+function db_version_compare($driver, $version, $operator = '>=') {
+ global $db_url, $db_active_name;
+
+ if (is_array($db_url)) {
+ $db_active_url = array_key_exists($db_active_name, $db_url) ? $db_url[$db_active_name] : $db_url['default'];
+ }
+ else {
+ $db_active_url = $db_url;
+ }
+
+ if (substr($db_active_url, 0, strlen($driver)) === $driver) {
+ if (version_compare(db_version(), $version, $operator)) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
/**
* Append a database prefix to all tables in a query.
*
@@ -79,29 +385,48 @@ function update_sql($sql) {
* The properly-prefixed string.
*/
function db_prefix_tables($sql) {
- global $db_prefix;
+ global $db_prefix, $db_mysql8_reserved_keywords;
+ // Add prefixes.
if (is_array($db_prefix)) {
if (array_key_exists('default', $db_prefix)) {
$tmp = $db_prefix;
unset($tmp['default']);
+
foreach ($tmp as $key => $val) {
- $sql = strtr($sql, array('{'. $key .'}' => $val . $key));
+ $sql = strtr($sql, array('{' . $key . '}' => '{' . $val . $key . '}'));
}
- return strtr($sql, array('{' => $db_prefix['default'], '}' => ''));
+ $sql = strtr($sql, array('{' => '{' . $db_prefix['default']));
}
else {
foreach ($db_prefix as $key => $val) {
- $sql = strtr($sql, array('{'. $key .'}' => $val . $key));
+ $sql = strtr($sql, array('{' . $key . '}' => '{' . $val . $key . '}'));
}
- return strtr($sql, array('{' => '', '}' => ''));
}
}
else {
- return strtr($sql, array('{' => $db_prefix, '}' => ''));
+ $sql = strtr($sql, array('{' => '{' . $db_prefix));
}
+
+ // Need to escape reserved keywords for MySQL 8.
+ if (db_version_compare('mysql', '8.0.0')) {
+ foreach ($db_mysql8_reserved_keywords as $keyword) {
+ $sql = str_replace('{' . $keyword . '}', '`{' . $keyword . '}`', $sql);
+ }
+ }
+
+ // Remove curly braces.
+ $sql = strtr($sql, array('{' => '', '}' => ''));
+
+ return $sql;
}
+/**
+ * Active database connection name.
+ */
+global $db_active_name;
+$db_active_name = FALSE;
+
/**
* Activate a database for future queries.
*
@@ -123,7 +448,8 @@ function db_prefix_tables($sql) {
*/
function db_set_active($name = 'default') {
global $db_url, $db_slave_url, $db_type, $active_db, $active_slave_db;
- static $db_conns, $db_slave_conns, $active_name = FALSE;
+ global $db_active_name;
+ static $db_conns, $db_slave_conns;
if (empty($db_url)) {
include_once 'includes/install.inc';
@@ -169,9 +495,9 @@ function db_set_active($name = 'default') {
}
}
- $previous_name = $active_name;
+ $previous_name = $db_active_name;
// Set the active connection.
- $active_name = $name;
+ $db_active_name = $name;
$active_db = $db_conns[$name];
if (isset($db_slave_conns[$name])) {
$active_slave_db = $db_slave_conns[$name];
diff --git a/includes/database.mysql.inc b/includes/database.mysql.inc
index 4ff53e1fa77..bed91fb0aff 100644
--- a/includes/database.mysql.inc
+++ b/includes/database.mysql.inc
@@ -89,6 +89,13 @@ function db_connect($url) {
mysql_query('SET NAMES utf8', $connection);
}
+ // MySQL 8 has ONLY_FULL_GROUP_BY mode enabled by default. There are too many queries in Drupal 6 that do not
+ // list all columns in GROUP BY clause. So we need to turn off this mode after connecting.
+ list ($version) = explode('-', mysql_get_server_info($connection));
+ if (version_compare($version, '8.0.0', '>=')){
+ mysql_query('SET sql_mode=(SELECT REPLACE(@@sql_mode,\'ONLY_FULL_GROUP_BY\',\'\'));', $connection);
+ }
+
return $connection;
}
diff --git a/includes/database.mysqli.inc b/includes/database.mysqli.inc
index 2ee710bdb75..050614a1df7 100644
--- a/includes/database.mysqli.inc
+++ b/includes/database.mysqli.inc
@@ -88,6 +88,13 @@ function db_connect($url) {
mysqli_query($connection, 'SET NAMES utf8');
}
+ // MySQL 8 has ONLY_FULL_GROUP_BY mode enabled by default. There are too many queries in Drupal 6 that do not
+ // list all columns in GROUP BY clause. So we need to turn off this mode after connecting.
+ list ($version) = explode('-', mysqli_get_server_info($connection));
+ if (version_compare($version, '8.0.0', '>=')){
+ mysqli_query($connection, 'SET sql_mode=(SELECT REPLACE(@@sql_mode,\'ONLY_FULL_GROUP_BY\',\'\'));');
+ }
+
return $connection;
}
diff --git a/includes/file.inc b/includes/file.inc
index bfa3583890d..c091b7f2754 100644
--- a/includes/file.inc
+++ b/includes/file.inc
@@ -704,7 +704,7 @@ function file_save_upload($source, $validators = array(), $dest = FALSE, $replac
}
// Rename potentially executable files, to help prevent exploits.
- if (preg_match('/\.(php|pl|py|cgi|asp|js)$/i', $file->filename) && (substr($file->filename, -4) != '.txt')) {
+ if (preg_match('/\.(php|phar|pl|py|cgi|asp|js)$/i', $file->filename) && (substr($file->filename, -4) != '.txt')) {
$file->filemime = 'text/plain';
$file->filepath .= '.txt';
$file->filename .= '.txt';
diff --git a/includes/file.phar.inc b/includes/file.phar.inc
new file mode 100644
index 00000000000..f3b24d332f8
--- /dev/null
+++ b/includes/file.phar.inc
@@ -0,0 +1,55 @@
+withAssertion(new PharExtensionInterceptor())
+ );
+ }
+ catch (\LogicException $e) {
+ // Continue if the PharStreamWrapperManager is already initialized.
+ // For example, this occurs following a drupal_static_reset(), such
+ // as during tests.
+ };
+
+ // To prevent file_stream_wrapper_valid_scheme() treating "phar" as a valid
+ // scheme, this is registered with PHP only, not with hook_stream_wrappers()
+ // or the internal storage of file_get_stream_wrappers().
+ stream_wrapper_register('phar', '\\TYPO3\\PharStreamWrapper\\PharStreamWrapper');
+}
diff --git a/includes/session.inc b/includes/session.inc
index 278693864d9..0cea8082669 100644
--- a/includes/session.inc
+++ b/includes/session.inc
@@ -180,6 +180,8 @@ function sess_destroy_sid($sid) {
unset($_COOKIE[session_name()]);
}
}
+
+ return TRUE;
}
/**
@@ -190,6 +192,7 @@ function sess_destroy_sid($sid) {
*/
function sess_destroy_uid($uid) {
db_query('DELETE FROM {sessions} WHERE uid = %d', $uid);
+ return TRUE;
}
function sess_gc($lifetime) {
diff --git a/misc/brumann/polyfill-unserialize/.gitignore b/misc/brumann/polyfill-unserialize/.gitignore
new file mode 100644
index 00000000000..767699f1b85
--- /dev/null
+++ b/misc/brumann/polyfill-unserialize/.gitignore
@@ -0,0 +1,4 @@
+/vendor/
+/phpunit.xml
+/.composer.lock
+
diff --git a/misc/brumann/polyfill-unserialize/.travis.yml b/misc/brumann/polyfill-unserialize/.travis.yml
new file mode 100644
index 00000000000..352536f4584
--- /dev/null
+++ b/misc/brumann/polyfill-unserialize/.travis.yml
@@ -0,0 +1,20 @@
+language: php
+
+sudo: false
+
+php:
+ - '5.3'
+ - '5.4'
+ - '5.5'
+ - '5.6'
+ - '7.0'
+ - '7.1'
+
+before_install:
+ - phpenv config-rm xdebug.ini
+ - composer self-update
+
+install:
+ - composer install
+
+script: phpunit
diff --git a/misc/brumann/polyfill-unserialize/LICENSE b/misc/brumann/polyfill-unserialize/LICENSE
new file mode 100644
index 00000000000..0cb53d3b026
--- /dev/null
+++ b/misc/brumann/polyfill-unserialize/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2016 Denis Brumann
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/misc/brumann/polyfill-unserialize/README.md b/misc/brumann/polyfill-unserialize/README.md
new file mode 100644
index 00000000000..bac25fe049c
--- /dev/null
+++ b/misc/brumann/polyfill-unserialize/README.md
@@ -0,0 +1,61 @@
+Polyfill unserialize [](https://travis-ci.org/dbrumann/polyfill-unserialize)
+===
+
+Backports unserialize options introduced in PHP 7.0 to older PHP versions.
+This was originally designed as a Proof of Concept for Symfony Issue [#21090](https://github.com/symfony/symfony/pull/21090).
+
+You can use this package in projects that rely on PHP versions older than PHP 7.0.
+In case you are using PHP 7.0+ the original `unserialize()` will be used instead.
+
+From the [documentation](https://secure.php.net/manual/en/function.unserialize.php):
+
+> Warning: Do not pass untrusted user input to unserialize(). Unserialization can
+> result in code being loaded and executed due to object instantiation
+> and autoloading, and a malicious user may be able to exploit this.
+
+This warning holds true even when `allowed_classes` is used.
+
+Requirements
+------------
+
+ - PHP 5.3+
+
+Installation
+------------
+
+You can install this package via composer:
+
+```
+composer require brumann/polyfill-unserialize "^1.0"
+```
+
+Known Issues
+------------
+
+There is a mismatch in behavior when `allowed_classes` in `$options` is not
+of the correct type (array or boolean). PHP 7.1 will issue a warning, whereas
+PHP 7.0 will not. I opted to copy the behavior of the former.
+
+Tests
+-----
+
+You can run the test suite using PHPUnit. It is intentionally not bundled as
+dev dependency to make sure this package has the lowest restrictions on the
+implementing system as possible.
+
+Please read the [PHPUnit Manual](https://phpunit.de/manual/current/en/installation.html)
+for information how to install it on your system.
+
+You can run the test suite as follows:
+
+```
+phpunit -c phpunit.xml.dist tests/
+```
+
+Contributing
+------------
+
+This package is considered feature complete. As such I will likely not update it
+unless there are security issues.
+
+Should you find any bugs or have questions, feel free to submit an Issue or a Pull Request.
diff --git a/misc/brumann/polyfill-unserialize/composer.json b/misc/brumann/polyfill-unserialize/composer.json
new file mode 100644
index 00000000000..ec4a2cf0eab
--- /dev/null
+++ b/misc/brumann/polyfill-unserialize/composer.json
@@ -0,0 +1,26 @@
+{
+ "name": "brumann/polyfill-unserialize",
+ "description": "Backports unserialize options introduced in PHP 7.0 to older PHP versions.",
+ "type": "library",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Denis Brumann",
+ "email": "denis.brumann@sensiolabs.de"
+ }
+ ],
+ "autoload": {
+ "psr-4": {
+ "Brumann\\Polyfill\\": "src/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "Tests\\Brumann\\Polyfill\\": "tests/"
+ }
+ },
+ "minimum-stability": "stable",
+ "require": {
+ "php": "^5.3|^7.0"
+ }
+}
diff --git a/misc/brumann/polyfill-unserialize/phpunit.xml.dist b/misc/brumann/polyfill-unserialize/phpunit.xml.dist
new file mode 100644
index 00000000000..8fea1bab869
--- /dev/null
+++ b/misc/brumann/polyfill-unserialize/phpunit.xml.dist
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+ ./tests/
+
+
+
+
+
+ ./src/
+
+
+
diff --git a/misc/brumann/polyfill-unserialize/src/Unserialize.php b/misc/brumann/polyfill-unserialize/src/Unserialize.php
new file mode 100644
index 00000000000..e025d55ed4e
--- /dev/null
+++ b/misc/brumann/polyfill-unserialize/src/Unserialize.php
@@ -0,0 +1,58 @@
+= 70000) {
+ return \unserialize($serialized, $options);
+ }
+ if (!array_key_exists('allowed_classes', $options)) {
+ $options['allowed_classes'] = true;
+ }
+ $allowedClasses = $options['allowed_classes'];
+ if (true === $allowedClasses) {
+ return \unserialize($serialized);
+ }
+ if (false === $allowedClasses) {
+ $allowedClasses = array();
+ }
+ if (!is_array($allowedClasses)) {
+ trigger_error(
+ 'unserialize(): allowed_classes option should be array or boolean',
+ E_USER_WARNING
+ );
+ $allowedClasses = array();
+ }
+
+ $sanitizedSerialized = preg_replace_callback(
+ '/(^|;)O:\d+:"([^"]*)":(\d+):{/',
+ function ($match) use ($allowedClasses) {
+ list($completeMatch, $leftBorder, $className, $objectSize) = $match;
+ if (in_array($className, $allowedClasses)) {
+ return $completeMatch;
+ } else {
+ return sprintf(
+ '%sO:22:"__PHP_Incomplete_Class":%d:{s:27:"__PHP_Incomplete_Class_Name";%s',
+ $leftBorder,
+ $objectSize + 1, // size of object + 1 for added string
+ \serialize($className)
+ );
+ }
+ },
+ $serialized
+ );
+
+ return \unserialize($sanitizedSerialized);
+ }
+}
diff --git a/misc/jquery-extend-3.4.0.js b/misc/jquery-extend-3.4.0.js
new file mode 100644
index 00000000000..179c097e8e8
--- /dev/null
+++ b/misc/jquery-extend-3.4.0.js
@@ -0,0 +1,174 @@
+/**
+ * For jQuery versions less than 3.4.0, this replaces the jQuery.extend
+ * function with the one from jQuery 3.4.0, slightly modified (documented
+ * below) to be compatible with older jQuery versions and browsers.
+ *
+ * This provides the Object.prototype pollution vulnerability fix to Drupal
+ * installations running older jQuery versions, including the versions shipped
+ * with Drupal core and https://www.drupal.org/project/jquery_update.
+ *
+ * @see https://github.com/jquery/jquery/pull/4333
+ */
+
+(function (jQuery) {
+
+// Do not override jQuery.extend() if the jQuery version is already >=3.4.0.
+var versionParts = jQuery.fn.jquery.split('.');
+var majorVersion = parseInt(versionParts[0]);
+var minorVersion = parseInt(versionParts[1]);
+var patchVersion = parseInt(versionParts[2]);
+var isPreReleaseVersion = (patchVersion.toString() !== versionParts[2]);
+if (
+ (majorVersion > 3) ||
+ (majorVersion === 3 && minorVersion > 4) ||
+ (majorVersion === 3 && minorVersion === 4 && patchVersion > 0) ||
+ (majorVersion === 3 && minorVersion === 4 && patchVersion === 0 && !isPreReleaseVersion)
+) {
+ return;
+}
+
+/**
+ * This adds some funtions from jQuery 1.4.4 (the default version used in
+ * Drupal 7) for when they aren't present, like when using jQuery 1.2.6 (the
+ * default version used in Drupal 6).
+ */
+
+if (typeof jQuery.type === 'undefined') {
+ var toString = Object.prototype.toString,
+ class2type = {};
+
+ // Populate the class2type map
+ jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) {
+ class2type[ "[object " + name + "]" ] = name.toLowerCase();
+ });
+
+ jQuery.type = function (obj) {
+ return obj == null ?
+ String( obj ) :
+ class2type[ toString.call(obj) ] || "object";
+ };
+}
+
+if (typeof jQuery.isArray === 'undefined') {
+ jQuery.isArray = function (obj) {
+ return jQuery.type(obj) === "array";
+ };
+}
+
+if (typeof jQuery.isWindow === 'undefined') {
+ jQuery.isWindow = function (obj) {
+ return obj && typeof obj === "object" && "setInterval" in obj;
+ };
+}
+
+if (typeof jQuery.isPlainObject === 'undefined') {
+ var hasOwn = Object.prototype.hasOwnProperty;
+
+ jQuery.isPlainObject = function (obj) {
+ // Must be an Object.
+ // Because of IE, we also have to check the presence of the constructor property.
+ // Make sure that DOM nodes and window objects don't pass through, as well
+ if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
+ return false;
+ }
+
+ // Not own constructor property must be Object
+ if ( obj.constructor &&
+ !hasOwn.call(obj, "constructor") &&
+ !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
+ return false;
+ }
+
+ // Own properties are enumerated firstly, so to speed up,
+ // if last one is own, then all properties are own.
+
+ var key;
+ for ( key in obj ) {}
+
+ return key === undefined || hasOwn.call( obj, key );
+ };
+}
+
+/**
+ * This is almost verbatim copied from jQuery 3.4.0.
+ *
+ * Only two minor changes have been made:
+ * - The call to isFunction() is changed to jQuery.isFunction().
+ * - The two calls to Array.isArray() is changed to jQuery.isArray().
+ *
+ * The above two changes ensure compatibility with all older jQuery versions
+ * (1.2.6 - 3.3.1) and older browser versions (e.g., IE8).
+ */
+jQuery.extend = jQuery.fn.extend = function() {
+ var options, name, src, copy, copyIsArray, clone,
+ target = arguments[ 0 ] || {},
+ i = 1,
+ length = arguments.length,
+ deep = false;
+
+ // Handle a deep copy situation
+ if ( typeof target === "boolean" ) {
+ deep = target;
+
+ // Skip the boolean and the target
+ target = arguments[ i ] || {};
+ i++;
+ }
+
+ // Handle case when target is a string or something (possible in deep copy)
+ if ( typeof target !== "object" && !jQuery.isFunction( target ) ) {
+ target = {};
+ }
+
+ // Extend jQuery itself if only one argument is passed
+ if ( i === length ) {
+ target = this;
+ i--;
+ }
+
+ for ( ; i < length; i++ ) {
+
+ // Only deal with non-null/undefined values
+ if ( ( options = arguments[ i ] ) != null ) {
+
+ // Extend the base object
+ for ( name in options ) {
+ copy = options[ name ];
+
+ // Prevent Object.prototype pollution
+ // Prevent never-ending loop
+ if ( name === "__proto__" || target === copy ) {
+ continue;
+ }
+
+ // Recurse if we're merging plain objects or arrays
+ if ( deep && copy && ( jQuery.isPlainObject( copy ) ||
+ ( copyIsArray = jQuery.isArray( copy ) ) ) ) {
+ src = target[ name ];
+
+ // Ensure proper type for the source value
+ if ( copyIsArray && !jQuery.isArray( src ) ) {
+ clone = [];
+ } else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) {
+ clone = {};
+ } else {
+ clone = src;
+ }
+ copyIsArray = false;
+
+ // Never move original objects, clone them
+ target[ name ] = jQuery.extend( deep, clone, copy );
+
+ // Don't bring in undefined values
+ } else if ( copy !== undefined ) {
+ target[ name ] = copy;
+ }
+ }
+ }
+ }
+
+ // Return the modified object
+ return target;
+};
+
+})(jQuery);
diff --git a/misc/jquery-html-prefilter-3.5.0-backport.js b/misc/jquery-html-prefilter-3.5.0-backport.js
new file mode 100644
index 00000000000..93771502210
--- /dev/null
+++ b/misc/jquery-html-prefilter-3.5.0-backport.js
@@ -0,0 +1,251 @@
+/**
+ * For jQuery versions less than 3.5.0, this replaces the jQuery.htmlPrefilter()
+ * function with one that fixes these security vulnerabilities while also
+ * retaining the pre-3.5.0 behavior where it's safe to do so.
+ * - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-11022
+ * - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-11023
+ *
+ * Additionally, for jQuery versions that do not have a jQuery.htmlPrefilter()
+ * function (1.x prior to 1.12 and 2.x prior to 2.2), this adds it, and
+ * extends the functions that need to call it to do so.
+ *
+ * Drupal core's jQuery version is 1.4.4, but jQuery Update can provide a
+ * different version, so this covers all versions between 1.4.4 and 3.4.1.
+ * The GitHub links in the code comments below link to jQuery 1.5 code, because
+ * 1.4.4 isn't on GitHub, but the referenced code didn't change from 1.4.4 to
+ * 1.5.
+ */
+
+(function (jQuery) {
+
+ // Parts of this backport differ by jQuery version.
+ var versionParts = jQuery.fn.jquery.split('.');
+ var majorVersion = parseInt(versionParts[0]);
+ var minorVersion = parseInt(versionParts[1]);
+
+ // No backport is needed if we're already on jQuery 3.5 or higher.
+ if ( (majorVersion > 3) || (majorVersion === 3 && minorVersion >= 5) ) {
+ return;
+ }
+
+ // Prior to jQuery 3.5, jQuery converted XHTML-style self-closing tags to
+ // their XML equivalent: e.g., "
" to "". This is
+ // problematic for several reasons, including that it's vulnerable to XSS
+ // attacks. However, since this was jQuery's behavior for many years, many
+ // Drupal modules and jQuery plugins may be relying on it. Therefore, we
+ // preserve that behavior, but for a limited set of tags only, that we believe
+ // to not be vulnerable. This is the set of HTML tags that satisfy all of the
+ // following conditions:
+ // - In DOMPurify's list of HTML tags. If an HTML tag isn't safe enough to
+ // appear in that list, then we don't want to mess with it here either.
+ // @see https://github.com/cure53/DOMPurify/blob/2.0.11/dist/purify.js#L128
+ // - A normal element (not a void, template, text, or foreign element).
+ // @see https://html.spec.whatwg.org/multipage/syntax.html#elements-2
+ // - An element that is still defined by the current HTML specification
+ // (not a deprecated element), because we do not want to rely on how
+ // browsers parse deprecated elements.
+ // @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element
+ // - Not 'html', 'head', or 'body', because this pseudo-XHTML expansion is
+ // designed for fragments, not entire documents.
+ // - Not 'colgroup', because due to an idiosyncrasy of jQuery's original
+ // regular expression, it didn't match on colgroup, and we don't want to
+ // introduce a behavior change for that.
+ var selfClosingTagsToReplace = [
+ 'a', 'abbr', 'address', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo',
+ 'blockquote', 'button', 'canvas', 'caption', 'cite', 'code', 'data',
+ 'datalist', 'dd', 'del', 'details', 'dfn', 'div', 'dl', 'dt', 'em',
+ 'fieldset', 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3',
+ 'h4', 'h5', 'h6', 'header', 'hgroup', 'i', 'ins', 'kbd', 'label', 'legend',
+ 'li', 'main', 'map', 'mark', 'menu', 'meter', 'nav', 'ol', 'optgroup',
+ 'option', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'rp', 'rt',
+ 'ruby', 's', 'samp', 'section', 'select', 'small', 'source', 'span',
+ 'strong', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'tfoot', 'th',
+ 'thead', 'time', 'tr', 'u', 'ul', 'var', 'video'
+ ];
+
+ // Define regular expressions for and . Doing this as
+ // two expressions makes it easier to target without also targeting
+ // every tag that starts with "a".
+ var xhtmlRegExpGroup = '(' + selfClosingTagsToReplace.join('|') + ')';
+ var whitespace = '[\\x20\\t\\r\\n\\f]';
+ var rxhtmlTagWithoutSpaceOrAttributes = new RegExp('<' + xhtmlRegExpGroup + '\\/>', 'gi');
+ var rxhtmlTagWithSpaceAndMaybeAttributes = new RegExp('<' + xhtmlRegExpGroup + '(' + whitespace + '[^>]*)\\/>', 'gi');
+
+ // jQuery 3.5 also fixed a vulnerability for when appears within
+ // an