LibJS: Protect DateParser from non-ASCII input
DateParser is a GenericLexer, which is not particularly UTF-8 aware.
Other engines do not accept ASCII input, so let's just reject non-ASCII
strings outright.
While we are here, let's avoid the std ctype header. It is explicitly
undefined behavior to provide code points to isspace, isalpha, etc. that
do not fit in an unsigned char. Although this isn't reachable any longer
let's just nip it in the bud and use our safe AK character types.
Just to demonstrate, the following returns different values for me on
different systems and with different compilers:
isdigit(0xff01)
isalpha(0xff01)
isspace(0xff01)
This commit is contained in:
parent
3dcbb4bbb0
commit
edaed9adfa
2 changed files with 20 additions and 9 deletions
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/CharacterTypes.h>
|
||||
#include <AK/Error.h>
|
||||
#include <AK/GenericLexer.h>
|
||||
#include <AK/Optional.h>
|
||||
|
|
@ -15,8 +16,6 @@
|
|||
#include <AK/Vector.h>
|
||||
#include <LibJS/Runtime/Date.h>
|
||||
|
||||
#include <cctype> // Not included by default on macOS.
|
||||
|
||||
// Parse simplified ISO8601 and non-standard date formats to milliseconds
|
||||
// from epoch (double). Synopsis:
|
||||
// (1) Try to parse the string as simplified ISO8601 (case sensitive).
|
||||
|
|
@ -50,6 +49,8 @@ class DateParser : public GenericLexer {
|
|||
public:
|
||||
ALWAYS_INLINE static double parse(StringView string)
|
||||
{
|
||||
if (!string.is_ascii())
|
||||
return NAN;
|
||||
return DateParser(string).parse().value_or(NAN);
|
||||
}
|
||||
|
||||
|
|
@ -107,13 +108,13 @@ private:
|
|||
ParsedNumber<T> result;
|
||||
|
||||
for (; result.digits < MaxLength; ++result.digits) {
|
||||
if (is_eof() || !next_is(isdigit))
|
||||
if (is_eof() || !next_is(is_ascii_digit))
|
||||
return result;
|
||||
result.number *= 10;
|
||||
result.number += consume() - '0';
|
||||
}
|
||||
|
||||
ignore_while(isdigit);
|
||||
ignore_while(is_ascii_digit);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -331,7 +332,7 @@ private:
|
|||
case '(':
|
||||
ignore_until(')'); // Consume time zone name (Anything in brackets).
|
||||
ignore();
|
||||
ignore_while(isspace);
|
||||
ignore_while(is_ascii_space);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -342,7 +343,7 @@ private:
|
|||
{
|
||||
VERIFY(m_hours.has_value());
|
||||
|
||||
consume_while(isspace);
|
||||
consume_while(is_ascii_space);
|
||||
|
||||
if (consume_specific("AM"sv)) {
|
||||
if (!separator())
|
||||
|
|
@ -588,7 +589,7 @@ private:
|
|||
|
||||
m_timezone_utc = true;
|
||||
|
||||
bool space = consume_while(isspace).length() > 0;
|
||||
bool space = consume_while(is_ascii_space).length() > 0;
|
||||
switch (peek()) {
|
||||
case '+':
|
||||
case '-':
|
||||
|
|
@ -622,7 +623,7 @@ private:
|
|||
if (str[i] != peek(i))
|
||||
return false;
|
||||
|
||||
ignore_while(isalpha); // ... which can followed by anything. Just like Firefox and Chrome.
|
||||
ignore_while(is_ascii_alpha); // ... which can followed by anything. Just like Firefox and Chrome.
|
||||
m_month = month;
|
||||
|
||||
return separator(); // Must end with a separator.
|
||||
|
|
@ -630,7 +631,7 @@ private:
|
|||
|
||||
ALWAYS_INLINE bool word() // Alphanumeric strings that are not date "keywords".
|
||||
{
|
||||
consume_while(isalpha);
|
||||
consume_while(is_ascii_alpha);
|
||||
// Just like Firefox and Chrome:
|
||||
// - Ignore junk (bare words) at the beginning (before time or a date fragment has been read).
|
||||
// - Fail if a word is read later in the date string (exception: final time zone name, in brackets).
|
||||
|
|
|
|||
|
|
@ -698,3 +698,13 @@ test("multiple month names", () => {
|
|||
|
||||
setTimeZone(originalTimeZone);
|
||||
});
|
||||
|
||||
test("non-ASCII input", () => {
|
||||
expect(Date.parse("2024\uff01")).toBeNaN();
|
||||
expect(Date.parse("1234567\uff01")).toBeNaN();
|
||||
expect(Date.parse("Jan\uff01 01 2024")).toBeNaN();
|
||||
expect(Date.parse("hello\uff01 Jan 01 2024")).toBeNaN();
|
||||
expect(Date.parse("Jan 01 2024 12:34\u00a0PM")).toBeNaN();
|
||||
expect(Date.parse("Jan 01 2024 GMT\u00a0")).toBeNaN();
|
||||
expect(Date.parse("Jan 01 2024 (UTC)\u00a0")).toBeNaN();
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue