Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NumericFilter: "value is out of range for type integer" sql exception #6361

Open
PawelSuwinski opened this issue May 8, 2024 · 1 comment

Comments

@PawelSuwinski
Copy link

PawelSuwinski commented May 8, 2024

API Platform version(s) affected: 3.3.2 / 3.2.21 / 3.1.28

NumericFilter does not sanitize values against SQL types limits, so for example filtered on integer type field with value out of limit ends with low level exception:

SQLSTATE[22003]: Numeric value out of range: 7 ERROR: value "2147483648" is out of range for type integer

I had to use decorator to handle this, in short:

final class NumericFilter implements FilterInterface
{
    private const TYPES_RANGE = [
        Types::SMALLINT => 32767,
        Types::INTEGER => 2147483647,
        Types::BIGINT => 9223372036854775807,
    ];
// ...
    public function apply(//...) 
    {
        foreach ($context['filters'] as $property => $value) {
            $type = $this->getDoctrineFieldType($property, $resourceClass);
            if (isset(self::TYPES_RANGE[$type])) {
                $context['filters'][$property] = self::sanitizedValue($value, self::TYPES_RANGE[$type]);
            }
        }
        $this->decorated->apply($queryBuilder, $queryNameGenerator, $resourceClass, $operation, $context);
    }
     public function getDescription(string $resourceClass): array
    {
        $description = $this->decoraged->getDescription($resourceClass);
        $types = [];
        foreach ($description as $key => $desc) {
            $property = $desc['property'];
            $type = $types[$property] ??= $this->getDoctrineFieldType($property, $resourceClass);
            if (isset(self::TYPES_RANGE[$type])) {
                $itemSchema = [
                    'type' => 'integer',
                    'minimum' => -self::TYPES_RANGE[$type],
                    'maximum' => self::TYPES_RANGE[$type],
                ];
                $description[$key]['schema'] = $desc['is_collection'] ? [
                    'type' => 'array',
                    'items' => $itemSchema,
                ] : $itemSchema;
            }
        }

        return $description;
    }

    private static function sanitizedValue($value, int $range)
    {
        return match (true) {
            is_array($value) => array_map(static fn ($v) => self::sanitizedValue($v, $range), $value),
            !is_numeric($value) => null,
            abs((int) $value) > $range => null,
            default => $value
        };
    }

I can possibly provide PR for NumericFilter if it should be fixed for that.

@soyuka
Copy link
Member

soyuka commented May 24, 2024

Doesn't that differ between DBMS? On one hand we could just catch the sql exception, on the other I assume that it'd be better to never hit this part of the code and use a Parameter assertion to avoid this:

#[GetCollection(
    parameters: [
        'num' => new QueryParameter(schema: ['minimum' => 1, 'maximum' => 3]),
    ],
)]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants