VYPR
Moderate severityNVD Advisory· Published Jun 24, 2021· Updated Aug 3, 2024

Internal hidden fields are visible on to many associations in admin api

CVE-2021-32716

Description

Shopware is an open source eCommerce platform. In versions prior to 6.4.1.1 the admin api has exposed some internal hidden fields when an association has been loaded with a to many reference. Users are recommend to update to version 6.4.1.1. You can get the update to 6.4.1.1 regularly via the Auto-Updater or directly via the download overview. For older versions of 6.1, 6.2, and 6.3, corresponding security measures are also available via a plugin.

AI Insight

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

Shopware versions prior to 6.4.1.1 expose internal hidden fields via the admin API when loading to-many associations, leading to information disclosure.

Vulnerability

In Shopware versions prior to 6.4.1.1, the admin API exposes internal hidden fields when an association is loaded with a to-many reference. This affects the default configuration of the admin API, which is accessible to authenticated users with administrative privileges. Versions 6.4.1.0 and earlier, including older 6.1, 6.2, and 6.3 lines, are vulnerable [1][3].

Exploitation

An attacker needs valid admin API credentials to exploit this vulnerability. By crafting API requests that load to-many associations on entities, the attacker can force the API to include hidden internal fields in the response. No additional user interaction or race condition is required [3].

Impact

Successful exploitation results in information disclosure of internal hidden fields that are not intended to be visible through the admin API. This may expose sensitive configuration details or data that could aid further attacks. The attacker gains no direct write or code execution access, but the exposed information may reduce the security posture [1][3].

Mitigation

Shopware recommends updating to version 6.4.1.1, released on June 24, 2021, via the Auto-Updater or direct download [1]. For older version lines (6.1, 6.2, 6.3), a plugin is available as a workaround [3]. No other mitigations are documented in the provided references.

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 packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
shopware/platformPackagist
< 6.4.1.16.4.1.1

Affected products

2

Patches

1
b5c3ce3e93bd

NEXT-15183 - Add Dangerfile

