Internal hidden fields are visible on to many associations in admin api
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.
| Package | Affected versions | Patched versions |
|---|---|---|
shopware/platformPackagist | < 6.4.1.1 | 6.4.1.1 |
Affected products
2- shopware/platformv5Range: < 6.4.1.1
Patches
1b5c3ce3e93bdNEXT-15183 - Add Dangerfile
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- github.com/advisories/GHSA-68v9-3jjq-rvp4ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2021-32716ghsaADVISORY
- docs.shopware.com/en/shopware-6-en/security-updates/security-update-06-2021ghsax_refsource_MISCWEB
- github.com/shopware/platform/commit/b5c3ce3e93bd121324d72aa9d367cb636ff1c0ebghsax_refsource_MISCWEB
- github.com/shopware/platform/security/advisories/GHSA-gpmh-g94g-qrhrghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.