VYPR
Medium severity6.6NVD Advisory· Published May 19, 2026· Updated May 20, 2026

CVE-2026-6366

CVE-2026-6366

Description

Improperly Controlled Modification of Dynamically-Determined Object Attributes vulnerability in Drupal Drupal core allows Object Injection.

This issue affects Drupal core: from 8.0.0 before 10.5.9, from 10.6.0 before 10.6.7, from 11.0.0 before 11.2.11, from 11.3.0 before 11.3.7.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Drupal core gadget chain enables object injection, requiring a separate deserialization vulnerability to achieve code execution.

Vulnerability

Drupal core versions 8.0.0 through 10.5.8, 10.6.0 through 10.6.6, 11.0.0 through 11.2.10, and 11.3.0 through 11.3.6 contain a chain of methods (a "gadget chain") that can be exploited when an insecure deserialization vulnerability exists on the site [1]. The issue is classified as an Improperly Controlled Modification of Dynamically-Determined Object Attributes vulnerability leading to Object Injection. The affected versions include all Drupal 11.1.x, 11.0.x, 10.4.x, and earlier releases which are end-of-life and no longer receive security coverage [1].

Exploitation

An attacker cannot directly exploit this vulnerability on its own. Exploitation requires a separate vulnerability that allows the attacker to pass unsafe input to the unserialize() function [1]. There are no known such vulnerabilities in Drupal core itself, but a site running third-party modules with their own deserialization flaws could provide the necessary entry point. The attacker would need the ability to control serialized data fed to unserialize() through some other means [1].

Impact

If triggered, the gadget chain enables remote code execution or SQL injection, depending on the available classes and methods [1]. The attacker could gain full control over the affected site or extract sensitive database contents. The privilege level of the compromise equals that of the user context in which the deserialization occurs, typically a privileged administrative user or the web server user.

Mitigation

Update to fixed versions: Drupal 10.5.9, 10.6.7, 11.2.11, or 11.3.7 [1]. For older branches, upgrade to a supported release. No workaround exists other than ensuring no other deserialization vulnerabilities are present on the site. Drupal 8, 9, and earlier releases are end-of-life and must be upgraded to a supported branch to receive the fix [1].

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 products

2
  • Drupal/Drupalinferred
    Range: >=8.0.0,<10.5.9 || >=10.6.0,<10.6.7 || >=11.0.0,<11.2.11 || >=11.3.0,<11.3.7
  • Range: >=8.0.0, <10.5.9 || >=10.6.0, <10.6.7 || >=11.0.0, <11.2.11 || >=11.3.0, <11.3.7

Patches

13
a0e6da01ecde

Drupal 10.5.9