https://github.com/shopware/platformSoner SayakciMay 11, 2021via ghsa
6 files changed · +341 6
  • dangerfile.js+54 0 added
    @@ -0,0 +1,54 @@
    +const GitlabClient = require('gitlab');
    +
    +const api = new GitlabClient.Gitlab({
    +    token: process.env['DANGER_GITLAB_API_TOKEN'],
    +    host: process.env['DANGER_GITLAB_HOST']
    +});
    +
    +const addLabel = (labels) => {
    +    const currentLabels = danger.gitlab.mr.labels;
    +
    +    for (const label of labels) {
    +        currentLabels.push(label);
    +    }
    +
    +    api.MergeRequests.edit(1, process.env['CI_MERGE_REQUEST_IID'], {
    +        'labels': currentLabels.join(',')
    +    })
    +};
    +
    +const removeLabel = (labels) => {
    +    const currentLabels = danger.gitlab.mr.labels;
    +
    +    for (const label of labels) {
    +        const index = currentLabels.indexOf(label);
    +        if (index > -1) {
    +            currentLabels.splice(index, 1);
    +        }
    +
    +        api.MergeRequests.edit(1, process.env['CI_MERGE_REQUEST_IID'], {
    +            'labels': currentLabels.join(',')
    +        })
    +    }
    +};
    +
    +const hasStoreApiRouteChanges = () => {
    +    for (let file of danger.git.modified_files) {
    +        if (file.includes('SalesChannel') && file.includes('Route.php') && !file.includes('/Test/')) {
    +            return true;
    +        }
    +    }
    +
    +    for (let file of danger.git.created_files) {
    +        if (file.includes('SalesChannel') && file.includes('Route.php') && !file.includes('/Test/')) {
    +            return true;
    +        }
    +    }
    +
    +    return false;
    +}
    +
    +if (hasStoreApiRouteChanges()) {
    +    warn('Store-API Route has been modified. @Reviewers please review carefully!')
    +    addLabel(['Security-Audit Required']);
    +}
    
  • .gitlab-ci.yml+8 0 modified
    @@ -59,6 +59,14 @@ default:
     
     # stage: unit
     
    +Danger:
    +    stage: unit
    +    image: node:14
    +    before_script: []
    +    script:
    +        - npm install danger gitlab
    +        - node node_modules/.bin/danger ci --base trunk
    +
     PHP Full:
         image: $DEV_IMAGE
         stage: unit
    
  • src/Core/Checkout/DependencyInjection/order.xml+1 0 modified
    @@ -95,6 +95,7 @@
     
             <service id="Shopware\Core\Checkout\Order\SalesChannel\CancelOrderRoute" public="true">
                 <argument type="service" id="Shopware\Core\Checkout\Order\SalesChannel\OrderService"/>
    +            <argument type="service" id="order.repository"/>
             </service>
     
             <service id="Shopware\Core\Checkout\Order\SalesChannel\SetPaymentOrderRoute" public="true">
    
  • src/Core/Checkout/Order/SalesChannel/CancelOrderRoute.php+34 6 modified
    @@ -3,10 +3,16 @@
     namespace Shopware\Core\Checkout\Order\SalesChannel;
     
     use OpenApi\Annotations as OA;
    +use Shopware\Core\Checkout\Cart\Exception\CustomerNotLoggedInException;
    +use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
    +use Shopware\Core\Framework\DataAbstractionLayer\Exception\EntityNotFoundException;
    +use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
    +use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
     use Shopware\Core\Framework\Plugin\Exception\DecorationPatternException;
     use Shopware\Core\Framework\Routing\Annotation\LoginRequired;
     use Shopware\Core\Framework\Routing\Annotation\RouteScope;
     use Shopware\Core\Framework\Routing\Annotation\Since;
    +use Shopware\Core\Framework\Routing\Exception\InvalidRequestParameterException;
     use Shopware\Core\System\SalesChannel\SalesChannelContext;
     use Symfony\Component\HttpFoundation\ParameterBag;
     use Symfony\Component\HttpFoundation\Request;
    @@ -17,14 +23,14 @@
      */
     class CancelOrderRoute extends AbstractCancelOrderRoute
     {
    -    /**
    -     * @var OrderService
    -     */
    -    private $orderService;
    +    private OrderService $orderService;
    +
    +    private EntityRepositoryInterface $orderRepository;
     
    -    public function __construct(OrderService $orderService)
    +    public function __construct(OrderService $orderService, EntityRepositoryInterface $orderRepository)
         {
             $this->orderService = $orderService;
    +        $this->orderRepository = $orderRepository;
         }
     
         public function getDecorated(): AbstractCancelOrderRoute
    @@ -56,13 +62,35 @@ public function getDecorated(): AbstractCancelOrderRoute
          */
         public function cancel(Request $request, SalesChannelContext $context): CancelOrderRouteResponse
         {
    +        $orderId = $request->get('orderId', null);
    +
    +        if ($orderId === null) {
    +            throw new InvalidRequestParameterException('orderId');
    +        }
    +
    +        $this->verify($orderId, $context);
    +
             $newState = $this->orderService->orderStateTransition(
    -            $request->get('orderId'),
    +            $orderId,
                 'cancel',
                 new ParameterBag(),
                 $context->getContext()
             );
     
             return new CancelOrderRouteResponse($newState);
         }
    +
    +    private function verify(string $orderId, SalesChannelContext $context): void
    +    {
    +        if ($context->getCustomer() === null) {
    +            throw new CustomerNotLoggedInException();
    +        }
    +
    +        $criteria = new Criteria([$orderId]);
    +        $criteria->addFilter(new EqualsFilter('orderCustomer.customerId', $context->getCustomer()->getId()));
    +
    +        if ($this->orderRepository->searchIds($criteria, $context->getContext())->firstId() === null) {
    +            throw new EntityNotFoundException('order', $orderId);
    +        }
    +    }
     }
    
  • src/Core/Checkout/Test/Order/SalesChannel/CancelOrderRouteTest.php+229 0 added
    @@ -0,0 +1,229 @@
    +<?php declare(strict_types=1);
    +
    +namespace Shopware\Core\Checkout\Test\Order\SalesChannel;
    +
    +use PHPUnit\Framework\TestCase;
    +use Shopware\Core\Checkout\Cart\LineItem\LineItem;
    +use Shopware\Core\Checkout\Cart\Price\Struct\CalculatedPrice;
    +use Shopware\Core\Checkout\Cart\Price\Struct\CartPrice;
    +use Shopware\Core\Checkout\Cart\Price\Struct\QuantityPriceDefinition;
    +use Shopware\Core\Checkout\Cart\Tax\Struct\CalculatedTaxCollection;
    +use Shopware\Core\Checkout\Cart\Tax\Struct\TaxRuleCollection;
    +use Shopware\Core\Checkout\Order\OrderEntity;
    +use Shopware\Core\Checkout\Test\Customer\SalesChannel\CustomerTestTrait;
    +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\Test\IdsCollection;
    +use Shopware\Core\Framework\Test\TestCaseBase\IntegrationTestBehaviour;
    +use Shopware\Core\Framework\Test\TestDataCollection;
    +use Shopware\Core\Framework\Uuid\Uuid;
    +use Symfony\Bundle\FrameworkBundle\KernelBrowser;
    +use Symfony\Component\HttpFoundation\Response;
    +
    +class CancelOrderRouteTest extends TestCase
    +{
    +    use IntegrationTestBehaviour;
    +    use CustomerTestTrait;
    +
    +    private KernelBrowser $browser;
    +
    +    private EntityRepositoryInterface $customerRepository;
    +
    +    private IdsCollection $ids;
    +
    +    protected function setUp(): void
    +    {
    +        parent::setUp();
    +
    +        $this->customerRepository = $this->getContainer()->get('customer.repository');
    +        $this->ids = new TestDataCollection();
    +
    +        $this->browser = $this->createCustomSalesChannelBrowser([
    +            'id' => $this->ids->create('sales-channel'),
    +        ]);
    +
    +        $this->assignSalesChannelContext($this->browser);
    +
    +        $email = Uuid::randomHex() . '@example.com';
    +        $customerId = $this->createCustomer('shopware', $email);
    +
    +        $this->ids->set('order-1', $this->createOrder($this->ids, $customerId));
    +        $this->ids->set('order-2', $this->createOrder($this->ids, $this->createCustomer('test', 'test-other@test.de')));
    +
    +        $this->browser
    +            ->request(
    +                'POST',
    +                '/store-api/account/login',
    +                [
    +                    'email' => $email,
    +                    'password' => 'shopware',
    +                ]
    +            );
    +
    +        $response = json_decode($this->browser->getResponse()->getContent(), true);
    +
    +        $this->browser->setServerParameter('HTTP_SW_CONTEXT_TOKEN', $response['contextToken']);
    +    }
    +
    +    public function testCancelMyOwnOrder(): void
    +    {
    +        $this->browser
    +            ->request(
    +                'POST',
    +                '/store-api/order/state/cancel',
    +                [
    +                    'orderId' => $this->ids->get('order-1'),
    +                ]
    +            );
    +
    +        $response = json_decode($this->browser->getResponse()->getContent(), true);
    +
    +        static::assertSame(Response::HTTP_OK, $this->browser->getResponse()->getStatusCode());
    +        static::assertSame('cancelled', $response['technicalName']);
    +
    +        $criteria = new Criteria([$this->ids->get('order-1')]);
    +        $criteria->addAssociation('stateMachineState');
    +
    +        /** @var OrderEntity $order */
    +        $order = $this->getContainer()->get('order.repository')->search($criteria, Context::createDefaultContext())->first();
    +
    +        static::assertSame('cancelled', $order->getStateMachineState()->getTechnicalName());
    +    }
    +
    +    public function testCancelRandomOrder(): void
    +    {
    +        $this->browser
    +            ->request(
    +                'POST',
    +                '/store-api/order/state/cancel',
    +                [
    +                    'orderId' => Uuid::randomHex(),
    +                ]
    +            );
    +
    +        $response = json_decode($this->browser->getResponse()->getContent(), true);
    +
    +        static::assertSame(Response::HTTP_NOT_FOUND, $this->browser->getResponse()->getStatusCode());
    +        static::assertSame('FRAMEWORK__ENTITY_NOT_FOUND', $response['errors'][0]['code']);
    +    }
    +
    +    public function testCancelOtherUsersOrder(): void
    +    {
    +        $this->browser
    +            ->request(
    +                'POST',
    +                '/store-api/order/state/cancel',
    +                [
    +                    'orderId' => $this->ids->get('order-2'),
    +                ]
    +            );
    +
    +        $response = json_decode($this->browser->getResponse()->getContent(), true);
    +
    +        static::assertSame(Response::HTTP_NOT_FOUND, $this->browser->getResponse()->getStatusCode());
    +        static::assertSame('FRAMEWORK__ENTITY_NOT_FOUND', $response['errors'][0]['code']);
    +    }
    +
    +    public function testCancelWithoutLogin(): void
    +    {
    +        $this->browser = $this->createCustomSalesChannelBrowser([
    +            'id' => Uuid::randomHex(),
    +        ]);
    +
    +        $this->browser
    +            ->request(
    +                'POST',
    +                '/store-api/order/state/cancel',
    +                [
    +                    'orderId' => $this->ids->get('order-2'),
    +                ]
    +            );
    +
    +        $response = json_decode($this->browser->getResponse()->getContent(), true);
    +
    +        static::assertSame(Response::HTTP_FORBIDDEN, $this->browser->getResponse()->getStatusCode());
    +        static::assertSame('CHECKOUT__CUSTOMER_NOT_LOGGED_IN', $response['errors'][0]['code']);
    +    }
    +
    +    private function createOrder(IdsCollection $ids, string $customerId): string
    +    {
    +        $id = Uuid::randomHex();
    +
    +        $this->getContainer()->get('order.repository')->create(
    +            [[
    +                'id' => $id,
    +                'orderDateTime' => (new \DateTimeImmutable())->format(Defaults::STORAGE_DATE_TIME_FORMAT),
    +                'price' => new CartPrice(10, 10, 10, new CalculatedTaxCollection(), new TaxRuleCollection(), CartPrice::TAX_STATE_NET),
    +                'shippingCosts' => new CalculatedPrice(10, 10, new CalculatedTaxCollection(), new TaxRuleCollection()),
    +                'orderCustomer' => [
    +                    'customerId' => $customerId,
    +                    'email' => 'test@example.com',
    +                    'salutationId' => $this->getValidSalutationId(),
    +                    'firstName' => 'Max',
    +                    'lastName' => 'Mustermann',
    +                ],
    +                'stateId' => $this->getStateMachineState(),
    +                'paymentMethodId' => $this->getValidPaymentMethodId(),
    +                'currencyId' => Defaults::CURRENCY,
    +                'currencyFactor' => 1.0,
    +                'salesChannelId' => Defaults::SALES_CHANNEL,
    +                'billingAddressId' => $billingAddressId = Uuid::randomHex(),
    +                'addresses' => [
    +                    [
    +                        'id' => $billingAddressId,
    +                        'salutationId' => $this->getValidSalutationId(),
    +                        'firstName' => 'Max',
    +                        'lastName' => 'Mustermann',
    +                        'street' => 'Ebbinghoff 10',
    +                        'zipcode' => '48624',
    +                        'city' => 'Schöppingen',
    +                        'countryId' => $this->getValidCountryId(),
    +                    ],
    +                ],
    +                'lineItems' => [
    +                    [
    +                        'id' => $ids->get('VoucherA'),
    +                        'type' => LineItem::PROMOTION_LINE_ITEM_TYPE,
    +                        'code' => $ids->get('VoucherA'),
    +                        'identifier' => $ids->get('VoucherA'),
    +                        'quantity' => 1,
    +                        'payload' => ['promotionId' => $ids->get('voucherA')],
    +                        'label' => 'label',
    +                        'price' => new CalculatedPrice(200, 200, new CalculatedTaxCollection(), new TaxRuleCollection()),
    +                        'priceDefinition' => new QuantityPriceDefinition(200, new TaxRuleCollection(), 2),
    +                    ],
    +                    [
    +                        'id' => $ids->get('VoucherC'),
    +                        'type' => LineItem::PROMOTION_LINE_ITEM_TYPE,
    +                        'code' => $ids->get('VoucherC'),
    +                        'identifier' => $ids->get('VoucherC'),
    +                        'payload' => ['promotionId' => $ids->get('voucherB')],
    +                        'quantity' => 1,
    +                        'label' => 'label',
    +                        'price' => new CalculatedPrice(200, 200, new CalculatedTaxCollection(), new TaxRuleCollection()),
    +                        'priceDefinition' => new QuantityPriceDefinition(200, new TaxRuleCollection(), 2),
    +                    ],
    +                    [
    +                        'id' => $ids->get('VoucherB'),
    +                        'type' => LineItem::PROMOTION_LINE_ITEM_TYPE,
    +                        'code' => $ids->get('VoucherB'),
    +                        'identifier' => $ids->get('VoucherB'),
    +                        'payload' => ['promotionId' => $ids->get('voucherB')],
    +                        'quantity' => 1,
    +                        'label' => 'label',
    +                        'price' => new CalculatedPrice(200, 200, new CalculatedTaxCollection(), new TaxRuleCollection()),
    +                        'priceDefinition' => new QuantityPriceDefinition(200, new TaxRuleCollection(), 2),
    +                    ],
    +                ],
    +                'deliveries' => [],
    +                'context' => '{}',
    +                'payload' => '{}',
    +            ]],
    +            Context::createDefaultContext()
    +        );
    +
    +        return $id;
    +    }
    +}
    
  • src/Core/Framework/Test/TestCaseBase/BasicTestDataBehaviour.php+15 0 modified
    @@ -2,6 +2,7 @@
     
     namespace Shopware\Core\Framework\Test\TestCaseBase;
     
    +use Shopware\Core\Checkout\Order\OrderStates;
     use Shopware\Core\Checkout\Payment\PaymentMethodEntity;
     use Shopware\Core\Checkout\Shipping\ShippingMethodEntity;
     use Shopware\Core\Defaults;
    @@ -180,4 +181,18 @@ protected function getValidTaxId(): string
     
             return $repository->searchIds($criteria, Context::createDefaultContext())->getIds()[0];
         }
    +
    +    protected function getStateMachineState(string $stateMachine = OrderStates::STATE_MACHINE, string $state = OrderStates::STATE_OPEN): string
    +    {
    +        /** @var EntityRepositoryInterface $repository */
    +        $repository = $this->getContainer()->get('state_machine_state.repository');
    +
    +        $criteria = new Criteria();
    +        $criteria
    +            ->setLimit(1)
    +            ->addFilter(new EqualsFilter('technicalName', $state))
    +            ->addFilter(new EqualsFilter('stateMachine.technicalName', $stateMachine));
    +
    +        return $repository->searchIds($criteria, Context::createDefaultContext())->getIds()[0];
    +    }
     }
    

Vulnerability mechanics

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

References

5

News mentions

0

No linked articles in our index yet.