diff --git a/vendor/magento/module-catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php b/vendor/magento/module-catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php
index 6cb9164f00a37..a1ac4ebb5a4bb 100644
--- a/vendor/magento/module-catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php
+++ b/vendor/magento/module-catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php
@@ -164,37 +164,34 @@ public function execute()
      */
     private function sanitizeProductAttributes($attributesData)
     {
+        $normalised = [];
         foreach ($attributesData as $attributeCode => $value) {
             if ($attributeCode === ProductAttributeInterface::CODE_HAS_WEIGHT) {
+                $normalised[$attributeCode] = $value;
                 continue;
             }
-
             $attribute = $this->eavConfig->getAttribute(\Magento\Catalog\Model\Product::ENTITY, $attributeCode);
-
             if (!$attribute->getAttributeId()) {
-                unset($attributesData[$attributeCode]);
                 continue;
             }
+            $canonicalCode = $attribute->getAttributeCode();
 
             if ($attribute->getBackendType() === 'datetime') {
-                $attributesData[$attributeCode] = $this->filterDate(
+                $value = $this->filterDate(
                     $value,
                     $attribute->getFrontendInput() === 'datetime'
                 );
             } elseif ($attribute->getFrontendInput() === 'multiselect') {
-                // Check if 'Change' checkbox has been checked by admin for this attribute
-                $isChanged = (bool)$this->getRequest()->getPost('toggle_' . $attributeCode);
+                $isChanged = (bool)$this->getRequest()->getPost('toggle_' . $canonicalCode);
                 if (!$isChanged) {
-                    unset($attributesData[$attributeCode]);
                     continue;
                 }
-                if (is_array($value)) {
-                    $value = implode(',', $value);
-                }
-                $attributesData[$attributeCode] = $value;
+                $value = is_array($value) ? implode(',', $value) : $value;
             }
+
+            $normalised[$canonicalCode] = $value;
         }
-        return $attributesData;
+        return $normalised;
     }
 
     /**
diff --git a/vendor/magento/module-customer/Model/ForgotPasswordToken/GetCustomerByToken.php b/vendor/magento/module-customer/Model/ForgotPasswordToken/GetCustomerByToken.php
index 994bf7b40deb5..e91bab987e906 100644
--- a/vendor/magento/module-customer/Model/ForgotPasswordToken/GetCustomerByToken.php
+++ b/vendor/magento/module-customer/Model/ForgotPasswordToken/GetCustomerByToken.php
@@ -9,15 +9,19 @@
 
 use Magento\Customer\Api\CustomerRepositoryInterface;
 use Magento\Customer\Api\Data\CustomerInterface;
+use Magento\Customer\Model\ResourceModel\Customer\CollectionFactory;
 use Magento\Framework\Api\SearchCriteriaBuilder;
+use Magento\Framework\App\ObjectManager;
 use Magento\Framework\Exception\NoSuchEntityException;
 use Magento\Framework\Exception\State\ExpiredException;
 use Magento\Framework\Phrase;
 
 /**
  * Get Customer By reset password token
+ *
  * @SuppressWarnings(PHPMD.LongVariable)
  * @deprecated Rp Tokens cannot be looked up directly
+ * @see not used anymore
  */
 class GetCustomerByToken
 {
@@ -32,17 +36,24 @@ class GetCustomerByToken
     private $searchCriteriaBuilder;
 
     /**
-     * ForgotPassword constructor.
-     *
+     * @var CollectionFactory
+     */
+    private $collectionFactory;
+
+    /**
      * @param \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository
      * @param \Magento\Framework\Api\SearchCriteriaBuilder $searchCriteriaBuilder
+     * @param CollectionFactory|null $collectionFactory
      */
     public function __construct(
         CustomerRepositoryInterface $customerRepository,
-        SearchCriteriaBuilder $searchCriteriaBuilder
+        SearchCriteriaBuilder $searchCriteriaBuilder,
+        ?CollectionFactory $collectionFactory = null
     ) {
         $this->customerRepository = $customerRepository;
         $this->searchCriteriaBuilder = $searchCriteriaBuilder;
+        $this->collectionFactory = $collectionFactory
+            ?? ObjectManager::getInstance()->get(CollectionFactory::class);
     }
 
     /**
@@ -53,38 +64,27 @@ public function __construct(
      * @return \Magento\Customer\Api\Data\CustomerInterface
      * @throws ExpiredException
      * @throws NoSuchEntityException
-     * @throws \Magento\Framework\Exception\LocalizedException
      */
     public function execute(string $resetPasswordToken): CustomerInterface
     {
-        $this->searchCriteriaBuilder->addFilter(
-            'rp_token',
-            $resetPasswordToken
-        );
-        $this->searchCriteriaBuilder->setPageSize(1);
-        $found = $this->customerRepository->getList(
-            $this->searchCriteriaBuilder->create()
-        );
+        $collection = $this->collectionFactory->create();
+        $collection->addAttributeToFilter('rp_token', $resetPasswordToken);
+        $size = $collection->getSize();
 
-        if ($found->getTotalCount() > 1) {
-            //Failed to generated unique RP token
+        if ($size > 1) {
             throw new ExpiredException(
                 new Phrase('Reset password token expired.')
             );
         }
-        if ($found->getTotalCount() === 0) {
-            //Customer with such token not found.
+        if ($size === 0) {
             throw new NoSuchEntityException(
                 new Phrase(
                     'No such entity with rp_token = %value',
-                    [
-                        'value' => $resetPasswordToken
-                    ]
+                    ['value' => $resetPasswordToken]
                 )
             );
         }
 
-        //Unique customer found.
-        return $found->getItems()[0];
+        return $collection->getFirstItem();
     }
 }
diff --git a/vendor/magento/module-customer/Model/Plugin/CustomerRepository/RestrictedFieldsPlugin.php b/vendor/magento/module-customer/Model/Plugin/CustomerRepository/RestrictedFieldsPlugin.php
new file mode 100644
index 0000000000000..764dc50b76add
--- /dev/null
+++ b/vendor/magento/module-customer/Model/Plugin/CustomerRepository/RestrictedFieldsPlugin.php
@@ -0,0 +1,69 @@
+<?php
+/**
+ * Copyright 2026 Adobe
+ * All Rights Reserved.
+ */
+declare(strict_types=1);
+
+namespace Magento\Customer\Model\Plugin\CustomerRepository;
+
+use Magento\Customer\Api\CustomerRepositoryInterface;
+use Magento\Framework\Api\SearchCriteriaInterface;
+use Magento\Framework\Exception\InputException;
+
+/**
+ * Forbidden customer fields
+ */
+class RestrictedFieldsPlugin
+{
+    /**
+     * @param array $restrictedFields
+     */
+    public function __construct(
+        private array $restrictedFields = []
+    ) {
+    }
+
+    /**
+     * Check forbidden customer fields
+     *
+     * @param CustomerRepositoryInterface $subject
+     * @param SearchCriteriaInterface $searchCriteria
+     * @return array
+     * @throws InputException
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     */
+    public function beforeGetList(
+        CustomerRepositoryInterface $subject,
+        SearchCriteriaInterface $searchCriteria
+    ): array {
+        foreach ($searchCriteria->getFilterGroups() as $filterGroup) {
+            foreach ($filterGroup->getFilters() as $filter) {
+                if ($this->isRestricted($filter->getField())) {
+                    throw new InputException(
+                        __('The search criteria filter field is invalid.')
+                    );
+                }
+            }
+        }
+        foreach ($searchCriteria->getSortOrders() ?? [] as $sortOrder) {
+            if ($this->isRestricted($sortOrder->getField())) {
+                throw new InputException(
+                    __('The search criteria sort field is invalid.')
+                );
+            }
+        }
+        return [$searchCriteria];
+    }
+
+    /**
+     * Check if the given field name matches a restricted field
+     *
+     * @param string|null $field
+     * @return bool
+     */
+    private function isRestricted(?string $field): bool
+    {
+        return $field !== null && in_array(strtolower($field), $this->restrictedFields, true);
+    }
+}
diff --git a/vendor/magento/module-customer/etc/di.xml b/vendor/magento/module-customer/etc/di.xml
index 00a8597d8c364..3570ffe831f55 100644
--- a/vendor/magento/module-customer/etc/di.xml
+++ b/vendor/magento/module-customer/etc/di.xml
@@ -85,6 +85,15 @@
             <argument name="validator" xsi:type="object">SessionValidator</argument>
         </arguments>
     </type>
+    <type name="Magento\Customer\Model\Plugin\CustomerRepository\RestrictedFieldsPlugin">
+        <arguments>
+            <argument name="restrictedFields" xsi:type="array">
+                <item name="password_hash" xsi:type="string">password_hash</item>
+                <item name="rp_token" xsi:type="string">rp_token</item>
+                <item name="confirmation" xsi:type="string">confirmation</item>
+            </argument>
+        </arguments>
+    </type>
     <type name="Magento\Customer\Helper\Address">
         <arguments>
             <argument name="addressConfig" xsi:type="object">Magento\Customer\Model\Address\Config\Proxy</argument>
diff --git a/vendor/magento/module-customer/etc/graphql/di.xml b/vendor/magento/module-customer/etc/graphql/di.xml
index e5ed4078b4a04..89095704f51e8 100644
--- a/vendor/magento/module-customer/etc/graphql/di.xml
+++ b/vendor/magento/module-customer/etc/graphql/di.xml
@@ -16,4 +16,7 @@
             </argument>
         </arguments>
     </type>
+    <type name="Magento\Customer\Api\CustomerRepositoryInterface">
+        <plugin name="restrictedSearchFields" type="Magento\Customer\Model\Plugin\CustomerRepository\RestrictedFieldsPlugin" sortOrder="1"/>
+    </type>
 </config>
diff --git a/vendor/magento/module-customer/etc/webapi_rest/di.xml b/vendor/magento/module-customer/etc/webapi_rest/di.xml
index 18627b68320ed..f649cb6732b94 100644
--- a/vendor/magento/module-customer/etc/webapi_rest/di.xml
+++ b/vendor/magento/module-customer/etc/webapi_rest/di.xml
@@ -21,6 +21,7 @@
     </type>
     <type name="Magento\Customer\Api\CustomerRepositoryInterface">
         <plugin name="updateCustomerByIdFromRequest" type="Magento\Customer\Model\Plugin\UpdateCustomer" />
+        <plugin name="restrictedSearchFields" type="Magento\Customer\Model\Plugin\CustomerRepository\RestrictedFieldsPlugin" sortOrder="1"/>
     </type>
     <type name="Magento\Customer\Model\Customer\AuthorizationComposite">
         <arguments>
diff --git a/vendor/magento/module-customer/etc/webapi_soap/di.xml b/vendor/magento/module-customer/etc/webapi_soap/di.xml
index cb0b1ce58a594..2f9c0cc786995 100644
--- a/vendor/magento/module-customer/etc/webapi_soap/di.xml
+++ b/vendor/magento/module-customer/etc/webapi_soap/di.xml
@@ -18,6 +18,9 @@
             </argument>
         </arguments>
     </type>
+    <type name="Magento\Customer\Api\CustomerRepositoryInterface">
+        <plugin name="restrictedSearchFields" type="Magento\Customer\Model\Plugin\CustomerRepository\RestrictedFieldsPlugin" sortOrder="1"/>
+    </type>
     <preference for="Magento\Customer\Api\AccountManagementInterface"
                 type="Magento\Customer\Model\AccountManagementApi" />
 </config>
diff --git a/vendor/magento/module-customer/i18n/en_US.csv b/vendor/magento/module-customer/i18n/en_US.csv
index 308cb9b05cb0e..b8c5e2a32bb54 100644
--- a/vendor/magento/module-customer/i18n/en_US.csv
+++ b/vendor/magento/module-customer/i18n/en_US.csv
@@ -551,4 +551,6 @@ T,T
 F,F
 VAT,VAT
 "Something went wrong while logging out customer.","Something went wrong while logging out customer."
+"The search criteria filter field is invalid.","The search criteria filter field is invalid."
+"The search criteria sort field is invalid.","The search criteria sort field is invalid."
 "Address not found.","Address not found."
diff --git a/vendor/magento/module-email/Block/Adminhtml/Template/Preview.php b/vendor/magento/module-email/Block/Adminhtml/Template/Preview.php
index d2fdcb8e315fe..f234ce8312297 100644
--- a/vendor/magento/module-email/Block/Adminhtml/Template/Preview.php
+++ b/vendor/magento/module-email/Block/Adminhtml/Template/Preview.php
@@ -7,6 +7,10 @@
 
 namespace Magento\Email\Block\Adminhtml\Template;
 
+use Magento\Email\Model\Template\Validator as TemplateValidator;
+use Magento\Framework\Validation\ValidationException;
+use Magento\Framework\App\ObjectManager;
+
 /**
  * Email template preview block.
  *
@@ -30,20 +34,29 @@ class Preview extends \Magento\Backend\Block\Widget
      */
     protected $profilerName = 'email_template_proccessing';
 
+    /**
+     * @var TemplateValidator
+     */
+    private $templateValidator;
+
     /**
      * @param \Magento\Backend\Block\Template\Context $context
      * @param \Magento\Framework\Filter\Input\MaliciousCode $maliciousCode
      * @param \Magento\Email\Model\TemplateFactory $emailFactory
      * @param array $data
+     * @param TemplateValidator $templateValidator
      */
     public function __construct(
         \Magento\Backend\Block\Template\Context $context,
         \Magento\Framework\Filter\Input\MaliciousCode $maliciousCode,
         \Magento\Email\Model\TemplateFactory $emailFactory,
-        array $data = []
+        array $data = [],
+        ?TemplateValidator $templateValidator = null
     ) {
         $this->_maliciousCode = $maliciousCode;
         $this->_emailFactory = $emailFactory;
+        $this->templateValidator = $templateValidator ?:
+            ObjectManager::getInstance()->get(TemplateValidator::class);
         parent::__construct($context, $data);
     }
 
@@ -64,10 +77,23 @@ protected function _toHtml()
         if ($id = (int)$request->getParam('id')) {
             $template->load($id);
         } else {
+            $rawText = $request->getParam('text');
+            $rawText = is_string($rawText) ? $rawText : '';
+            $rawStyles = $request->getParam('styles');
+            $rawStyles = is_string($rawStyles) ? $rawStyles : '';
+            try {
+                $this->templateValidator->validate($rawText);
+                if ($rawStyles !== '') {
+                    $this->templateValidator->validate($rawStyles);
+                }
+            } catch (ValidationException $e) {
+                throw new \Magento\Framework\Exception\LocalizedException(
+                    __('Template content validation failed. Please review template syntax.')
+                );
+            }
             $template->setTemplateType($request->getParam('type'));
-            $template->setTemplateText($this->_maliciousCode->filter($request->getParam('text')));
-            $template->setTemplateStyles($request->getParam('styles'));
-
+            $template->setTemplateText($this->_maliciousCode->filter($rawText));
+            $template->setTemplateStyles($rawStyles);
         }
 
         \Magento\Framework\Profiler::start($this->profilerName);
diff --git a/vendor/magento/module-email/Model/AbstractTemplate.php b/vendor/magento/module-email/Model/AbstractTemplate.php
index 7c5c973c60555..7c48928df3c9e 100644
--- a/vendor/magento/module-email/Model/AbstractTemplate.php
+++ b/vendor/magento/module-email/Model/AbstractTemplate.php
@@ -16,6 +16,8 @@
 use Magento\Store\Model\Store;
 use Magento\MediaStorage\Helper\File\Storage\Database;
 use Magento\Framework\Validation\ValidationException;
+use Magento\Email\Model\Template\Validator as TemplateValidator;
+use Magento\Framework\App\ObjectManager;
 
 /**
  * Template model class.
@@ -168,6 +170,11 @@ abstract class AbstractTemplate extends AbstractModel implements TemplateTypesIn
      */
     private $fileStorageDatabase;
 
+    /**
+     * @var TemplateValidator
+     */
+    private $templateValidator;
+
     /**
      * @param \Magento\Framework\Model\Context $context
      * @param \Magento\Framework\View\DesignInterface $design
@@ -183,6 +190,7 @@ abstract class AbstractTemplate extends AbstractModel implements TemplateTypesIn
      * @param \Magento\Framework\UrlInterface $urlModel
      * @param array $data
      * @param Database $fileStorageDatabase
+     * @param TemplateValidator|null $templateValidator
      *
      * @SuppressWarnings(PHPMD.ExcessiveParameterList)
      */
@@ -200,7 +208,8 @@ public function __construct(
         \Magento\Framework\Filter\FilterManager $filterManager,
         \Magento\Framework\UrlInterface $urlModel,
         array $data = [],
-        ?Database $fileStorageDatabase = null
+        ?Database $fileStorageDatabase = null,
+        ?TemplateValidator $templateValidator = null
     ) {
         $this->design = $design;
         $this->area = isset($data['area']) ? $data['area'] : null;
@@ -216,6 +225,8 @@ public function __construct(
         $this->urlModel = $urlModel;
         $this->fileStorageDatabase = $fileStorageDatabase ?:
             \Magento\Framework\App\ObjectManager::getInstance()->get(Database::class);
+        $this->templateValidator = $templateValidator ?:
+            ObjectManager::getInstance()->get(TemplateValidator::class);
         parent::__construct($context, $registry, null, null, $data);
     }
 
@@ -772,7 +783,7 @@ public function beforeSave()
         $templateText = $this->getTemplateText();
         if ($templateText) {
             try {
-                $this->validateTemplateText($templateText);
+                $this->templateValidator->validate($templateText);
             } catch (ValidationException $exception) {
                 throw new ValidationException(
                     __('Content field contains restricted HTML elements. %1', $exception->getMessage()),
@@ -780,27 +791,18 @@ public function beforeSave()
                 );
             }
         }
+        $templateStyles = $this->getTemplateStyles();
+        if ($templateStyles) {
+            try {
+                $this->templateValidator->validate($templateStyles);
+            } catch (ValidationException $exception) {
+                throw new ValidationException(
+                    __('Styles field validation failed. %1', $exception->getMessage()),
+                    $exception
+                );
+            }
+        }
         parent::beforeSave();
         return $this;
     }
-
-    /**
-     * Validate template text
-     *
-     * @param string $content
-     * @throws ValidationException
-     */
-    private function validateTemplateText(string $content): void
-    {
-        $expression = '/\b(?:src|href|action)\s*=\s*([\'"])(?:[^\'"]*?)https?:\/\/[^\/\s]+\/' .
-            '(?:[^\'"]*?)\{\{\s*[^}]+\s*\}\}(?:[^\'"]*?)\1/iu';
-
-        if (preg_match($expression, $content)) {
-            throw new ValidationException(
-                __(
-                    'Template directives are not allowed in external URLs.'
-                )
-            );
-        }
-    }
 }
diff --git a/vendor/magento/module-email/Model/Template/Filter.php b/vendor/magento/module-email/Model/Template/Filter.php
index f86deacad3b4e..70248cec31d6e 100644
--- a/vendor/magento/module-email/Model/Template/Filter.php
+++ b/vendor/magento/module-email/Model/Template/Filter.php
@@ -980,7 +980,14 @@ public function inlinecssDirective($construction)
         // If this template is a child of another template, skip processing so that the parent template will process
         // this directive. This is important as CSS inlining must operate on the entire HTML document.
         if ($this->isChildTemplate()) {
-            return $construction[0];
+            try {
+                return $this->deferToParent($construction[0]);
+            } catch (\Exception $e) {
+                throw new MailException(
+                    __('Unable to defer the "inlinecss" directive for later processing.'),
+                    $e
+                );
+            }
         }
 
         $params = $this->getParameters($construction[2]);
diff --git a/vendor/magento/module-email/Model/Template/Validator.php b/vendor/magento/module-email/Model/Template/Validator.php
new file mode 100644
index 0000000000000..3173d7a57d8ab
--- /dev/null
+++ b/vendor/magento/module-email/Model/Template/Validator.php
@@ -0,0 +1,96 @@
+<?php
+/**
+ * Copyright 2026 Adobe
+ * All Rights Reserved.
+ */
+declare(strict_types=1);
+
+namespace Magento\Email\Model\Template;
+
+use Magento\Framework\Validation\ValidationException;
+
+/**
+ * Validates email template content
+ */
+class Validator
+{
+    /**
+     * Regular expressions for HTML attribute value containing a template directive
+     *
+     * @var string
+     */
+    private const ATTRIBUTE_VALUE_EXPRESSION =
+        '/\b[a-zA-Z_:][-a-zA-Z0-9_:.]*\s*=\s*' .
+        '(?|"([^"]*\{\{[^}]*\}\}[^"]*)"' .
+        '|\'([^\']*\{\{[^}]*\}\}[^\']*)\'' .
+        '|((?:\{\{[^}]*\}\}|[^\s"\'<>`])+))/iu';
+
+    /**
+     * Regular expressions for CSS url argument containing a template directive
+     *
+     * @var string
+     */
+    private const CSS_URL_VALUE_EXPRESSION =
+        '/\burl\(\s*(?|"([^"]*\{\{[^}]*\}\}[^"]*)"' .
+        '|\'([^\']*\{\{[^}]*\}\}[^\']*)\'' .
+        '|((?:\{\{[^}]*\}\}|[^\s"\')])+))\s*\)/iu';
+
+    /**
+     * Validate template content
+     *
+     * @param string $content
+     *
+     * @throws ValidationException
+     */
+    public function validate(string $content): void
+    {
+        foreach ([self::ATTRIBUTE_VALUE_EXPRESSION, self::CSS_URL_VALUE_EXPRESSION] as $expression) {
+            $matchCount = preg_match_all($expression, $content, $matches);
+            if ($matchCount === false) {
+                throw new ValidationException(
+                    __('Template content validation failed. Please review template syntax.')
+                );
+            }
+            foreach ($matches[1] as $value) {
+                if ($this->isForbiddenUrlValue($value)) {
+                    throw new ValidationException(
+                        __('Template content validation failed. Please review template syntax.')
+                    );
+                }
+            }
+        }
+    }
+
+    /**
+     * Check whether a URL-bearing value resolves to an absolute external target
+     *
+     * @param string $value
+     * @return bool
+     */
+    private function isForbiddenUrlValue(string $value): bool
+    {
+        $normalized = $this->normalizeUrlValue($value);
+        if (strpos($normalized, '{{') === false) {
+            return false;
+        }
+
+        return preg_match('#^https?:/#i', $normalized) === 1
+            || strncmp($normalized, '//', 2) === 0;
+    }
+
+    /**
+     * Normalize a URL-bearing value
+     *
+     * @param string $value
+     * @return string
+     */
+    private function normalizeUrlValue(string $value): string
+    {
+        // phpcs:ignore Magento2.Functions.DiscouragedFunction
+        $value = html_entity_decode($value, ENT_QUOTES | ENT_HTML5, 'UTF-8');
+        $value = str_ireplace(['%2f', '%5c'], ['/', '\\'], $value);
+        $value = str_replace('\\', '/', $value);
+
+        return preg_replace('/[\x00-\x20]+/', '', $value) ?? $value;
+    }
+}
diff --git a/vendor/magento/module-persistent/Model/Plugin/AllowPersistentCustomerGuestCart.php b/vendor/magento/module-persistent/Model/Plugin/AllowPersistentCustomerGuestCart.php
new file mode 100644
index 0000000000000..5ba173320b2ad
--- /dev/null
+++ b/vendor/magento/module-persistent/Model/Plugin/AllowPersistentCustomerGuestCart.php
@@ -0,0 +1,58 @@
+<?php
+/**
+ * Copyright 2026 Adobe
+ * All Rights Reserved.
+ */
+declare(strict_types=1);
+
+namespace Magento\Persistent\Model\Plugin;
+
+use Magento\Customer\Model\Session as CustomerSession;
+use Magento\Persistent\Helper\Data as PersistentHelper;
+use Magento\Persistent\Helper\Session as PersistentSession;
+use Magento\Quote\Model\GuestCart\GetGuestCart;
+
+/**
+ * @SuppressWarnings(PHPMD.CookieAndSessionMisuse)
+ */
+class AllowPersistentCustomerGuestCart
+{
+    /**
+     * @param CustomerSession $customerSession
+     * @param PersistentSession $persistentSession
+     * @param PersistentHelper $persistentHelper
+     */
+    public function __construct(
+        private readonly CustomerSession $customerSession,
+        private readonly PersistentSession $persistentSession,
+        private readonly PersistentHelper $persistentHelper
+    ) {
+    }
+
+    /**
+     * Allow the guest checkout endpoints to read a cart that still belongs to a persisted customer.
+     *
+     * @param GetGuestCart $subject
+     * @param callable $proceed
+     * @param int $customerId
+     * @param string $maskedCartId
+     * @return void
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     */
+    public function aroundCheckIsGuestCart(
+        GetGuestCart $subject,
+        callable $proceed,
+        int $customerId,
+        string $maskedCartId
+    ): void {
+        $isOwnedByPersistedCustomer = $customerId !== 0
+            && $this->persistentHelper->isShoppingCartPersist()
+            && !$this->customerSession->isLoggedIn()
+            && $this->persistentSession->isPersistent()
+            && (int) $this->persistentSession->getSession()->getCustomerId() === $customerId;
+
+        if (!$isOwnedByPersistedCustomer) {
+            $proceed($customerId, $maskedCartId);
+        }
+    }
+}
diff --git a/vendor/magento/module-persistent/etc/di.xml b/vendor/magento/module-persistent/etc/di.xml
index bec28c0bbbcfd..e233ac9a4adb5 100644
--- a/vendor/magento/module-persistent/etc/di.xml
+++ b/vendor/magento/module-persistent/etc/di.xml
@@ -25,4 +25,7 @@
     <type name="Magento\Quote\Model\QuoteManagement">
         <plugin name="persistent_convert_customer_cart_to_guest_cart" type="Magento\Persistent\Model\Plugin\ConvertCustomerCartToGuest" />
     </type>
+    <type name="Magento\Quote\Model\GuestCart\GetGuestCart">
+        <plugin name="persistent_allow_guest_cart_access" type="Magento\Persistent\Model\Plugin\AllowPersistentCustomerGuestCart" />
+    </type>
 </config>
diff --git a/vendor/magento/framework/Escaper.php b/vendor/magento/framework/Escaper.php
index e873a9a8a933d..3c5db93968776 100644
--- a/vendor/magento/framework/Escaper.php
+++ b/vendor/magento/framework/Escaper.php
@@ -387,8 +387,9 @@ public function escapeJsQuote($data, $quote = '\'')
      */
     public function escapeXssInUrl($data)
     {
-        $data = $this->decodeHtmlEntitiesToFixedPoint((string)$data);
+        $data = (string)$data;
         $this->getTranslateInline()->processResponseBody($data);
+        $data = $this->decodeHtmlEntitiesToFixedPoint($data);
 
         return htmlspecialchars(
             $this->escapeScriptIdentifiers($data),
diff --git a/vendor/magento/framework/Filter/Template.php b/vendor/magento/framework/Filter/Template.php
index ccf67df9b5f79..d82176310d541 100644
--- a/vendor/magento/framework/Filter/Template.php
+++ b/vendor/magento/framework/Filter/Template.php
@@ -18,6 +18,7 @@
 use Magento\Framework\Stdlib\StringUtils;
 use Magento\Framework\Filter\Template\SignatureProvider;
 use Magento\Framework\Filter\Template\FilteringDepthMeter;
+use Magento\Framework\Filter\Template\DeferredDirectiveRegistry;
 
 /**
  * Template constructions filter
@@ -102,6 +103,8 @@ class Template implements FilterInterface
 
     /**
      * @var SignatureProvider|null
+     * @deprecated Signature-based deferral was superseded by out-of-band deferral via DeferredDirectiveRegistry.
+     * @see DeferredDirectiveRegistry
      */
     private $signatureProvider;
 
@@ -110,6 +113,11 @@ class Template implements FilterInterface
      */
     private $filteringDepthMeter;
 
+    /**
+     * @var DeferredDirectiveRegistry|null
+     */
+    private $deferredDirectiveRegistry;
+
     /**
      * @param StringUtils $string
      * @param array $variables
@@ -117,6 +125,7 @@ class Template implements FilterInterface
      * @param VariableResolverInterface|null $variableResolver
      * @param SignatureProvider|null $signatureProvider
      * @param FilteringDepthMeter|null $filteringDepthMeter
+     * @param DeferredDirectiveRegistry|null $deferredDirectiveRegistry
      */
     public function __construct(
         StringUtils $string,
@@ -124,7 +133,8 @@ public function __construct(
         $directiveProcessors = [],
         ?VariableResolverInterface $variableResolver = null,
         ?SignatureProvider $signatureProvider = null,
-        ?FilteringDepthMeter $filteringDepthMeter = null
+        ?FilteringDepthMeter $filteringDepthMeter = null,
+        ?DeferredDirectiveRegistry $deferredDirectiveRegistry = null
     ) {
         $this->string = $string;
         $this->setVariables($variables);
@@ -138,6 +148,9 @@ public function __construct(
         $this->filteringDepthMeter = $filteringDepthMeter ?? ObjectManager::getInstance()
                 ->get(FilteringDepthMeter::class);
 
+        $this->deferredDirectiveRegistry = $deferredDirectiveRegistry ?? ObjectManager::getInstance()
+                ->get(DeferredDirectiveRegistry::class);
+
         if (empty($directiveProcessors)) {
             $this->directiveProcessors = [
                 'depend' => ObjectManager::getInstance()->get(DependDirective::class),
@@ -204,35 +217,11 @@ public function filter($value)
         $this->filteringDepthMeter->descend();
 
         // Processing of template directives.
-        $templateDirectivesResults = array_unique(
-            $this->processDirectives($value),
-            SORT_REGULAR
-        );
-
-        $value = $this->applyDirectivesResults($value, $templateDirectivesResults);
-
-        // Processing of deferred directives received from child templates
-        // or nested directives.
-        $deferredDirectivesResults = array_unique(
-            $this->processDirectives($value, true),
-            SORT_REGULAR
-        );
-
-        $value = $this->applyDirectivesResults($value, $deferredDirectivesResults);
-
-        if ($this->filteringDepthMeter->showMark() > 1) {
-            // Signing own deferred directives (if any).
-            $signature = $this->signatureProvider->get();
-
-            foreach ($templateDirectivesResults as $result) {
-                if ($result['directive'] === $result['output']) {
-                    $value = str_replace(
-                        $result['output'],
-                        $signature . $result['output'] . $signature,
-                        $value
-                    );
-                }
-            }
+        $value = $this->resolveDirectives($value);
+
+        // At the root template, resolve directives that child templates deferred to it.
+        if ($this->filteringDepthMeter->showMark() === 1) {
+            $value = $this->resolveDeferredDirectives($value);
         }
 
         $value = $this->afterFilter($value);
@@ -243,17 +232,50 @@ public function filter($value)
     }
 
     /**
-     * Processes template directives and returns an array that contains results produced by each directive.
+     * Defers a directive to the root template and returns the placeholder that represents it.
+     *
+     * @param string $directive
+     * @return string
+     * @throws \Magento\Framework\Exception\LocalizedException
+     */
+    protected function deferToParent(string $directive): string
+    {
+        return $this->deferredDirectiveRegistry->defer($directive);
+    }
+
+    /**
+     * Resolves directives that child templates deferred to the root template.
+     *
+     * @param string $value
+     * @return string
+     * @throws \Exception
+     */
+    private function resolveDeferredDirectives(string $value): string
+    {
+        foreach ($this->deferredDirectiveRegistry->getAll() as $placeholder => $directive) {
+            if (strpos($value, $placeholder) === false) {
+                continue;
+            }
+
+            $value = str_replace($placeholder, $this->resolveDirectives($directive), $value);
+        }
+
+        $this->deferredDirectiveRegistry->clear();
+
+        return $value;
+    }
+
+    /**
+     * Processes directives and applies the results produced by each directive to the given value.
      *
      * @param string $value
-     * @param bool $isSigned
      *
-     * @return array
+     * @return string
      *
      * @throws InvalidArgumentException
      * @throws \Magento\Framework\Exception\LocalizedException
      */
-    private function processDirectives($value, $isSigned = false): array
+    private function resolveDirectives(string $value): string
     {
         $results = [];
 
@@ -266,10 +288,6 @@ private function processDirectives($value, $isSigned = false): array
 
             $pattern = $directiveProcessor->getRegularExpression();
 
-            if ($isSigned) {
-                $pattern = $this->embedSignatureIntoPattern($pattern);
-            }
-
             if (preg_match_all($pattern, $value, $constructions, PREG_SET_ORDER)) {
                 foreach ($constructions as $construction) {
                     $replacedValue = $directiveProcessor->process($construction, $this, $this->templateVars);
@@ -290,7 +308,7 @@ private function processDirectives($value, $isSigned = false): array
             }
         }
 
-        return $results;
+        return $this->applyDirectivesResults($value, array_unique($results, SORT_REGULAR));
     }
 
     /**
@@ -328,38 +346,6 @@ private function applyDirectivesResults(string $value, array $results): string
         return $value;
     }
 
-    /**
-     * Modifies given regular expression pattern to be able to recognize signed directives.
-     *
-     * @param string $pattern
-     *
-     * @return string
-     *
-     * @throws \Magento\Framework\Exception\LocalizedException
-     */
-    private function embedSignatureIntoPattern(string $pattern): string
-    {
-        $signature = $this->signatureProvider->get();
-
-        $closingDelimiters = [
-            '(' => ')',
-            '{' => '}',
-            '[' => ']',
-            '<' => '>'
-        ];
-
-        $closingDelimiter = $openingDelimiter = substr(trim($pattern), 0, 1);
-
-        if (array_key_exists($openingDelimiter, $closingDelimiters)) {
-            $closingDelimiter = $closingDelimiters[$openingDelimiter];
-        }
-
-        $pattern = substr_replace($pattern, $signature, strpos($pattern, $openingDelimiter) + 1, 0);
-        $pattern = substr_replace($pattern, $signature, strrpos($pattern, $closingDelimiter), 0);
-
-        return $pattern;
-    }
-
     /**
      * Runs callbacks that have been added to filter content after directive processing is finished.
      *
diff --git a/vendor/magento/framework/Filter/Template/DeferredDirectiveRegistry.php b/vendor/magento/framework/Filter/Template/DeferredDirectiveRegistry.php
new file mode 100644
index 0000000000000..cc33f04824b77
--- /dev/null
+++ b/vendor/magento/framework/Filter/Template/DeferredDirectiveRegistry.php
@@ -0,0 +1,102 @@
+<?php
+/**
+ * Copyright 2026 Adobe
+ * All Rights Reserved.
+ */
+declare(strict_types=1);
+
+namespace Magento\Framework\Filter\Template;
+
+use Magento\Framework\Math\Random;
+use Magento\Framework\Exception\LocalizedException;
+use Magento\Framework\ObjectManager\ResetAfterRequestInterface;
+
+/**
+ * Per-request registry of directives that child templates have deferred to the root template.
+ *
+ * The service is shared for the duration of a request so that a child template (filtered by its
+ * own Filter instance) and the root template can exchange deferrals through it.
+ */
+class DeferredDirectiveRegistry implements ResetAfterRequestInterface
+{
+    /**
+     * Length of the per-request salt.
+     */
+    private const TOKEN_LENGTH = 32;
+
+    /**
+     * @var Random
+     */
+    private Random $random;
+
+    /**
+     * Map of placeholder token => deferred directive text.
+     *
+     * @var string[]
+     */
+    private array $directives = [];
+
+    /**
+     * Per-request secret salt used to derive placeholder tokens deterministically.
+     *
+     * @var string|null
+     */
+    private ?string $salt = null;
+
+    /**
+     * @param Random $random
+     */
+    public function __construct(Random $random)
+    {
+        $this->random = $random;
+    }
+
+    /**
+     * Register a directive as deferred and return the placeholder token that represents it.
+     *
+     * @param string $directive
+     * @return string
+     * @throws LocalizedException
+     */
+    public function defer(string $directive): string
+    {
+        if ($this->salt === null) {
+            $this->salt = $this->random->getRandomString(self::TOKEN_LENGTH);
+        }
+
+        $placeholder = ':deferred:' . hash('sha256', $this->salt . $directive) . ':';
+
+        $this->directives[$placeholder] = $directive;
+
+        return $placeholder;
+    }
+
+    /**
+     * Get all registered deferrals keyed by their placeholder token.
+     *
+     * @return string[]
+     */
+    public function getAll(): array
+    {
+        return $this->directives;
+    }
+
+    /**
+     * Forget all registered deferrals.
+     *
+     * @return void
+     */
+    public function clear(): void
+    {
+        $this->directives = [];
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function _resetState(): void
+    {
+        $this->directives = [];
+        $this->salt = null;
+    }
+}
diff --git a/vendor/magento/framework/Filter/Template/SignatureProvider.php b/vendor/magento/framework/Filter/Template/SignatureProvider.php
index be87b40d9caed..2dc4c24c2a8aa 100644
--- a/vendor/magento/framework/Filter/Template/SignatureProvider.php
+++ b/vendor/magento/framework/Filter/Template/SignatureProvider.php
@@ -15,6 +15,10 @@
  * Provides a signature which should be used to sign deferred directives
  * (directives that should be processed in scope of a parent template
  * instead of own scope, e.g. {{inlinecss}}).
+ *
+ * @deprecated Signature-based deferral was superseded by out-of-band deferral via
+ *             \Magento\Framework\Filter\Template\DeferredDirectiveRegistry.
+ * @see \Magento\Framework\Filter\Template\DeferredDirectiveRegistry
  */
 class SignatureProvider implements ResetAfterRequestInterface
 {
diff --git a/vendor/magento/framework/Lock/Backend/FileLock.php b/vendor/magento/framework/Lock/Backend/FileLock.php
index dd8eda9621667..08b949f56ad9f 100644
--- a/vendor/magento/framework/Lock/Backend/FileLock.php
+++ b/vendor/magento/framework/Lock/Backend/FileLock.php
@@ -161,7 +161,7 @@ public function unlock(string $name): bool
      */
     private function getLockPath(string $name): string
     {
-        return $this->path . $name;
+        return $this->path . hash('sha256', $name);
     }
 
     /**
diff --git a/vendor/magento/framework/Validator/HTML/ConfigurableWYSIWYGValidator.php b/vendor/magento/framework/Validator/HTML/ConfigurableWYSIWYGValidator.php
index f3b3a6bc9216e..2061228d568cc 100644
--- a/vendor/magento/framework/Validator/HTML/ConfigurableWYSIWYGValidator.php
+++ b/vendor/magento/framework/Validator/HTML/ConfigurableWYSIWYGValidator.php
@@ -24,12 +24,19 @@ class ConfigurableWYSIWYGValidator implements WYSIWYGValidatorInterface
         . '(\\\\x64\\\\x61\\\\x74\\\\x61(\\\\x3a|:|%3A)))/i';
 
     /**
-     * Pattern to detect malformed syntax
+     * Pattern to detect malformed or unsupported markup syntax.
      *
      * @var string
      */
     private static string $contentFiltrationPattern =
-        '#<\w+[^>]*/[^\s/>]+/[^>]*=|<\w+/[^\s>]*on\w+\s*=|<\w+[^>]*//[^>]*=|(<body)#i';
+        '#<!---?>'
+        . '|<\s*<[a-zA-Z]'
+        . '|\x00'
+        . '|<\w+[^>]*/[^\s/>]+/[^>]*='
+        . '|<\w+/[^\s>]*on\w+\s*='
+        . '|<\w+[^>]*//[^>]*='
+        . '|(?<body><body)'
+        . '#i';
 
     /**
      * @var string[]
@@ -256,12 +263,11 @@ function () use (&$loaded) {
         );
         $matches = [];
         preg_match_all(self::$contentFiltrationPattern, $content, $matches);
-        $valueCounts = array_count_values($matches[0]);
+        $bodyMatchCount = count(array_filter($matches['body'] ?? []));
+        $otherMatchCount = count($matches[0]) - $bodyMatchCount;
 
-        $hasMultipleBody = isset($valueCounts['<body']) && $valueCounts['<body'] > 1;
-        $hasOtherMalicious = count($matches[0]) > 0 && !isset($valueCounts['<body']);
-
-        $loaded = !($hasMultipleBody || $hasOtherMalicious)
+        $loaded = $bodyMatchCount <= 1
+            && $otherMatchCount === 0
             && $dom->loadHTML($content, LIBXML_HTML_NOIMPLIED);
 
         restore_error_handler();
diff --git a/vendor/magento/framework/View/Element/UiComponent/DataProvider/Sanitizer.php b/vendor/magento/framework/View/Element/UiComponent/DataProvider/Sanitizer.php
index c813297bf252d..e395422782950 100644
--- a/vendor/magento/framework/View/Element/UiComponent/DataProvider/Sanitizer.php
+++ b/vendor/magento/framework/View/Element/UiComponent/DataProvider/Sanitizer.php
@@ -50,7 +50,7 @@ public function sanitize(array $data): array
             } elseif (!is_bool($config)
                 && !array_key_exists($key, $config)
                 && (is_string($datum) || $datum instanceof Phrase)
-                && preg_match('/\$\{.+\}/s', (string)$datum)
+                && $this->validateSyntax((string)$datum)
             ) {
                 //Templating is not disabled for all properties or for this property specifically
                 //Property is a string that contains template syntax, so we are disabling its rendering
@@ -65,6 +65,17 @@ public function sanitize(array $data): array
         return $data;
     }
 
+    /**
+     * Validate value syntax.
+     *
+     * @param string $value
+     * @return bool
+     */
+    private function validateSyntax(string $value): bool
+    {
+        return strpos($value, '${') !== false && strpos($value, '${{') === false;
+    }
+
     /**
      * Sanitize a component's metadata.
      *
diff --git a/vendor/magento/framework/Validator/EmailAddress.php b/vendor/magento/framework/Validator/EmailAddress.php
index dd402cba79155..6b77f0d37cc04 100644
--- a/vendor/magento/framework/Validator/EmailAddress.php
+++ b/vendor/magento/framework/Validator/EmailAddress.php
@@ -9,6 +9,13 @@
 
 class EmailAddress extends LaminasEmailAddress implements ValidatorInterface
 {
+    /**
+     * Quoted Pattern
+     *
+     * @var string
+     */
+    private const QUOTED_LOCAL_PART_PATTERN = '/^"/';
+
     /**
      * @var string[]
      */
@@ -55,4 +62,18 @@ public function setValidateTld(bool $shouldValidate)
     {
         $this->getHostnameValidator()->setOptions(['useTldCheck' => $shouldValidate]);
     }
+
+    /**
+     * @inheritdoc
+     */
+    public function isValid($value)
+    {
+        if (is_string($value) && preg_match(self::QUOTED_LOCAL_PART_PATTERN, $value)) {
+            $this->setValue($value);
+            $this->error(self::INVALID_FORMAT);
+            return false;
+        }
+
+        return parent::isValid($value);
+    }
 }
