Browse Source

Deny Unicode Escapes in boolean and null expressions (#2931)

* Deny Unicode Escapes in boolean and null expressions

* Add tests
pull/2981/head
Veera 1 year ago committed by GitHub
parent
commit
4a68fb5120
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 12
      boa_parser/src/lexer/identifier.rs
  2. 4
      boa_parser/src/lexer/tests.rs
  3. 12
      boa_parser/src/lexer/token.rs
  4. 2
      boa_parser/src/parser/expression/assignment/yield.rs
  5. 8
      boa_parser/src/parser/expression/left_hand_side/call.rs
  6. 14
      boa_parser/src/parser/expression/left_hand_side/member.rs
  7. 18
      boa_parser/src/parser/expression/left_hand_side/optional/mod.rs
  8. 13
      boa_parser/src/parser/expression/primary/mod.rs
  9. 4
      boa_parser/src/parser/expression/primary/object_initializer/mod.rs
  10. 8
      boa_parser/src/parser/statement/declaration/hoistable/class_decl/mod.rs
  11. 15
      boa_parser/src/parser/tests/mod.rs

12
boa_parser/src/lexer/identifier.rs

@ -94,9 +94,15 @@ impl<R> Tokenizer<R> for Identifier {
Self::take_identifier_name(cursor, start_pos, self.init)?; Self::take_identifier_name(cursor, start_pos, self.init)?;
let token_kind = match identifier_name.parse() { let token_kind = match identifier_name.parse() {
Ok(Keyword::True) => TokenKind::BooleanLiteral(true), Ok(Keyword::True) => {
Ok(Keyword::False) => TokenKind::BooleanLiteral(false), TokenKind::BooleanLiteral((true, ContainsEscapeSequence(contains_escaped_chars)))
Ok(Keyword::Null) => TokenKind::NullLiteral, }
Ok(Keyword::False) => {
TokenKind::BooleanLiteral((false, ContainsEscapeSequence(contains_escaped_chars)))
}
Ok(Keyword::Null) => {
TokenKind::NullLiteral(ContainsEscapeSequence(contains_escaped_chars))
}
Ok(keyword) => TokenKind::Keyword((keyword, contains_escaped_chars)), Ok(keyword) => TokenKind::Keyword((keyword, contains_escaped_chars)),
_ => TokenKind::IdentifierName(( _ => TokenKind::IdentifierName((
interner.get_or_intern(identifier_name.as_str()), interner.get_or_intern(identifier_name.as_str()),

4
boa_parser/src/lexer/tests.rs

@ -38,7 +38,7 @@ fn check_single_line_comment() {
TokenKind::Keyword((Keyword::Var, false)), TokenKind::Keyword((Keyword::Var, false)),
TokenKind::LineTerminator, TokenKind::LineTerminator,
TokenKind::LineTerminator, TokenKind::LineTerminator,
TokenKind::BooleanLiteral(true), TokenKind::BooleanLiteral((true, ContainsEscapeSequence(false))),
]; ];
expect_tokens(&mut lexer, &expected, interner); expect_tokens(&mut lexer, &expected, interner);
@ -54,7 +54,7 @@ fn check_single_line_comment_with_crlf_ending() {
TokenKind::Keyword((Keyword::Var, false)), TokenKind::Keyword((Keyword::Var, false)),
TokenKind::LineTerminator, TokenKind::LineTerminator,
TokenKind::LineTerminator, TokenKind::LineTerminator,
TokenKind::BooleanLiteral(true), TokenKind::BooleanLiteral((true, ContainsEscapeSequence(false))),
]; ];
expect_tokens(&mut lexer, &expected, interner); expect_tokens(&mut lexer, &expected, interner);

12
boa_parser/src/lexer/token.rs

@ -95,7 +95,7 @@ impl From<BigInt> for Numeric {
#[derive(Clone, PartialEq, Debug)] #[derive(Clone, PartialEq, Debug)]
pub enum TokenKind { pub enum TokenKind {
/// A boolean literal, which is either `true` or `false`. /// A boolean literal, which is either `true` or `false`.
BooleanLiteral(bool), BooleanLiteral((bool, ContainsEscapeSequence)),
/// The end of the file. /// The end of the file.
EOF, EOF,
@ -118,7 +118,7 @@ pub enum TokenKind {
/// The [`null` literal][spec]. /// The [`null` literal][spec].
/// ///
/// [spec]: https://tc39.es/ecma262/#prod-NullLiteral /// [spec]: https://tc39.es/ecma262/#prod-NullLiteral
NullLiteral, NullLiteral(ContainsEscapeSequence),
/// A numeric literal. /// A numeric literal.
NumericLiteral(Numeric), NumericLiteral(Numeric),
@ -152,7 +152,7 @@ pub enum TokenKind {
impl From<bool> for TokenKind { impl From<bool> for TokenKind {
#[inline] #[inline]
fn from(oth: bool) -> Self { fn from(oth: bool) -> Self {
Self::BooleanLiteral(oth) Self::BooleanLiteral((oth, ContainsEscapeSequence(false)))
} }
} }
@ -182,7 +182,7 @@ impl TokenKind {
#[inline] #[inline]
#[must_use] #[must_use]
pub const fn boolean_literal(lit: bool) -> Self { pub const fn boolean_literal(lit: bool) -> Self {
Self::BooleanLiteral(lit) Self::BooleanLiteral((lit, ContainsEscapeSequence(false)))
} }
/// Creates an `EOF` token kind. /// Creates an `EOF` token kind.
@ -261,12 +261,12 @@ impl TokenKind {
#[must_use] #[must_use]
pub fn to_string(&self, interner: &Interner) -> String { pub fn to_string(&self, interner: &Interner) -> String {
match *self { match *self {
Self::BooleanLiteral(val) => val.to_string(), Self::BooleanLiteral((val, _)) => val.to_string(),
Self::EOF => "end of file".to_owned(), Self::EOF => "end of file".to_owned(),
Self::IdentifierName((ident, _)) => interner.resolve_expect(ident).to_string(), Self::IdentifierName((ident, _)) => interner.resolve_expect(ident).to_string(),
Self::PrivateIdentifier(ident) => format!("#{}", interner.resolve_expect(ident)), Self::PrivateIdentifier(ident) => format!("#{}", interner.resolve_expect(ident)),
Self::Keyword((word, _)) => word.to_string(), Self::Keyword((word, _)) => word.to_string(),
Self::NullLiteral => "null".to_owned(), Self::NullLiteral(_) => "null".to_owned(),
Self::NumericLiteral(Numeric::Rational(num)) => num.to_string(), Self::NumericLiteral(Numeric::Rational(num)) => num.to_string(),
Self::NumericLiteral(Numeric::Integer(num)) => num.to_string(), Self::NumericLiteral(Numeric::Integer(num)) => num.to_string(),
Self::NumericLiteral(Numeric::BigInt(ref num)) => format!("{num}n"), Self::NumericLiteral(Numeric::BigInt(ref num)) => format!("{num}n"),

2
boa_parser/src/parser/expression/assignment/yield.rs

@ -103,7 +103,7 @@ where
_, _,
)) ))
| TokenKind::BooleanLiteral(_) | TokenKind::BooleanLiteral(_)
| TokenKind::NullLiteral | TokenKind::NullLiteral(_)
| TokenKind::StringLiteral(_) | TokenKind::StringLiteral(_)
| TokenKind::TemplateNoSubstitution(_) | TokenKind::TemplateNoSubstitution(_)
| TokenKind::NumericLiteral(_) | TokenKind::NumericLiteral(_)

8
boa_parser/src/parser/expression/left_hand_side/call.rs

@ -140,13 +140,15 @@ where
TokenKind::Keyword((kw, _)) => { TokenKind::Keyword((kw, _)) => {
SimplePropertyAccess::new(lhs, kw.to_sym()).into() SimplePropertyAccess::new(lhs, kw.to_sym()).into()
} }
TokenKind::BooleanLiteral(true) => { TokenKind::BooleanLiteral((true, _)) => {
SimplePropertyAccess::new(lhs, Sym::TRUE).into() SimplePropertyAccess::new(lhs, Sym::TRUE).into()
} }
TokenKind::BooleanLiteral(false) => { TokenKind::BooleanLiteral((false, _)) => {
SimplePropertyAccess::new(lhs, Sym::FALSE).into() SimplePropertyAccess::new(lhs, Sym::FALSE).into()
} }
TokenKind::NullLiteral => SimplePropertyAccess::new(lhs, Sym::NULL).into(), TokenKind::NullLiteral(_) => {
SimplePropertyAccess::new(lhs, Sym::NULL).into()
}
TokenKind::PrivateIdentifier(name) => { TokenKind::PrivateIdentifier(name) => {
PrivatePropertyAccess::new(lhs, PrivateName::new(*name)).into() PrivatePropertyAccess::new(lhs, PrivateName::new(*name)).into()
} }

14
boa_parser/src/parser/expression/left_hand_side/member.rs

@ -171,13 +171,13 @@ where
TokenKind::Keyword((kw, _)) => { TokenKind::Keyword((kw, _)) => {
SuperPropertyAccess::new(kw.to_sym().into()) SuperPropertyAccess::new(kw.to_sym().into())
} }
TokenKind::BooleanLiteral(true) => { TokenKind::BooleanLiteral((true, _)) => {
SuperPropertyAccess::new(Sym::TRUE.into()) SuperPropertyAccess::new(Sym::TRUE.into())
} }
TokenKind::BooleanLiteral(false) => { TokenKind::BooleanLiteral((false, _)) => {
SuperPropertyAccess::new(Sym::FALSE.into()) SuperPropertyAccess::new(Sym::FALSE.into())
} }
TokenKind::NullLiteral => SuperPropertyAccess::new(Sym::NULL.into()), TokenKind::NullLiteral(_) => SuperPropertyAccess::new(Sym::NULL.into()),
TokenKind::PrivateIdentifier(_) => { TokenKind::PrivateIdentifier(_) => {
return Err(Error::general( return Err(Error::general(
"unexpected private identifier", "unexpected private identifier",
@ -233,13 +233,15 @@ where
TokenKind::Keyword((kw, _)) => { TokenKind::Keyword((kw, _)) => {
SimplePropertyAccess::new(lhs, kw.to_sym()).into() SimplePropertyAccess::new(lhs, kw.to_sym()).into()
} }
TokenKind::BooleanLiteral(true) => { TokenKind::BooleanLiteral((true, _)) => {
SimplePropertyAccess::new(lhs, Sym::TRUE).into() SimplePropertyAccess::new(lhs, Sym::TRUE).into()
} }
TokenKind::BooleanLiteral(false) => { TokenKind::BooleanLiteral((false, _)) => {
SimplePropertyAccess::new(lhs, Sym::FALSE).into() SimplePropertyAccess::new(lhs, Sym::FALSE).into()
} }
TokenKind::NullLiteral => SimplePropertyAccess::new(lhs, Sym::NULL).into(), TokenKind::NullLiteral(_) => {
SimplePropertyAccess::new(lhs, Sym::NULL).into()
}
TokenKind::PrivateIdentifier(name) => { TokenKind::PrivateIdentifier(name) => {
PrivatePropertyAccess::new(lhs, PrivateName::new(*name)).into() PrivatePropertyAccess::new(lhs, PrivateName::new(*name)).into()
} }

18
boa_parser/src/parser/expression/left_hand_side/optional/mod.rs

@ -73,13 +73,17 @@ where
TokenKind::Keyword((kw, _)) => OptionalOperationKind::SimplePropertyAccess { TokenKind::Keyword((kw, _)) => OptionalOperationKind::SimplePropertyAccess {
field: PropertyAccessField::Const(kw.to_sym()), field: PropertyAccessField::Const(kw.to_sym()),
}, },
TokenKind::BooleanLiteral(true) => OptionalOperationKind::SimplePropertyAccess { TokenKind::BooleanLiteral((true, _)) => {
field: PropertyAccessField::Const(Sym::TRUE), OptionalOperationKind::SimplePropertyAccess {
}, field: PropertyAccessField::Const(Sym::TRUE),
TokenKind::BooleanLiteral(false) => OptionalOperationKind::SimplePropertyAccess { }
field: PropertyAccessField::Const(Sym::FALSE), }
}, TokenKind::BooleanLiteral((false, _)) => {
TokenKind::NullLiteral => OptionalOperationKind::SimplePropertyAccess { OptionalOperationKind::SimplePropertyAccess {
field: PropertyAccessField::Const(Sym::FALSE),
}
}
TokenKind::NullLiteral(_) => OptionalOperationKind::SimplePropertyAccess {
field: PropertyAccessField::Const(Sym::NULL), field: PropertyAccessField::Const(Sym::NULL),
}, },
TokenKind::PrivateIdentifier(name) => { TokenKind::PrivateIdentifier(name) => {

13
boa_parser/src/parser/expression/primary/mod.rs

@ -27,7 +27,10 @@ use self::{
object_initializer::ObjectLiteral, object_initializer::ObjectLiteral,
}; };
use crate::{ use crate::{
lexer::{token::Numeric, InputElement, Token, TokenKind}, lexer::{
token::{ContainsEscapeSequence, Numeric},
InputElement, Token, TokenKind,
},
parser::{ parser::{
expression::{ expression::{
identifiers::IdentifierReference, primary::template::TemplateLiteral, identifiers::IdentifierReference, primary::template::TemplateLiteral,
@ -103,7 +106,9 @@ where
let tok_position = tok.span().start(); let tok_position = tok.span().start();
match tok.kind() { match tok.kind() {
TokenKind::Keyword((Keyword::This, true)) => Err(Error::general( TokenKind::Keyword((Keyword::This, true))
| TokenKind::BooleanLiteral((_, ContainsEscapeSequence(true)))
| TokenKind::NullLiteral(ContainsEscapeSequence(true)) => Err(Error::general(
"Keyword must not contain escaped characters", "Keyword must not contain escaped characters",
tok_position, tok_position,
)), )),
@ -187,12 +192,12 @@ where
.parse(cursor, interner) .parse(cursor, interner)
.map(Into::into) .map(Into::into)
} }
TokenKind::BooleanLiteral(boolean) => { TokenKind::BooleanLiteral((boolean, _)) => {
let node = Literal::from(*boolean).into(); let node = Literal::from(*boolean).into();
cursor.advance(interner); cursor.advance(interner);
Ok(node) Ok(node)
} }
TokenKind::NullLiteral => { TokenKind::NullLiteral(_) => {
cursor.advance(interner); cursor.advance(interner);
Ok(Literal::Null.into()) Ok(Literal::Null.into())
} }

4
boa_parser/src/parser/expression/primary/object_initializer/mod.rs

@ -600,8 +600,8 @@ where
let (utf8, utf16) = word.as_str(); let (utf8, utf16) = word.as_str();
interner.get_or_intern_static(utf8, utf16).into() interner.get_or_intern_static(utf8, utf16).into()
} }
TokenKind::NullLiteral => (Sym::NULL).into(), TokenKind::NullLiteral(_) => (Sym::NULL).into(),
TokenKind::BooleanLiteral(bool) => match bool { TokenKind::BooleanLiteral((bool, _)) => match bool {
true => Sym::TRUE.into(), true => Sym::TRUE.into(),
false => Sym::FALSE.into(), false => Sym::FALSE.into(),
}, },

8
boa_parser/src/parser/statement/declaration/hoistable/class_decl/mod.rs

@ -589,7 +589,7 @@ where
| TokenKind::StringLiteral(_) | TokenKind::StringLiteral(_)
| TokenKind::NumericLiteral(_) | TokenKind::NumericLiteral(_)
| TokenKind::Keyword(_) | TokenKind::Keyword(_)
| TokenKind::NullLiteral | TokenKind::NullLiteral(_)
| TokenKind::PrivateIdentifier(_) | TokenKind::PrivateIdentifier(_)
| TokenKind::Punctuator( | TokenKind::Punctuator(
Punctuator::OpenBracket | Punctuator::Mul | Punctuator::OpenBlock, Punctuator::OpenBracket | Punctuator::Mul | Punctuator::OpenBlock,
@ -931,7 +931,7 @@ where
| TokenKind::StringLiteral(_) | TokenKind::StringLiteral(_)
| TokenKind::NumericLiteral(_) | TokenKind::NumericLiteral(_)
| TokenKind::Keyword(_) | TokenKind::Keyword(_)
| TokenKind::NullLiteral | TokenKind::NullLiteral(_)
| TokenKind::Punctuator(Punctuator::OpenBracket) => { | TokenKind::Punctuator(Punctuator::OpenBracket) => {
let name_position = token.span().start(); let name_position = token.span().start();
let name = PropertyName::new(self.allow_yield, self.allow_await) let name = PropertyName::new(self.allow_yield, self.allow_await)
@ -1062,7 +1062,7 @@ where
| TokenKind::StringLiteral(_) | TokenKind::StringLiteral(_)
| TokenKind::NumericLiteral(_) | TokenKind::NumericLiteral(_)
| TokenKind::Keyword(_) | TokenKind::Keyword(_)
| TokenKind::NullLiteral | TokenKind::NullLiteral(_)
| TokenKind::Punctuator(Punctuator::OpenBracket) => { | TokenKind::Punctuator(Punctuator::OpenBracket) => {
let name_position = token.span().start(); let name_position = token.span().start();
let name = PropertyName::new(self.allow_yield, self.allow_await) let name = PropertyName::new(self.allow_yield, self.allow_await)
@ -1223,7 +1223,7 @@ where
| TokenKind::StringLiteral(_) | TokenKind::StringLiteral(_)
| TokenKind::NumericLiteral(_) | TokenKind::NumericLiteral(_)
| TokenKind::Keyword(_) | TokenKind::Keyword(_)
| TokenKind::NullLiteral | TokenKind::NullLiteral(_)
| TokenKind::Punctuator(Punctuator::OpenBracket) => { | TokenKind::Punctuator(Punctuator::OpenBracket) => {
let name_position = token.span().start(); let name_position = token.span().start();
let name = PropertyName::new(self.allow_yield, self.allow_await) let name = PropertyName::new(self.allow_yield, self.allow_await)

15
boa_parser/src/parser/tests/mod.rs

@ -604,3 +604,18 @@ fn hashbang_use_strict_with_with_statement() {
fn hashbang_comment() { fn hashbang_comment() {
check_script_parser(r"#!Comment Here", vec![], &mut Interner::default()); check_script_parser(r"#!Comment Here", vec![], &mut Interner::default());
} }
#[test]
fn deny_unicode_escape_in_false_expression() {
check_invalid_script(r"let x = f\u{61}lse;");
}
#[test]
fn deny_unicode_escape_in_true_expression() {
check_invalid_script(r"let x = tru\u{65};");
}
#[test]
fn deny_unicode_escape_in_null_expression() {
check_invalid_script(r"let x = n\u{75}ll;");
}

Loading…
Cancel
Save