Skip to content

Commit 25aba8e

Browse files
committed
SA-CORE-2018-002 by Jasu_M, samuel.mortenson, David_Rothstein, xjm, mlhess, larowlan, pwolanin, alexpott, dsnopek, Pere Orga, cashwilliams, dawehner, tim.plunkett, drumm
1 parent 44a857d commit 25aba8e

File tree

3 files changed

+153
-0
lines changed

3 files changed

+153
-0
lines changed

lib/Drupal/Core/DrupalKernel.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
use Drupal\Core\Http\TrustedHostsRequestFactory;
2121
use Drupal\Core\Installer\InstallerRedirectTrait;
2222
use Drupal\Core\Language\Language;
23+
use Drupal\Core\Security\RequestSanitizer;
2324
use Drupal\Core\Site\Settings;
2425
use Drupal\Core\Test\TestDatabase;
2526
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
@@ -542,6 +543,12 @@ public function loadLegacyIncludes() {
542543
* {@inheritdoc}
543544
*/
544545
public function preHandle(Request $request) {
546+
// Sanitize the request.
547+
$request = RequestSanitizer::sanitize(
548+
$request,
549+
(array) Settings::get(RequestSanitizer::SANITIZE_WHITELIST, []),
550+
(bool) Settings::get(RequestSanitizer::SANITIZE_LOG, FALSE)
551+
);
545552

546553
$this->loadLegacyIncludes();
547554

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
<?php
2+
3+
namespace Drupal\Core\Security;
4+
5+
use Symfony\Component\HttpFoundation\Request;
6+
7+
/**
8+
* Sanitizes user input.
9+
*/
10+
class RequestSanitizer {
11+
12+
/**
13+
* Request attribute to mark the request as sanitized.
14+
*/
15+
const SANITIZED = '_drupal_request_sanitized';
16+
17+
/**
18+
* The name of the setting that configures the whitelist.
19+
*/
20+
const SANITIZE_WHITELIST = 'sanitize_input_whitelist';
21+
22+
/**
23+
* The name of the setting that determines if sanitized keys are logged.
24+
*/
25+
const SANITIZE_LOG = 'sanitize_input_logging';
26+
27+
/**
28+
* Strips dangerous keys from user input.
29+
*
30+
* @param \Symfony\Component\HttpFoundation\Request $request
31+
* The incoming request to sanitize.
32+
* @param string[] $whitelist
33+
* An array of keys to whitelist as safe. See default.settings.php.
34+
* @param bool $log_sanitized_keys
35+
* (optional) Set to TRUE to log an keys that are sanitized.
36+
*
37+
* @return \Symfony\Component\HttpFoundation\Request
38+
* The sanitized request.
39+
*/
40+
public static function sanitize(Request $request, $whitelist, $log_sanitized_keys = FALSE) {
41+
if (!$request->attributes->get(self::SANITIZED, FALSE)) {
42+
// Process query string parameters.
43+
$get_sanitized_keys = [];
44+
$request->query->replace(static::stripDangerousValues($request->query->all(), $whitelist, $get_sanitized_keys));
45+
if ($log_sanitized_keys && !empty($get_sanitized_keys)) {
46+
trigger_error(sprintf('Potentially unsafe keys removed from query string parameters (GET): %s', implode(', ', $get_sanitized_keys)));
47+
}
48+
49+
// Request body parameters.
50+
$post_sanitized_keys = [];
51+
$request->request->replace(static::stripDangerousValues($request->request->all(), $whitelist, $post_sanitized_keys));
52+
if ($log_sanitized_keys && !empty($post_sanitized_keys)) {
53+
trigger_error(sprintf('Potentially unsafe keys removed from request body parameters (POST): %s', implode(', ', $post_sanitized_keys)));
54+
}
55+
56+
// Cookie parameters.
57+
$cookie_sanitized_keys = [];
58+
$request->cookies->replace(static::stripDangerousValues($request->cookies->all(), $whitelist, $cookie_sanitized_keys));
59+
if ($log_sanitized_keys && !empty($cookie_sanitized_keys)) {
60+
trigger_error(sprintf('Potentially unsafe keys removed from cookie parameters: %s', implode(', ', $cookie_sanitized_keys)));
61+
}
62+
63+
if (!empty($get_sanitized_keys) || !empty($post_sanitized_keys) || !empty($cookie_sanitized_keys)) {
64+
$request->overrideGlobals();
65+
}
66+
$request->attributes->set(self::SANITIZED, TRUE);
67+
}
68+
return $request;
69+
}
70+
71+
/**
72+
* Strips dangerous keys from $input.
73+
*
74+
* @param mixed $input
75+
* The input to sanitize.
76+
* @param string[] $whitelist
77+
* An array of keys to whitelist as safe.
78+
* @param string[] $sanitized_keys
79+
* An array of keys that have been removed.
80+
*
81+
* @return mixed
82+
* The sanitized input.
83+
*/
84+
protected static function stripDangerousValues($input, array $whitelist, array &$sanitized_keys) {
85+
if (is_array($input)) {
86+
foreach ($input as $key => $value) {
87+
if ($key !== '' && $key[0] === '#' && !in_array($key, $whitelist, TRUE)) {
88+
unset($input[$key]);
89+
$sanitized_keys[] = $key;
90+
}
91+
else {
92+
$input[$key] = static::stripDangerousValues($input[$key], $whitelist, $sanitized_keys);
93+
}
94+
}
95+
}
96+
return $input;
97+
}
98+
99+
}

modules/update/update.module

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,9 +404,56 @@ function update_get_available($refresh = FALSE) {
404404
$available = \Drupal::keyValueExpirable('update_available_releases')->getAll();
405405
}
406406

407+
// Check for security releases that are covered under the same security
408+
// advisories as the site's current release, and override the update status
409+
// data so that those releases are not flagged as needed security updates.
410+
// Any security releases beyond those specific releases will still be shown
411+
// as required security updates.
412+
413+
// @todo This is a temporary fix to allow minor-version backports of security
414+
// fixes to be shown as secure. It should not be included in the codebase of
415+
// any release or branch other than such backports. Replace this with
416+
// https://www.drupal.org/project/drupal/issues/2766491.
417+
foreach (_update_equivalent_security_releases() as $equivalent_release) {
418+
if (!empty($available['drupal']['releases'][$equivalent_release]['terms']['Release type'])) {
419+
$security_release_key = array_search('Security update', $available['drupal']['releases'][$equivalent_release]['terms']['Release type']);
420+
if ($security_release_key !== FALSE) {
421+
unset($available['drupal']['releases'][$equivalent_release]['terms']['Release type'][$security_release_key]);
422+
}
423+
}
424+
}
407425
return $available;
408426
}
409427

428+
/**
429+
* Identifies equivalent security releases with a hardcoded list.
430+
*
431+
* Generally, only the latest minor version of Drupal 8 is supported. However,
432+
* when security fixes are backported to an old branch, and the site owner
433+
* updates to the release containing the backported fix, they should not
434+
* see "Security update required!" again if the only other security releases
435+
* are releases for the same advisories.
436+
*
437+
* @return string[]
438+
* A list of security release numbers that are equivalent to this release
439+
* (i.e. covered by the same advisory), for backported security fixes only.
440+
*
441+
* @todo This is a temporary fix to allow minor-version backports of security
442+
* fixes to be shown as secure. It should not be included in the codebase of
443+
* any release or branch other than such backports. Replace this with
444+
* https://www.drupal.org/project/drupal/issues/2766491.
445+
*/
446+
function _update_equivalent_security_releases() {
447+
switch (\Drupal::VERSION) {
448+
case '8.4.5':
449+
return ['8.5.0-rc1'];
450+
case '8.4.6':
451+
return ['8.5.1'];
452+
}
453+
454+
return [];
455+
}
456+
410457
/**
411458
* Adds a task to the queue for fetching release history data for a project.
412459
*

0 commit comments

Comments
 (0)