VYPR
High severityNVD Advisory· Published Aug 8, 2024· Updated Aug 9, 2024

Shopware vulnerable to Server Side Template Injection in Twig using Context functions

CVE-2024-42356

Description

Shopware is an open commerce platform. Prior to versions 6.6.5.1 and 6.5.8.13, the context variable is injected into almost any Twig Template and allows to access to current language, currency information. The context object allows also to switch for a short time the scope of the Context as a helper with a callable function. The function can be called also from Twig and as the second parameter allows any callable, it's possible to call from Twig any statically callable PHP function/method. It's not possible as customer to provide any Twig code, the attacker would require access to Administration to exploit it using Mail templates or using App Scripts. Update to Shopware 6.6.5.1 or 6.5.8.13 to receive a patch. For older versions of 6.1, 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.

PackageAffected versionsPatched versions
shopware/corePackagist
< 6.5.8.136.5.8.13
shopware/platformPackagist
< 6.5.8.136.5.8.13
shopware/platformPackagist
>= 6.6.0.0, < 6.6.5.16.6.5.1
shopware/corePackagist
>= 6.6.0.0, < 6.6.5.16.6.5.1

Affected products

1

Patches

4
a784aa1cec06

NEXT-37397 - Security picks

https://github.com/shopware/coreSoner SayakciAug 7, 2024via ghsa
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];
                 }
             }
         }
    
8504ba7e56e5

NEXT-37397 - Security picks

https://github.com/shopware/shopwareSoner SayakciAug 7, 2024via ghsa
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]);
    +    }
    +}
    
e43423bcc93c

NEXT-37399 - Allow only Closure in Context class callbacks

https://github.com/shopware/shopwareSoner SayakciJul 24, 2024via ghsa
2 files changed · +43 6
  • src/Core/Framework/Context.php+6 6 modified
    @@ -144,11 +144,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;
    @@ -211,11 +211,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;
    @@ -228,11 +228,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;
    
  • tests/unit/Framework/ContextTest.php+37 0 added
    @@ -0,0 +1,37 @@
    +<?php declare(strict_types=1);
    +
    +namespace Shopware\Tests\Unit\Framework;
    +
    +use PHPUnit\Framework\Attributes\CoversClass;
    +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") }}'];
    +    }
    +
    +    public function testCallableCannotBeCalledFromTwig(): void
    +    {
    +        $context = Context::createDefaultContext();
    +
    +        $twig = new Environment(new ArrayLoader([
    +            'tpl' => '{{ context.enableInheritance("print_r") }}',
    +        ]));
    +
    +        static::expectException(RuntimeError::class);
    +
    +        $twig->render('tpl', ['context' => $context]);
    +    }
    +}
    
04183e0c02af

NEXT-37399 - Allow only Closure in Context class callbacks

https://github.com/shopware/coreSoner SayakciJul 24, 2024via ghsa
1 file changed · +6 6
  • Framework/Context.php+6 6 modified
    @@ -144,11 +144,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;
    @@ -211,11 +211,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;
    @@ -228,11 +228,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;
    

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

News mentions

0

No linked articles in our index yet.