Skip to content

Commit

Permalink
chore(parser): remove recursion in field parsing, fixes #514 (#519)
Browse files Browse the repository at this point in the history
  • Loading branch information
goto-bus-stop authored Apr 13, 2023
1 parent df6905f commit eacadd5
Show file tree
Hide file tree
Showing 48 changed files with 90 additions and 86 deletions.
14 changes: 10 additions & 4 deletions crates/apollo-compiler/src/database/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,9 @@ mod tests {
let schema = r#"
query {
Q1 {
url
url {
hostname
}
}
}
"#;
Expand All @@ -104,15 +106,19 @@ mod tests {

assert_eq!(ast.recursion_limit().high, 2);
assert_eq!(ast.errors().len(), 1);
assert_eq!(ast.document().definitions().count(), 2);
assert_eq!(ast.document().definitions().count(), 1);
}

#[test]
fn it_passes_when_selection_set_recursion_limit_is_not_exceeded() {
let schema = r#"
query {
Q1 {
url
Q2 {
Q3 {
url
}
}
}
}
"#;
Expand Down Expand Up @@ -184,7 +190,7 @@ mod tests {
assert_eq!(ast.errors().len(), 1);
assert_eq!(
ast.errors().next(),
Some(&apollo_parser::Error::limit("parser limit(3) reached", 61))
Some(&apollo_parser::Error::limit("parser limit(3) reached", 121))
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,4 @@
- [email protected] "String"
- [email protected] "\n"
- [email protected] "}"
recursion limit: 4096, high: 4
recursion limit: 4096, high: 2
Original file line number Diff line number Diff line change
Expand Up @@ -128,4 +128,4 @@
- [email protected] "\n"
- [email protected] "}"
- [email protected] "\n"
recursion limit: 4096, high: 6
recursion limit: 4096, high: 3
Original file line number Diff line number Diff line change
Expand Up @@ -214,4 +214,4 @@
- [email protected]
- [email protected]
- [email protected] "FIELD_DEFINITION"
recursion limit: 4096, high: 6
recursion limit: 4096, high: 2
Original file line number Diff line number Diff line change
Expand Up @@ -143,4 +143,4 @@
- [email protected] "\n"
- [email protected] "}"
- [email protected] "\n"
recursion limit: 4096, high: 3
recursion limit: 4096, high: 2
Original file line number Diff line number Diff line change
Expand Up @@ -477,4 +477,4 @@
- [email protected] "}"
- [email protected] "\n"
- [email protected] "}"
recursion limit: 4096, high: 31
recursion limit: 4096, high: 8
Original file line number Diff line number Diff line change
Expand Up @@ -231,4 +231,4 @@
- [email protected] "Int"
- [email protected] "\n"
- [email protected] "}"
recursion limit: 4096, high: 5
recursion limit: 4096, high: 2
Original file line number Diff line number Diff line change
Expand Up @@ -186,4 +186,4 @@
- [email protected] "!"
- [email protected] "\n"
- [email protected] "}"
recursion limit: 4096, high: 4
recursion limit: 4096, high: 2
Original file line number Diff line number Diff line change
Expand Up @@ -300,4 +300,4 @@
- [email protected] "Boolean"
- [email protected] "\n"
- [email protected] "}"
recursion limit: 4096, high: 4
recursion limit: 4096, high: 2
Original file line number Diff line number Diff line change
Expand Up @@ -110,4 +110,4 @@
- [email protected] "\n"
- [email protected] "}"
- [email protected] "\n"
recursion limit: 4096, high: 3
recursion limit: 4096, high: 2
Original file line number Diff line number Diff line change
Expand Up @@ -147,4 +147,4 @@
- [email protected] "\n"
- [email protected] "}"
- [email protected] "\n"
recursion limit: 4096, high: 3
recursion limit: 4096, high: 2
Original file line number Diff line number Diff line change
Expand Up @@ -238,4 +238,4 @@
- [email protected] "\n"
- [email protected] "}"
- [email protected] "\n"
recursion limit: 4096, high: 3
recursion limit: 4096, high: 2
Original file line number Diff line number Diff line change
Expand Up @@ -541,4 +541,4 @@
- [email protected] "\n"
- [email protected] "}"
- [email protected] "\n"
recursion limit: 4096, high: 3
recursion limit: 4096, high: 2
Original file line number Diff line number Diff line change
Expand Up @@ -281,4 +281,4 @@
- [email protected] "\n"
- [email protected] "}"
- [email protected] "\n"
recursion limit: 4096, high: 2
recursion limit: 4096, high: 1
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,4 @@
- [email protected] "\n"
- [email protected] "}"
- [email protected] "\n"
recursion limit: 4096, high: 4
recursion limit: 4096, high: 1
Original file line number Diff line number Diff line change
Expand Up @@ -113,4 +113,4 @@
- [email protected] "}"
- [email protected] "\n"
- [email protected] "}"
recursion limit: 4096, high: 10
recursion limit: 4096, high: 4
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,4 @@
- [email protected] "__typename"
- [email protected] "\n"
- [email protected] "}"
recursion limit: 4096, high: 3
recursion limit: 4096, high: 1
Original file line number Diff line number Diff line change
Expand Up @@ -152,4 +152,4 @@
- [email protected] "}"
- [email protected] "\n"
- [email protected] "}"
recursion limit: 4096, high: 4
recursion limit: 4096, high: 2
Original file line number Diff line number Diff line change
Expand Up @@ -214,4 +214,4 @@
- [email protected] "\n"
- [email protected] "}"
- [email protected] "\n"
recursion limit: 4096, high: 4
recursion limit: 4096, high: 2
4 changes: 4 additions & 0 deletions crates/apollo-parser/src/parser/grammar/document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ pub(crate) fn document(p: &mut Parser) {
let doc = p.start_node(SyntaxKind::DOCUMENT);

while let Some(node) = p.peek() {
if p.recursion_limit.limited() {
break;
}

match node {
TokenKind::StringValue => {
let def = p.peek_data_n(2).unwrap();
Expand Down
24 changes: 1 addition & 23 deletions crates/apollo-parser/src/parser/grammar/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,7 @@ use crate::{
/// *Field*:
/// Alias? Name Arguments? Directives? SelectionSet?
pub(crate) fn field(p: &mut Parser) {
// We need to enforce recursion limits to prevent
// excessive resource consumption or (more seriously)
// stack overflows.
p.recursion_limit.consume();
if p.recursion_limit.limited() {
p.limit_err(format!("parser limit({}) reached", p.recursion_limit.limit));
return;
}

let guard = p.start_node(SyntaxKind::FIELD);
let _guard = p.start_node(SyntaxKind::FIELD);

if let Some(TokenKind::Name) = p.peek() {
if let Some(T![:]) = p.peek_n(2) {
Expand All @@ -41,19 +32,6 @@ pub(crate) fn field(p: &mut Parser) {
if let Some(T!['{']) = p.peek() {
selection::selection_set(p);
}

match p.peek() {
Some(TokenKind::Name) => {
guard.finish_node();

field(p)
}

Some(T!['}']) => {
guard.finish_node();
}
_ => guard.finish_node(),
}
}

/// See: https://spec.graphql.org/October2021/#FieldsDefinition
Expand Down
44 changes: 31 additions & 13 deletions crates/apollo-parser/src/parser/grammar/selection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,9 @@ query SomeQuery(
let schema = r#"
query {
Q1 {
url
Q2 {
url
}
}
}
"#;
Expand All @@ -322,23 +324,25 @@ query SomeQuery(

assert_eq!(ast.recursion_limit().high, 2);
assert_eq!(ast.errors().len(), 1);
assert_eq!(ast.document().definitions().count(), 2);
assert_eq!(ast.document().definitions().count(), 1);
}

#[test]
fn it_passes_when_selection_set_recursion_limit_is_not_exceeded() {
let schema = r#"
query {
Q1 {
url
Q2 {
url
}
}
}
"#;
let parser = Parser::new(schema).recursion_limit(7);

let ast = parser.parse();

assert_eq!(ast.recursion_limit().high, 4);
assert_eq!(ast.recursion_limit().high, 3);
assert_eq!(ast.errors().len(), 0);
assert_eq!(ast.document().definitions().count(), 1);
}
Expand All @@ -347,9 +351,13 @@ query SomeQuery(
fn it_errors_when_selection_set_recursion_limit_is_exceeded_with_inline_fragment() {
let schema = r#"
query {
... on Page {
price
name
Q1 {
Q2 {
... on Page {
price
name
}
}
}
}
"#;
Expand All @@ -366,8 +374,12 @@ query SomeQuery(
fn it_errors_when_selection_set_recursion_limit_is_exceeded_fragment_spread() {
let schema = r#"
query {
product {
...Page
Q1 {
Q2 {
product {
...Page
}
}
}
}
"#;
Expand All @@ -377,7 +389,7 @@ query SomeQuery(

assert_eq!(ast.recursion_limit().high, 3);
assert_eq!(ast.errors().len(), 1);
assert_eq!(ast.document().definitions().count(), 1);
assert_eq!(dbg!(ast.document()).definitions().count(), 1);
}

#[test]
Expand All @@ -403,23 +415,29 @@ query SomeQuery(

assert_eq!(ast.recursion_limit().high, 2);
assert_eq!(ast.errors().len(), 1);
assert_eq!(ast.document().definitions().count(), 4);
assert_eq!(ast.document().definitions().count(), 1);
}

#[test]
fn it_passes_when_selection_set_recursion_limit_is_at_default() {
let schema = r#"
query {
Q1 {
url
Q2 {
Q3 {
Q4 {
Q5 { url }
}
}
}
},
}
"#;
let parser = Parser::new(schema);

let ast = parser.parse();

assert_eq!(ast.recursion_limit().high, 4);
assert_eq!(ast.recursion_limit().high, 6);
assert_eq!(ast.errors().len(), 0);
assert_eq!(ast.document().definitions().count(), 1);
}
Expand Down
4 changes: 1 addition & 3 deletions crates/apollo-parser/src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -495,7 +495,7 @@ mod tests {
let parser = Parser::new(source).recursion_limit(3).token_limit(200);
let ast = parser.parse();
let errors = ast.errors().collect::<Vec<_>>();
assert_eq!(errors, &[&Error::limit("parser limit(3) reached", 61),]);
assert_eq!(errors, &[&Error::limit("parser limit(3) reached", 121),]);
}

#[test]
Expand All @@ -522,8 +522,6 @@ mod tests {
);
assert_eq!(errors.next(), None);

// TODO(@goto-bus-stop) the comment is positioned wrong:
// https://github.com/apollographql/apollo-rs/issues/362
let tree = expect![[r##"
[email protected]
[email protected] "\n "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,4 @@
- [email protected] "\n"
- [email protected] "}"
- ERROR@9:11 "Fragment Name cannot be 'on'" on
recursion limit: 4096, high: 2
recursion limit: 4096, high: 1
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@
- [email protected] "\n"
- [email protected] "}"
- ERROR@22:26 "exptected 'on'" User
recursion limit: 4096, high: 2
recursion limit: 4096, high: 1
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@
- [email protected] "\n"
- [email protected] "}"
- ERROR@0:14 "expected definition" uasdf21230jkdw
recursion limit: 4096, high: 3
recursion limit: 4096, high: 1
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,4 @@
- [email protected] "\n"
- [email protected] "}"
- ERROR@99:113 "expected definition" uasdf21230jkdw
recursion limit: 4096, high: 3
recursion limit: 4096, high: 1
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@
- [email protected] "}"
- ERROR@13:25 "unexpected escaped character" "\string ID"
- ERROR@25:26 "expected a valid Value" )
recursion limit: 4096, high: 2
recursion limit: 4096, high: 1
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,4 @@
- ERROR@116:116 "expected }" EOF
- ERROR@116:116 "expected R_PAREN, got EOF" EOF
- ERROR@116:116 "expected R_CURLY, got EOF" EOF
recursion limit: 4096, high: 2
recursion limit: 4096, high: 1
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,4 @@
- [email protected] "}"
- ERROR@0:1 "expected a StringValue, Name or OperationDefinition" @
- ERROR@49:50 "expected a StringValue, Name or OperationDefinition" }
recursion limit: 4096, high: 3
recursion limit: 4096, high: 1
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@
- [email protected] "}"
- [email protected] "\n"
- ERROR@10:13 "expected an Inline Fragment or a Fragment Spread" ...
recursion limit: 4096, high: 3
recursion limit: 4096, high: 2
Loading

0 comments on commit eacadd5

Please sign in to comment.