Commit ae592152 authored by catch's avatar catch
Browse files

Issue #3151093 by dww, cburschka, Manisha111, alexpott, longwave: Replace use...

Issue #3151093 by dww, cburschka, Manisha111, alexpott, longwave: Replace use of whitelist/blacklist in \Drupal\Core\Security\RequestSanitizer and its test
parent eecc9bf2
Loading
Loading
Loading
Loading
+1 −1
Changes for core/lib/Drupal/Core/DrupalKernel.php: 1 added line, 1 removed line.
Original line number Diff line number Diff line
@@ -574,7 +574,7 @@ public function preHandle(Request $request) {
    // Sanitize the request.
    $request = RequestSanitizer::sanitize(
      $request,
      (array) Settings::get(RequestSanitizer::SANITIZE_WHITELIST, []),
      (array) Settings::get(RequestSanitizer::SANITIZE_INPUT_SAFE_KEYS, []),
      (bool) Settings::get(RequestSanitizer::SANITIZE_LOG, FALSE)
    );

+28 −19
Changes for core/lib/Drupal/Core/Security/RequestSanitizer.php: 28 added lines, 19 removed lines.
Original line number Diff line number Diff line
@@ -17,7 +17,16 @@ class RequestSanitizer {
  const SANITIZED = '_drupal_request_sanitized';

  /**
   * The name of the setting that configures the whitelist.
   * The name of the setting that configures the sanitize input safe keys.
   */
  const SANITIZE_INPUT_SAFE_KEYS = 'sanitize_input_safe_keys';

  /**
   * Previous name of SANITIZE_INPUT_SAFE_KEYS.
   *
   * @deprecated in drupal:9.1.0 and is removed from drupal:10.0.0. Use
   *   SANITIZE_INPUT_SAFE_KEYS instead.
   * @see https://www.drupal.org/node/3163148
   */
  const SANITIZE_WHITELIST = 'sanitize_input_whitelist';

@@ -31,15 +40,15 @@ class RequestSanitizer {
   *
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   The incoming request to sanitize.
   * @param string[] $whitelist
   *   An array of keys to whitelist as safe. See default.settings.php.
   * @param string[] $safe_keys
   *   An array of keys to consider safe.
   * @param bool $log_sanitized_keys
   *   (optional) Set to TRUE to log keys that are sanitized.
   *
   * @return \Symfony\Component\HttpFoundation\Request
   *   The sanitized request.
   */
  public static function sanitize(Request $request, $whitelist, $log_sanitized_keys = FALSE) {
  public static function sanitize(Request $request, array $safe_keys, $log_sanitized_keys = FALSE) {
    if (!$request->attributes->get(self::SANITIZED, FALSE)) {
      $update_globals = FALSE;
      $bags = [
@@ -48,7 +57,7 @@ public static function sanitize(Request $request, $whitelist, $log_sanitized_key
        'cookies' => 'Potentially unsafe keys removed from cookie parameters: %s',
      ];
      foreach ($bags as $bag => $message) {
        if (static::processParameterBag($request->$bag, $whitelist, $log_sanitized_keys, $bag, $message)) {
        if (static::processParameterBag($request->$bag, $safe_keys, $log_sanitized_keys, $bag, $message)) {
          $update_globals = TRUE;
        }
      }
@@ -65,8 +74,8 @@ public static function sanitize(Request $request, $whitelist, $log_sanitized_key
   *
   * @param \Symfony\Component\HttpFoundation\ParameterBag $bag
   *   The parameter bag to process.
   * @param string[] $whitelist
   *   An array of keys to whitelist as safe.
   * @param string[] $safe_keys
   *   An array of keys to consider safe.
   * @param bool $log_sanitized_keys
   *   Set to TRUE to log keys that are sanitized.
   * @param string $bag_name
@@ -78,10 +87,10 @@ public static function sanitize(Request $request, $whitelist, $log_sanitized_key
   * @return bool
   *   TRUE if the parameter bag has been sanitized, FALSE if not.
   */
  protected static function processParameterBag(ParameterBag $bag, $whitelist, $log_sanitized_keys, $bag_name, $message) {
  protected static function processParameterBag(ParameterBag $bag, array $safe_keys, $log_sanitized_keys, $bag_name, $message) {
    $sanitized = FALSE;
    $sanitized_keys = [];
    $bag->replace(static::stripDangerousValues($bag->all(), $whitelist, $sanitized_keys));
    $bag->replace(static::stripDangerousValues($bag->all(), $safe_keys, $sanitized_keys));
    if (!empty($sanitized_keys)) {
      $sanitized = TRUE;
      if ($log_sanitized_keys) {
@@ -91,7 +100,7 @@ protected static function processParameterBag(ParameterBag $bag, $whitelist, $lo

    if ($bag->has('destination')) {
      $destination = $bag->get('destination');
      $destination_dangerous_keys = static::checkDestination($destination, $whitelist);
      $destination_dangerous_keys = static::checkDestination($destination, $safe_keys);
      if (!empty($destination_dangerous_keys)) {
        // The destination is removed rather than sanitized because the URL
        // generator service is not available and this method is called very
@@ -121,18 +130,18 @@ protected static function processParameterBag(ParameterBag $bag, $whitelist, $lo
   *
   * @param string $destination
   *   The destination string to check.
   * @param array $whitelist
   *   An array of keys to whitelist as safe.
   * @param string[] $safe_keys
   *   An array of keys to consider safe.
   *
   * @return array
   *   The dangerous keys found in the destination parameter.
   */
  protected static function checkDestination($destination, array $whitelist) {
  protected static function checkDestination($destination, array $safe_keys) {
    $dangerous_keys = [];
    $parts = UrlHelper::parse($destination);
    // If there is a query string, check its query parameters.
    if (!empty($parts['query'])) {
      static::stripDangerousValues($parts['query'], $whitelist, $dangerous_keys);
      static::stripDangerousValues($parts['query'], $safe_keys, $dangerous_keys);
    }
    return $dangerous_keys;
  }
@@ -142,23 +151,23 @@ protected static function checkDestination($destination, array $whitelist) {
   *
   * @param mixed $input
   *   The input to sanitize.
   * @param string[] $whitelist
   *   An array of keys to whitelist as safe.
   * @param string[] $safe_keys
   *   An array of keys to consider safe.
   * @param string[] $sanitized_keys
   *   An array of keys that have been removed.
   *
   * @return mixed
   *   The sanitized input.
   */
  protected static function stripDangerousValues($input, array $whitelist, array &$sanitized_keys) {
  protected static function stripDangerousValues($input, array $safe_keys, array &$sanitized_keys) {
    if (is_array($input)) {
      foreach ($input as $key => $value) {
        if ($key !== '' && ((string) $key)[0] === '#' && !in_array($key, $whitelist, TRUE)) {
        if ($key !== '' && ((string) $key)[0] === '#' && !in_array($key, $safe_keys, TRUE)) {
          unset($input[$key]);
          $sanitized_keys[] = $key;
        }
        else {
          $input[$key] = static::stripDangerousValues($input[$key], $whitelist, $sanitized_keys);
          $input[$key] = static::stripDangerousValues($input[$key], $safe_keys, $sanitized_keys);
        }
      }
    }
+4 −0
Changes for core/lib/Drupal/Core/Site/Settings.php: 4 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -38,6 +38,10 @@ final class Settings {
   * @see self::handleDeprecations()
   */
  private static $deprecatedSettings = [
    'sanitize_input_whitelist' => [
      'replacement' => 'sanitize_input_safe_keys',
      'message' => 'The "sanitize_input_whitelist" setting is deprecated in drupal:9.1.0 and will be removed in drupal:10.0.0. Use Drupal\Core\Security\RequestSanitizer::SANITIZE_INPUT_SAFE_KEYS instead. See https://www.drupal.org/node/3163148.',
    ],
    'twig_sandbox_whitelisted_classes' => [
      'replacement' => 'twig_sandbox_allowed_classes',
      'message' => 'The "twig_sandbox_whitelisted_classes" setting is deprecated in drupal:9.1.0 and is removed from drupal:10.0.0. Use "twig_sandbox_allowed_classes" instead. See https://www.drupal.org/node/3162897.',
+45 −15
Changes for core/tests/Drupal/Tests/Core/Site/SettingsTest.php: 45 added lines, 15 removed lines.
Original line number Diff line number Diff line
@@ -159,6 +159,9 @@ public function testGetInstanceReflection() {
   * or provider. This test is only for the general deprecated settings API
   * itself.
   *
   * @see self::testRealDeprecatedSettings()
   * @see self::providerTestRealDeprecatedSettings()
   *
   * @param string[] $settings_config
   *   Array of settings to put in the settings.php file for testing.
   * @param string $setting_name
@@ -212,8 +215,9 @@ public function testFakeDeprecatedSettings(array $settings_config, string $setti
  /**
   * Provides data for testFakeDeprecatedSettings().
   *
   * @return array
   *   Test case data.
   * Note: Tests for real deprecated settings should not be added here.
   *
   * @see self::providerTestRealDeprecatedSettings()
   */
  public function providerTestFakeDeprecatedSettings(): array {

@@ -272,28 +276,54 @@ public function providerTestFakeDeprecatedSettings(): array {
  }

  /**
   * Tests legacy twig_sandbox_* settings.
   * Tests deprecation messages for real deprecated settings.
   *
   * @group legacy
   * @param string $legacy_setting
   *   The legacy name of the setting to test.
   * @param string $expected_deprecation
   *   The expected deprecation message.
   *
   * @expectedDeprecation The "twig_sandbox_whitelisted_classes" setting is deprecated in drupal:9.1.0 and is removed from drupal:10.0.0. Use "twig_sandbox_allowed_classes" instead. See https://www.drupal.org/node/3162897.
   * @expectedDeprecation The "twig_sandbox_whitelisted_methods" setting is deprecated in drupal:9.1.0 and is removed from drupal:10.0.0. Use "twig_sandbox_allowed_methods" instead. See https://www.drupal.org/node/3162897.
   * @expectedDeprecation The "twig_sandbox_whitelisted_prefixes" setting is deprecated in drupal:9.1.0 and is removed from drupal:10.0.0. Use "twig_sandbox_allowed_prefixes" instead. See https://www.drupal.org/node/3162897.
   * @dataProvider providerTestRealDeprecatedSettings
   * @group legacy
   */
  public function testLegacyTwigSandboxSettings(): void {
    $settings = <<<'EOD'
<?php
$settings['twig_sandbox_whitelisted_classes'] = ['a', 'b'];
$settings['twig_sandbox_whitelisted_methods'] = ['aFoo', 'bBar'];
$settings['twig_sandbox_whitelisted_prefixes'] = ['aPrefix', 'bPrefix'];
EOD;
  public function testRealDeprecatedSettings(string $legacy_setting, string $expected_deprecation): void {

    $settings_file_content = "<?php\n\$settings['$legacy_setting'] = 'foo';\n";
    $class_loader = NULL;
    $vfs_root = vfsStream::setup('root');
    $sites_directory = vfsStream::newDirectory('sites')->at($vfs_root);
    vfsStream::newFile('settings.php')
      ->at($sites_directory)
      ->setContent($settings);
      ->setContent($settings_file_content);

    $this->expectDeprecation($expected_deprecation);

    // Presence of the old name in settings.php is enough to trigger messages.
    Settings::initialize(vfsStream::url('root'), 'sites', $class_loader);
  }

  /**
   * Provides data for testRealDeprecatedSettings().
   */
  public function providerTestRealDeprecatedSettings(): array {
    return [
      [
        'sanitize_input_whitelist',
        'The "sanitize_input_whitelist" setting is deprecated in drupal:9.1.0 and will be removed in drupal:10.0.0. Use Drupal\Core\Security\RequestSanitizer::SANITIZE_INPUT_SAFE_KEYS instead. See https://www.drupal.org/node/3163148.',
      ],
      [
        'twig_sandbox_whitelisted_classes',
        'The "twig_sandbox_whitelisted_classes" setting is deprecated in drupal:9.1.0 and is removed from drupal:10.0.0. Use "twig_sandbox_allowed_classes" instead. See https://www.drupal.org/node/3162897.',
      ],
      [
        'twig_sandbox_whitelisted_methods',
        'The "twig_sandbox_whitelisted_methods" setting is deprecated in drupal:9.1.0 and is removed from drupal:10.0.0. Use "twig_sandbox_allowed_methods" instead. See https://www.drupal.org/node/3162897.',
      ],
      [
        'twig_sandbox_whitelisted_prefixes',
        'The "twig_sandbox_whitelisted_prefixes" setting is deprecated in drupal:9.1.0 and is removed from drupal:10.0.0. Use "twig_sandbox_allowed_prefixes" instead. See https://www.drupal.org/node/3162897.',
      ],
    ];
  }

}