Fix possible stack overflow in validator.
This commit is contained in:
parent
b233ea3e41
commit
e8f14f328f
|
@ -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/),
|
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).
|
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
|
# [3.0.26] 2022-1-26
|
||||||
|
|
||||||
- Add `skip_input` attribute to `InputObject` macro, `skip_output` attribute to `SimpleObject` macro.
|
- Add `skip_input` attribute to `InputObject` macro, `skip_output` attribute to `SimpleObject` macro.
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
## Supported Versions
|
## Supported Versions
|
||||||
|
|
||||||
| Version | Supported |
|
| Version | Supported |
|
||||||
| ------- | ------------------ |
|
|----------|--------------------|
|
||||||
| >= 3.0.0 | :white_check_mark: |
|
| >= 3.0.0 | :white_check_mark: |
|
||||||
|
|
||||||
## Reporting a Vulnerability
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
use crate::parser::types::{Field, Selection, SelectionSet};
|
use crate::parser::types::{Field, Selection, SelectionSet};
|
||||||
use crate::validation::visitor::{Visitor, VisitorContext};
|
use crate::validation::visitor::{Visitor, VisitorContext};
|
||||||
|
@ -15,6 +15,7 @@ impl<'a> Visitor<'a> for OverlappingFieldsCanBeMerged {
|
||||||
) {
|
) {
|
||||||
let mut find_conflicts = FindConflicts {
|
let mut find_conflicts = FindConflicts {
|
||||||
outputs: Default::default(),
|
outputs: Default::default(),
|
||||||
|
visited: Default::default(),
|
||||||
ctx,
|
ctx,
|
||||||
};
|
};
|
||||||
find_conflicts.find(selection_set);
|
find_conflicts.find(selection_set);
|
||||||
|
@ -23,6 +24,7 @@ impl<'a> Visitor<'a> for OverlappingFieldsCanBeMerged {
|
||||||
|
|
||||||
struct FindConflicts<'a, 'ctx> {
|
struct FindConflicts<'a, 'ctx> {
|
||||||
outputs: HashMap<&'a str, &'a Positioned<Field>>,
|
outputs: HashMap<&'a str, &'a Positioned<Field>>,
|
||||||
|
visited: HashSet<&'a str>,
|
||||||
ctx: &'a mut VisitorContext<'ctx>,
|
ctx: &'a mut VisitorContext<'ctx>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,6 +48,13 @@ impl<'a, 'ctx> FindConflicts<'a, 'ctx> {
|
||||||
if let Some(fragment) =
|
if let Some(fragment) =
|
||||||
self.ctx.fragment(&fragment_spread.node.fragment_name.node)
|
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);
|
self.find(&fragment.node.selection_set);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue