From 2775bd430dd0a7fec38397afb286446e677d3012 Mon Sep 17 00:00:00 2001 From: Nicolas Morel Date: Sat, 22 Oct 2022 19:27:46 +0200 Subject: [PATCH] fix: better unsafe check of exponential numbers Fixes #2542. Fixes #2672. --- benchmarks/suite.js | 9 +++++++++ lib/types/number.js | 23 ++++++++++++----------- test/types/number.js | 8 ++++++++ 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/benchmarks/suite.js b/benchmarks/suite.js index 4afdb14c..0f9d73da 100755 --- a/benchmarks/suite.js +++ b/benchmarks/suite.js @@ -199,5 +199,14 @@ module.exports = (Joi) => [ { a: 'foo', b: 'bar' } ], (schema, value) => schema.validate(value) + ], + [ + 'Parsing of exponential numbers', + () => [ + Joi.number(), + '+001231.0133210e003', + '90071992547409811e-1' + ], + (schema, value) => schema.validate(value) ] ]; diff --git a/lib/types/number.js b/lib/types/number.js index 3561c5fa..2031bb3e 100755 --- a/lib/types/number.js +++ b/lib/types/number.js @@ -8,7 +8,11 @@ const Common = require('../common'); const internals = { numberRx: /^\s*[+-]?(?:(?:\d+(?:\.\d*)?)|(?:\.\d+))(?:e([+-]?\d+))?\s*$/i, - precisionRx: /(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/ + precisionRx: /(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/, + exponentialPartRegex: /[eE][+-]?\d+$/, + leadingSignAndZerosRegex: /^[+-]?(0*)?/, + dotRegex: /\./, + trailingZerosRegex: /0+$/ }; @@ -39,8 +43,7 @@ module.exports = Any.extend({ if (!schema._flags.unsafe) { if (value.match(/e/i)) { - const constructed = internals.normalizeExponent(`${result.value / Math.pow(10, matches[1])}e${matches[1]}`); - if (constructed !== internals.normalizeExponent(value)) { + if (internals.extractSignificantDigits(value) !== internals.extractSignificantDigits(String(result.value))) { result.errors = error('number.unsafe'); return result; } @@ -297,15 +300,13 @@ module.exports = Any.extend({ // Helpers -internals.normalizeExponent = function (str) { +internals.extractSignificantDigits = function (value) { - return str - .replace(/E/, 'e') - .replace(/\.(\d*[1-9])?0+e/, '.$1e') - .replace(/\.e/, 'e') - .replace(/e\+/, 'e') - .replace(/^\+/, '') - .replace(/^(-?)0+([1-9])/, '$1$2'); + return value + .replace(internals.exponentialPartRegex, '') + .replace(internals.dotRegex, '') + .replace(internals.trailingZerosRegex, '') + .replace(internals.leadingSignAndZerosRegex, ''); }; diff --git a/test/types/number.js b/test/types/number.js index e5062de7..c6135f41 100755 --- a/test/types/number.js +++ b/test/types/number.js @@ -1146,6 +1146,8 @@ describe('number', () => { ['-00100e-003', true, -0.1], ['-001231.0133210e003', true, -1231013.321], ['+001231.0133210e003', true, 1231013.321], + ['1.9642346977926364E-5', true, 0.000019642346977926364], + ['9.4e-1', true, 0.94], ['0.00000095', true, 0.00000095], ['.5', true, 0.5], ['1 some text', false, { @@ -1999,6 +2001,12 @@ describe('number', () => { type: 'number.unsafe', context: { value: 90071992549000000, label: 'value' } }], + ['1.9642346977926366E-5', false, { + message: '"value" must be a safe number', + path: [], + type: 'number.unsafe', + context: { value: '1.9642346977926366E-5', label: 'value' } + }], [9007199254740992, false, { message: '"value" must be a safe number', path: [],