CVE-2022-24871 (PHP, published 2022-04-20T19:15Z)
Description: value: Shopware is an open commerce platform based on Symfony Framework and Vue. In affected versions an attacker can abuse the Admin SDK functionality on the server to read or update internal resources. 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: 5.5)
Weakness: CWE-918 \u2014 Server-Side Request Forgery (SSRF)
The web server receives a URL or similar request from an upstream component and retrieves the contents of this URL, but it does not sufficiently ensure that the request is being sent to the expected destination.
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());