SQL Injection through sorting parameters in SyliusGridBundle
Description
SyliusGridBundle is a package of generic data grids for Symfony applications. Prior to versions 1.10.1 and 1.11-rc2, values added at the end of query sorting were passed directly to the database. The maintainers do not know if this could lead to direct SQL injections but took steps to remediate the vulnerability. The issue is fixed in versions 1.10.1 and 1.11-rc2. As a workaround, overwrite theSylius\Component\Grid\Sorting\Sorter.php class and register it in the container. More information about this workaround is available in the GitHub Security Advisory.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
sylius/grid-bundlePackagist | < 1.10.1 | 1.10.1 |
Affected products
1- Range: < 1.10.1
Patches
173d0791d0575bug #222 [Security] Dql injection through sorting parameters blocked (TheMilek)
10 files changed · +265 −28
src/Bundle/Resources/config/services.xml+6 −1 modified@@ -46,7 +46,12 @@ <argument type="service" id="sylius.registry.grid_filter" /> <argument type="service" id="sylius.grid.filters_criteria_resolver" /> </service> - <service id="sylius.grid.sorter" class="Sylius\Component\Grid\Sorting\Sorter" /> + <service id="sylius.grid.sorter.validator" class="Sylius\Component\Grid\Validation\SortingParametersValidator" /> + <service id="sylius.grid.field.validator" class="Sylius\Component\Grid\Validation\FieldValidator" /> + <service id="sylius.grid.sorter" class="Sylius\Component\Grid\Sorting\Sorter"> + <argument type="service" id="sylius.grid.sorter.validator" /> + <argument type="service" id="sylius.grid.field.validator" /> + </service> <service id="sylius.grid.data_source_provider" class="Sylius\Component\Grid\Data\DataSourceProvider"> <argument type="service" id="sylius.registry.grid_driver" /> </service>
src/Component/composer.json+1 −0 modified@@ -33,6 +33,7 @@ "sylius/registry": "^1.5", "symfony/deprecation-contracts": "^2.2", "symfony/event-dispatcher": "^4.4 || ^5.2", + "symfony/http-kernel": "^4.4 || ^5.2", "webmozart/assert": "^1.9" }, "require-dev": {
src/Component/Sorting/Sorter.php+18 −0 modified@@ -13,19 +13,37 @@ namespace Sylius\Component\Grid\Sorting; +use Sylius\Component\Grid\Validation\FieldValidator; +use Sylius\Component\Grid\Validation\SortingParametersValidator; use Sylius\Component\Grid\Data\DataSourceInterface; use Sylius\Component\Grid\Definition\Grid; use Sylius\Component\Grid\Parameters; +use Sylius\Component\Grid\Validation\SortingParametersValidatorInterface; +use Sylius\Component\Grid\Validation\FieldValidatorInterface; final class Sorter implements SorterInterface { + private SortingParametersValidatorInterface $sortingValidator; + + private FieldValidatorInterface $fieldValidator; + + public function __construct(?SortingParametersValidatorInterface $sortingValidator = null, ?FieldValidatorInterface $fieldValidator = null) + { + $this->sortingValidator = $sortingValidator ?? new SortingParametersValidator(); + $this->fieldValidator = $fieldValidator ?? new FieldValidator(); + } + public function sort(DataSourceInterface $dataSource, Grid $grid, Parameters $parameters): void { + $enabledFields = $grid->getEnabledFields(); + $expressionBuilder = $dataSource->getExpressionBuilder(); $sorting = $parameters->get('sorting', $grid->getSorting()); + $this->sortingValidator->validateSortingParameters($sorting, $enabledFields); foreach ($sorting as $field => $order) { + $this->fieldValidator->validateFieldName($field, $enabledFields); $gridField = $grid->getField($field); $property = $gridField->getSortable();
src/Component/spec/Sorting/SorterSpec.php+34 −27 modified@@ -20,68 +20,75 @@ use Sylius\Component\Grid\Definition\Grid; use Sylius\Component\Grid\Parameters; use Sylius\Component\Grid\Sorting\SorterInterface; +use Sylius\Component\Grid\Validation\SortingParametersValidatorInterface; +use Sylius\Component\Grid\Validation\FieldValidatorInterface; final class SorterSpec extends ObjectBehavior { + function let(SortingParametersValidatorInterface $sortingValidator, FieldValidatorInterface $fieldValidator): void + { + $this->beConstructedWith($sortingValidator, $fieldValidator); + } + function it_implements_grid_data_source_sorter_interface(): void { $this->shouldImplement(SorterInterface::class); } function it_sorts_the_data_source_via_expression_builder_based_on_the_grid_definition( Grid $grid, - Field $nameField, - Field $nonSortableField, + Field $field, + Field $anotherField, DataSourceInterface $dataSource, - ExpressionBuilderInterface $expressionBuilder + ExpressionBuilderInterface $expressionBuilder, + SortingParametersValidatorInterface $sortingValidator, + FieldValidatorInterface $fieldValidator ): void { $parameters = new Parameters(); $dataSource->getExpressionBuilder()->willReturn($expressionBuilder); - $grid->getSorting()->willReturn(['name' => 'desc', 'non_sortable_field' => 'asc']); + $grid->getSorting()->willReturn(['name' => 'desc']); + $grid->getEnabledFields()->willReturn(['name'=> $field, 'code' => $anotherField]); - $grid->hasField('name')->willReturn(true); - $grid->getField('name')->willReturn($nameField); - $nameField->isSortable()->willReturn(true); - $nameField->getSortable()->willReturn('translation.name'); + $sortingValidator->validateSortingParameters(['name' => 'desc'], ['name' => $field , 'code' => $anotherField])->shouldBeCalled(); + $fieldValidator->validateFieldName('name', ['name' => $field , 'code' => $anotherField])->shouldBeCalled(); - $grid->hasField('non_sortable_field')->willReturn(true); - $grid->getField('non_sortable_field')->willReturn($nonSortableField); - $nonSortableField->isSortable()->willReturn(false); - $nonSortableField->getSortable()->willReturn(null); + $grid->hasField('name')->willReturn(true); + $grid->getField('name')->willReturn($field); + $field->isSortable()->willReturn(true); + $field->getSortable()->willReturn('translation.name'); $expressionBuilder->addOrderBy('translation.name', 'desc')->shouldBeCalled(); - $expressionBuilder->addOrderBy(null, 'asc')->shouldNotBeCalled(); $this->sort($dataSource, $grid, $parameters); } function it_sorts_the_data_source_via_expression_builder_based_on_sorting_parameter( Grid $grid, - Field $nameField, - Field $nonSortableField, + Field $field, + Field $anotherField, DataSourceInterface $dataSource, - ExpressionBuilderInterface $expressionBuilder + ExpressionBuilderInterface $expressionBuilder, + SortingParametersValidatorInterface $sortingValidator, + FieldValidatorInterface $fieldValidator ): void { - $parameters = new Parameters(['sorting' => ['name' => 'asc', 'non_sortable_field' => 'asc']]); + $parameters = new Parameters(['sorting' => ['name' => 'asc']]); $dataSource->getExpressionBuilder()->willReturn($expressionBuilder); - $grid->getSorting()->willReturn(['code' => 'desc']); + $grid->getSorting()->willReturn(['code' => 'asc']); + $grid->getEnabledFields()->willReturn(['name' => $field , 'code' => $anotherField]); - $grid->hasField('name')->willReturn(true); - $grid->getField('name')->willReturn($nameField); - $nameField->isSortable()->willReturn(true); - $nameField->getSortable()->willReturn('translation.name'); + $sortingValidator->validateSortingParameters(['name' => 'asc'], ['name' => $field , 'code' => $anotherField])->shouldBeCalled(); + $fieldValidator->validateFieldName('name', ['name' => $field , 'code' => $anotherField])->shouldBeCalled(); - $grid->hasField('non_sortable_field')->willReturn(true); - $grid->getField('non_sortable_field')->willReturn($nonSortableField); - $nonSortableField->isSortable()->willReturn(false); - $nonSortableField->getSortable()->willReturn(null); + $grid->hasField('name')->willReturn(true); + $grid->getField('name')->willReturn($field); + $field->isSortable()->willReturn(true); + $field->getSortable()->willReturn('translation.name'); $expressionBuilder->addOrderBy('translation.name', 'asc')->shouldBeCalled(); - $expressionBuilder->addOrderBy(null, 'asc')->shouldNotBeCalled(); $this->sort($dataSource, $grid, $parameters); }
src/Component/spec/Validation/FieldValidatorSpec.php+56 −0 added@@ -0,0 +1,56 @@ +<?php + +/* + * This file is part of the Sylius package. + * + * (c) Paweł Jędrzejewski + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace spec\Sylius\Component\Grid\Validation; + +use PhpSpec\ObjectBehavior; +use Sylius\Component\Grid\Definition\Field; +use Sylius\Component\Grid\Definition\Grid; +use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; +use Sylius\Component\Grid\Validation\FieldValidatorInterface; + +final class FieldValidatorSpec extends ObjectBehavior +{ + function it_implements_field_validator_interface(): void + { + $this->shouldImplement(FieldValidatorInterface::class); + } + + function it_throws_exception_if_wrong_field_name_provided( + Grid $grid, + Field $field, + Field $anotherField + ): void { + $grid->getEnabledFields()->willReturn(['name' => $field , 'code' => $anotherField]); + $grid->getSorting()->willReturn(['sorting' => ['non_sortable_field' => 'desc']]); + + $this + ->shouldThrow(new BadRequestHttpException('non_sortable_field is not valid field, did you mean one of these: name, code?')) + ->during('validateFieldName', ['non_sortable_field', ['name' => $field , 'code' => $anotherField]]) + ; + } + + function it_passes_if_valid_sorting_parameter_provided( + Grid $grid, + Field $field, + Field $anotherField + ): void { + $grid->getEnabledFields()->willReturn(['name' => $field , 'code' => $anotherField]); + $grid->getSorting()->willReturn(['sorting' => ['sortable_field' => 'desc']]); + + $this + ->shouldNotThrow(new BadRequestHttpException()) + ->during('validateFieldName', ['sortable_field', ['sortable_field' => $field , 'code' => $anotherField]]) + ; + } +}
src/Component/spec/Validation/SortingParametersValidatorSpec.php+56 −0 added@@ -0,0 +1,56 @@ +<?php + +/* + * This file is part of the Sylius package. + * + * (c) Paweł Jędrzejewski + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace spec\Sylius\Component\Grid\Validation; + +use PhpSpec\ObjectBehavior; +use Sylius\Component\Grid\Definition\Field; +use Sylius\Component\Grid\Definition\Grid; +use Sylius\Component\Grid\Validation\SortingParametersValidatorInterface; +use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; + +final class SortingParametersValidatorSpec extends ObjectBehavior +{ + function it_implements_grid_data_source_sorting_validator_interface(): void + { + $this->shouldImplement(SortingParametersValidatorInterface::class); + } + + function it_throws_exception_if_wrong_sorting_parameter_provided( + Grid $grid, + Field $field, + Field $anotherField + ): void { + $grid->getEnabledFields()->willReturn(['name' => $field , 'code' => $anotherField]); + $grid->getSorting()->willReturn(['name' => 'non_sortable_parameter']); + + $this + ->shouldThrow(new BadRequestHttpException('non_sortable_parameter is not valid, use asc or desc instead.')) + ->during('validateSortingParameters', [['name' => 'non_sortable_parameter'], ['name' => $field , 'code' => $anotherField]]) + ; + } + + function it_passes_if_valid_sorting_parameter_provided( + Grid $grid, + Field $field, + Field $anotherField + ): void { + $grid->getEnabledFields()->willReturn(['name' => $field , 'code' => $anotherField]); + $grid->getSorting()->willReturn(['name' => 'asc']); + + $this + ->shouldNotThrow(new BadRequestHttpException()) + ->during('validateSortingParameters', [['name' => 'asc'], ['name' => $field , 'code' => $anotherField]]) + ; + } +}
src/Component/Validation/FieldValidatorInterface.php+19 −0 added@@ -0,0 +1,19 @@ +<?php + +/* + * This file is part of the Sylius package. + * + * (c) Paweł Jędrzejewski + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Sylius\Component\Grid\Validation; + +interface FieldValidatorInterface +{ + public function validateFieldName(string $fieldName, array $enabledFields): void; +}
src/Component/Validation/FieldValidator.php+28 −0 added@@ -0,0 +1,28 @@ +<?php + +/* + * This file is part of the Sylius package. + * + * (c) Paweł Jędrzejewski + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Sylius\Component\Grid\Validation; + +use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; + +final class FieldValidator implements FieldValidatorInterface +{ + public function validateFieldName(string $fieldName, array $enabledFields): void + { + $enabledFieldsNames = array_keys($enabledFields); + + if (!in_array($fieldName, $enabledFieldsNames, true)) { + throw new BadRequestHttpException(sprintf('%s is not valid field, did you mean one of these: %s?', $fieldName, implode(', ', $enabledFieldsNames))); + } + } +}
src/Component/Validation/SortingParametersValidatorInterface.php+19 −0 added@@ -0,0 +1,19 @@ +<?php + +/* + * This file is part of the Sylius package. + * + * (c) Paweł Jędrzejewski + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Sylius\Component\Grid\Validation; + +interface SortingParametersValidatorInterface +{ + public function validateSortingParameters(array $sorting, array $enabledFields): void; +}
src/Component/Validation/SortingParametersValidator.php+28 −0 added@@ -0,0 +1,28 @@ +<?php + +/* + * This file is part of the Sylius package. + * + * (c) Paweł Jędrzejewski + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Sylius\Component\Grid\Validation; + +use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; + +final class SortingParametersValidator implements SortingParametersValidatorInterface +{ + public function validateSortingParameters(array $sorting, array $enabledFields): void + { + foreach (array_keys($enabledFields) as $key) { + if (array_key_exists($key, $sorting) && !in_array($sorting[$key], ['asc', 'desc'])) { + throw new BadRequestHttpException(sprintf('%s is not valid, use asc or desc instead.', $sorting[$key])); + } + } + } +}
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-2xmm-g482-4439ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2022-24752ghsaADVISORY
- github.com/Sylius/SyliusGridBundle/commit/73d0791d0575f955e830a3da4c3345f420d2f784ghsax_refsource_MISCWEB
- github.com/Sylius/SyliusGridBundle/pull/222ghsax_refsource_MISCWEB
- github.com/Sylius/SyliusGridBundle/releases/tag/v1.10.1ghsax_refsource_MISCWEB
- github.com/Sylius/SyliusGridBundle/releases/tag/v1.11.0-RC.2ghsax_refsource_MISCWEB
- github.com/Sylius/SyliusGridBundle/security/advisories/GHSA-2xmm-g482-4439ghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.