https://github.com/drupal/drupalxjmApr 15, 2026Fixed in 10.5.9via llm-release-walk
4 files changed · +7 11
  • composer.lock+4 8 modified
    @@ -493,7 +493,7 @@
             },
             {
                 "name": "drupal/core",
    -            "version": "10.5.8",
    +            "version": "10.5.9",
                 "dist": {
                     "type": "path",
                     "url": "core",
    @@ -655,7 +655,7 @@
             },
             {
                 "name": "drupal/core-project-message",
    -            "version": "10.5.8",
    +            "version": "10.5.9",
                 "dist": {
                     "type": "path",
                     "url": "composer/Plugin/ProjectMessage",
    @@ -688,7 +688,7 @@
             },
             {
                 "name": "drupal/core-vendor-hardening",
    -            "version": "10.5.8",
    +            "version": "10.5.9",
                 "dist": {
                     "type": "path",
                     "url": "composer/Plugin/VendorHardening",
    @@ -10278,11 +10278,7 @@
         ],
         "aliases": [],
         "minimum-stability": "dev",
    -    "stability-flags": {
    -        "drupal/core": 20,
    -        "drupal/core-project-message": 20,
    -        "drupal/core-vendor-hardening": 20
    -    },
    +    "stability-flags": {},
         "prefer-stable": true,
         "prefer-lowest": false,
         "platform": {},
    
  • composer/Metapackage/CoreRecommended/composer.json+1 1 modified
    @@ -7,7 +7,7 @@
             "webflo/drupal-core-strict": "*"
         },
         "require": {
    -        "drupal/core": "10.5.8",
    +        "drupal/core": "10.5.9",
             "asm89/stack-cors": "~v2.3.0",
             "composer/semver": "~3.4.3",
             "doctrine/annotations": "~1.14.4",
    
  • composer/Metapackage/PinnedDevDependencies/composer.json+1 1 modified
    @@ -7,7 +7,7 @@
             "webflo/drupal-core-require-dev": "*"
         },
         "require": {
    -        "drupal/core": "10.5.8",
    +        "drupal/core": "10.5.9",
             "behat/mink": "v1.12.0",
             "behat/mink-browserkit-driver": "v2.2.0",
             "brick/math": "0.12.3",
    
  • core/lib/Drupal.php+1 1 modified
    @@ -75,7 +75,7 @@ class Drupal {
       /**
        * The current system version.
        */
    -  const VERSION = '10.5.8';
    +  const VERSION = '10.5.9';
     
       /**
        * Core API compatibility.
    
8f6bd25f6094

SA-CORE-2026-002 by hswww, menon, t-chen, benjifisher, cilefen, drumm, greggles, larowlan, longwave, mcdruid, ram4nd, xjm, poker10

https://github.com/drupal/drupalxjmApr 15, 2026Fixed in 10.5.9via llm-release-walk
2 files changed · +30 0
  • core/lib/Drupal/Core/Template/Attribute.php+3 0 modified
    @@ -330,6 +330,9 @@ public function __toString() {
         $return = '';
         /** @var \Drupal\Core\Template\AttributeValueBase $value */
         foreach ($this->storage as $value) {
    +      if (!$value instanceof AttributeValueBase) {
    +        throw new \RuntimeException(sprintf('Unexpected type for $value (%s).', get_debug_type($value)));
    +      }
           $rendered = $value->render();
           if ($rendered) {
             $return .= ' ' . $rendered;
    
  • core/modules/views/src/ViewExecutable.php+27 0 modified
    @@ -2557,6 +2557,33 @@ public function __sleep() {
        * Magic method implementation to unserialize the view executable.
        */
       public function __wakeup() {
    +    $reflection = new \ReflectionClass($this);
    +    $defaults = $reflection->getDefaultProperties();
    +    foreach ($reflection->getProperties() as $property) {
    +      $name = $property->getName();
    +      if ($name === 'serializationData') {
    +        $expected_keys = [
    +          'args',
    +          'current_display',
    +          'current_page',
    +          'dom_id',
    +          'executed',
    +          'exposed_data',
    +          'exposed_input',
    +          'exposed_raw_input',
    +          'storage',
    +        ];
    +        $actual_keys = array_keys($this->serializationData);
    +        sort($actual_keys);
    +        if ($actual_keys !== $expected_keys) {
    +          throw new \RuntimeException(sprintf('Unexpected keys in %s::$%s.', __CLASS__, $name));
    +        }
    +      }
    +      elseif (array_key_exists($name, $defaults) && $this->$name !== $defaults[$name]) {
    +        throw new \RuntimeException(sprintf('Deserialization of %s::$%s is not allowed.', __CLASS__, $name));
    +      }
    +    }
    +
         // There are cases, like in testing where we don't have a container
         // available.
         if (\Drupal::hasContainer() && !empty($this->serializationData)) {
    
695037059cb4

SA-CORE-2026-001 by murat_kekic, akalata, benjifisher, drumm, larowlan, mlhess, neclimdul, pandaski, poker10, ram4nd, xjm, prufloff, greggles

https://github.com/drupal/drupalxjmApr 15, 2026Fixed in 10.5.9via llm-release-walk
1 file changed · +15 0
  • core/lib/Drupal/Core/Ajax/OpenDialogCommand.php+15 0 modified
    @@ -3,6 +3,7 @@
     namespace Drupal\Core\Ajax;
     
     use Drupal\Component\Render\PlainTextOutput;
    +use Drupal\Component\Utility\Xss;
     
     /**
      * Defines an AJAX command to open certain content in a dialog.
    @@ -141,6 +142,20 @@ public function setDialogTitle($title) {
       public function render() {
         // For consistency ensure the modal option is set to TRUE or FALSE.
         $this->dialogOptions['modal'] = isset($this->dialogOptions['modal']) && $this->dialogOptions['modal'];
    +
    +    if (!empty($this->dialogOptions['buttons'])) {
    +      foreach ($this->dialogOptions['buttons'] as &$button) {
    +        // Only allow specific attributes to be defined for a button.
    +        $button = \array_intersect_key($button, \array_flip(['disabled', 'icons', 'label', 'text']));
    +        foreach ($button as &$value) {
    +          if (is_string($value)) {
    +            // Apply Xss::filter to button attribute values.
    +            $value = Xss::filter($value);
    +          }
    +        }
    +      }
    +    }
    +
         return [
           'command' => 'openDialog',
           'selector' => $this->selector,
    
439f02c7b7e1

SA-CORE-2026-002 by hswww, menon, t-chen, benjifisher, cilefen, drumm, greggles, larowlan, longwave, mcdruid, ram4nd, xjm, poker10

https://github.com/drupal/drupalxjmApr 15, 2026Fixed in 10.6.7via llm-release-walk
2 files changed · +30 0
  • core/lib/Drupal/Core/Template/Attribute.php+3 0 modified
    @@ -330,6 +330,9 @@ public function __toString() {
         $return = '';
         /** @var \Drupal\Core\Template\AttributeValueBase $value */
         foreach ($this->storage as $value) {
    +      if (!$value instanceof AttributeValueBase) {
    +        throw new \RuntimeException(sprintf('Unexpected type for $value (%s).', get_debug_type($value)));
    +      }
           $rendered = $value->render();
           if ($rendered) {
             $return .= ' ' . $rendered;
    
  • core/modules/views/src/ViewExecutable.php+27 0 modified
    @@ -2557,6 +2557,33 @@ public function __sleep() {
        * Magic method implementation to unserialize the view executable.
        */
       public function __wakeup() {
    +    $reflection = new \ReflectionClass($this);
    +    $defaults = $reflection->getDefaultProperties();
    +    foreach ($reflection->getProperties() as $property) {
    +      $name = $property->getName();
    +      if ($name === 'serializationData') {
    +        $expected_keys = [
    +          'args',
    +          'current_display',
    +          'current_page',
    +          'dom_id',
    +          'executed',
    +          'exposed_data',
    +          'exposed_input',
    +          'exposed_raw_input',
    +          'storage',
    +        ];
    +        $actual_keys = array_keys($this->serializationData);
    +        sort($actual_keys);
    +        if ($actual_keys !== $expected_keys) {
    +          throw new \RuntimeException(sprintf('Unexpected keys in %s::$%s.', __CLASS__, $name));
    +        }
    +      }
    +      elseif (array_key_exists($name, $defaults) && $this->$name !== $defaults[$name]) {
    +        throw new \RuntimeException(sprintf('Deserialization of %s::$%s is not allowed.', __CLASS__, $name));
    +      }
    +    }
    +
         // There are cases, like in testing where we don't have a container
         // available.
         if (\Drupal::hasContainer() && !empty($this->serializationData)) {
    
78c9c7b7091a

SA-CORE-2026-001 by murat_kekic, akalata, benjifisher, drumm, larowlan, mlhess, neclimdul, pandaski, poker10, ram4nd, xjm, prufloff, greggles

https://github.com/drupal/drupalxjmApr 15, 2026Fixed in 10.6.7via llm-release-walk
1 file changed · +15 0
  • core/lib/Drupal/Core/Ajax/OpenDialogCommand.php+15 0 modified
    @@ -3,6 +3,7 @@
     namespace Drupal\Core\Ajax;
     
     use Drupal\Component\Render\PlainTextOutput;
    +use Drupal\Component\Utility\Xss;
     
     /**
      * Defines an AJAX command to open certain content in a dialog.
    @@ -141,6 +142,20 @@ public function setDialogTitle($title) {
       public function render() {
         // For consistency ensure the modal option is set to TRUE or FALSE.
         $this->dialogOptions['modal'] = isset($this->dialogOptions['modal']) && $this->dialogOptions['modal'];
    +
    +    if (!empty($this->dialogOptions['buttons'])) {
    +      foreach ($this->dialogOptions['buttons'] as &$button) {
    +        // Only allow specific attributes to be defined for a button.
    +        $button = \array_intersect_key($button, \array_flip(['disabled', 'icons', 'label', 'text']));
    +        foreach ($button as &$value) {
    +          if (is_string($value)) {
    +            // Apply Xss::filter to button attribute values.
    +            $value = Xss::filter($value);
    +          }
    +        }
    +      }
    +    }
    +
         return [
           'command' => 'openDialog',
           'selector' => $this->selector,
    
a057ebf74005

SA-CORE-2026-002 by hswww, menon, t-chen, benjifisher, cilefen, drumm, greggles, larowlan, longwave, mcdruid, ram4nd, xjm, poker10

https://github.com/drupal/drupalxjmApr 15, 2026Fixed in 11.2.11via llm-release-walk
2 files changed · +30 0
  • core/lib/Drupal/Core/Template/Attribute.php+3 0 modified
    @@ -326,6 +326,9 @@ public function __toString() {
         $return = '';
         /** @var \Drupal\Core\Template\AttributeValueBase $value */
         foreach ($this->storage as $value) {
    +      if (!$value instanceof AttributeValueBase) {
    +        throw new \RuntimeException(sprintf('Unexpected type for $value (%s).', get_debug_type($value)));
    +      }
           $rendered = $value->render();
           if ($rendered) {
             $return .= ' ' . $rendered;
    
  • core/modules/views/src/ViewExecutable.php+27 0 modified
    @@ -2559,6 +2559,33 @@ public function __sleep(): array {
        * Magic method implementation to unserialize the view executable.
        */
       public function __wakeup(): void {
    +    $reflection = new \ReflectionClass($this);
    +    $defaults = $reflection->getDefaultProperties();
    +    foreach ($reflection->getProperties() as $property) {
    +      $name = $property->getName();
    +      if ($name === 'serializationData') {
    +        $expected_keys = [
    +          'args',
    +          'current_display',
    +          'current_page',
    +          'dom_id',
    +          'executed',
    +          'exposed_data',
    +          'exposed_input',
    +          'exposed_raw_input',
    +          'storage',
    +        ];
    +        $actual_keys = array_keys($this->serializationData);
    +        sort($actual_keys);
    +        if ($actual_keys !== $expected_keys) {
    +          throw new \RuntimeException(sprintf('Unexpected keys in %s::$%s.', __CLASS__, $name));
    +        }
    +      }
    +      elseif (array_key_exists($name, $defaults) && $this->$name !== $defaults[$name]) {
    +        throw new \RuntimeException(sprintf('Deserialization of %s::$%s is not allowed.', __CLASS__, $name));
    +      }
    +    }
    +
         // There are cases, like in testing where we don't have a container
         // available.
         if (\Drupal::hasContainer() && !empty($this->serializationData)) {
    
81b594dd1380

SA-CORE-2026-001 by murat_kekic, akalata, benjifisher, drumm, larowlan, mlhess, neclimdul, pandaski, poker10, ram4nd, xjm, prufloff, greggles

https://github.com/drupal/drupalxjmApr 15, 2026Fixed in 11.2.11via llm-release-walk
1 file changed · +15 0
  • core/lib/Drupal/Core/Ajax/OpenDialogCommand.php+15 0 modified
    @@ -3,6 +3,7 @@
     namespace Drupal\Core\Ajax;
     
     use Drupal\Component\Render\PlainTextOutput;
    +use Drupal\Component\Utility\Xss;
     
     /**
      * Defines an AJAX command to open certain content in a dialog.
    @@ -139,6 +140,20 @@ public function setDialogTitle($title) {
       public function render() {
         // For consistency ensure the modal option is set to TRUE or FALSE.
         $this->dialogOptions['modal'] = isset($this->dialogOptions['modal']) && $this->dialogOptions['modal'];
    +
    +    if (!empty($this->dialogOptions['buttons'])) {
    +      foreach ($this->dialogOptions['buttons'] as &$button) {
    +        // Only allow specific attributes to be defined for a button.
    +        $button = \array_intersect_key($button, \array_flip(['disabled', 'icons', 'label', 'text']));
    +        foreach ($button as &$value) {
    +          if (is_string($value)) {
    +            // Apply Xss::filter to button attribute values.
    +            $value = Xss::filter($value);
    +          }
    +        }
    +      }
    +    }
    +
         return [
           'command' => 'openDialog',
           'selector' => $this->selector,
    
0cf26c62bc48

SA-CORE-2026-002 by hswww, menon, t-chen, benjifisher, cilefen, drumm, greggles, larowlan, longwave, mcdruid, ram4nd, xjm, poker10

https://github.com/drupal/drupalxjmApr 15, 2026Fixed in 11.3.7via llm-release-walk
2 files changed · +30 0
  • core/lib/Drupal/Core/Template/Attribute.php+3 0 modified
    @@ -329,6 +329,9 @@ public function __toString() {
         $return = '';
         /** @var \Drupal\Core\Template\AttributeValueBase $value */
         foreach ($this->storage as $value) {
    +      if (!$value instanceof AttributeValueBase) {
    +        throw new \RuntimeException(sprintf('Unexpected type for $value (%s).', get_debug_type($value)));
    +      }
           $rendered = $value->render();
           if ($rendered) {
             $return .= ' ' . $rendered;
    
  • core/modules/views/src/ViewExecutable.php+27 0 modified
    @@ -2553,6 +2553,33 @@ public function __sleep(): array {
        * Magic method implementation to unserialize the view executable.
        */
       public function __wakeup(): void {
    +    $reflection = new \ReflectionClass($this);
    +    $defaults = $reflection->getDefaultProperties();
    +    foreach ($reflection->getProperties() as $property) {
    +      $name = $property->getName();
    +      if ($name === 'serializationData') {
    +        $expected_keys = [
    +          'args',
    +          'current_display',
    +          'current_page',
    +          'dom_id',
    +          'executed',
    +          'exposed_data',
    +          'exposed_input',
    +          'exposed_raw_input',
    +          'storage',
    +        ];
    +        $actual_keys = array_keys($this->serializationData);
    +        sort($actual_keys);
    +        if ($actual_keys !== $expected_keys) {
    +          throw new \RuntimeException(sprintf('Unexpected keys in %s::$%s.', __CLASS__, $name));
    +        }
    +      }
    +      elseif (array_key_exists($name, $defaults) && $this->$name !== $defaults[$name]) {
    +        throw new \RuntimeException(sprintf('Deserialization of %s::$%s is not allowed.', __CLASS__, $name));
    +      }
    +    }
    +
         // There are cases, like in testing where we don't have a container
         // available.
         if (\Drupal::hasContainer() && !empty($this->serializationData)) {
    
8232489c2ded

SA-CORE-2026-003 by cantina_security, dries, shirshaw64@gmail.com, larowlan, mcdruid, mingsong, damienmckenna, greggles, poker10, xjm

https://github.com/drupal/drupalxjmApr 15, 2026Fixed in 11.3.7via llm-release-walk
1 file changed · +1 1
  • core/modules/ckeditor5/src/Controller/EntityLinkSuggestionsController.php+1 1 modified
    @@ -233,7 +233,7 @@ protected function createSuggestion(EntityInterface $entity): array {
           'entity_type_id' => $entity->getEntityTypeId(),
           'entity_uuid' => $entity->uuid(),
           'group' => $this->computeGroup($entity),
    -      'label' => $entity->label(),
    +      'label' => Html::escape($entity->label() ?? ''),
           // Use the canonical URI as a valid fallback for the href. The
           // text_format filter will transform this to the final URL (e.g., alias).
           'path' => $entity->toUrl('canonical')->toString(),
    
8812078f73e6

SA-CORE-2026-001 by murat_kekic, akalata, benjifisher, drumm, larowlan, mlhess, neclimdul, pandaski, poker10, ram4nd, xjm, prufloff, greggles

https://github.com/drupal/drupalxjmApr 15, 2026Fixed in 11.3.7via llm-release-walk
1 file changed · +15 0
  • core/lib/Drupal/Core/Ajax/OpenDialogCommand.php+15 0 modified
    @@ -3,6 +3,7 @@
     namespace Drupal\Core\Ajax;
     
     use Drupal\Component\Render\PlainTextOutput;
    +use Drupal\Component\Utility\Xss;
     
     /**
      * Defines an AJAX command to open certain content in a dialog.
    @@ -146,6 +147,20 @@ public function setDialogTitle($title) {
       public function render() {
         // For consistency ensure the modal option is set to TRUE or FALSE.
         $this->dialogOptions['modal'] = isset($this->dialogOptions['modal']) && $this->dialogOptions['modal'];
    +
    +    if (!empty($this->dialogOptions['buttons'])) {
    +      foreach ($this->dialogOptions['buttons'] as &$button) {
    +        // Only allow specific attributes to be defined for a button.
    +        $button = \array_intersect_key($button, \array_flip(['disabled', 'icons', 'label', 'text']));
    +        foreach ($button as &$value) {
    +          if (is_string($value)) {
    +            // Apply Xss::filter to button attribute values.
    +            $value = Xss::filter($value);
    +          }
    +        }
    +      }
    +    }
    +
         return [
           'command' => 'openDialog',
           'selector' => $this->selector,
    
02c7b7e1e83c
https://github.com/drupal/drupalFixed in 10.6.7via llm-release-walk
a057ebfded32
https://github.com/drupal/drupalFixed in 11.2.11via llm-release-walk
1c85a9e06fa9

Issue #3584774 by longwave, xjm: Update to Composer 2.9.7

https://github.com/drupal/drupalxjmApr 15, 2026Fixed in 10.6.7via llm-release-walk
2 files changed · +7 7
  • composer.lock+6 6 modified
    @@ -4827,16 +4827,16 @@
             },
             {
                 "name": "composer/composer",
    -            "version": "2.9.3",
    +            "version": "2.9.7",
                 "source": {
                     "type": "git",
                     "url": "https://github.com/composer/composer.git",
    -                "reference": "fb3bee27676fd852a8a11ebbb1de19b4dada5aba"
    +                "reference": "82a2fbd1372a98d7915cfb092acf05207d9b4113"
                 },
                 "dist": {
                     "type": "zip",
    -                "url": "https://api.github.com/repos/composer/composer/zipball/fb3bee27676fd852a8a11ebbb1de19b4dada5aba",
    -                "reference": "fb3bee27676fd852a8a11ebbb1de19b4dada5aba",
    +                "url": "https://api.github.com/repos/composer/composer/zipball/82a2fbd1372a98d7915cfb092acf05207d9b4113",
    +                "reference": "82a2fbd1372a98d7915cfb092acf05207d9b4113",
                     "shasum": ""
                 },
                 "require": {
    @@ -4924,7 +4924,7 @@
                     "irc": "ircs://irc.libera.chat:6697/composer",
                     "issues": "https://github.com/composer/composer/issues",
                     "security": "https://github.com/composer/composer/security/policy",
    -                "source": "https://github.com/composer/composer/tree/2.9.3"
    +                "source": "https://github.com/composer/composer/tree/2.9.7"
                 },
                 "funding": [
                     {
    @@ -4936,7 +4936,7 @@
                         "type": "github"
                     }
                 ],
    -            "time": "2025-12-30T12:40:17+00:00"
    +            "time": "2026-04-14T11:31:52+00:00"
             },
             {
                 "name": "composer/metadata-minifier",
    
  • composer/Metapackage/PinnedDevDependencies/composer.json+1 1 modified
    @@ -14,7 +14,7 @@
             "colinodell/psr-testlogger": "v1.3.1",
             "composer/ca-bundle": "1.5.9",
             "composer/class-map-generator": "1.7.0",
    -        "composer/composer": "2.9.3",
    +        "composer/composer": "2.9.7",
             "composer/metadata-minifier": "1.0.0",
             "composer/pcre": "3.3.2",
             "composer/spdx-licenses": "1.5.9",
    

Vulnerability mechanics

Root cause

"Unrestricted deserialization in ViewExecutable::__wakeup() allows an attacker with high privileges to inject arbitrary object properties, and unsanitized button attributes in OpenDialogCommand::render() allow XSS-based object manipulation."

Attack vector

An attacker with high privileges (e.g., a content administrator) can craft a malicious serialized ViewExecutable object where the `serializationData` array contains unexpected keys or where other properties differ from their defaults. When the object is unserialized, the `__wakeup()` method (prior to the patch) did not validate the structure, enabling arbitrary property injection [CWE-915]. Additionally, in the AJAX OpenDialogCommand, the `buttons` array in `dialogOptions` was not sanitized; an attacker could inject arbitrary HTML attributes or XSS payloads through button values, which are rendered unsafely [CWE-915]. Both vectors require an authenticated user with the ability to create or manipulate serialized data or AJAX command configurations.

Affected code

The primary vulnerable code is in `core/modules/views/src/ViewExecutable.php` in the `__wakeup()` method, which previously performed no validation on deserialized properties. The second vulnerable location is `core/lib/Drupal/Core/Ajax/OpenDialogCommand.php` in the `render()` method, where the `buttons` array within `dialogOptions` was rendered without sanitization. A third, less severe issue is in `core/modules/ckeditor5/src/Controller/EntityLinkSuggestionsController.php` where the entity label was not escaped.

What the fix does

The ViewExecutable patch [patch_id=895524, patch_id=897830] adds a `__wakeup()` validation loop that uses reflection to compare every property against its default value; any property that differs from the default (except `serializationData`) causes a `RuntimeException`. For `serializationData`, the patch enforces an exact set of allowed keys, rejecting any unexpected keys. The OpenDialogCommand patch [patch_id=895523, patch_id=897832] sanitizes button attributes by whitelisting only `disabled`, `icons`, `label`, and `text`, and applies `Xss::filter()` to all string values. The EntityLinkSuggestionsController patch [patch_id=897831] escapes the entity label with `Html::escape()` to prevent XSS in CKEditor 5 link suggestions.

Preconditions

  • authAttacker must have a Drupal account with high privileges (e.g., administrator or content administrator) capable of creating or manipulating serialized ViewExecutable objects or AJAX command configurations.
  • inputFor the deserialization vector, the attacker must supply a crafted serialized object. For the AJAX vector, the attacker must control the `dialogOptions['buttons']` array.

Generated on May 20, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

1

News mentions

1