Fix panic on malformed queries with recursive fragments.
This is a potential denial-of-service attack vector. Thanks to [@quapka](https://github.com/quapka) for the detailed vulnerability report and reproduction steps.
This commit is contained in:
parent
88b10fe009
commit
c28c77e458
2 changed files with 39 additions and 1 deletions
|
@ -1,6 +1,6 @@
|
||||||
# master
|
# master
|
||||||
|
|
||||||
- No changes yet
|
- Fix panic on malformed queries with recursive fragments. *This is a potential denial-of-service attack vector.* Thanks to [@quapka](https://github.com/quapka) for the detailed vulnerability report and reproduction steps.
|
||||||
|
|
||||||
# [[0.15.7] 2021-07-08](https://github.com/graphql-rust/juniper/releases/tag/juniper-v0.15.7)
|
# [[0.15.7] 2021-07-08](https://github.com/graphql-rust/juniper/releases/tag/juniper-v0.15.7)
|
||||||
|
|
||||||
|
|
|
@ -172,6 +172,13 @@ impl<'a, S: Debug> OverlappingFieldsCanBeMerged<'a, S> {
|
||||||
);
|
);
|
||||||
|
|
||||||
for frag_name2 in &fragment_names[i + 1..] {
|
for frag_name2 in &fragment_names[i + 1..] {
|
||||||
|
// Prevent infinite fragment recursion. This case is
|
||||||
|
// caught by fragment validators, but because the validation is
|
||||||
|
// done in parallel we can't rely on fragments being
|
||||||
|
// non-recursive here.
|
||||||
|
if frag_name1 == frag_name2 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
self.collect_conflicts_between_fragments(
|
self.collect_conflicts_between_fragments(
|
||||||
&mut conflicts,
|
&mut conflicts,
|
||||||
frag_name1,
|
frag_name1,
|
||||||
|
@ -195,6 +202,10 @@ impl<'a, S: Debug> OverlappingFieldsCanBeMerged<'a, S> {
|
||||||
) where
|
) where
|
||||||
S: ScalarValue,
|
S: ScalarValue,
|
||||||
{
|
{
|
||||||
|
// Prevent infinite fragment recursion. This case is
|
||||||
|
// caught by fragment validators, but because the validation is
|
||||||
|
// done in parallel we can't rely on fragments being
|
||||||
|
// non-recursive here.
|
||||||
if fragment_name1 == fragment_name2 {
|
if fragment_name1 == fragment_name2 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -282,6 +293,13 @@ impl<'a, S: Debug> OverlappingFieldsCanBeMerged<'a, S> {
|
||||||
self.collect_conflicts_between(conflicts, mutually_exclusive, field_map, &field_map2, ctx);
|
self.collect_conflicts_between(conflicts, mutually_exclusive, field_map, &field_map2, ctx);
|
||||||
|
|
||||||
for fragment_name2 in fragment_names2 {
|
for fragment_name2 in fragment_names2 {
|
||||||
|
// Prevent infinite fragment recursion. This case is
|
||||||
|
// caught by fragment validators, but because the validation is
|
||||||
|
// done in parallel we can't rely on fragments being
|
||||||
|
// non-recursive here.
|
||||||
|
if fragment_name == fragment_name2 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
self.collect_conflicts_between_fields_and_fragment(
|
self.collect_conflicts_between_fields_and_fragment(
|
||||||
conflicts,
|
conflicts,
|
||||||
field_map,
|
field_map,
|
||||||
|
@ -2261,6 +2279,26 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn handles_recursive_fragments() {
|
||||||
|
expect_passes_rule_with_schema::<
|
||||||
|
_,
|
||||||
|
EmptyMutation<()>,
|
||||||
|
EmptySubscription<()>,
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
DefaultScalarValue,
|
||||||
|
>(
|
||||||
|
QueryRoot,
|
||||||
|
EmptyMutation::new(),
|
||||||
|
EmptySubscription::new(),
|
||||||
|
factory,
|
||||||
|
r#"
|
||||||
|
fragment f on Query { ...f }
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn error_message_contains_hint_for_alias_conflict() {
|
fn error_message_contains_hint_for_alias_conflict() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|
Loading…
Reference in a new issue