Skip to content

Commit e1f7794

Browse files
authored
[10.x] Gracefully handle scientific notation (#48002)
* Limit scientific notation exponent size * Naming * Flip guard * Lint
1 parent 5d0c695 commit e1f7794

File tree

3 files changed

+122
-1
lines changed

3 files changed

+122
-1
lines changed

src/Illuminate/Validation/Concerns/ValidatesAttributes.php

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2352,7 +2352,7 @@ protected function getSize($attribute, $value)
23522352
// is the size. If it is a file, we take kilobytes, and for a string the
23532353
// entire length of the string will be considered the attribute size.
23542354
if (is_numeric($value) && $hasNumeric) {
2355-
return $this->trim($value);
2355+
return $this->ensureExponentWithinAllowedRange($attribute, $this->trim($value));
23562356
} elseif (is_array($value)) {
23572357
return count($value);
23582358
} elseif ($value instanceof File) {
@@ -2469,4 +2469,34 @@ protected function trim($value)
24692469
{
24702470
return is_string($value) ? trim($value) : $value;
24712471
}
2472+
2473+
/**
2474+
* Ensure the exponent is within the allowed range.
2475+
*
2476+
* @param string $attribute
2477+
* @param mixed $value
2478+
* @return mixed
2479+
*/
2480+
protected function ensureExponentWithinAllowedRange($attribute, $value)
2481+
{
2482+
$stringValue = (string) $value;
2483+
2484+
if (! is_numeric($value) || ! Str::contains($stringValue, 'e', ignoreCase: true)) {
2485+
return $value;
2486+
}
2487+
2488+
$scale = (int) (Str::contains($stringValue, 'e')
2489+
? Str::after($stringValue, 'e')
2490+
: Str::after($stringValue, 'E'));
2491+
2492+
$withinRange = (
2493+
$this->ensureExponentWithinAllowedRangeUsing ?? fn ($scale) => $scale <= 1000 && $scale >= -1000
2494+
)($scale, $attribute, $value);
2495+
2496+
if (! $withinRange) {
2497+
throw new MathException('Scientific notation exponent outside of allowed range.');
2498+
}
2499+
2500+
return $value;
2501+
}
24722502
}

src/Illuminate/Validation/Validator.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,13 @@ class Validator implements ValidatorContract
295295
*/
296296
protected $exception = ValidationException::class;
297297

298+
/**
299+
* The custom callback to determine if an exponent is within allowed range.
300+
*
301+
* @var callable|null
302+
*/
303+
protected $ensureExponentWithinAllowedRangeUsing;
304+
298305
/**
299306
* Create a new Validator instance.
300307
*
@@ -1491,6 +1498,19 @@ public function setException($exception)
14911498
return $this;
14921499
}
14931500

1501+
/**
1502+
* Ensure exponent is within range with the given callback.
1503+
*
1504+
* @param callable(int $scale, string $attribute, mixed $value) $callback
1505+
* @return $this
1506+
*/
1507+
public function ensureExponentWithinAllowedRangeUsing($callback)
1508+
{
1509+
$this->ensureExponentWithinAllowedRangeUsing = $callback;
1510+
1511+
return $this;
1512+
}
1513+
14941514
/**
14951515
* Get the Translator implementation.
14961516
*

tests/Validation/ValidationValidatorTest.php

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use Illuminate\Database\Eloquent\Model;
1919
use Illuminate\Support\Arr;
2020
use Illuminate\Support\Carbon;
21+
use Illuminate\Support\Exceptions\MathException;
2122
use Illuminate\Translation\ArrayLoader;
2223
use Illuminate\Translation\Translator;
2324
use Illuminate\Validation\DatabasePresenceVerifierInterface;
@@ -8767,6 +8768,76 @@ public function testItTrimsSpaceFromParameters()
87678768
], $validator->messages()->keys());
87688769
}
87698770

8771+
/** @dataProvider outsideRangeExponents */
8772+
public function testItLimitsLengthOfScientificNotationExponent($value)
8773+
{
8774+
$trans = $this->getIlluminateArrayTranslator();
8775+
$validator = new Validator($trans, ['foo' => $value], ['foo' => 'numeric|min:3']);
8776+
8777+
$this->expectException(MathException::class);
8778+
$this->expectExceptionMessage('Scientific notation exponent outside of allowed range.');
8779+
8780+
$validator->passes();
8781+
}
8782+
8783+
public static function outsideRangeExponents()
8784+
{
8785+
return [
8786+
['1.0e+1001'],
8787+
['1.0E+1001'],
8788+
['1.0e1001'],
8789+
['1.0E1001'],
8790+
['1.0e-1001'],
8791+
['1.0E-1001'],
8792+
];
8793+
}
8794+
8795+
/** @dataProvider withinRangeExponents */
8796+
public function testItAllowsScientificNotationWithinRange($value, $rule)
8797+
{
8798+
$trans = $this->getIlluminateArrayTranslator();
8799+
$validator = new Validator($trans, ['foo' => $value], ['foo' => ['numeric', $rule]]);
8800+
8801+
$this->assertTrue($validator->passes());
8802+
}
8803+
8804+
public static function withinRangeExponents()
8805+
{
8806+
return [
8807+
['1.0e+1000', 'min:3'],
8808+
['1.0E+1000', 'min:3'],
8809+
['1.0e1000', 'min:3'],
8810+
['1.0E1000', 'min:3'],
8811+
['1.0e-1000', 'max:3'],
8812+
['1.0E-1000', 'max:3'],
8813+
];
8814+
}
8815+
8816+
public function testItCanConfigureAllowedExponentRange()
8817+
{
8818+
$trans = $this->getIlluminateArrayTranslator();
8819+
$validator = new Validator($trans, ['foo' => '1.0e-1000'], ['foo' => ['numeric', 'max:3']]);
8820+
$scale = $attribute = $value = null;
8821+
$withinRange = true;
8822+
8823+
$validator->ensureExponentWithinAllowedRangeUsing(function () use (&$scale, &$attribute, &$value, &$withinRange) {
8824+
[$scale, $attribute, $value] = func_get_args();
8825+
8826+
return $withinRange;
8827+
});
8828+
8829+
$this->assertTrue($validator->passes());
8830+
$this->assertSame(-1000, $scale);
8831+
$this->assertSame('foo', $attribute);
8832+
$this->assertSame('1.0e-1000', $value);
8833+
8834+
$withinRange = false;
8835+
$this->expectException(MathException::class);
8836+
$this->expectExceptionMessage('Scientific notation exponent outside of allowed range.');
8837+
8838+
$validator->passes();
8839+
}
8840+
87708841
protected function getTranslator()
87718842
{
87728843
return m::mock(TranslatorContract::class);

0 commit comments

Comments
 (0)