Fix possible stack overflow in validator.

This commit is contained in:
Sunli 2022-01-28 09:46:14 +08:00
parent b233ea3e41
commit e8f14f328f
4 changed files with 65 additions and 4 deletions

View File

@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
# [3.0.27] 2022-1-28
- Fix possible stack overflow in validator, thanks @quapka.
# [3.0.26] 2022-1-26
- Add `skip_input` attribute to `InputObject` macro, `skip_output` attribute to `SimpleObject` macro.

View File

@ -2,9 +2,9 @@
## Supported Versions
| Version | Supported |
| ------- | ------------------ |
| >= 3.0.0 | :white_check_mark: |
| Version | Supported |
|----------|--------------------|
| >= 3.0.0 | :white_check_mark: |
## Reporting a Vulnerability

View File

@ -1,4 +1,4 @@
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};
use crate::parser::types::{Field, Selection, SelectionSet};
use crate::validation::visitor::{Visitor, VisitorContext};
@ -15,6 +15,7 @@ impl<'a> Visitor<'a> for OverlappingFieldsCanBeMerged {
) {
let mut find_conflicts = FindConflicts {
outputs: Default::default(),
visited: Default::default(),
ctx,
};
find_conflicts.find(selection_set);
@ -23,6 +24,7 @@ impl<'a> Visitor<'a> for OverlappingFieldsCanBeMerged {
struct FindConflicts<'a, 'ctx> {
outputs: HashMap<&'a str, &'a Positioned<Field>>,
visited: HashSet<&'a str>,
ctx: &'a mut VisitorContext<'ctx>,
}
@ -46,6 +48,13 @@ impl<'a, 'ctx> FindConflicts<'a, 'ctx> {
if let Some(fragment) =
self.ctx.fragment(&fragment_spread.node.fragment_name.node)
{
if !self
.visited
.insert(fragment_spread.node.fragment_name.node.as_str())
{
// To avoid recursing itself, this error is detected by the `NoFragmentCycles` validator.
continue;
}
self.find(&fragment.node.selection_set);
}
}

View File

@ -54,3 +54,51 @@ async fn test_flatten() {
})
);
}
#[tokio::test]
async fn recursive_fragment_definition() {
#[derive(SimpleObject)]
struct Hello {
world: String,
}
struct Query;
// this setup is actually completely irrelevant we just need to be able ot execute a query
#[Object]
impl Query {
async fn obj(&self) -> Hello {
Hello {
world: "Hello World".into(),
}
}
}
let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
let query = "fragment f on Query {...f} { __typename }";
assert!(schema.execute(query).await.into_result().is_err());
}
#[tokio::test]
async fn recursive_fragment_definition_nested() {
#[derive(SimpleObject)]
struct Hello {
world: String,
}
struct Query;
// this setup is actually completely irrelevant we just need to be able ot execute a query
#[Object]
impl Query {
async fn obj(&self) -> Hello {
Hello {
world: "Hello World".into(),
}
}
}
let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
let query = "fragment f on Query { a { ...f a { ...f } } } { __typename }";
assert!(schema.execute(query).await.into_result().is_err());
}