CVE-2022-24872 (published 2022-04-20T20:15Z)

Shopware is an open commerce platform based on Symfony Framework and Vue. Permissions set to sales channel context by admin-api are still usable within normal user session. Users are advised to update to the current version 6.4.10.1. For older versions of 6.1, 6.2, and 6.3, corresponding security measures are also available via a plugin. There are no known workarounds for this issue.

Severity: MEDIUM
CVSS v2 base score: 5.5 (AV:N/AC:L/Au:S/C:P/I:P/A:N)
CVSS v3 base score: 8.1, HIGH (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N)

Weakness information:
cwe_id: CWE-732
cwe_name: Incorrect Permission Assignment for Critical Resource
description: The product specifies permissions for a security-critical resource in a way that allows that resource to be read or modified by unintended actors.
extended_description: When a resource is given a permission setting that provides access to a wider range of actors than required, it could lead to the exposure of sensitive information, or the modification of that resource by unintended parties. This is especially dangerous when the resource is related to program configuration, execution, or sensitive user data. For example, consider a misconfigured storage account for the cloud that can be read or written by a public or anonymous user.
         

Fix information:
repo_url: https://github.com/shopware/platform
commit_details: repo_url: https://github.com/shopware/platform
author: Soner Sayakci
author_date: 2022-04-12 11:44:17+02:00
msg: NEXT-21034 - Dont restore permissions
repository: repo_url: https://github.com/shopware/platform
repo_name: shopware/shopware
description: Shopware 6 is an open commerce platform based on Symfony Framework and Vue and supported by a worldwide community and more than 1.500 community extensions
repo_language: PHP
owner: shopware
file_changes: filename: SalesChannelProxyController.php
change_type: ModificationType.MODIFY
diff: @@ -198,6 +198,7 @@ class SalesChannelProxyController extends AbstractController
             PlatformRequest::HEADER_CONTEXT_TOKEN => $salesChannelContext->getToken(),
         ], \JSON_THROW_ON_ERROR);
         $response = new Response();
+        $response->headers->set('content-type', 'application/json');
         $response->setContent($content ?: null);
 
         return $response;

programming_language: PHP
filename: CartRestorer.php
change_type: ModificationType.MODIFY
diff: @@ -46,7 +46,7 @@ class CartRestorer
             $customerId
         );
 
-        if (empty($customerPayload) || !($customerPayload['expired'] ?? false) && $customerPayload['token'] === $currentContext->getToken()) {
+        if (empty($customerPayload) || !empty($customerPayload['permissions']) || !($customerPayload['expired'] ?? false) && $customerPayload['token'] === $currentContext->getToken()) {
             return $this->replaceContextToken($customerId, $currentContext);
         }
 
@@ -114,6 +114,7 @@ class CartRestorer
                 'customerId' => $customerId,
                 'billingAddressId' => null,
                 'shippingAddressId' => null,
+                'permissions' => [],
             ],
             $currentContext->getSalesChannel()->getId(),
             $customerId

programming_language: PHP
filename: SalesChannelContextPersister.php
change_type: ModificationType.MODIFY
diff: @@ -32,11 +32,14 @@ class SalesChannelContextPersister
         $this->lifetimeInterval = $lifetimeInterval ?? 'P1D';
     }
 
-    public function save(string $token, array $parameters, string $salesChannelId, ?string $customerId = null): void
+    public function save(string $token, array $newParameters, string $salesChannelId, ?string $customerId = null): void
     {
         $existing = $this->load($token, $salesChannelId, $customerId);
 
-        $parameters = array_replace_recursive($existing, $parameters);
+        $parameters = array_replace_recursive($existing, $newParameters);
+        if (isset($newParameters['permissions']) && $newParameters['permissions'] === []) {
+            $parameters['permissions'] = [];
+        }
 
         unset($parameters['token']);
 

programming_language: PHP
filename: CartRestorerTest.php
change_type: ModificationType.MODIFY
diff: @@ -12,13 +12,10 @@ use Shopware\Core\Checkout\Cart\LineItem\LineItem;
 use Shopware\Core\Checkout\Cart\LineItem\LineItemCollection;
 use Shopware\Core\Checkout\Cart\SalesChannel\CartService;
 use Shopware\Core\Checkout\Customer\CustomerEntity;
-use Shopware\Core\Checkout\Payment\Cart\PaymentHandler\PrePayment;
 use Shopware\Core\Content\Product\Aggregate\ProductVisibility\ProductVisibilityDefinition;
 use Shopware\Core\Defaults;
 use Shopware\Core\Framework\Context;
-use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
 use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
-use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
 use Shopware\Core\Framework\Feature;
 use Shopware\Core\Framework\Test\TestCaseBase\IntegrationTestBehaviour;
 use Shopware\Core\Framework\Util\Random;
@@ -33,6 +30,7 @@ use Shopware\Core\System\SalesChannel\SalesChannelContext;
 use Shopware\Core\Test\TestDefaults;
 use Symfony\Component\EventDispatcher\EventDispatcher;
 use Symfony\Contracts\EventDispatcher\Event;
+use function json_encode;
 
 class CartRestorerTest extends TestCase
 {
@@ -325,33 +323,25 @@ class CartRestorerTest extends TestCase
         static::assertEquals(5, $p2->getQuantity());
     }
 
-    private function getStateId(string $state, string $machine)
+    public function testPermissionsAreIgnoredOnRestoer(): void
     {
-        return $this->getContainer()->get(Connection::class)
-            ->fetchColumn('
-                SELECT LOWER(HEX(state_machine_state.id))
-                FROM state_machine_state
-                    INNER JOIN  state_machine
-                    ON state_machine.id = state_machine_state.state_machine_id
-                    AND state_machine.technical_name = :machine
-                WHERE state_machine_state.technical_name = :state
-            ', [
-                'state' => $state,
-                'machine' => $machine,
-            ]);
-    }
+        $currentContextToken = Random::getAlphanumericString(32);
 
-    private function getPrePaymentMethodId(): string
-    {
-        /** @var EntityRepositoryInterface $repository */
-        $repository = $this->getContainer()->get('payment_method.repository');
+        $currentContext = $this->createSalesChannelContext($currentContextToken, []);
 
-        $criteria = (new Criteria())
-            ->setLimit(1)
-            ->addFilter(new EqualsFilter('active', true))
-            ->addFilter(new EqualsFilter('handlerIdentifier', PrePayment::class));
+        $con = $this->getContainer()->get(Connection::class);
+
+        $con->insert('sales_channel_api_context', [
+            'token' => Random::getAlphanumericString(32),
+            'payload' => json_encode(['expired' => false, 'customerId' => $this->customerId, 'permissions' => ['foo']], \JSON_THROW_ON_ERROR),
+            'sales_channel_id' => Uuid::fromHexToBytes($currentContext->getSalesChannelId()),
+            'customer_id' => Uuid::fromHexToBytes($this->customerId),
+            'updated_at' => (new \DateTime())->format(Defaults::STORAGE_DATE_TIME_FORMAT),
+        ]);
+
+        $restoreContext = $this->cartRestorer->restore($this->customerId, $currentContext);
 
-        return $repository->searchIds($criteria, Context::createDefaultContext())->getIds()[0];
+        static::assertSame([], $restoreContext->getPermissions());
     }
 
     private function createProduct(Context $context): string

programming_language: PHP