Skip to content

Commit

Permalink
[10.x] Gracefully handle scientific notation (#48002)
Browse files Browse the repository at this point in the history
* Limit scientific notation exponent size

* Naming

* Flip guard

* Lint
  • Loading branch information
timacdonald authored Aug 9, 2023
1 parent 5d0c695 commit e1f7794
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 1 deletion.
32 changes: 31 additions & 1 deletion src/Illuminate/Validation/Concerns/ValidatesAttributes.php
Original file line number Diff line number Diff line change
Expand Up @@ -2352,7 +2352,7 @@ protected function getSize($attribute, $value)
// is the size. If it is a file, we take kilobytes, and for a string the
// entire length of the string will be considered the attribute size.
if (is_numeric($value) && $hasNumeric) {
return $this->trim($value);
return $this->ensureExponentWithinAllowedRange($attribute, $this->trim($value));
} elseif (is_array($value)) {
return count($value);
} elseif ($value instanceof File) {
Expand Down Expand Up @@ -2469,4 +2469,34 @@ protected function trim($value)
{
return is_string($value) ? trim($value) : $value;
}

/**
* Ensure the exponent is within the allowed range.
*
* @param string $attribute
* @param mixed $value
* @return mixed
*/
protected function ensureExponentWithinAllowedRange($attribute, $value)
{
$stringValue = (string) $value;

if (! is_numeric($value) || ! Str::contains($stringValue, 'e', ignoreCase: true)) {
return $value;
}

$scale = (int) (Str::contains($stringValue, 'e')
? Str::after($stringValue, 'e')
: Str::after($stringValue, 'E'));

$withinRange = (
$this->ensureExponentWithinAllowedRangeUsing ?? fn ($scale) => $scale <= 1000 && $scale >= -1000
)($scale, $attribute, $value);

if (! $withinRange) {
throw new MathException('Scientific notation exponent outside of allowed range.');
}

return $value;
}
}
20 changes: 20 additions & 0 deletions src/Illuminate/Validation/Validator.php
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,13 @@ class Validator implements ValidatorContract
*/
protected $exception = ValidationException::class;

/**
* The custom callback to determine if an exponent is within allowed range.
*
* @var callable|null
*/
protected $ensureExponentWithinAllowedRangeUsing;

/**
* Create a new Validator instance.
*
Expand Down Expand Up @@ -1491,6 +1498,19 @@ public function setException($exception)
return $this;
}

/**
* Ensure exponent is within range with the given callback.
*
* @param callable(int $scale, string $attribute, mixed $value) $callback
* @return $this
*/
public function ensureExponentWithinAllowedRangeUsing($callback)
{
$this->ensureExponentWithinAllowedRangeUsing = $callback;

return $this;
}

/**
* Get the Translator implementation.
*
Expand Down
71 changes: 71 additions & 0 deletions tests/Validation/ValidationValidatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Arr;
use Illuminate\Support\Carbon;
use Illuminate\Support\Exceptions\MathException;
use Illuminate\Translation\ArrayLoader;
use Illuminate\Translation\Translator;
use Illuminate\Validation\DatabasePresenceVerifierInterface;
Expand Down Expand Up @@ -8767,6 +8768,76 @@ public function testItTrimsSpaceFromParameters()
], $validator->messages()->keys());
}

/** @dataProvider outsideRangeExponents */
public function testItLimitsLengthOfScientificNotationExponent($value)
{
$trans = $this->getIlluminateArrayTranslator();
$validator = new Validator($trans, ['foo' => $value], ['foo' => 'numeric|min:3']);

$this->expectException(MathException::class);
$this->expectExceptionMessage('Scientific notation exponent outside of allowed range.');

$validator->passes();
}

public static function outsideRangeExponents()
{
return [
['1.0e+1001'],
['1.0E+1001'],
['1.0e1001'],
['1.0E1001'],
['1.0e-1001'],
['1.0E-1001'],
];
}

/** @dataProvider withinRangeExponents */
public function testItAllowsScientificNotationWithinRange($value, $rule)
{
$trans = $this->getIlluminateArrayTranslator();
$validator = new Validator($trans, ['foo' => $value], ['foo' => ['numeric', $rule]]);

$this->assertTrue($validator->passes());
}

public static function withinRangeExponents()
{
return [
['1.0e+1000', 'min:3'],
['1.0E+1000', 'min:3'],
['1.0e1000', 'min:3'],
['1.0E1000', 'min:3'],
['1.0e-1000', 'max:3'],
['1.0E-1000', 'max:3'],
];
}

public function testItCanConfigureAllowedExponentRange()
{
$trans = $this->getIlluminateArrayTranslator();
$validator = new Validator($trans, ['foo' => '1.0e-1000'], ['foo' => ['numeric', 'max:3']]);
$scale = $attribute = $value = null;
$withinRange = true;

$validator->ensureExponentWithinAllowedRangeUsing(function () use (&$scale, &$attribute, &$value, &$withinRange) {
[$scale, $attribute, $value] = func_get_args();

return $withinRange;
});

$this->assertTrue($validator->passes());
$this->assertSame(-1000, $scale);
$this->assertSame('foo', $attribute);
$this->assertSame('1.0e-1000', $value);

$withinRange = false;
$this->expectException(MathException::class);
$this->expectExceptionMessage('Scientific notation exponent outside of allowed range.');

$validator->passes();
}

protected function getTranslator()
{
return m::mock(TranslatorContract::class);
Expand Down

0 comments on commit e1f7794

Please sign in to comment.