Shopware vulnerable to Improper Access Control with ManyToMany associations in store-api
Description
Shopware is an open commerce platform. The store-API works with regular entities and not expose all fields for the public API; fields need to be marked as ApiAware in the EntityDefinition. So only ApiAware fields of the EntityDefinition will be encoded to the final JSON. Prior to versions 6.6.5.1 and 6.5.8.13, the processing of the Criteria did not considered ManyToMany associations and so they were not considered properly and the protections didn't get used. This issue cannot be reproduced with the default entities by Shopware, but can be triggered with extensions. Update to Shopware 6.6.5.1 or 6.5.8.13 to receive a patch. For older versions of 6.2, 6.3, and 6.4, corresponding security measures are also available via a plugin.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
shopware/corePackagist | < 6.5.8.13 | 6.5.8.13 |
shopware/platformPackagist | < 6.5.8.13 | 6.5.8.13 |
shopware/corePackagist | >= 6.6.0.0, < 6.6.5.1 | 6.6.5.1 |
shopware/platformPackagist | >= 6.6.0.0, < 6.6.5.1 | 6.6.5.1 |
Affected products
1Patches
48504ba7e56e5NEXT-37397 - Security picks
15 files changed · +158 −12
.bc-exclude.php+2 −0 modified@@ -42,6 +42,8 @@ 'Shopware\\\\Core\\\\Framework\\\\App\\\\Payment\\\\Payload\\\\Struct\\\\SyncPayPayload#__construct()', 'Shopware\\\\Core\\\\Framework\\\\Api\\\\Sync\\\\FkReference#__construct\(\)', + 'Shopware\\\\Core\\\\Framework\\\\Context.*changed from callable.*', + // Removed boot method from Bundle 'Shopware\\\\Core\\\\Framework\\\\Bundle#boot',
changelog/_unreleased/2024-07-24-context-object-improvements.md+8 −0 added@@ -0,0 +1,8 @@ +--- +title: Context object improvements +issue: NEXT-37399 +--- + +# Core + +* Changed `\Shopware\Core\Framework\Context` to allow only \Closure in `enableInheritance`, `disableInheritance` and `scope` method to prevent misuse of the context object in sandbox environments.
changelog/_unreleased/2024-07-24-improve-aggregation-name-validation.md+8 −0 added@@ -0,0 +1,8 @@ +--- +title: Improve aggregation name validation +issue: NEXT-37397 +--- + +# Core + +* Changed `\Shopware\Core\Framework\DataAbstractionLayer\Search\Parser\AggregationParser` to validate that the aggregation name does not contain question marks or colon,
changelog/_unreleased/2024-07-24-improve-feature-silent-token-validation.md+8 −0 added@@ -0,0 +1,8 @@ +--- +title: Improve feature silent token validation +issue: NEXT-37398 +--- + +# Core + +* Changed `\Shopware\Core\Framework\Adapter\Twig\Node\FeatureCallSilentToken` to properly inject the feature flag into the compiled template file.
src/Core/Framework/Adapter/Twig/Node/FeatureCallSilentToken.php+3 −1 modified@@ -24,7 +24,9 @@ public function compile(Compiler $compiler): void { $compiler ->addDebugInfo($this) - ->raw('\Shopware\Core\Framework\Feature::callSilentIfInactive(\'' . $this->flag . '\', function () use(&$context) { ') + ->raw('\Shopware\Core\Framework\Feature::callSilentIfInactive(') + ->string($this->flag) + ->raw(', function () use(&$context) { ') ->subcompile($this->getNode('body')) ->raw('});'); }
src/Core/Framework/Context.php+6 −6 modified@@ -149,11 +149,11 @@ public function createWithVersionId(string $versionId): self /** * @template TReturn of mixed * - * @param callable(Context): TReturn $callback + * @param \Closure(Context): TReturn $callback * * @return TReturn the return value of the provided callback function */ - public function scope(string $scope, callable $callback) + public function scope(string $scope, \Closure $callback) { $currentScope = $this->getScope(); $this->scope = $scope; @@ -216,11 +216,11 @@ public function setRuleIds(array $ruleIds): void /** * @template TReturn of mixed * - * @param callable(Context): TReturn $function + * @param \Closure(Context): TReturn $function * * @return TReturn */ - public function enableInheritance(callable $function) + public function enableInheritance(\Closure $function) { $previous = $this->considerInheritance; $this->considerInheritance = true; @@ -233,11 +233,11 @@ public function enableInheritance(callable $function) /** * @template TReturn of mixed * - * @param callable(Context): TReturn $function + * @param \Closure(Context): TReturn $function * * @return TReturn */ - public function disableInheritance(callable $function) + public function disableInheritance(\Closure $function) { $previous = $this->considerInheritance; $this->considerInheritance = false;
src/Core/Framework/DataAbstractionLayer/DataAbstractionLayerException.php+11 −0 modified@@ -44,6 +44,7 @@ class DataAbstractionLayerException extends HttpException public const INVALID_WRITE_INPUT = 'FRAMEWORK__INVALID_WRITE_INPUT'; public const DECODE_HANDLED_BY_HYDRATOR = 'FRAMEWORK__DECODE_HANDLED_BY_HYDRATOR'; public const ATTRIBUTE_NOT_FOUND = 'FRAMEWORK__ATTRIBUTE_NOT_FOUND'; + public const INVALID_AGGREGATION_NAME = 'FRAMEWORK__INVALID_AGGREGATION_NAME'; public static function invalidSerializerField(string $expectedClass, Field $field): self { @@ -402,4 +403,14 @@ public static function canNotFindAttribute(string $attribute, string $property): ['attribute' => $attribute, 'property' => $property] ); } + + public static function invalidAggregationName(string $name): self + { + return new self( + Response::HTTP_BAD_REQUEST, + self::INVALID_AGGREGATION_NAME, + 'Invalid aggregation name "{{ name }}", cannot contain question marks und colon.', + ['name' => $name] + ); + } }
src/Core/Framework/DataAbstractionLayer/Dbal/EntityAggregator.php+5 −0 modified@@ -4,6 +4,7 @@ use Doctrine\DBAL\Connection; use Shopware\Core\Framework\Context; +use Shopware\Core\Framework\DataAbstractionLayer\DataAbstractionLayerException; use Shopware\Core\Framework\DataAbstractionLayer\DefinitionInstanceRegistry; use Shopware\Core\Framework\DataAbstractionLayer\EntityCollection; use Shopware\Core\Framework\DataAbstractionLayer\EntityDefinition; @@ -116,6 +117,10 @@ private function fetchAggregation( Criteria $criteria, Context $context ): AggregationResult { + if (str_contains($aggregation->getName(), '?') || str_contains($aggregation->getName(), ':')) { + throw DataAbstractionLayerException::invalidAggregationName($aggregation->getName()); + } + $clone = clone $criteria; $clone->resetAggregations(); $clone->resetSorting();
src/Core/Framework/DataAbstractionLayer/Search/Parser/AggregationParser.php+6 −0 modified@@ -199,6 +199,12 @@ private function parseAggregation(int $index, EntityDefinition $definition, arra return null; } + if (str_contains($name, '?') || str_contains($name, ':')) { + $exceptions->add(new InvalidAggregationQueryException('The aggregation name should not contain a question mark or colon.'), '/aggregations/' . $index); + + return null; + } + $type = $aggregation['type'] ?? null; if (!\is_string($type) || empty($type) || is_numeric($type)) {
src/Core/Framework/Test/DataAbstractionLayer/Search/EntityAggregatorTest.php+13 −0 modified@@ -11,6 +11,7 @@ use Shopware\Core\Defaults; use Shopware\Core\DevOps\Environment\EnvironmentHelper; use Shopware\Core\Framework\Context; +use Shopware\Core\Framework\DataAbstractionLayer\DataAbstractionLayerException; use Shopware\Core\Framework\DataAbstractionLayer\Exception\InvalidAggregationQueryException; use Shopware\Core\Framework\DataAbstractionLayer\Search\Aggregation\Bucket\DateHistogramAggregation; use Shopware\Core\Framework\DataAbstractionLayer\Search\Aggregation\Bucket\FilterAggregation; @@ -1338,6 +1339,18 @@ public function testAggregationWithBacktickInName(): void $this->aggregator->aggregate($this->getContainer()->get(TaxDefinition::class), $criteria, $context); } + public function testAggregationNameWithDisallowedName(): void + { + $context = Context::createDefaultContext(); + + $criteria = new Criteria(); + $criteria->addAggregation(new SumAggregation('foo?foo', 'taxRate')); + + static::expectExceptionObject(DataAbstractionLayerException::invalidAggregationName('foo?foo')); + + $this->aggregator->aggregate($this->getContainer()->get(TaxDefinition::class), $criteria, $context); + } + private function insertData(): void { $repository = $this->getContainer()->get('product.repository');
src/Core/Framework/Test/DataAbstractionLayer/Search/Parser/AggregationParserTest.php+29 −0 modified@@ -318,4 +318,33 @@ public function testICanCreateARangeAggregation(): void static::assertEquals($expectedRanges[0] + ['key' => '1-2'], $computedRanges[0]); static::assertEquals($expectedRanges[1] + ['key' => '2-3'], $computedRanges[1]); } + + public function testQuestionMarkNotAllowedInAggregationName(): void + { + $criteria = new Criteria(); + $searchRequestException = new SearchRequestException(); + $this->parser->buildAggregations( + self::getContainer()->get(ProductDefinition::class), + [ + 'aggregations' => [ + [ + 'name' => 'max?agg', + 'type' => 'max', + 'field' => 'tax.taxRate', + ], + ], + ], + $criteria, + $searchRequestException + ); + + $errors = iterator_to_array($searchRequestException->getErrors(), false); + static::assertCount(1, $errors); + + $error = array_shift($errors); + + static::assertNotNull($error); + + static::assertSame('The aggregation name should not contain a question mark or colon.', $error['detail']); + } }
src/Core/System/SalesChannel/Entity/SalesChannelRepository.php+14 −4 modified@@ -9,6 +9,7 @@ use Shopware\Core\Framework\DataAbstractionLayer\Event\EntitySearchResultLoadedEvent; use Shopware\Core\Framework\DataAbstractionLayer\Exception\InconsistentCriteriaIdsException; use Shopware\Core\Framework\DataAbstractionLayer\Field\AssociationField; +use Shopware\Core\Framework\DataAbstractionLayer\Field\ManyToManyAssociationField; use Shopware\Core\Framework\DataAbstractionLayer\Read\EntityReaderInterface; use Shopware\Core\Framework\DataAbstractionLayer\RepositorySearchDetector; use Shopware\Core\Framework\DataAbstractionLayer\Search\AggregationResult\AggregationResultCollection; @@ -203,7 +204,7 @@ private function processCriteria(Criteria $topCriteria, SalesChannelContext $sal } $queue = [ - ['definition' => $this->definition, 'criteria' => $topCriteria], + ['definition' => $this->definition, 'criteria' => $topCriteria, 'path' => ''], ]; $maxCount = 100; @@ -216,8 +217,10 @@ private function processCriteria(Criteria $topCriteria, SalesChannelContext $sal $definition = $cur['definition']; $criteria = $cur['criteria']; + $path = $cur['path']; + $processedKey = $path . $definition::class; - if (isset($processed[$definition::class])) { + if (isset($processed[$processedKey])) { continue; } @@ -230,7 +233,7 @@ private function processCriteria(Criteria $topCriteria, SalesChannelContext $sal $this->eventDispatcher->dispatch($event, $eventName); } - $processed[$definition::class] = true; + $processed[$processedKey] = true; foreach ($criteria->getAssociations() as $associationName => $associationCriteria) { // find definition @@ -240,7 +243,14 @@ private function processCriteria(Criteria $topCriteria, SalesChannelContext $sal } $referenceDefinition = $field->getReferenceDefinition(); - $queue[] = ['definition' => $referenceDefinition, 'criteria' => $associationCriteria]; + $queue[] = ['definition' => $referenceDefinition, 'criteria' => $associationCriteria, 'path' => $path . '.' . $associationName]; + + if (!$field instanceof ManyToManyAssociationField) { + continue; + } + + $referenceDefinition = $field->getToManyReferenceDefinition(); + $queue[] = ['definition' => $referenceDefinition, 'criteria' => $associationCriteria, 'path' => $path . '.' . $associationName]; } } }
tests/unit/Core/Framework/Adapter/Twig/Node/FeatureCallSilentTokenTest.php+1 −1 modified@@ -26,7 +26,7 @@ public function testCompile(): void $code = <<<'PHP' // line 1 -\Shopware\Core\Framework\Feature::callSilentIfInactive('v6.5.0.0', function () use(&$context) { yield "test"; +\Shopware\Core\Framework\Feature::callSilentIfInactive("v6.5.0.0", function () use(&$context) { yield "test"; }); PHP;
tests/unit/Core/Framework/Adapter/Twig/TokenParser/FeatureFlagCallTokenParserTest.php+5 −0 modified@@ -59,6 +59,11 @@ public static function providerCode(): iterable '{% do foo.call %}', true, ]; + + yield 'test injection' => [ + '{% sw_silent_feature_call "aaa\' . system(\'id\') . \'bbb" %}{% do foo.call %}{% endsw_silent_feature_call %}', + true, + ]; } }
tests/unit/Framework/ContextTest.php+39 −0 added@@ -0,0 +1,39 @@ +<?php declare(strict_types=1); + +namespace Shopware\Tests\Unit\Framework; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\TestCase; +use Shopware\Core\Framework\Context; +use Twig\Environment; +use Twig\Error\RuntimeError; +use Twig\Loader\ArrayLoader; + +/** + * @internal + */ +#[CoversClass(Context::class)] +class ContextTest extends TestCase +{ + public static function twigMethodProviders(): \Generator + { + yield 'enableInheritance' => ['{{ context.enableInheritance("print_r") }}']; + yield 'disableInheritance' => ['{{ context.disableInheritance("print_r") }}']; + yield 'scope' => ['{{ context.scope("system", "print_r") }}']; + } + + #[DataProvider(methodName: 'twigMethodProviders')] + public function testCallableCannotBeCalledFromTwig(string $tpl): void + { + $context = Context::createDefaultContext(); + + $twig = new Environment(new ArrayLoader([ + 'tpl' => $tpl, + ])); + + static::expectException(RuntimeError::class); + + $twig->render('tpl', ['context' => $context]); + } +}
a784aa1cec06NEXT-37397 - Security picks
8 files changed · +87 −11
Framework/Adapter/Twig/Node/FeatureCallSilentToken.php+3 −1 modified@@ -24,7 +24,9 @@ public function compile(Compiler $compiler): void { $compiler ->addDebugInfo($this) - ->raw('\Shopware\Core\Framework\Feature::callSilentIfInactive(\'' . $this->flag . '\', function () use(&$context) { ') + ->raw('\Shopware\Core\Framework\Feature::callSilentIfInactive(') + ->string($this->flag) + ->raw(', function () use(&$context) { ') ->subcompile($this->getNode('body')) ->raw('});'); }
Framework/Context.php+6 −6 modified@@ -149,11 +149,11 @@ public function createWithVersionId(string $versionId): self /** * @template TReturn of mixed * - * @param callable(Context): TReturn $callback + * @param \Closure(Context): TReturn $callback * * @return TReturn the return value of the provided callback function */ - public function scope(string $scope, callable $callback) + public function scope(string $scope, \Closure $callback) { $currentScope = $this->getScope(); $this->scope = $scope; @@ -216,11 +216,11 @@ public function setRuleIds(array $ruleIds): void /** * @template TReturn of mixed * - * @param callable(Context): TReturn $function + * @param \Closure(Context): TReturn $function * * @return TReturn */ - public function enableInheritance(callable $function) + public function enableInheritance(\Closure $function) { $previous = $this->considerInheritance; $this->considerInheritance = true; @@ -233,11 +233,11 @@ public function enableInheritance(callable $function) /** * @template TReturn of mixed * - * @param callable(Context): TReturn $function + * @param \Closure(Context): TReturn $function * * @return TReturn */ - public function disableInheritance(callable $function) + public function disableInheritance(\Closure $function) { $previous = $this->considerInheritance; $this->considerInheritance = false;
Framework/DataAbstractionLayer/DataAbstractionLayerException.php+11 −0 modified@@ -44,6 +44,7 @@ class DataAbstractionLayerException extends HttpException public const INVALID_WRITE_INPUT = 'FRAMEWORK__INVALID_WRITE_INPUT'; public const DECODE_HANDLED_BY_HYDRATOR = 'FRAMEWORK__DECODE_HANDLED_BY_HYDRATOR'; public const ATTRIBUTE_NOT_FOUND = 'FRAMEWORK__ATTRIBUTE_NOT_FOUND'; + public const INVALID_AGGREGATION_NAME = 'FRAMEWORK__INVALID_AGGREGATION_NAME'; public static function invalidSerializerField(string $expectedClass, Field $field): self { @@ -402,4 +403,14 @@ public static function canNotFindAttribute(string $attribute, string $property): ['attribute' => $attribute, 'property' => $property] ); } + + public static function invalidAggregationName(string $name): self + { + return new self( + Response::HTTP_BAD_REQUEST, + self::INVALID_AGGREGATION_NAME, + 'Invalid aggregation name "{{ name }}", cannot contain question marks und colon.', + ['name' => $name] + ); + } }
Framework/DataAbstractionLayer/Dbal/EntityAggregator.php+5 −0 modified@@ -4,6 +4,7 @@ use Doctrine\DBAL\Connection; use Shopware\Core\Framework\Context; +use Shopware\Core\Framework\DataAbstractionLayer\DataAbstractionLayerException; use Shopware\Core\Framework\DataAbstractionLayer\DefinitionInstanceRegistry; use Shopware\Core\Framework\DataAbstractionLayer\EntityCollection; use Shopware\Core\Framework\DataAbstractionLayer\EntityDefinition; @@ -116,6 +117,10 @@ private function fetchAggregation( Criteria $criteria, Context $context ): AggregationResult { + if (str_contains($aggregation->getName(), '?') || str_contains($aggregation->getName(), ':')) { + throw DataAbstractionLayerException::invalidAggregationName($aggregation->getName()); + } + $clone = clone $criteria; $clone->resetAggregations(); $clone->resetSorting();
Framework/DataAbstractionLayer/Search/Parser/AggregationParser.php+6 −0 modified@@ -199,6 +199,12 @@ private function parseAggregation(int $index, EntityDefinition $definition, arra return null; } + if (str_contains($name, '?') || str_contains($name, ':')) { + $exceptions->add(new InvalidAggregationQueryException('The aggregation name should not contain a question mark or colon.'), '/aggregations/' . $index); + + return null; + } + $type = $aggregation['type'] ?? null; if (!\is_string($type) || empty($type) || is_numeric($type)) {
Framework/Test/DataAbstractionLayer/Search/EntityAggregatorTest.php+13 −0 modified@@ -11,6 +11,7 @@ use Shopware\Core\Defaults; use Shopware\Core\DevOps\Environment\EnvironmentHelper; use Shopware\Core\Framework\Context; +use Shopware\Core\Framework\DataAbstractionLayer\DataAbstractionLayerException; use Shopware\Core\Framework\DataAbstractionLayer\Exception\InvalidAggregationQueryException; use Shopware\Core\Framework\DataAbstractionLayer\Search\Aggregation\Bucket\DateHistogramAggregation; use Shopware\Core\Framework\DataAbstractionLayer\Search\Aggregation\Bucket\FilterAggregation; @@ -1338,6 +1339,18 @@ public function testAggregationWithBacktickInName(): void $this->aggregator->aggregate($this->getContainer()->get(TaxDefinition::class), $criteria, $context); } + public function testAggregationNameWithDisallowedName(): void + { + $context = Context::createDefaultContext(); + + $criteria = new Criteria(); + $criteria->addAggregation(new SumAggregation('foo?foo', 'taxRate')); + + static::expectExceptionObject(DataAbstractionLayerException::invalidAggregationName('foo?foo')); + + $this->aggregator->aggregate($this->getContainer()->get(TaxDefinition::class), $criteria, $context); + } + private function insertData(): void { $repository = $this->getContainer()->get('product.repository');
Framework/Test/DataAbstractionLayer/Search/Parser/AggregationParserTest.php+29 −0 modified@@ -318,4 +318,33 @@ public function testICanCreateARangeAggregation(): void static::assertEquals($expectedRanges[0] + ['key' => '1-2'], $computedRanges[0]); static::assertEquals($expectedRanges[1] + ['key' => '2-3'], $computedRanges[1]); } + + public function testQuestionMarkNotAllowedInAggregationName(): void + { + $criteria = new Criteria(); + $searchRequestException = new SearchRequestException(); + $this->parser->buildAggregations( + self::getContainer()->get(ProductDefinition::class), + [ + 'aggregations' => [ + [ + 'name' => 'max?agg', + 'type' => 'max', + 'field' => 'tax.taxRate', + ], + ], + ], + $criteria, + $searchRequestException + ); + + $errors = iterator_to_array($searchRequestException->getErrors(), false); + static::assertCount(1, $errors); + + $error = array_shift($errors); + + static::assertNotNull($error); + + static::assertSame('The aggregation name should not contain a question mark or colon.', $error['detail']); + } }
System/SalesChannel/Entity/SalesChannelRepository.php+14 −4 modified@@ -9,6 +9,7 @@ use Shopware\Core\Framework\DataAbstractionLayer\Event\EntitySearchResultLoadedEvent; use Shopware\Core\Framework\DataAbstractionLayer\Exception\InconsistentCriteriaIdsException; use Shopware\Core\Framework\DataAbstractionLayer\Field\AssociationField; +use Shopware\Core\Framework\DataAbstractionLayer\Field\ManyToManyAssociationField; use Shopware\Core\Framework\DataAbstractionLayer\Read\EntityReaderInterface; use Shopware\Core\Framework\DataAbstractionLayer\RepositorySearchDetector; use Shopware\Core\Framework\DataAbstractionLayer\Search\AggregationResult\AggregationResultCollection; @@ -203,7 +204,7 @@ private function processCriteria(Criteria $topCriteria, SalesChannelContext $sal } $queue = [ - ['definition' => $this->definition, 'criteria' => $topCriteria], + ['definition' => $this->definition, 'criteria' => $topCriteria, 'path' => ''], ]; $maxCount = 100; @@ -216,8 +217,10 @@ private function processCriteria(Criteria $topCriteria, SalesChannelContext $sal $definition = $cur['definition']; $criteria = $cur['criteria']; + $path = $cur['path']; + $processedKey = $path . $definition::class; - if (isset($processed[$definition::class])) { + if (isset($processed[$processedKey])) { continue; } @@ -230,7 +233,7 @@ private function processCriteria(Criteria $topCriteria, SalesChannelContext $sal $this->eventDispatcher->dispatch($event, $eventName); } - $processed[$definition::class] = true; + $processed[$processedKey] = true; foreach ($criteria->getAssociations() as $associationName => $associationCriteria) { // find definition @@ -240,7 +243,14 @@ private function processCriteria(Criteria $topCriteria, SalesChannelContext $sal } $referenceDefinition = $field->getReferenceDefinition(); - $queue[] = ['definition' => $referenceDefinition, 'criteria' => $associationCriteria]; + $queue[] = ['definition' => $referenceDefinition, 'criteria' => $associationCriteria, 'path' => $path . '.' . $associationName]; + + if (!$field instanceof ManyToManyAssociationField) { + continue; + } + + $referenceDefinition = $field->getToManyReferenceDefinition(); + $queue[] = ['definition' => $referenceDefinition, 'criteria' => $associationCriteria, 'path' => $path . '.' . $associationName]; } } }
ad83d38809dfNEXT-37527 - Consider many to many assosications for store-api protection
1 file changed · +14 −4
src/Core/System/SalesChannel/Entity/SalesChannelRepository.php+14 −4 modified@@ -9,6 +9,7 @@ use Shopware\Core\Framework\DataAbstractionLayer\Event\EntitySearchResultLoadedEvent; use Shopware\Core\Framework\DataAbstractionLayer\Exception\InconsistentCriteriaIdsException; use Shopware\Core\Framework\DataAbstractionLayer\Field\AssociationField; +use Shopware\Core\Framework\DataAbstractionLayer\Field\ManyToManyAssociationField; use Shopware\Core\Framework\DataAbstractionLayer\Read\EntityReaderInterface; use Shopware\Core\Framework\DataAbstractionLayer\RepositorySearchDetector; use Shopware\Core\Framework\DataAbstractionLayer\Search\AggregationResult\AggregationResultCollection; @@ -170,7 +171,7 @@ private function processCriteria(Criteria $topCriteria, SalesChannelContext $sal } $queue = [ - ['definition' => $this->definition, 'criteria' => $topCriteria], + ['definition' => $this->definition, 'criteria' => $topCriteria, 'path' => ''], ]; $maxCount = 100; @@ -183,8 +184,10 @@ private function processCriteria(Criteria $topCriteria, SalesChannelContext $sal $definition = $cur['definition']; $criteria = $cur['criteria']; + $path = $cur['path']; + $processedKey = $path . $definition::class; - if (isset($processed[$definition::class])) { + if (isset($processed[$processedKey])) { continue; } @@ -197,7 +200,7 @@ private function processCriteria(Criteria $topCriteria, SalesChannelContext $sal $this->eventDispatcher->dispatch($event, $eventName); } - $processed[$definition::class] = true; + $processed[$processedKey] = true; foreach ($criteria->getAssociations() as $associationName => $associationCriteria) { // find definition @@ -207,7 +210,14 @@ private function processCriteria(Criteria $topCriteria, SalesChannelContext $sal } $referenceDefinition = $field->getReferenceDefinition(); - $queue[] = ['definition' => $referenceDefinition, 'criteria' => $associationCriteria]; + $queue[] = ['definition' => $referenceDefinition, 'criteria' => $associationCriteria, 'path' => $path . '.' . $associationName]; + + if (!$field instanceof ManyToManyAssociationField) { + continue; + } + + $referenceDefinition = $field->getToManyReferenceDefinition(); + $queue[] = ['definition' => $referenceDefinition, 'criteria' => $associationCriteria, 'path' => $path . '.' . $associationName]; } } }
d35ee2eda5c9NEXT-37398 - Improve validation of feature flag name in FeatureCallSilentToken
1 file changed · +3 −1
Framework/Adapter/Twig/Node/FeatureCallSilentToken.php+3 −1 modified@@ -22,7 +22,9 @@ public function compile(Compiler $compiler): void { $compiler ->addDebugInfo($this) - ->raw('\Shopware\Core\Framework\Feature::callSilentIfInactive(\'' . $this->flag . '\', function () use(&$context) { ') + ->raw('\Shopware\Core\Framework\Feature::callSilentIfInactive(') + ->string($this->flag) + ->raw(', function () use(&$context) { ') ->subcompile($this->getNode('body')) ->raw('});'); }
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
7- github.com/advisories/GHSA-hhcq-ph6w-494gghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2024-42354ghsaADVISORY
- github.com/shopware/core/commit/a784aa1cec0624e36e0ee4d41aeebaed40e0442fghsax_refsource_MISCWEB
- github.com/shopware/core/commit/d35ee2eda5c995faeb08b3dad127eab65c64e2a2ghsax_refsource_MISCWEB
- github.com/shopware/shopware/commit/8504ba7e56e53add6a1d5b9d45015e3d899cd0acghsax_refsource_MISCWEB
- github.com/shopware/shopware/commit/ad83d38809df457efef21c37ce0996430334bf01ghsax_refsource_MISCWEB
- github.com/shopware/shopware/security/advisories/GHSA-hhcq-ph6w-494gghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.