CVE-2022-24872 (PHP, published 2022-04-20T20:15Z)
Description: value: 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 v3 base score: 8.1)
Weakness: CWE-732 \u2014 Incorrect Permission Assignment for Critical Resource
The product specifies permissions for a security-critical resource in a way that allows that resource to be read or modified by unintended actors.
Repository: https://github.com/shopware/platform
Files changed:
src/Core/Framework/Api/Controller/SalesChannelProxyController.php
src/Core/System/SalesChannel/Context/CartRestorer.php
src/Core/System/SalesChannel/Context/SalesChannelContextPersister.php
src/Core/System/Test/SalesChannel/Context/CartRestorerTest.php
Commit message: NEXT-21034 - Dont restore permissions


Vulnerable code:
        if (empty($customerPayload) || !($customerPayload['expired'] ?? false) && $customerPayload['token'] === $currentContext->getToken()) {
    public function save(string $token, array $parameters, string $salesChannelId, ?string $customerId = null): void
        $parameters = array_replace_recursive($existing, $parameters);
use Shopware\Core\Checkout\Payment\Cart\PaymentHandler\PrePayment;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
    private function getStateId(string $state, string $machine)
        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,
            ]);
    }
    private function getPrePaymentMethodId(): string
    {
        /** @var EntityRepositoryInterface $repository */
        $repository = $this->getContainer()->get('payment_method.repository');
        $criteria = (new Criteria())
            ->setLimit(1)
            ->addFilter(new EqualsFilter('active', true))
            ->addFilter(new EqualsFilter('handlerIdentifier', PrePayment::class));
        return $repository->searchIds($criteria, Context::createDefaultContext())->getIds()[0];

Fixed code:
        $response->headers->set('content-type', 'application/json');
        if (empty($customerPayload) || !empty($customerPayload['permissions']) || !($customerPayload['expired'] ?? false) && $customerPayload['token'] === $currentContext->getToken()) {
                'permissions' => [],
    public function save(string $token, array $newParameters, string $salesChannelId, ?string $customerId = null): void
        $parameters = array_replace_recursive($existing, $newParameters);
        if (isset($newParameters['permissions']) && $newParameters['permissions'] === []) {
            $parameters['permissions'] = [];
        }
use function json_encode;
    public function testPermissionsAreIgnoredOnRestoer(): void
        $currentContextToken = Random::getAlphanumericString(32);
        $currentContext = $this->createSalesChannelContext($currentContextToken, []);
        $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);
        static::assertSame([], $restoreContext->getPermissions());