Limit recursive depth to 256 by default
This commit is contained in:
parent
ccc6a7ec82
commit
521769b800
|
@ -15,7 +15,10 @@ use crate::{
|
||||||
model::__DirectiveLocation,
|
model::__DirectiveLocation,
|
||||||
parser::{
|
parser::{
|
||||||
parse_query,
|
parse_query,
|
||||||
types::{Directive, DocumentOperations, OperationType, Selection, SelectionSet},
|
types::{
|
||||||
|
Directive, DocumentOperations, ExecutableDocument, OperationType, Selection,
|
||||||
|
SelectionSet,
|
||||||
|
},
|
||||||
Positioned,
|
Positioned,
|
||||||
},
|
},
|
||||||
registry::{MetaDirective, MetaInputValue, Registry, SDLExportOptions},
|
registry::{MetaDirective, MetaInputValue, Registry, SDLExportOptions},
|
||||||
|
@ -24,8 +27,8 @@ use crate::{
|
||||||
types::QueryRoot,
|
types::QueryRoot,
|
||||||
validation::{check_rules, ValidationMode},
|
validation::{check_rules, ValidationMode},
|
||||||
BatchRequest, BatchResponse, CacheControl, ContextBase, EmptyMutation, EmptySubscription,
|
BatchRequest, BatchResponse, CacheControl, ContextBase, EmptyMutation, EmptySubscription,
|
||||||
InputType, ObjectType, OutputType, QueryEnv, Request, Response, ServerError, SubscriptionType,
|
InputType, ObjectType, OutputType, QueryEnv, Request, Response, ServerError, ServerResult,
|
||||||
Variables, ID,
|
SubscriptionType, Variables, ID,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Introspection mode
|
/// Introspection mode
|
||||||
|
@ -55,6 +58,7 @@ pub struct SchemaBuilder<Query, Mutation, Subscription> {
|
||||||
data: Data,
|
data: Data,
|
||||||
complexity: Option<usize>,
|
complexity: Option<usize>,
|
||||||
depth: Option<usize>,
|
depth: Option<usize>,
|
||||||
|
recursive_depth: usize,
|
||||||
extensions: Vec<Box<dyn ExtensionFactory>>,
|
extensions: Vec<Box<dyn ExtensionFactory>>,
|
||||||
custom_directives: HashMap<&'static str, Box<dyn CustomDirectiveFactory>>,
|
custom_directives: HashMap<&'static str, Box<dyn CustomDirectiveFactory>>,
|
||||||
}
|
}
|
||||||
|
@ -110,6 +114,16 @@ impl<Query, Mutation, Subscription> SchemaBuilder<Query, Mutation, Subscription>
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the maximum recursive depth a query can have. (default: 256)
|
||||||
|
///
|
||||||
|
/// If the value is too large, stack overflow may occur, usually `256` is
|
||||||
|
/// enough.
|
||||||
|
#[must_use]
|
||||||
|
pub fn limit_recursive_depth(mut self, depth: usize) -> Self {
|
||||||
|
self.recursive_depth = depth;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Add an extension to the schema.
|
/// Add an extension to the schema.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
|
@ -219,6 +233,7 @@ impl<Query, Mutation, Subscription> SchemaBuilder<Query, Mutation, Subscription>
|
||||||
subscription: self.subscription,
|
subscription: self.subscription,
|
||||||
complexity: self.complexity,
|
complexity: self.complexity,
|
||||||
depth: self.depth,
|
depth: self.depth,
|
||||||
|
recursive_depth: self.recursive_depth,
|
||||||
extensions: self.extensions,
|
extensions: self.extensions,
|
||||||
env: SchemaEnv(Arc::new(SchemaEnvInner {
|
env: SchemaEnv(Arc::new(SchemaEnvInner {
|
||||||
registry: self.registry,
|
registry: self.registry,
|
||||||
|
@ -256,6 +271,7 @@ pub struct SchemaInner<Query, Mutation, Subscription> {
|
||||||
pub(crate) subscription: Subscription,
|
pub(crate) subscription: Subscription,
|
||||||
pub(crate) complexity: Option<usize>,
|
pub(crate) complexity: Option<usize>,
|
||||||
pub(crate) depth: Option<usize>,
|
pub(crate) depth: Option<usize>,
|
||||||
|
pub(crate) recursive_depth: usize,
|
||||||
pub(crate) extensions: Vec<Box<dyn ExtensionFactory>>,
|
pub(crate) extensions: Vec<Box<dyn ExtensionFactory>>,
|
||||||
pub(crate) env: SchemaEnv,
|
pub(crate) env: SchemaEnv,
|
||||||
}
|
}
|
||||||
|
@ -339,6 +355,7 @@ where
|
||||||
data: Default::default(),
|
data: Default::default(),
|
||||||
complexity: None,
|
complexity: None,
|
||||||
depth: None,
|
depth: None,
|
||||||
|
recursive_depth: 256,
|
||||||
extensions: Default::default(),
|
extensions: Default::default(),
|
||||||
custom_directives: Default::default(),
|
custom_directives: Default::default(),
|
||||||
}
|
}
|
||||||
|
@ -490,11 +507,14 @@ where
|
||||||
let mut document = {
|
let mut document = {
|
||||||
let query = &request.query;
|
let query = &request.query;
|
||||||
let parsed_doc = request.parsed_query.take();
|
let parsed_doc = request.parsed_query.take();
|
||||||
|
let recursive_depth = self.recursive_depth;
|
||||||
let fut_parse = async move {
|
let fut_parse = async move {
|
||||||
match parsed_doc {
|
let doc = match parsed_doc {
|
||||||
Some(parsed_doc) => Ok(parsed_doc),
|
Some(parsed_doc) => parsed_doc,
|
||||||
None => parse_query(query).map_err(Into::into),
|
None => parse_query(query)?,
|
||||||
}
|
};
|
||||||
|
check_recursive_depth(&doc, recursive_depth)?;
|
||||||
|
Ok(doc)
|
||||||
};
|
};
|
||||||
futures_util::pin_mut!(fut_parse);
|
futures_util::pin_mut!(fut_parse);
|
||||||
extensions
|
extensions
|
||||||
|
@ -771,3 +791,65 @@ fn remove_skipped_selection(selection_set: &mut SelectionSet, variables: &Variab
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_recursive_depth(doc: &ExecutableDocument, max_depth: usize) -> ServerResult<()> {
|
||||||
|
fn check_selection_set(
|
||||||
|
doc: &ExecutableDocument,
|
||||||
|
selection_set: &Positioned<SelectionSet>,
|
||||||
|
current_depth: usize,
|
||||||
|
max_depth: usize,
|
||||||
|
) -> ServerResult<()> {
|
||||||
|
if current_depth > max_depth {
|
||||||
|
return Err(ServerError::new(
|
||||||
|
format!(
|
||||||
|
"The recursion depth of the query cannot be greater than `{}`",
|
||||||
|
max_depth
|
||||||
|
),
|
||||||
|
Some(selection_set.pos),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
for selection in &selection_set.node.items {
|
||||||
|
match &selection.node {
|
||||||
|
Selection::Field(field) => {
|
||||||
|
if !field.node.selection_set.node.items.is_empty() {
|
||||||
|
check_selection_set(
|
||||||
|
doc,
|
||||||
|
&field.node.selection_set,
|
||||||
|
current_depth + 1,
|
||||||
|
max_depth,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Selection::FragmentSpread(fragment_spread) => {
|
||||||
|
if let Some(fragment) =
|
||||||
|
doc.fragments.get(&fragment_spread.node.fragment_name.node)
|
||||||
|
{
|
||||||
|
check_selection_set(
|
||||||
|
doc,
|
||||||
|
&fragment.node.selection_set,
|
||||||
|
current_depth + 1,
|
||||||
|
max_depth,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Selection::InlineFragment(inline_fragment) => {
|
||||||
|
check_selection_set(
|
||||||
|
doc,
|
||||||
|
&inline_fragment.node.selection_set,
|
||||||
|
current_depth + 1,
|
||||||
|
max_depth,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
for (_, operation) in doc.operations.iter() {
|
||||||
|
check_selection_set(doc, &operation.node.selection_set, 0, max_depth)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user