diff --git a/Classes/Domain/Repository/AbstractRepository.php b/Classes/Domain/Repository/AbstractRepository.php index 3b7acee1..9b31d367 100644 --- a/Classes/Domain/Repository/AbstractRepository.php +++ b/Classes/Domain/Repository/AbstractRepository.php @@ -343,7 +343,7 @@ protected function extendWhereClauseWithFilterSizeClass(FilterDto $filter, strin if ($table !== '') { $table .= '.'; } - $sql .= ' and ' . $table . 'size_class = ' . $filter->getSizeClass(); + $sql .= ' and ' . $table . 'size_class = ' . (int)$filter->getSizeClass(); } return $sql; } @@ -355,7 +355,7 @@ protected function extendWhereClauseWithFilterRevenueClass(FilterDto $filter, st if ($table !== '') { $table .= '.'; } - $sql .= ' and ' . $table . 'revenue_class = ' . $filter->getRevenueClass(); + $sql .= ' and ' . $table . 'revenue_class = ' . (int)$filter->getRevenueClass(); } return $sql; } diff --git a/Tests/Unit/Domain/Repository/AbstractRepositoryTest.php b/Tests/Unit/Domain/Repository/AbstractRepositoryTest.php new file mode 100644 index 00000000..58d1382e --- /dev/null +++ b/Tests/Unit/Domain/Repository/AbstractRepositoryTest.php @@ -0,0 +1,86 @@ +repository = new AbstractRepositoryFixture(); + } + + public static function sizeAndRevenueClassDataProvider(): array + { + return [ + 'empty value produces no clause' => [ + '', + '', + ], + 'numeric class code is kept' => [ + '3', + ' and c.size_class = 3', + ], + 'zero padded class code is normalised to integer' => [ + '01', + ' and c.size_class = 1', + ], + 'union based sql injection collapses to integer' => [ + '0 union select 99999999,1 order by 1 desc', + ' and c.size_class = 0', + ], + 'boolean based sql injection collapses to integer' => [ + '1 or 1', + ' and c.size_class = 1', + ], + 'injection with denylist characters collapses to integer' => [ + '1) union select password from be_users -- ', + ' and c.size_class = 1', + ], + ]; + } + + #[DataProvider('sizeAndRevenueClassDataProvider')] + public function testExtendWhereClauseWithFilterSizeClassIsNotInjectable( + string $sizeClass, + string $expectedSql + ): void { + $filter = new FilterDto(); + $filter->setSizeClass($sizeClass); + self::assertSame( + $expectedSql, + $this->repository->callExtendWhereClauseWithFilterSizeClass($filter, 'c') + ); + } + + #[DataProvider('sizeAndRevenueClassDataProvider')] + public function testExtendWhereClauseWithFilterRevenueClassIsNotInjectable( + string $revenueClass, + string $expectedSql + ): void { + $filter = new FilterDto(); + $filter->setRevenueClass($revenueClass); + self::assertSame( + str_replace('size_class', 'revenue_class', $expectedSql), + $this->repository->callExtendWhereClauseWithFilterRevenueClass($filter, 'c') + ); + } +} diff --git a/Tests/Unit/Fixtures/Domain/Repository/AbstractRepositoryFixture.php b/Tests/Unit/Fixtures/Domain/Repository/AbstractRepositoryFixture.php new file mode 100644 index 00000000..8a6c496c --- /dev/null +++ b/Tests/Unit/Fixtures/Domain/Repository/AbstractRepositoryFixture.php @@ -0,0 +1,29 @@ +extendWhereClauseWithFilterSizeClass($filter, $table); + } + + public function callExtendWhereClauseWithFilterRevenueClass(FilterDto $filter, string $table = ''): string + { + return $this->extendWhereClauseWithFilterRevenueClass($filter, $table); + } +}