Skip to content

Commit

Permalink
Limit the amount of GraphQL validation errors returned in the response (
Browse files Browse the repository at this point in the history
  • Loading branch information
goto-bus-stop authored Nov 6, 2024
1 parent 36bdb5e commit 5a868b6
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 1 deletion.
6 changes: 6 additions & 0 deletions .changesets/fix_renee_limit_errors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
### Limit the amount of GraphQL validation errors returned in the response ([PR #6187](https://github.com/apollographql/router/pull/6187))

When an invalid query is submitted, the router now returns at most 100 GraphQL parsing and validation errors in the response.
This prevents generating a very large response for nonsense documents.

By [@goto-bus-stop](https://github.com/goto-bus-stop) in https://github.com/apollographql/router/pull/6187
14 changes: 13 additions & 1 deletion apollo-router/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ use crate::json_ext::Value;
use crate::spec::operation_limits::OperationLimits;
use crate::spec::SpecError;

/// Return up to this many GraphQL parsing or validation errors.
///
/// Any remaining errors get silently dropped.
const MAX_VALIDATION_ERRORS: usize = 100;

/// Error types for execution.
///
/// Note that these are not actually returned to the client, but are instead converted to JSON for
Expand Down Expand Up @@ -333,6 +338,7 @@ impl IntoGraphQLErrors for Vec<apollo_compiler::execution::GraphQLError> {
.extension_code("GRAPHQL_VALIDATION_FAILED")
.build()
})
.take(MAX_VALIDATION_ERRORS)
.collect())
}
}
Expand Down Expand Up @@ -605,6 +611,7 @@ impl IntoGraphQLErrors for ParseErrors {
.extension_code("GRAPHQL_PARSING_FAILED")
.build()
})
.take(MAX_VALIDATION_ERRORS)
.collect())
}
}
Expand Down Expand Up @@ -635,6 +642,7 @@ impl ValidationErrors {
.extension_code("GRAPHQL_VALIDATION_FAILED")
.build()
})
.take(MAX_VALIDATION_ERRORS)
.collect()
}
}
Expand All @@ -647,7 +655,11 @@ impl IntoGraphQLErrors for ValidationErrors {
impl From<DiagnosticList> for ValidationErrors {
fn from(errors: DiagnosticList) -> Self {
Self {
errors: errors.iter().map(|e| e.unstable_to_json_compat()).collect(),
errors: errors
.iter()
.map(|e| e.unstable_to_json_compat())
.take(MAX_VALIDATION_ERRORS)
.collect(),
}
}
}
Expand Down
38 changes: 38 additions & 0 deletions apollo-router/tests/integration/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,3 +167,41 @@ async fn test_validation_error() {
}
"###);
}

#[tokio::test]
async fn test_lots_of_validation_errors() {
let query = format!(
"{{ __typename {} }}",
"@a".repeat(4_000), // Stay under the token limit: "@a" is two tokens every time
);

let request = serde_json::json!({ "query": query });
let request = apollo_router::services::router::Request::fake_builder()
.body(request.to_string())
.method(hyper::Method::POST)
.header("content-type", "application/json")
.build()
.unwrap();
let response = apollo_router::TestHarness::builder()
.schema(include_str!("../fixtures/supergraph.graphql"))
.build_router()
.await
.unwrap()
.oneshot(request)
.await
.unwrap()
.next_response()
.await
.unwrap()
.unwrap();

let v: serde_json::Value = serde_json::from_slice(&response).unwrap();
let errors = v["errors"].as_array().unwrap();
assert!(!errors.is_empty(), "should have errors");
// Make sure we're actually testing the validation errors, and we're not hitting some other limit
assert_eq!(
errors.first().unwrap()["extensions"]["code"],
serde_json::Value::from("GRAPHQL_VALIDATION_FAILED")
);
assert!(errors.len() <= 100, "should return limited error count");
}

0 comments on commit 5a868b6

Please sign in to comment.