From c58d1e213714c99bbb09d0f91a3c9199e43710dd Mon Sep 17 00:00:00 2001 From: Aditi Khare <106987683+aditi-khare-mongoDB@users.noreply.github.com> Date: Fri, 19 Apr 2024 12:42:30 -0400 Subject: [PATCH] fix(NODE-6102): Double.fromString prohibiting '+' character and prohibiting exponential notation (#674) --- src/double.ts | 25 ++++++++++++++----------- test/node/double.test.ts | 34 +++++++++++++++++++++++----------- 2 files changed, 37 insertions(+), 22 deletions(-) diff --git a/src/double.ts b/src/double.ts index 8f7d31b8..4a3d1298 100644 --- a/src/double.ts +++ b/src/double.ts @@ -38,29 +38,32 @@ export class Double extends BSONValue { * * This method will throw a BSONError on any string input that is not representable as a IEEE-754 64-bit double. * Notably, this method will also throw on the following string formats: - * - Strings in non-decimal formats (exponent notation, binary, hex, or octal digits) + * - Strings in non-decimal and non-exponential formats (binary, hex, or octal digits) * - Strings with characters other than numeric, floating point, or leading sign characters (Note: 'Infinity', '-Infinity', and 'NaN' input strings are still allowed) * - Strings with leading and/or trailing whitespace * * Strings with leading zeros, however, are also allowed * - * @param value - the string we want to represent as an double. + * @param value - the string we want to represent as a double. */ static fromString(value: string): Double { const coercedValue = Number(value); - const nonFiniteValidInputs = ['Infinity', '-Infinity', 'NaN']; + if (value === 'NaN') return new Double(NaN); + if (value === 'Infinity') return new Double(Infinity); + if (value === '-Infinity') return new Double(-Infinity); + + if (!Number.isFinite(coercedValue)) { + throw new BSONError(`Input: ${value} is not representable as a Double`); + } if (value.trim() !== value) { throw new BSONError(`Input: '${value}' contains whitespace`); - } else if (value === '') { + } + if (value === '') { throw new BSONError(`Input is an empty string`); - } else if (/[^-0-9.]/.test(value) && !nonFiniteValidInputs.includes(value)) { - throw new BSONError(`Input: '${value}' contains invalid characters`); - } else if ( - (!Number.isFinite(coercedValue) && !nonFiniteValidInputs.includes(value)) || - (Number.isNaN(coercedValue) && value !== 'NaN') - ) { - throw new BSONError(`Input: ${value} is not representable as a Double`); // generic case + } + if (/[^-0-9.+eE]/.test(value)) { + throw new BSONError(`Input: '${value}' is not in decimal or exponential notation`); } return new Double(coercedValue); } diff --git a/test/node/double.test.ts b/test/node/double.test.ts index 4fa6e03d..e5f3a259 100644 --- a/test/node/double.test.ts +++ b/test/node/double.test.ts @@ -239,21 +239,33 @@ describe('BSON Double Precision', function () { ['-Infinity', '-Infinity', -Infinity], ['NaN', 'NaN', NaN], ['basic floating point', '-4.556000', -4.556], - ['negative zero', '-0', -0] + ['negative zero', '-0', -0], + ['explicit plus zero', '+0', 0], + ['explicit plus decimal', '+78.23456', 78.23456], + ['explicit plus leading zeros', '+00000000000001.11', 1.11], + ['exponentiation notation', '1.34e16', 1.34e16], + ['exponentiation notation with negative exponent', '1.34e-16', 1.34e-16], + ['exponentiation notation with explicit positive exponent', '1.34e+16', 1.34e16], + ['exponentiation notation with negative base', '-1.34e16', -1.34e16], + ['exponentiation notation with capital E', '-1.34E16', -1.34e16] ]; const errorInputs = [ - ['commas', '34,450', 'contains invalid characters'], - ['exponentiation notation', '1.34e16', 'contains invalid characters'], - ['octal', '0o1', 'contains invalid characters'], - ['binary', '0b1', 'contains invalid characters'], - ['hex', '0x1', 'contains invalid characters'], + ['commas', '34,450', 'is not representable as a Double'], + ['octal', '0o1', 'is not in decimal or exponential notation'], + ['binary', '0b1', 'is not in decimal or exponential notation'], + ['hex', '0x1', 'is not in decimal or exponential notation'], ['empty string', '', 'is an empty string'], ['leading and trailing whitespace', ' 89 ', 'contains whitespace'], - ['fake positive infinity', '2e308', 'contains invalid characters'], - ['fake negative infinity', '-2e308', 'contains invalid characters'], - ['fraction', '3/4', 'contains invalid characters'], - ['foo', 'foo', 'contains invalid characters'] + ['fake positive infinity', '2e308', 'is not representable as a Double'], + ['fake negative infinity', '-2e308', 'is not representable as a Double'], + ['fraction', '3/4', 'is not representable as a Double'], + ['foo', 'foo', 'is not representable as a Double'], + [ + 'malformed number without invalid characters', + '9.0.+76', + 'is not representable as a Double' + ] ]; for (const [testName, value, expectedDouble] of acceptedInputs) { @@ -262,7 +274,7 @@ describe('BSON Double Precision', function () { if (value === 'NaN') { expect(isNaN(Double.fromString(value))).to.be.true; } else { - expect(Double.fromString(value).value).to.equal(expectedDouble); + expect(Double.fromString(value).value).to.deep.equal(expectedDouble); } }); });