core icon indicating copy to clipboard operation
core copied to clipboard

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

Open PawelSuwinski opened this issue 1 year ago • 1 comments

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.

PawelSuwinski avatar May 08 '24 06:05 PawelSuwinski

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]),
    ],
)]

soyuka avatar May 24 '24 07:05 soyuka

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Jul 23 '24 23:07 stale[bot]