Skip to content

Commit

Permalink
fix(NODE-6102): Double.fromString prohibiting '+' character and prohi…
Browse files Browse the repository at this point in the history
…biting exponential notation (#674)
  • Loading branch information
aditi-khare-mongoDB committed Apr 19, 2024
1 parent 23d13f2 commit c58d1e2
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 22 deletions.
25 changes: 14 additions & 11 deletions src/double.ts
Expand Up @@ -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);
}
Expand Down
34 changes: 23 additions & 11 deletions test/node/double.test.ts
Expand Up @@ -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) {
Expand All @@ -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);
}
});
});
Expand Down

0 comments on commit c58d1e2

Please sign in to comment.