Skip to content

Commit c58d1e2

Browse files
fix(NODE-6102): Double.fromString prohibiting '+' character and prohibiting exponential notation (#674)
1 parent 23d13f2 commit c58d1e2

File tree

2 files changed

+37
-22
lines changed

2 files changed

+37
-22
lines changed

src/double.ts

+14-11
Original file line numberDiff line numberDiff line change
@@ -38,29 +38,32 @@ export class Double extends BSONValue {
3838
*
3939
* This method will throw a BSONError on any string input that is not representable as a IEEE-754 64-bit double.
4040
* Notably, this method will also throw on the following string formats:
41-
* - Strings in non-decimal formats (exponent notation, binary, hex, or octal digits)
41+
* - Strings in non-decimal and non-exponential formats (binary, hex, or octal digits)
4242
* - Strings with characters other than numeric, floating point, or leading sign characters (Note: 'Infinity', '-Infinity', and 'NaN' input strings are still allowed)
4343
* - Strings with leading and/or trailing whitespace
4444
*
4545
* Strings with leading zeros, however, are also allowed
4646
*
47-
* @param value - the string we want to represent as an double.
47+
* @param value - the string we want to represent as a double.
4848
*/
4949
static fromString(value: string): Double {
5050
const coercedValue = Number(value);
51-
const nonFiniteValidInputs = ['Infinity', '-Infinity', 'NaN'];
5251

52+
if (value === 'NaN') return new Double(NaN);
53+
if (value === 'Infinity') return new Double(Infinity);
54+
if (value === '-Infinity') return new Double(-Infinity);
55+
56+
if (!Number.isFinite(coercedValue)) {
57+
throw new BSONError(`Input: ${value} is not representable as a Double`);
58+
}
5359
if (value.trim() !== value) {
5460
throw new BSONError(`Input: '${value}' contains whitespace`);
55-
} else if (value === '') {
61+
}
62+
if (value === '') {
5663
throw new BSONError(`Input is an empty string`);
57-
} else if (/[^-0-9.]/.test(value) && !nonFiniteValidInputs.includes(value)) {
58-
throw new BSONError(`Input: '${value}' contains invalid characters`);
59-
} else if (
60-
(!Number.isFinite(coercedValue) && !nonFiniteValidInputs.includes(value)) ||
61-
(Number.isNaN(coercedValue) && value !== 'NaN')
62-
) {
63-
throw new BSONError(`Input: ${value} is not representable as a Double`); // generic case
64+
}
65+
if (/[^-0-9.+eE]/.test(value)) {
66+
throw new BSONError(`Input: '${value}' is not in decimal or exponential notation`);
6467
}
6568
return new Double(coercedValue);
6669
}

test/node/double.test.ts

+23-11
Original file line numberDiff line numberDiff line change
@@ -239,21 +239,33 @@ describe('BSON Double Precision', function () {
239239
['-Infinity', '-Infinity', -Infinity],
240240
['NaN', 'NaN', NaN],
241241
['basic floating point', '-4.556000', -4.556],
242-
['negative zero', '-0', -0]
242+
['negative zero', '-0', -0],
243+
['explicit plus zero', '+0', 0],
244+
['explicit plus decimal', '+78.23456', 78.23456],
245+
['explicit plus leading zeros', '+00000000000001.11', 1.11],
246+
['exponentiation notation', '1.34e16', 1.34e16],
247+
['exponentiation notation with negative exponent', '1.34e-16', 1.34e-16],
248+
['exponentiation notation with explicit positive exponent', '1.34e+16', 1.34e16],
249+
['exponentiation notation with negative base', '-1.34e16', -1.34e16],
250+
['exponentiation notation with capital E', '-1.34E16', -1.34e16]
243251
];
244252

245253
const errorInputs = [
246-
['commas', '34,450', 'contains invalid characters'],
247-
['exponentiation notation', '1.34e16', 'contains invalid characters'],
248-
['octal', '0o1', 'contains invalid characters'],
249-
['binary', '0b1', 'contains invalid characters'],
250-
['hex', '0x1', 'contains invalid characters'],
254+
['commas', '34,450', 'is not representable as a Double'],
255+
['octal', '0o1', 'is not in decimal or exponential notation'],
256+
['binary', '0b1', 'is not in decimal or exponential notation'],
257+
['hex', '0x1', 'is not in decimal or exponential notation'],
251258
['empty string', '', 'is an empty string'],
252259
['leading and trailing whitespace', ' 89 ', 'contains whitespace'],
253-
['fake positive infinity', '2e308', 'contains invalid characters'],
254-
['fake negative infinity', '-2e308', 'contains invalid characters'],
255-
['fraction', '3/4', 'contains invalid characters'],
256-
['foo', 'foo', 'contains invalid characters']
260+
['fake positive infinity', '2e308', 'is not representable as a Double'],
261+
['fake negative infinity', '-2e308', 'is not representable as a Double'],
262+
['fraction', '3/4', 'is not representable as a Double'],
263+
['foo', 'foo', 'is not representable as a Double'],
264+
[
265+
'malformed number without invalid characters',
266+
'9.0.+76',
267+
'is not representable as a Double'
268+
]
257269
];
258270

259271
for (const [testName, value, expectedDouble] of acceptedInputs) {
@@ -262,7 +274,7 @@ describe('BSON Double Precision', function () {
262274
if (value === 'NaN') {
263275
expect(isNaN(Double.fromString(value))).to.be.true;
264276
} else {
265-
expect(Double.fromString(value).value).to.equal(expectedDouble);
277+
expect(Double.fromString(value).value).to.deep.equal(expectedDouble);
266278
}
267279
});
268280
});

0 commit comments

Comments
 (0)