CVE-2020-13676
Description
The QuickEdit module does not properly check access to fields in some circumstances, which can lead to unintended disclosure of field data. Sites are only affected if the QuickEdit module (which comes with the Standard profile) is installed.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Drupal QuickEdit module access bypass vulnerability allows unintended disclosure of field data in affected versions.
Vulnerability
The QuickEdit module in Drupal core fails to properly check access to fields in certain circumstances, leading to unintended disclosure of field data. Affected versions include Drupal 8.0.0 to 8.9.18, 9.1.0 to 9.1.12, and 9.2.0 to 9.2.5 [2][4]. Specifically, fields such as uid (user ID) and created (creation timestamp) were incorrectly made accessible via the QuickEdit interface [3]. Sites are only affected if the QuickEdit module is installed (it is included in the Standard profile) [2].
Exploitation
An attacker with the ability to use QuickEdit on a site (typically any authenticated user with appropriate permissions to edit content in-place) can view field data that would normally be restricted [2]. The attacker interacts with the QuickEdit toolbar on an entity, and the module reveals the values of fields that lack proper access checks [3]. No special privileges beyond QuickEdit access are required [2].
Impact
Successful exploitation results in unauthorized disclosure of field data, including sensitive information such as the user ID of the author and the creation timestamp of the content [3]. This constitutes a confidentiality breach and may aid further attacks by revealing internal details about the site's content and users [2].
Mitigation
The vulnerability is fixed in Drupal 8.9.19, 9.1.13, and 9.2.6 [4]. Users should upgrade to these or later versions. If upgrading is not immediately possible, uninstalling the QuickEdit module fully mitigates the issue [4]. Versions of Drupal 8 prior to 8.9.x and Drupal 9 prior to 9.1.x are end-of-life and no longer receive security coverage [4]. Drupal 7 is not affected as it lacks the QuickEdit module [4].
AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
drupal/corePackagist | >= 8.0.0, < 8.9.19 | 8.9.19 |
drupal/corePackagist | >= 9.1.0, < 9.1.13 | 9.1.13 |
drupal/corePackagist | >= 9.2.0, < 9.2.6 | 9.2.6 |
Affected products
3- osv-coords2 versions
>= 8.9.0, < 8.9.19+ 1 more
- (no CPE)range: >= 8.9.0, < 8.9.19
- (no CPE)range: >= 8.0.0, < 8.9.19
- Drupal/Corev5Range: 9.2
Patches
18e8e3d2ddd72SA-CORE-2021-009 by illeace, Wim Leers, xjm, effulgentsia, larowlan, pandaski, vijaycs85, phenaproxima, mcdruid
7 files changed · +106 −51
modules/quickedit/src/MetadataGenerator.php+1 −1 modified@@ -68,7 +68,7 @@ public function generateFieldMetadata(FieldItemListInterface $items, $view_mode) // Early-return if user does not have access. $access = $this->accessChecker->accessEditEntityField($entity, $field_name); - if (!$access) { + if (!$access->isAllowed()) { return ['access' => FALSE]; }
modules/quickedit/tests/modules/src/MockQuickEditEntityFieldAccessCheck.php+14 −1 modified@@ -2,6 +2,7 @@ namespace Drupal\quickedit_test; +use Drupal\Core\Access\AccessResult; use Drupal\Core\Entity\EntityInterface; use Drupal\quickedit\Access\QuickEditEntityFieldAccessCheckInterface; @@ -14,7 +15,19 @@ class MockQuickEditEntityFieldAccessCheck implements QuickEditEntityFieldAccessC * {@inheritdoc} */ public function accessEditEntityField(EntityInterface $entity, $field_name) { - return TRUE; + switch (\Drupal::state()->get('quickedit_test_field_access')) { + case 'allowed': + return AccessResult::allowed(); + + case 'neutral': + return AccessResult::neutral(); + + case 'forbidden': + return AccessResult::forbidden(); + + default: + throw new \OutOfRangeException("The state for the 'quickedit_test_field_access' key must be either 'allowed', 'neutral' or 'forbidden'."); + } } }
modules/quickedit/tests/src/FunctionalJavascript/LayoutBuilderQuickEditTest.php+23 −5 modified@@ -112,6 +112,14 @@ public function testQuickEditIgnoresDuplicateFields() { $this->drupalLogin($this->contentAuthorUser); $this->usingLayoutBuilder = TRUE; + $this->assertQuickEditInit(['title']); + $this->drupalLogin($this->drupalCreateUser([ + 'access contextual links', + 'access in-place editing', + 'access content', + 'edit any article content', + 'administer nodes', + ])); $this->assertQuickEditInit(['title', 'uid', 'created']); } @@ -123,18 +131,26 @@ public function testQuickEditIgnoresDuplicateFields() { * * @dataProvider providerEnableDisableLayoutBuilder */ - public function testEnableDisableLayoutBuilder($use_revisions) { + public function testEnableDisableLayoutBuilder($use_revisions, $admin_permission = FALSE) { if (!$use_revisions) { $content_type = NodeType::load('article'); $content_type->setNewRevision(FALSE); $content_type->save(); } $fields = [ 'title', - 'uid', - 'created', 'body', ]; + if ($admin_permission) { + $fields = array_merge($fields, ['uid', 'created']); + $this->drupalLogin($this->drupalCreateUser([ + 'access contextual links', + 'access in-place editing', + 'access content', + 'edit any article content', + 'administer nodes', + ])); + } // Test article with Layout Builder disabled. $this->assertQuickEditInit($fields); @@ -168,8 +184,10 @@ public function testEnableDisableLayoutBuilder($use_revisions) { */ public function providerEnableDisableLayoutBuilder() { return [ - 'use revisions' => [TRUE], - 'do not use revisions' => [FALSE], + 'use revisions, not admin' => [TRUE], + 'do not use revisions, not admin' => [FALSE], + 'use revisions, admin' => [TRUE, TRUE], + 'do not use revisions, admin' => [FALSE, TRUE], ]; }
modules/quickedit/tests/src/FunctionalJavascript/QuickEditImageTest.php+48 −26 modified@@ -41,27 +41,32 @@ protected function setUp(): void { // Create the Article node type. $this->drupalCreateContentType(['type' => 'article', 'name' => 'Article']); + } + /** + * Tests that quick editor works correctly with images. + * + * @covers ::isCompatible + * @covers ::getAttachments + * + * @dataProvider providerTestImageInPlaceEditor + */ + public function testImageInPlaceEditor($admin_permission = FALSE) { // Log in as a content author who can use Quick Edit and edit Articles. - $this->contentAuthorUser = $this->drupalCreateUser([ + $permissions = [ 'access contextual links', 'access toolbar', 'access in-place editing', 'access content', 'create article content', 'edit any article content', 'delete any article content', - ]); + ]; + if ($admin_permission) { + $permissions[] = 'administer nodes'; + } + $this->contentAuthorUser = $this->drupalCreateUser($permissions); $this->drupalLogin($this->contentAuthorUser); - } - - /** - * Tests that quick editor works correctly with images. - * - * @covers ::isCompatible - * @covers ::getAttachments - */ - public function testImageInPlaceEditor() { // Create a field with a basic filetype restriction. $field_name = strtolower($this->randomMachineName()); $field_settings = [ @@ -126,13 +131,25 @@ public function testImageInPlaceEditor() { $this->assertEntityInstanceStates([ 'node/1[0]' => 'closed', ]); + + $admin_inactive = []; + $admin_candidate = []; + if ($admin_permission) { + $admin_inactive = [ + 'node/1/uid/en/full' => 'inactive', + 'node/1/created/en/full' => 'inactive', + ]; + $admin_candidate = [ + 'node/1/uid/en/full' => 'candidate', + 'node/1/created/en/full' => 'candidate', + ]; + } + $this->assertEntityInstanceFieldStates('node', 1, 0, [ 'node/1/title/en/full' => 'inactive', - 'node/1/uid/en/full' => 'inactive', - 'node/1/created/en/full' => 'inactive', 'node/1/body/en/full' => 'inactive', 'node/1/' . $field_name . '/en/full' => 'inactive', - ]); + ] + $admin_inactive); // Start in-place editing of the article node. $this->startQuickEditViaToolbar('node', 1, 0); @@ -142,33 +159,27 @@ public function testImageInPlaceEditor() { $this->assertQuickEditEntityToolbar((string) $node->label(), NULL); $this->assertEntityInstanceFieldStates('node', 1, 0, [ 'node/1/title/en/full' => 'candidate', - 'node/1/uid/en/full' => 'candidate', - 'node/1/created/en/full' => 'candidate', 'node/1/body/en/full' => 'candidate', 'node/1/' . $field_name . '/en/full' => 'candidate', - ]); + ] + $admin_candidate); // Click the image field. $this->click($field_selector); $this->awaitImageEditor(); $this->assertSession()->elementExists('css', $field_selector . ' .quickedit-image-dropzone'); $this->assertEntityInstanceFieldStates('node', 1, 0, [ 'node/1/title/en/full' => 'candidate', - 'node/1/uid/en/full' => 'candidate', - 'node/1/created/en/full' => 'candidate', 'node/1/body/en/full' => 'candidate', 'node/1/' . $field_name . '/en/full' => 'active', - ]); + ] + $admin_candidate); // Type new 'alt' text. $this->typeInImageEditorAltTextInput('New text'); $this->assertEntityInstanceFieldStates('node', 1, 0, [ 'node/1/title/en/full' => 'candidate', - 'node/1/uid/en/full' => 'candidate', - 'node/1/created/en/full' => 'candidate', 'node/1/body/en/full' => 'candidate', 'node/1/' . $field_name . '/en/full' => 'changed', - ]); + ] + $admin_candidate); // Drag and drop an image. $this->dropImageOnImageEditor($valid_images[1]->uri); @@ -184,11 +195,9 @@ public function testImageInPlaceEditor() { ]); $this->assertEntityInstanceFieldStates('node', 1, 0, [ 'node/1/title/en/full' => 'candidate', - 'node/1/uid/en/full' => 'candidate', - 'node/1/created/en/full' => 'candidate', 'node/1/body/en/full' => 'candidate', 'node/1/' . $field_name . '/en/full' => 'saving', - ]); + ] + $admin_candidate); $this->assertEntityInstanceFieldMarkup([ 'node/1/' . $field_name . '/en/full' => '.quickedit-changed', ]); @@ -208,4 +217,17 @@ public function testImageInPlaceEditor() { $this->assertSession()->elementExists('css', $entity_selector . ' ' . $field_selector . ' ' . $new_image_selector); } + /** + * Data provider for ::testImageInPlaceEditor(). + * + * @return array + * Test cases. + */ + public function providerTestImageInPlaceEditor(): array { + return [ + 'with permission' => [TRUE], + 'without permission' => [FALSE], + ]; + } + }
modules/quickedit/tests/src/FunctionalJavascript/QuickEditIntegrationTest.php+0 −18 modified@@ -146,8 +146,6 @@ public function testArticleNode() { ]); $this->assertEntityInstanceFieldStates('node', 1, 0, [ 'node/1/title/en/full' => 'inactive', - 'node/1/uid/en/full' => 'inactive', - 'node/1/created/en/full' => 'inactive', 'node/1/body/en/full' => 'inactive', 'node/1/field_tags/en/full' => 'inactive', ]); @@ -160,8 +158,6 @@ public function testArticleNode() { $this->assertQuickEditEntityToolbar((string) $node->label(), NULL); $this->assertEntityInstanceFieldStates('node', 1, 0, [ 'node/1/title/en/full' => 'candidate', - 'node/1/uid/en/full' => 'candidate', - 'node/1/created/en/full' => 'candidate', 'node/1/body/en/full' => 'candidate', 'node/1/field_tags/en/full' => 'candidate', ]); @@ -174,8 +170,6 @@ public function testArticleNode() { $this->assertQuickEditEntityToolbar((string) $node->label(), 'Title'); $this->assertEntityInstanceFieldStates('node', 1, 0, [ 'node/1/title/en/full' => 'active', - 'node/1/uid/en/full' => 'candidate', - 'node/1/created/en/full' => 'candidate', 'node/1/body/en/full' => 'candidate', 'node/1/field_tags/en/full' => 'candidate', ]); @@ -188,8 +182,6 @@ public function testArticleNode() { $this->awaitEntityInstanceFieldState('node', 1, 0, 'title', 'en', 'changed'); $this->assertEntityInstanceFieldStates('node', 1, 0, [ 'node/1/title/en/full' => 'changed', - 'node/1/uid/en/full' => 'candidate', - 'node/1/created/en/full' => 'candidate', 'node/1/body/en/full' => 'candidate', 'node/1/field_tags/en/full' => 'candidate', ]); @@ -201,8 +193,6 @@ public function testArticleNode() { $this->assertQuickEditEntityToolbar((string) $node->label(), 'Body'); $this->assertEntityInstanceFieldStates('node', 1, 0, [ 'node/1/title/en/full' => 'saving', - 'node/1/uid/en/full' => 'candidate', - 'node/1/created/en/full' => 'candidate', 'node/1/body/en/full' => 'active', 'node/1/field_tags/en/full' => 'candidate', ]); @@ -223,8 +213,6 @@ public function testArticleNode() { $assert_session->waitForElement('css', '.quickedit-toolbar-field div[id*="tags"]'); $this->assertQuickEditEntityToolbar((string) $node->label(), 'Tags'); $this->assertEntityInstanceFieldStates('node', 1, 0, [ - 'node/1/uid/en/full' => 'candidate', - 'node/1/created/en/full' => 'candidate', 'node/1/body/en/full' => 'candidate', 'node/1/field_tags/en/full' => 'activating', 'node/1/title/en/full' => 'candidate', @@ -239,8 +227,6 @@ public function testArticleNode() { // Wait for the form to load. $this->assertJsCondition('document.querySelector(\'.quickedit-form-container > .quickedit-form[role="dialog"] > .placeholder\') === null'); $this->assertEntityInstanceFieldStates('node', 1, 0, [ - 'node/1/uid/en/full' => 'candidate', - 'node/1/created/en/full' => 'candidate', 'node/1/body/en/full' => 'candidate', 'node/1/field_tags/en/full' => 'active', 'node/1/title/en/full' => 'candidate', @@ -250,8 +236,6 @@ public function testArticleNode() { $this->typeInFormEditorTextInputField('field_tags[target_id]', 'foo, bar'); $this->awaitEntityInstanceFieldState('node', 1, 0, 'field_tags', 'en', 'changed'); $this->assertEntityInstanceFieldStates('node', 1, 0, [ - 'node/1/uid/en/full' => 'candidate', - 'node/1/created/en/full' => 'candidate', 'node/1/body/en/full' => 'candidate', 'node/1/field_tags/en/full' => 'changed', 'node/1/title/en/full' => 'candidate', @@ -264,8 +248,6 @@ public function testArticleNode() { 'node/1[0]' => 'committing', ]); $this->assertEntityInstanceFieldStates('node', 1, 0, [ - 'node/1/uid/en/full' => 'candidate', - 'node/1/created/en/full' => 'candidate', 'node/1/body/en/full' => 'candidate', 'node/1/field_tags/en/full' => 'saving', 'node/1/title/en/full' => 'candidate',
modules/quickedit/tests/src/Kernel/EditorIntegrationTest.php+5 −0 modified@@ -179,6 +179,11 @@ public function testMetadata() { // Verify metadata. $items = $entity->get($this->fieldName); + \Drupal::state()->set('quickedit_test_field_access', 'forbidden'); + $this->assertSame(['access' => FALSE], $this->metadataGenerator->generateFieldMetadata($items, 'default')); + \Drupal::state()->set('quickedit_test_field_access', 'neutral'); + $this->assertSame(['access' => FALSE], $this->metadataGenerator->generateFieldMetadata($items, 'default')); + \Drupal::state()->set('quickedit_test_field_access', 'allowed'); $metadata = $this->metadataGenerator->generateFieldMetadata($items, 'default'); $expected = [ 'access' => TRUE,
modules/quickedit/tests/src/Kernel/MetadataGeneratorTest.php+15 −0 modified@@ -97,6 +97,11 @@ public function testSimpleEntityType() { // Verify metadata for field 1. $items_1 = $entity->get($field_1_name); + \Drupal::state()->set('quickedit_test_field_access', 'forbidden'); + $this->assertSame(['access' => FALSE], $this->metadataGenerator->generateFieldMetadata($items_1, 'default')); + \Drupal::state()->set('quickedit_test_field_access', 'neutral'); + $this->assertSame(['access' => FALSE], $this->metadataGenerator->generateFieldMetadata($items_1, 'default')); + \Drupal::state()->set('quickedit_test_field_access', 'allowed'); $metadata_1 = $this->metadataGenerator->generateFieldMetadata($items_1, 'default'); $expected_1 = [ 'access' => TRUE, @@ -107,6 +112,11 @@ public function testSimpleEntityType() { // Verify metadata for field 2. $items_2 = $entity->get($field_2_name); + \Drupal::state()->set('quickedit_test_field_access', 'forbidden'); + $this->assertSame(['access' => FALSE], $this->metadataGenerator->generateFieldMetadata($items_2, 'default')); + \Drupal::state()->set('quickedit_test_field_access', 'neutral'); + $this->assertSame(['access' => FALSE], $this->metadataGenerator->generateFieldMetadata($items_2, 'default')); + \Drupal::state()->set('quickedit_test_field_access', 'allowed'); $metadata_2 = $this->metadataGenerator->generateFieldMetadata($items_2, 'default'); $expected_2 = [ 'access' => TRUE, @@ -163,6 +173,11 @@ public function testEditorWithCustomMetadata() { // Verify metadata. $items = $entity->get($field_name); + \Drupal::state()->set('quickedit_test_field_access', 'forbidden'); + $this->assertSame(['access' => FALSE], $this->metadataGenerator->generateFieldMetadata($items, 'default')); + \Drupal::state()->set('quickedit_test_field_access', 'neutral'); + $this->assertSame(['access' => FALSE], $this->metadataGenerator->generateFieldMetadata($items, 'default')); + \Drupal::state()->set('quickedit_test_field_access', 'allowed'); $metadata = $this->metadataGenerator->generateFieldMetadata($items, 'default'); $expected = [ 'access' => TRUE,
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4- github.com/advisories/GHSA-qfhg-m6r8-xxpjghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2020-13676ghsaADVISORY
- github.com/drupal/core/commit/8e8e3d2ddd72471ba886346ecabfb5d98fd27d9bghsaWEB
- www.drupal.org/sa-core-2021-009ghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.