diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 68fc5bae..4f438e7b 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,5 +1,47 @@ -Drupal 7.xx, xxxx-xx-xx (development version) +Drupal 7.72, 2020-06-17 ----------------------- +- Fixed security issues: + - SA-CORE-2020-004 + +Drupal 7.71, 2020-06-03 +----------------------- +- Fix for jQuery Form bug in Chromium-based browsers +- Full support for PHP 7.4 + +Drupal 7.70, 2020-05-19 +----------------------- +- Fixed security issues: + - SA-CORE-2020-002 + - SA-CORE-2020-003 + +Drupal 7.69, 2019-12-18 +----------------------- +- Fixed security issues: + - SA-CORE-2019-012 + +Drupal 7.68, 2019-12-04 +----------------------- +- Fixed: Hide toolbar when printing +- Fixed: Settings returned via ajax are not run through hook_js_alter() +- Fixed: Use drupal_http_build_query() in drupal_http_request() +- Fixed: DrupalRequestSanitizer not found fatal error when bootstrap phase order is changed +- Fixed: Block web.config in .htaccess (and vice-versa) +- Fixed: Create "scripts" element to align rendering workflow to how "styles" are handled +- PHP 7.3: Fixed 'Cannot change session id when session is active' +- PHP 7.1: Fixed 'A non-numeric value encountered in theme_pager()' +- PHP 7.x: Fixed file.inc generated .htaccess does not cover PHP 7 +- PHP 5.3: Fixed check_plain() 'Invalid multibyte sequence in argument' test failures +- Fixed: Allow passing data as array to drupal_http_request() +- Fixed: Skip module_invoke/module_hook in calling hook_watchdog (excessive function_exist) +- Fixed: HTTP status 200 returned for 'Additional uncaught exception thrown while handling exception' +- Fixed: theme_table() should take an optional footer variable and produce +- Fixed: 'uasort() expects parameter 1 to be array, null given in node_view_multiple()' +- [regression] Fix default.settings.php permission + +Drupal 7.67, 2019-05-08 +----------------------- +- Fixed security issues: + - SA-CORE-2019-007 Drupal 7.66, 2019-04-17 ----------------------- diff --git a/MAINTAINERS.txt b/MAINTAINERS.txt index 460658c1..4ea5572a 100644 --- a/MAINTAINERS.txt +++ b/MAINTAINERS.txt @@ -11,11 +11,8 @@ The Drupal Core branch maintainers oversee the development of Drupal as a whole. The branch maintainers for Drupal 7 are: - Dries Buytaert 'dries' https://www.drupal.org/u/dries -- Angela Byron 'webchick' https://www.drupal.org/u/webchick - Fabian Franz 'Fabianx' https://www.drupal.org/u/fabianx -- David Rothstein 'David_Rothstein' https://www.drupal.org/u/david_rothstein -- Stefan Ruijsenaars 'stefan.r' https://www.drupal.org/u/stefanr-0 -- (provisional) Pol Dellaiera 'Pol' https://www.drupal.org/u/pol +- (provisional) Drew Webber 'mcdruid' https://www.drupal.org/u/mcdruid Component maintainers diff --git a/includes/ajax.inc b/includes/ajax.inc index f059209b..db53e31b 100644 --- a/includes/ajax.inc +++ b/includes/ajax.inc @@ -294,6 +294,7 @@ function ajax_render($commands = array()) { // Now add a command to merge changes and additions to Drupal.settings. $scripts = drupal_add_js(); + drupal_alter('js', $scripts); if (!empty($scripts['settings'])) { $settings = $scripts['settings']; array_unshift($commands, ajax_command_settings(drupal_array_merge_deep_array($settings['data']), TRUE)); diff --git a/includes/batch.inc b/includes/batch.inc index e89ab8de..4d4e504d 100644 --- a/includes/batch.inc +++ b/includes/batch.inc @@ -478,18 +478,17 @@ function _batch_finished() { $queue->deleteQueue(); } } + // Clean-up the session. Not needed for CLI updates. + if (isset($_SESSION)) { + unset($_SESSION['batches'][$batch['id']]); + if (empty($_SESSION['batches'])) { + unset($_SESSION['batches']); + } + } } $_batch = $batch; $batch = NULL; - // Clean-up the session. Not needed for CLI updates. - if (isset($_SESSION)) { - unset($_SESSION['batches'][$batch['id']]); - if (empty($_SESSION['batches'])) { - unset($_SESSION['batches']); - } - } - // Redirect if needed. if ($_batch['progressive']) { // Revert the 'destination' that was saved in batch_process(). diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc index 98c31ad3..7ac17ab7 100644 --- a/includes/bootstrap.inc +++ b/includes/bootstrap.inc @@ -8,7 +8,7 @@ /** * The current system version. */ -define('VERSION', '7.66'); +define('VERSION', '7.72'); /** * Core API compatibility. @@ -1998,7 +1998,7 @@ function watchdog($type, $message, $variables = array(), $severity = WATCHDOG_NO // It is possible that the error handling will itself trigger an error. In that case, we could // end up in an infinite loop. To avoid that, we implement a simple static semaphore. - if (!$in_error_state && function_exists('module_implements')) { + if (!$in_error_state && function_exists('module_invoke_all')) { $in_error_state = TRUE; // The user object may not exist in all conditions, so 0 is substituted if needed. @@ -2021,9 +2021,7 @@ function watchdog($type, $message, $variables = array(), $severity = WATCHDOG_NO ); // Call the logging hooks to log/process the message - foreach (module_implements('watchdog') as $module) { - module_invoke($module, 'watchdog', $log_entry); - } + module_invoke_all('watchdog', $log_entry); // It is critical that the semaphore is only cleared here, in the parent // watchdog() call (not outside the loop), to prevent recursive execution. @@ -2518,6 +2516,7 @@ function drupal_bootstrap($phase = NULL, $new_phase = TRUE) { switch ($current_phase) { case DRUPAL_BOOTSTRAP_CONFIGURATION: + require_once DRUPAL_ROOT . '/includes/request-sanitizer.inc'; _drupal_bootstrap_configuration(); break; @@ -2622,6 +2621,10 @@ function _drupal_exception_handler($exception) { _drupal_log_error(_drupal_decode_exception($exception), TRUE); } catch (Exception $exception2) { + // Add a 500 status code in case an exception was thrown before the 500 + // status could be set (e.g. while loading a maintenance theme from cache). + drupal_add_http_header('Status', '500 Internal Server Error'); + // Another uncaught exception was thrown while handling the first one. // If we are displaying errors, then do so with no possibility of a further uncaught exception being thrown. if (error_displayable()) { @@ -2647,7 +2650,6 @@ function _drupal_bootstrap_configuration() { drupal_settings_initialize(); // Sanitize unsafe keys from the request. - require_once DRUPAL_ROOT . '/includes/request-sanitizer.inc'; DrupalRequestSanitizer::sanitize(); } diff --git a/includes/common.inc b/includes/common.inc index 44ff4607..07373ac4 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -391,7 +391,7 @@ function drupal_add_feed($url = NULL, $title = '') { */ function drupal_get_feeds($delimiter = "\n") { $feeds = drupal_add_feed(); - return implode($feeds, $delimiter); + return implode($delimiter, $feeds); } /** @@ -684,7 +684,10 @@ function drupal_goto($path = '', array $options = array(), $http_response_code = // We do not allow absolute URLs to be passed via $_GET, as this can be an attack vector. if (isset($_GET['destination']) && !url_is_external($_GET['destination'])) { $destination = drupal_parse_url($_GET['destination']); - $path = $destination['path']; + // Double check the path derived by drupal_parse_url() is not external. + if (!url_is_external($destination['path'])) { + $path = $destination['path']; + } $options['query'] = $destination['query']; $options['fragment'] = $destination['fragment']; } @@ -760,9 +763,10 @@ function drupal_access_denied() { * (optional) An array that can have one or more of the following elements: * - headers: An array containing request headers to send as name/value pairs. * - method: A string containing the request method. Defaults to 'GET'. - * - data: A string containing the request body, formatted as - * 'param=value¶m=value&...'; to generate this, use http_build_query(). - * Defaults to NULL. + * - data: An array containing the values for the request body or a string + * containing the request body, formatted as + * 'param=value¶m=value&...'; to generate this, use + * drupal_http_build_query(). Defaults to NULL. * - max_redirects: An integer representing how many times a redirect * may be followed. Defaults to 3. * - timeout: A float representing the maximum number of seconds the function @@ -788,7 +792,7 @@ function drupal_access_denied() { * easy access the array keys are returned in lower case. * - data: A string containing the response body that was received. * - * @see http_build_query() + * @see drupal_http_build_query() */ function drupal_http_request($url, array $options = array()) { // Allow an alternate HTTP client library to replace Drupal's default @@ -930,6 +934,11 @@ function drupal_http_request($url, array $options = array()) { $path .= '?' . $uri['query']; } + // Convert array $options['data'] to query string. + if (is_array($options['data'])) { + $options['data'] = drupal_http_build_query($options['data']); + } + // Only add Content-Length if we actually have any content or if it is a POST // or PUT request. Some non-standard servers get confused by Content-Length in // at least HEAD/GET requests, and Squid always requires Content-Length in @@ -3734,7 +3743,7 @@ function _drupal_build_css_path($matches, $base = NULL) { } // Prefix with base and remove '../' segments where possible. - $path = $_base . $matches[1]; + $path = $_base . (isset($matches[1]) ? $matches[1] : ''); $last = ''; while ($path != $last) { $last = $path; @@ -4441,12 +4450,54 @@ function drupal_get_js($scope = 'header', $javascript = NULL, $skip_alter = FALS } } - $output = ''; - // The index counter is used to keep aggregated and non-aggregated files in - // order by weight. - $index = 1; - $processed = array(); - $files = array(); + // Sort the JavaScript so that it appears in the correct order. + uasort($items, 'drupal_sort_css_js'); + + // Provide the page with information about the individual JavaScript files + // used, information not otherwise available when aggregation is enabled. + $setting['ajaxPageState']['js'] = array_fill_keys(array_keys($items), 1); + unset($setting['ajaxPageState']['js']['settings']); + drupal_add_js($setting, 'setting'); + + // If we're outputting the header scope, then this might be the final time + // that drupal_get_js() is running, so add the setting to this output as well + // as to the drupal_add_js() cache. If $items['settings'] doesn't exist, it's + // because drupal_get_js() was intentionally passed a $javascript argument + // stripped off settings, potentially in order to override how settings get + // output, so in this case, do not add the setting to this output. + if ($scope == 'header' && isset($items['settings'])) { + $items['settings']['data'][] = $setting; + } + + $elements = array( + '#type' => 'scripts', + '#items' => $items, + ); + + return drupal_render($elements); +} + +/** + * The #pre_render callback for the "scripts" element. + * + * This callback adds elements needed for