-
Notifications
You must be signed in to change notification settings - Fork 618
fix(parse): reject numeric strings with non-numbers #2729
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -233,15 +233,8 @@ export const expectString = (value: any): string | undefined => { | |
* @returns The value as a number, or undefined if it's null/undefined. | ||
*/ | ||
export const strictParseDouble = (value: string | number): number | undefined => { | ||
if (value === "NaN") { | ||
return NaN; | ||
} | ||
if (typeof value == "string") { | ||
const parsed: number = parseFloat(value); | ||
if (Number.isNaN(parsed)) { | ||
throw new TypeError(`Expected real number, got implicit NaN`); | ||
} | ||
return expectNumber(parsed); | ||
return expectNumber(parseNumber(value)); | ||
} | ||
return expectNumber(value); | ||
}; | ||
|
@@ -262,19 +255,28 @@ export const strictParseFloat = strictParseDouble; | |
* @returns The value as a number, or undefined if it's null/undefined. | ||
*/ | ||
export const strictParseFloat32 = (value: string | number): number | undefined => { | ||
if (value === "NaN") { | ||
return NaN; | ||
} | ||
if (typeof value == "string") { | ||
const parsed: number = parseFloat(value); | ||
if (Number.isNaN(parsed)) { | ||
throw new TypeError(`Expected real number, got implicit NaN`); | ||
} | ||
return expectFloat32(parsed); | ||
return expectFloat32(parseNumber(value)); | ||
} | ||
return expectFloat32(value); | ||
}; | ||
|
||
// This regex matches JSON-style numbers. In short: | ||
// * The integral may start with a negative sign, but not a positive one | ||
// * No leading 0 on the integral unless it's immediately followed by a '.' | ||
// * Exponent indicated by a case-insensitive 'E' optionally followed by a | ||
// positive/negative sign and some number of digits. | ||
// It also matches both positive and negative infinity as well and explicit NaN. | ||
const NUMBER_REGEX = /(-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?)|(-?Infinity)|(NaN)/g; | ||
|
||
const parseNumber = (value: string): number => { | ||
const matches = value.match(NUMBER_REGEX); | ||
if (matches === null || matches[0].length !== value.length) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you anchor the regex so you can just use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hahaha your faith in js is heartwarming, no it'll match partials anyway |
||
throw new TypeError(`Expected real number, got implicit NaN`); | ||
} | ||
return parseFloat(value); | ||
}; | ||
|
||
/** | ||
* Asserts a value is a number and returns it. If the value is a string | ||
* representation of a non-numeric number type (NaN, Infinity, -Infinity), | ||
|
@@ -346,7 +348,7 @@ export const strictParseLong = (value: string | number): number | undefined => { | |
if (typeof value === "string") { | ||
// parseInt can't be used here, because it will silently discard any | ||
// existing decimals. We want to instead throw an error if there are any. | ||
return expectLong(parseFloat(value)); | ||
return expectLong(parseNumber(value)); | ||
} | ||
return expectLong(value); | ||
}; | ||
|
@@ -370,7 +372,7 @@ export const strictParseInt32 = (value: string | number): number | undefined => | |
if (typeof value === "string") { | ||
// parseInt can't be used here, because it will silently discard any | ||
// existing decimals. We want to instead throw an error if there are any. | ||
return expectInt32(parseFloat(value)); | ||
return expectInt32(parseNumber(value)); | ||
} | ||
return expectInt32(value); | ||
}; | ||
|
@@ -389,7 +391,7 @@ export const strictParseShort = (value: string | number): number | undefined => | |
if (typeof value === "string") { | ||
// parseInt can't be used here, because it will silently discard any | ||
// existing decimals. We want to instead throw an error if there are any. | ||
return expectShort(parseFloat(value)); | ||
return expectShort(parseNumber(value)); | ||
} | ||
return expectShort(value); | ||
}; | ||
|
@@ -408,7 +410,7 @@ export const strictParseByte = (value: string | number): number | undefined => { | |
if (typeof value === "string") { | ||
// parseInt can't be used here, because it will silently discard any | ||
// existing decimals. We want to instead throw an error if there are any. | ||
return expectByte(parseFloat(value)); | ||
return expectByte(parseNumber(value)); | ||
} | ||
return expectByte(value); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nit: Is this too strict?
parseFloat()
handles strings leading with "0" just fine.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be overkill at this point (I think the concern was stuff like this) but we can always loosen this later. It's much more difficult to tighten it.