From baecb096b6d9dede72c646027b6065b7fc6f540d Mon Sep 17 00:00:00 2001 From: xjm Date: Wed, 16 Sep 2020 06:22:03 -0500 Subject: [PATCH 1/6] SA-CORE-2020-007 by samuel.mortenson, nod_, larowlan, dsnopek, catch, effulgentsia, mcdruid --- core/misc/ajax.es6.js | 1 + core/misc/ajax.js | 1 + core/misc/autocomplete.es6.js | 1 + core/misc/autocomplete.js | 3 ++- 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/core/misc/ajax.es6.js b/core/misc/ajax.es6.js index 6eeddf3df74..119642dba71 100644 --- a/core/misc/ajax.es6.js +++ b/core/misc/ajax.es6.js @@ -559,6 +559,7 @@ } }, dataType: 'json', + jsonp: false, type: 'POST', }; diff --git a/core/misc/ajax.js b/core/misc/ajax.js index cc3936ca6ae..b817b77b055 100644 --- a/core/misc/ajax.js +++ b/core/misc/ajax.js @@ -243,6 +243,7 @@ function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr }, dataType: 'json', + jsonp: false, type: 'POST' }; diff --git a/core/misc/autocomplete.es6.js b/core/misc/autocomplete.es6.js index 65f6332af69..1b5d8791469 100644 --- a/core/misc/autocomplete.es6.js +++ b/core/misc/autocomplete.es6.js @@ -281,6 +281,7 @@ }, ajax: { dataType: 'json', + jsonp: false, }, }; diff --git a/core/misc/autocomplete.js b/core/misc/autocomplete.js index 52ddfd2ab48..fc90d775580 100644 --- a/core/misc/autocomplete.js +++ b/core/misc/autocomplete.js @@ -156,7 +156,8 @@ isComposing: false }, ajax: { - dataType: 'json' + dataType: 'json', + jsonp: false } }; From 509723e7aff95a0efff6bd6d6ef879fd57ebf8a4 Mon Sep 17 00:00:00 2001 From: xjm Date: Wed, 16 Sep 2020 06:22:03 -0500 Subject: [PATCH 2/6] SA-CORE-2020-008 by amateescu, xjm, catch, larowlan, greggles, dixon --- core/modules/workspaces/src/WorkspaceManager.php | 6 +++++- .../UpdateSystem/ActiveWorkspaceUpdateTest.php | 10 +++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/core/modules/workspaces/src/WorkspaceManager.php b/core/modules/workspaces/src/WorkspaceManager.php index 541ca88564a..3421d31ec63 100644 --- a/core/modules/workspaces/src/WorkspaceManager.php +++ b/core/modules/workspaces/src/WorkspaceManager.php @@ -186,7 +186,11 @@ class WorkspaceManager implements WorkspaceManagerInterface { foreach ($this->negotiatorIds as $negotiator_id) { $negotiator = $this->classResolver->getInstanceFromDefinition($negotiator_id); if ($negotiator->applies($request)) { - if ($active_workspace = $negotiator->getActiveWorkspace($request)) { + // By default, 'view' access is checked when a workspace is activated, + // but it should also be checked when retrieving the currently active + // workspace. + if (($negotiated_workspace = $negotiator->getActiveWorkspace($request)) && $negotiated_workspace->access('view')) { + $active_workspace = $negotiated_workspace; break; } } diff --git a/core/modules/workspaces/tests/src/Functional/UpdateSystem/ActiveWorkspaceUpdateTest.php b/core/modules/workspaces/tests/src/Functional/UpdateSystem/ActiveWorkspaceUpdateTest.php index 98cc5a3bfe2..1c4f9b90abb 100644 --- a/core/modules/workspaces/tests/src/Functional/UpdateSystem/ActiveWorkspaceUpdateTest.php +++ b/core/modules/workspaces/tests/src/Functional/UpdateSystem/ActiveWorkspaceUpdateTest.php @@ -4,6 +4,7 @@ namespace Drupal\Tests\workspaces\Functional\UpdateSystem; use Drupal\Tests\BrowserTestBase; use Drupal\Tests\UpdatePathTestTrait; +use Drupal\Tests\user\Traits\UserCreationTrait; /** * Tests that there is no active workspace during database updates. @@ -12,12 +13,14 @@ use Drupal\Tests\UpdatePathTestTrait; * @group Update */ class ActiveWorkspaceUpdateTest extends BrowserTestBase { + use UpdatePathTestTrait; + use UserCreationTrait; /** * {@inheritdoc} */ - protected static $modules = ['workspaces', 'workspace_update_test']; + protected static $modules = ['workspaces']; /** * {@inheritdoc} @@ -29,6 +32,11 @@ class ActiveWorkspaceUpdateTest extends BrowserTestBase { */ protected function setUp() { parent::setUp(); + + $this->setUpCurrentUser([], ['view any workspace']); + $this->container->get('module_installer')->install(['workspace_update_test']); + $this->rebuildContainer(); + // Ensure the workspace_update_test_post_update_check_active_workspace() // update runs. $existing_updates = \Drupal::keyValue('post_update')->get('existing_updates', []); From 3e3f35290cf8263e2f536d7c338333311c239cde Mon Sep 17 00:00:00 2001 From: xjm Date: Wed, 16 Sep 2020 06:22:03 -0500 Subject: [PATCH 3/6] SA-CORE-2020-009 by _nzr_, markwittens, nathandentzau, marcaddeo, janusman, larowlan, David_Rothstein, Wim Leers, vijaycs85, mcdruid, Heine, pandaski, xjm, tim.plunkett --- core/lib/Drupal/Core/Form/FormBuilder.php | 3 ++- core/modules/user/src/Plugin/Block/UserLoginBlock.php | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/core/lib/Drupal/Core/Form/FormBuilder.php b/core/lib/Drupal/Core/Form/FormBuilder.php index 5e8e22a6042..44475346587 100644 --- a/core/lib/Drupal/Core/Form/FormBuilder.php +++ b/core/lib/Drupal/Core/Form/FormBuilder.php @@ -861,7 +861,8 @@ class FormBuilder implements FormBuilderInterface, FormValidatorInterface, FormS // https://www.drupal.org/node/2504709. $parsed = UrlHelper::parse($request_uri); unset($parsed['query'][static::AJAX_FORM_REQUEST], $parsed['query'][MainContentViewSubscriber::WRAPPER_FORMAT]); - return $parsed['path'] . ($parsed['query'] ? ('?' . UrlHelper::buildQuery($parsed['query'])) : ''); + $action = $parsed['path'] . ($parsed['query'] ? ('?' . UrlHelper::buildQuery($parsed['query'])) : ''); + return UrlHelper::filterBadProtocol($action); } /** diff --git a/core/modules/user/src/Plugin/Block/UserLoginBlock.php b/core/modules/user/src/Plugin/Block/UserLoginBlock.php index d9971c564d5..f55a70d6118 100644 --- a/core/modules/user/src/Plugin/Block/UserLoginBlock.php +++ b/core/modules/user/src/Plugin/Block/UserLoginBlock.php @@ -2,6 +2,7 @@ namespace Drupal\user\Plugin\Block; +use Drupal\Component\Utility\UrlHelper; use Drupal\Core\Access\AccessResult; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Security\TrustedCallbackInterface; @@ -155,7 +156,7 @@ class UserLoginBlock extends BlockBase implements ContainerFactoryPluginInterfac public static function renderPlaceholderFormAction() { return [ '#type' => 'markup', - '#markup' => Url::fromRoute('', [], ['query' => \Drupal::destination()->getAsArray(), 'external' => FALSE])->toString(), + '#markup' => UrlHelper::filterBadProtocol(Url::fromRoute('', [], ['query' => \Drupal::destination()->getAsArray(), 'external' => FALSE])->toString()), '#cache' => ['contexts' => ['url.path', 'url.query_args']], ]; } From 487c407bfc1c1c7f8079a15c7b11933708ac7040 Mon Sep 17 00:00:00 2001 From: xjm Date: Wed, 16 Sep 2020 06:22:03 -0500 Subject: [PATCH 4/6] SA-CORE-2020-010 by DorTumarkin, kkrzton, samuel.mortenson, TwoD, Wim Leers, larowlan, xjm --- .../plugins/drupalimagecaption/plugin.es6.js | 3 + .../js/plugins/drupalimagecaption/plugin.js | 3 + .../src/Plugin/Filter/FilterCaption.php | 63 +++++++++++++++++-- 3 files changed, 65 insertions(+), 4 deletions(-) diff --git a/core/modules/ckeditor/js/plugins/drupalimagecaption/plugin.es6.js b/core/modules/ckeditor/js/plugins/drupalimagecaption/plugin.es6.js index 1e63c4b782e..f90a77088d2 100644 --- a/core/modules/ckeditor/js/plugins/drupalimagecaption/plugin.es6.js +++ b/core/modules/ckeditor/js/plugins/drupalimagecaption/plugin.es6.js @@ -215,6 +215,9 @@ 'figcaption', ); + const captionFilter = new CKEDITOR.filter(widgetDefinition.editables.caption.allowedContent); + captionFilter.applyTo(caption); + // Use Drupal's data-placeholder attribute to insert a CSS-based, // translation-ready placeholder for empty captions. Note that it // also must to be done for new instances (see diff --git a/core/modules/ckeditor/js/plugins/drupalimagecaption/plugin.js b/core/modules/ckeditor/js/plugins/drupalimagecaption/plugin.js index 7cd215a7058..2f43b9d9c6b 100644 --- a/core/modules/ckeditor/js/plugins/drupalimagecaption/plugin.js +++ b/core/modules/ckeditor/js/plugins/drupalimagecaption/plugin.js @@ -139,6 +139,9 @@ var figure = new CKEDITOR.htmlParser.element('figure'); caption = new CKEDITOR.htmlParser.fragment.fromHtml(caption, 'figcaption'); + var captionFilter = new CKEDITOR.filter(widgetDefinition.editables.caption.allowedContent); + captionFilter.applyTo(caption); + caption.attributes['data-placeholder'] = placeholderText; element.replaceWith(figure); diff --git a/core/modules/filter/src/Plugin/Filter/FilterCaption.php b/core/modules/filter/src/Plugin/Filter/FilterCaption.php index a03c95f5c8c..b5a58563d3e 100644 --- a/core/modules/filter/src/Plugin/Filter/FilterCaption.php +++ b/core/modules/filter/src/Plugin/Filter/FilterCaption.php @@ -4,9 +4,12 @@ namespace Drupal\filter\Plugin\Filter; use Drupal\Component\Utility\Html; use Drupal\Component\Utility\Xss; +use Drupal\Core\Plugin\ContainerFactoryPluginInterface; +use Drupal\filter\FilterPluginManager; use Drupal\filter\FilterProcessResult; use Drupal\filter\Plugin\FilterBase; use Drupal\filter\Render\FilteredMarkup; +use Symfony\Component\DependencyInjection\ContainerInterface; /** * Provides a filter to caption elements. @@ -20,7 +23,43 @@ use Drupal\filter\Render\FilteredMarkup; * type = Drupal\filter\Plugin\FilterInterface::TYPE_TRANSFORM_REVERSIBLE * ) */ -class FilterCaption extends FilterBase { +class FilterCaption extends FilterBase implements ContainerFactoryPluginInterface { + + /** + * Filter manager. + * + * @var \Drupal\filter\FilterPluginManager + */ + protected $filterManager; + + /** + * Constructs a new FilterCaption. + * + * @param array $configuration + * Configuration. + * @param string $plugin_id + * Plugin ID. + * @param mixed $plugin_definition + * Definition. + * @param \Drupal\filter\FilterPluginManager $filter_manager + * Filter plugin manager. + */ + public function __construct(array $configuration, $plugin_id, $plugin_definition, FilterPluginManager $filter_manager = NULL) { + parent::__construct($configuration, $plugin_id, $plugin_definition); + $this->filterManager = $filter_manager ?: \Drupal::service('plugin.manager.filter'); + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('plugin.manager.filter') + ); + } /** * {@inheritdoc} @@ -31,6 +70,13 @@ class FilterCaption extends FilterBase { if (stristr($text, 'data-caption') !== FALSE) { $dom = Html::load($text); $xpath = new \DOMXPath($dom); + $html_filter = $this->filterManager->createInstance('filter_html', [ + 'settings' => [ + 'allowed_html' => '
', + 'filter_html_help' => FALSE, + 'filter_html_nofollow' => FALSE, + ], + ]); foreach ($xpath->query('//*[@data-caption]') as $node) { // Read the data-caption attribute's value, then delete it. $caption = Html::escape($node->getAttribute('data-caption')); @@ -39,10 +85,19 @@ class FilterCaption extends FilterBase { // Sanitize caption: decode HTML encoding, limit allowed HTML tags; only // allow inline tags that are allowed by default, plus
. $caption = Html::decodeEntities($caption); - $caption = FilteredMarkup::create(Xss::filter($caption, ['a', 'em', 'strong', 'cite', 'code', 'br'])); + $raw_caption = $caption; + $filtered_caption = $html_filter->process($caption, $langcode); + $result->addCacheableDependency($filtered_caption); + $caption = FilteredMarkup::create($filtered_caption->getProcessedText()); - // The caption must be non-empty. - if (mb_strlen($caption) === 0) { + // The caption must be non-empty - however the Media Embed CKEditor + // plugin uses a single space to represent a newly added caption. The + // HTML filter will transform this into an empty string and prevent the + // content editor from adding a new caption. To allow for this we treat + // a raw caption value of ' ' as valid and adding the wrapping figure + // element. + // @see core/modules/media/js/plugins/drupalmedia/plugin.es6.js + if (mb_strlen($caption) === 0 && $raw_caption !== ' ') { continue; } From 17e91ba7402236a36e49074a390a49c69edccdd6 Mon Sep 17 00:00:00 2001 From: xjm Date: Wed, 16 Sep 2020 06:22:03 -0500 Subject: [PATCH 5/6] SA-CORE-2020-011 by David_Rothstein, Chi, elarlang, dokumori, kyk, xjm, mlhess, pwolanin, stefan.r, benjy, fgm, samuel.mortenson, larowlan, pandaski --- core/modules/file/src/Element/ManagedFile.php | 4 ++++ .../tests/src/Functional/FilePrivateTest.php | 16 ++++++++-------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/core/modules/file/src/Element/ManagedFile.php b/core/modules/file/src/Element/ManagedFile.php index 8e7843fcbb7..d1728b1202a 100644 --- a/core/modules/file/src/Element/ManagedFile.php +++ b/core/modules/file/src/Element/ManagedFile.php @@ -96,6 +96,10 @@ class ManagedFile extends FormElement { foreach ($input['fids'] as $fid) { if ($file = File::load($fid)) { $fids[] = $file->id(); + if (!$file->access('download')) { + $force_default = TRUE; + break; + } // Temporary files that belong to other users should never be // allowed. if ($file->isTemporary()) { diff --git a/core/modules/file/tests/src/Functional/FilePrivateTest.php b/core/modules/file/tests/src/Functional/FilePrivateTest.php index d9f30d53fa7..93c7523de1f 100644 --- a/core/modules/file/tests/src/Functional/FilePrivateTest.php +++ b/core/modules/file/tests/src/Functional/FilePrivateTest.php @@ -92,11 +92,10 @@ class FilePrivateTest extends FileFieldTestBase { $this->drupalGet('node/' . $new_node->id() . '/edit'); $this->getSession()->getPage()->find('css', 'input[name="' . $field_name . '[0][fids]"]')->setValue($node_file->id()); $this->getSession()->getPage()->pressButton(t('Save')); - // Make sure the form submit failed - we stayed on the edit form. - $this->assertUrl('node/' . $new_node->id() . '/edit'); - // Check that we got the expected constraint form error. - $constraint = new ReferenceAccessConstraint(); - $this->assertRaw(new FormattableMarkup($constraint->message, ['%type' => 'file', '%id' => $node_file->id()])); + $this->assertUrl('node/' . $new_node->id()); + // Make sure the submitted hidden file field is empty. + $new_node = \Drupal::entityTypeManager()->getStorage('node')->loadUnchanged($new_node->id()); + $this->assertTrue($new_node->get($field_name)->isEmpty()); // Attempt to reuse the existing file when creating a new node, and confirm // that access is still denied. $edit = []; @@ -107,9 +106,10 @@ class FilePrivateTest extends FileFieldTestBase { $this->getSession()->getPage()->find('css', 'input[name="' . $field_name . '[0][fids]"]')->setValue($node_file->id()); $this->getSession()->getPage()->pressButton(t('Save')); $new_node = $this->drupalGetNodeByTitle($edit['title[0][value]']); - $this->assertTrue(empty($new_node), 'Node was not created.'); - $this->assertUrl('node/add/' . $type_name); - $this->assertRaw(new FormattableMarkup($constraint->message, ['%type' => 'file', '%id' => $node_file->id()])); + $this->assertUrl('node/' . $new_node->id()); + // Make sure the submitted hidden file field is empty. + $new_node = \Drupal::entityTypeManager()->getStorage('node')->loadUnchanged($new_node->id()); + $this->assertTrue($new_node->get($field_name)->isEmpty()); // Now make file_test_file_download() return everything. \Drupal::state()->set('file_test.allow_all', TRUE); From db320ce405a948090ca031d32531195d85aded4e Mon Sep 17 00:00:00 2001 From: xjm Date: Wed, 16 Sep 2020 06:22:21 -0500 Subject: [PATCH 6/6] Drupal 8.9.6 --- composer.lock | 6 +++--- composer/Metapackage/CoreRecommended/composer.json | 2 +- composer/Metapackage/PinnedDevDependencies/composer.json | 2 +- core/lib/Drupal.php | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/composer.lock b/composer.lock index 044ef91d362..c006fa00506 100644 --- a/composer.lock +++ b/composer.lock @@ -667,7 +667,7 @@ }, { "name": "drupal/core", - "version": "8.9.5", + "version": "8.9.6", "dist": { "type": "path", "url": "core", @@ -901,7 +901,7 @@ }, { "name": "drupal/core-project-message", - "version": "8.9.5", + "version": "8.9.6", "dist": { "type": "path", "url": "composer/Plugin/ProjectMessage", @@ -934,7 +934,7 @@ }, { "name": "drupal/core-vendor-hardening", - "version": "8.9.5", + "version": "8.9.6", "dist": { "type": "path", "url": "composer/Plugin/VendorHardening", diff --git a/composer/Metapackage/CoreRecommended/composer.json b/composer/Metapackage/CoreRecommended/composer.json index ee24de4d11f..9128c1be140 100644 --- a/composer/Metapackage/CoreRecommended/composer.json +++ b/composer/Metapackage/CoreRecommended/composer.json @@ -7,7 +7,7 @@ "webflo/drupal-core-strict": "*" }, "require": { - "drupal/core": "8.9.5", + "drupal/core": "8.9.6", "asm89/stack-cors": "1.3.0", "composer/semver": "1.5.1", "doctrine/annotations": "v1.4.0", diff --git a/composer/Metapackage/PinnedDevDependencies/composer.json b/composer/Metapackage/PinnedDevDependencies/composer.json index 3666aa5d235..3dde09681c1 100644 --- a/composer/Metapackage/PinnedDevDependencies/composer.json +++ b/composer/Metapackage/PinnedDevDependencies/composer.json @@ -7,7 +7,7 @@ "webflo/drupal-core-require-dev": "*" }, "require": { - "drupal/core": "8.9.5", + "drupal/core": "8.9.6", "behat/mink": "v1.8.1", "behat/mink-browserkit-driver": "v1.3.4", "behat/mink-goutte-driver": "v1.2.1", diff --git a/core/lib/Drupal.php b/core/lib/Drupal.php index 01609b2c1cf..2d5754aa94b 100644 --- a/core/lib/Drupal.php +++ b/core/lib/Drupal.php @@ -82,7 +82,7 @@ class Drupal { /** * The current system version. */ - const VERSION = '8.9.5'; + const VERSION = '8.9.6'; /** * Core API compatibility.