From 8d28cdba6eb10f53490ba41d1b5cb40506c2de22 Mon Sep 17 00:00:00 2001 From: tyranron <tyranron@gmail.com> Date: Thu, 28 Jul 2022 14:33:16 +0300 Subject: [PATCH 1/2] Backport CVE-2022-31173 fix from GHSA-4rx6-g5vg-5f3j Co-authored-by: ilslv <ilya.solovyiov@gmail.com> --- integration_tests/juniper_tests/Cargo.toml | 1 + .../juniper_tests/src/cve_2022_31173.rs | 56 +++++++++++++++ integration_tests/juniper_tests/src/lib.rs | 2 + juniper/CHANGELOG.md | 1 + .../validation/rules/no_fragment_cycles.rs | 68 ++++++++++++------- .../rules/no_undefined_variables.rs | 48 +++++++++---- .../validation/rules/no_unused_fragments.rs | 49 ++++++++----- .../validation/rules/no_unused_variables.rs | 50 ++++++++++---- .../rules/overlapping_fields_can_be_merged.rs | 67 +++++++++++++----- .../rules/variables_in_allowed_position.rs | 57 ++++++++++++---- 10 files changed, 297 insertions(+), 102 deletions(-) create mode 100644 integration_tests/juniper_tests/src/cve_2022_31173.rs diff --git a/integration_tests/juniper_tests/Cargo.toml b/integration_tests/juniper_tests/Cargo.toml index e21f60f4..e3db4af9 100644 --- a/integration_tests/juniper_tests/Cargo.toml +++ b/integration_tests/juniper_tests/Cargo.toml @@ -7,6 +7,7 @@ publish = false [dependencies] derive_more = "0.99" futures = "0.3" +itertools = "0.10" juniper = { path = "../../juniper" } juniper_subscriptions = { path = "../../juniper_subscriptions" } diff --git a/integration_tests/juniper_tests/src/cve_2022_31173.rs b/integration_tests/juniper_tests/src/cve_2022_31173.rs new file mode 100644 index 00000000..81d14c02 --- /dev/null +++ b/integration_tests/juniper_tests/src/cve_2022_31173.rs @@ -0,0 +1,56 @@ +//! Checks that long looping chain of fragments doesn't cause a stack overflow. +//! +//! ```graphql +//! # Fragment loop example +//! query { +//! ...a +//! } +//! +//! fragment a on Query { +//! ...b +//! } +//! +//! fragment b on Query { +//! ...a +//! } +//! ``` + +use std::iter; + +use itertools::Itertools as _; +use juniper::{graphql_object, EmptyMutation, EmptySubscription, Variables}; + +struct Query; + +#[graphql_object] +impl Query { + fn dummy() -> bool { + false + } +} + +type Schema = juniper::RootNode<'static, Query, EmptyMutation, EmptySubscription>; + +#[tokio::test] +async fn test() { + const PERM: &str = "abcefghijk"; + const CIRCLE_SIZE: usize = 7500; + + let query = iter::once(format!("query {{ ...{PERM} }} ")) + .chain( + PERM.chars() + .permutations(PERM.len()) + .map(|vec| vec.into_iter().collect::<String>()) + .take(CIRCLE_SIZE) + .collect::<Vec<_>>() + .into_iter() + .circular_tuple_windows::<(_, _)>() + .map(|(cur, next)| format!("fragment {cur} on Query {{ ...{next} }} ")), + ) + .collect::<String>(); + + let schema = Schema::new(Query, EmptyMutation::new(), EmptySubscription::new()); + let _ = juniper::execute(&query, None, &schema, &Variables::new(), &()) + .await + .unwrap_err(); +} diff --git a/integration_tests/juniper_tests/src/lib.rs b/integration_tests/juniper_tests/src/lib.rs index 5592c146..8728494e 100644 --- a/integration_tests/juniper_tests/src/lib.rs +++ b/integration_tests/juniper_tests/src/lib.rs @@ -7,6 +7,8 @@ mod codegen; #[cfg(test)] mod custom_scalar; #[cfg(test)] +mod cve_2022_31173; +#[cfg(test)] mod explicit_null; #[cfg(test)] mod infallible_as_field_error; diff --git a/juniper/CHANGELOG.md b/juniper/CHANGELOG.md index 921b700c..6ae5c2c8 100644 --- a/juniper/CHANGELOG.md +++ b/juniper/CHANGELOG.md @@ -1,5 +1,6 @@ # master +- Fix [CVE-2022-31173](https://github.com/graphql-rust/juniper/security/advisories/GHSA-4rx6-g5vg-5f3j). - Fix incorrect error when explicit `null` provided for `null`able list input parameter. ([#1086](https://github.com/graphql-rust/juniper/pull/1086)) # [[0.15.9] 2022-02-02](https://github.com/graphql-rust/juniper/releases/tag/juniper-v0.15.9) diff --git a/juniper/src/validation/rules/no_fragment_cycles.rs b/juniper/src/validation/rules/no_fragment_cycles.rs index c5489e08..b1f119d8 100644 --- a/juniper/src/validation/rules/no_fragment_cycles.rs +++ b/juniper/src/validation/rules/no_fragment_cycles.rs @@ -7,19 +7,6 @@ use crate::{ value::ScalarValue, }; -pub struct NoFragmentCycles<'a> { - current_fragment: Option<&'a str>, - spreads: HashMap<&'a str, Vec<Spanning<&'a str>>>, - fragment_order: Vec<&'a str>, -} - -struct CycleDetector<'a> { - visited: HashSet<&'a str>, - spreads: &'a HashMap<&'a str, Vec<Spanning<&'a str>>>, - path_indices: HashMap<&'a str, usize>, - errors: Vec<RuleError>, -} - pub fn factory<'a>() -> NoFragmentCycles<'a> { NoFragmentCycles { current_fragment: None, @@ -28,6 +15,12 @@ pub fn factory<'a>() -> NoFragmentCycles<'a> { } } +pub struct NoFragmentCycles<'a> { + current_fragment: Option<&'a str>, + spreads: HashMap<&'a str, Vec<Spanning<&'a str>>>, + fragment_order: Vec<&'a str>, +} + impl<'a, S> Visitor<'a, S> for NoFragmentCycles<'a> where S: ScalarValue, @@ -38,14 +31,12 @@ where let mut detector = CycleDetector { visited: HashSet::new(), spreads: &self.spreads, - path_indices: HashMap::new(), errors: Vec::new(), }; for frag in &self.fragment_order { if !detector.visited.contains(frag) { - let mut path = Vec::new(); - detector.detect_from(frag, &mut path); + detector.detect_from(frag); } } @@ -91,19 +82,46 @@ where } } +type CycleDetectorState<'a> = (&'a str, Vec<&'a Spanning<&'a str>>, HashMap<&'a str, usize>); + +struct CycleDetector<'a> { + visited: HashSet<&'a str>, + spreads: &'a HashMap<&'a str, Vec<Spanning<&'a str>>>, + errors: Vec<RuleError>, +} + impl<'a> CycleDetector<'a> { - fn detect_from(&mut self, from: &'a str, path: &mut Vec<&'a Spanning<&'a str>>) { + fn detect_from(&mut self, from: &'a str) { + let mut to_visit = Vec::new(); + to_visit.push((from, Vec::new(), HashMap::new())); + + while let Some((from, path, path_indices)) = to_visit.pop() { + to_visit.extend(self.detect_from_inner(from, path, path_indices)); + } + } + + /// This function should be called only inside [`Self::detect_from()`], as + /// it's a recursive function using heap instead of a stack. So, instead of + /// the recursive call, we return a [`Vec`] that is visited inside + /// [`Self::detect_from()`]. + fn detect_from_inner( + &mut self, + from: &'a str, + path: Vec<&'a Spanning<&'a str>>, + mut path_indices: HashMap<&'a str, usize>, + ) -> Vec<CycleDetectorState<'a>> { self.visited.insert(from); if !self.spreads.contains_key(from) { - return; + return Vec::new(); } - self.path_indices.insert(from, path.len()); + path_indices.insert(from, path.len()); + let mut to_visit = Vec::new(); for node in &self.spreads[from] { - let name = &node.item; - let index = self.path_indices.get(name).cloned(); + let name = node.item; + let index = path_indices.get(name).cloned(); if let Some(index) = index { let err_pos = if index < path.len() { @@ -114,14 +132,14 @@ impl<'a> CycleDetector<'a> { self.errors .push(RuleError::new(&error_message(name), &[err_pos.start])); - } else if !self.visited.contains(name) { + } else { + let mut path = path.clone(); path.push(node); - self.detect_from(name, path); - path.pop(); + to_visit.push((name, path, path_indices.clone())); } } - self.path_indices.remove(from); + to_visit } } diff --git a/juniper/src/validation/rules/no_undefined_variables.rs b/juniper/src/validation/rules/no_undefined_variables.rs index 8f13f191..3b73e9b8 100644 --- a/juniper/src/validation/rules/no_undefined_variables.rs +++ b/juniper/src/validation/rules/no_undefined_variables.rs @@ -12,13 +12,6 @@ pub enum Scope<'a> { Fragment(&'a str), } -pub struct NoUndefinedVariables<'a> { - defined_variables: HashMap<Option<&'a str>, (SourcePosition, HashSet<&'a str>)>, - used_variables: HashMap<Scope<'a>, Vec<Spanning<&'a str>>>, - current_scope: Option<Scope<'a>>, - spreads: HashMap<Scope<'a>, Vec<&'a str>>, -} - pub fn factory<'a>() -> NoUndefinedVariables<'a> { NoUndefinedVariables { defined_variables: HashMap::new(), @@ -28,6 +21,13 @@ pub fn factory<'a>() -> NoUndefinedVariables<'a> { } } +pub struct NoUndefinedVariables<'a> { + defined_variables: HashMap<Option<&'a str>, (SourcePosition, HashSet<&'a str>)>, + used_variables: HashMap<Scope<'a>, Vec<Spanning<&'a str>>>, + current_scope: Option<Scope<'a>>, + spreads: HashMap<Scope<'a>, Vec<&'a str>>, +} + impl<'a> NoUndefinedVariables<'a> { fn find_undef_vars( &'a self, @@ -36,8 +36,34 @@ impl<'a> NoUndefinedVariables<'a> { unused: &mut Vec<&'a Spanning<&'a str>>, visited: &mut HashSet<Scope<'a>>, ) { + let mut to_visit = Vec::new(); + if let Some(spreads) = self.find_undef_vars_inner(scope, defined, unused, visited) { + to_visit.push(spreads); + } + while let Some(spreads) = to_visit.pop() { + for spread in spreads { + if let Some(spreads) = + self.find_undef_vars_inner(&Scope::Fragment(spread), defined, unused, visited) + { + to_visit.push(spreads); + } + } + } + } + + /// This function should be called only inside [`Self::find_undef_vars()`], + /// as it's a recursive function using heap instead of a stack. So, instead + /// of the recursive call, we return a [`Vec`] that is visited inside + /// [`Self::find_undef_vars()`]. + fn find_undef_vars_inner( + &'a self, + scope: &Scope<'a>, + defined: &HashSet<&'a str>, + unused: &mut Vec<&'a Spanning<&'a str>>, + visited: &mut HashSet<Scope<'a>>, + ) -> Option<&'a Vec<&'a str>> { if visited.contains(scope) { - return; + return None; } visited.insert(scope.clone()); @@ -50,11 +76,7 @@ impl<'a> NoUndefinedVariables<'a> { } } - if let Some(spreads) = self.spreads.get(scope) { - for spread in spreads { - self.find_undef_vars(&Scope::Fragment(spread), defined, unused, visited); - } - } + self.spreads.get(scope) } } diff --git a/juniper/src/validation/rules/no_unused_fragments.rs b/juniper/src/validation/rules/no_unused_fragments.rs index 97b2bcf8..7e5ca7f6 100644 --- a/juniper/src/validation/rules/no_unused_fragments.rs +++ b/juniper/src/validation/rules/no_unused_fragments.rs @@ -7,18 +7,12 @@ use crate::{ value::ScalarValue, }; -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Scope<'a> { Operation(Option<&'a str>), Fragment(&'a str), } -pub struct NoUnusedFragments<'a> { - spreads: HashMap<Scope<'a>, Vec<&'a str>>, - defined_fragments: HashSet<Spanning<&'a str>>, - current_scope: Option<Scope<'a>>, -} - pub fn factory<'a>() -> NoUnusedFragments<'a> { NoUnusedFragments { spreads: HashMap::new(), @@ -27,21 +21,42 @@ pub fn factory<'a>() -> NoUnusedFragments<'a> { } } +pub struct NoUnusedFragments<'a> { + spreads: HashMap<Scope<'a>, Vec<&'a str>>, + defined_fragments: HashSet<Spanning<&'a str>>, + current_scope: Option<Scope<'a>>, +} + impl<'a> NoUnusedFragments<'a> { - fn find_reachable_fragments(&self, from: &Scope<'a>, result: &mut HashSet<&'a str>) { - if let Scope::Fragment(name) = *from { + fn find_reachable_fragments(&'a self, from: Scope<'a>, result: &mut HashSet<&'a str>) { + let mut to_visit = Vec::new(); + to_visit.push(from); + + while let Some(from) = to_visit.pop() { + if let Some(next) = self.find_reachable_fragments_inner(from, result) { + to_visit.extend(next.iter().map(|s| Scope::Fragment(s))); + } + } + } + + /// This function should be called only inside + /// [`Self::find_reachable_fragments()`], as it's a recursive function using + /// heap instead of a stack. So, instead of the recursive call, we return a + /// [`Vec`] that is visited inside [`Self::find_reachable_fragments()`]. + fn find_reachable_fragments_inner( + &'a self, + from: Scope<'a>, + result: &mut HashSet<&'a str>, + ) -> Option<&'a Vec<&'a str>> { + if let Scope::Fragment(name) = from { if result.contains(name) { - return; + return None; } else { result.insert(name); } } - if let Some(spreads) = self.spreads.get(from) { - for spread in spreads { - self.find_reachable_fragments(&Scope::Fragment(spread), result) - } - } + self.spreads.get(&from) } } @@ -59,7 +74,7 @@ where }) = *def { let op_name = name.as_ref().map(|s| s.item); - self.find_reachable_fragments(&Scope::Operation(op_name), &mut reachable); + self.find_reachable_fragments(Scope::Operation(op_name), &mut reachable); } } @@ -96,7 +111,7 @@ where ) { if let Some(ref scope) = self.current_scope { self.spreads - .entry(scope.clone()) + .entry(*scope) .or_insert_with(Vec::new) .push(spread.item.name.item); } diff --git a/juniper/src/validation/rules/no_unused_variables.rs b/juniper/src/validation/rules/no_unused_variables.rs index 35e5f933..66b92299 100644 --- a/juniper/src/validation/rules/no_unused_variables.rs +++ b/juniper/src/validation/rules/no_unused_variables.rs @@ -12,13 +12,6 @@ pub enum Scope<'a> { Fragment(&'a str), } -pub struct NoUnusedVariables<'a> { - defined_variables: HashMap<Option<&'a str>, HashSet<&'a Spanning<&'a str>>>, - used_variables: HashMap<Scope<'a>, Vec<&'a str>>, - current_scope: Option<Scope<'a>>, - spreads: HashMap<Scope<'a>, Vec<&'a str>>, -} - pub fn factory<'a>() -> NoUnusedVariables<'a> { NoUnusedVariables { defined_variables: HashMap::new(), @@ -28,16 +21,49 @@ pub fn factory<'a>() -> NoUnusedVariables<'a> { } } +pub struct NoUnusedVariables<'a> { + defined_variables: HashMap<Option<&'a str>, HashSet<&'a Spanning<&'a str>>>, + used_variables: HashMap<Scope<'a>, Vec<&'a str>>, + current_scope: Option<Scope<'a>>, + spreads: HashMap<Scope<'a>, Vec<&'a str>>, +} + impl<'a> NoUnusedVariables<'a> { fn find_used_vars( - &self, + &'a self, from: &Scope<'a>, defined: &HashSet<&'a str>, used: &mut HashSet<&'a str>, visited: &mut HashSet<Scope<'a>>, ) { + let mut to_visit = Vec::new(); + if let Some(spreads) = self.find_used_vars_inner(from, defined, used, visited) { + to_visit.push(spreads); + } + while let Some(spreads) = to_visit.pop() { + for spread in spreads { + if let Some(spreads) = + self.find_used_vars_inner(&Scope::Fragment(spread), defined, used, visited) + { + to_visit.push(spreads); + } + } + } + } + + /// This function should be called only inside [`Self::find_used_vars()`], + /// as it's a recursive function using heap instead of a stack. So, instead + /// of the recursive call, we return a [`Vec`] that is visited inside + /// [`Self::find_used_vars()`]. + fn find_used_vars_inner( + &'a self, + from: &Scope<'a>, + defined: &HashSet<&'a str>, + used: &mut HashSet<&'a str>, + visited: &mut HashSet<Scope<'a>>, + ) -> Option<&'a Vec<&'a str>> { if visited.contains(from) { - return; + return None; } visited.insert(from.clone()); @@ -50,11 +76,7 @@ impl<'a> NoUnusedVariables<'a> { } } - if let Some(spreads) = self.spreads.get(from) { - for spread in spreads { - self.find_used_vars(&Scope::Fragment(spread), defined, used, visited); - } - } + self.spreads.get(from) } } diff --git a/juniper/src/validation/rules/overlapping_fields_can_be_merged.rs b/juniper/src/validation/rules/overlapping_fields_can_be_merged.rs index 30ec9226..5aa82120 100644 --- a/juniper/src/validation/rules/overlapping_fields_can_be_merged.rs +++ b/juniper/src/validation/rules/overlapping_fields_can_be_merged.rs @@ -274,30 +274,61 @@ impl<'a, S: Debug> OverlappingFieldsCanBeMerged<'a, S> { ) where S: ScalarValue, { - let fragment = match self.named_fragments.get(fragment_name) { - Some(f) => f, - None => return, - }; + let mut to_check = Vec::new(); + if let Some(fragments) = self.collect_conflicts_between_fields_and_fragment_inner( + conflicts, + field_map, + fragment_name, + mutually_exclusive, + ctx, + ) { + to_check.push((fragment_name, fragments)) + } + + while let Some((fragment_name, fragment_names2)) = to_check.pop() { + for fragment_name2 in fragment_names2 { + // Early return on fragment recursion, as it makes no sense. + // Fragment recursions are prevented by `no_fragment_cycles` validator. + if fragment_name == fragment_name2 { + return; + } + if let Some(fragments) = self.collect_conflicts_between_fields_and_fragment_inner( + conflicts, + field_map, + fragment_name2, + mutually_exclusive, + ctx, + ) { + to_check.push((fragment_name2, fragments)); + }; + } + } + } + + /// This function should be called only inside + /// [`Self::collect_conflicts_between_fields_and_fragment()`], as it's a + /// recursive function using heap instead of a stack. So, instead of the + /// recursive call, we return a [`Vec`] that is visited inside + /// [`Self::collect_conflicts_between_fields_and_fragment()`]. + fn collect_conflicts_between_fields_and_fragment_inner( + &self, + conflicts: &mut Vec<Conflict>, + field_map: &AstAndDefCollection<'a, S>, + fragment_name: &str, + mutually_exclusive: bool, + ctx: &ValidatorContext<'a, S>, + ) -> Option<Vec<&'a str>> + where + S: ScalarValue, + { + let fragment = self.named_fragments.get(fragment_name)?; let (field_map2, fragment_names2) = self.get_referenced_fields_and_fragment_names(fragment, ctx); self.collect_conflicts_between(conflicts, mutually_exclusive, field_map, &field_map2, ctx); - for fragment_name2 in fragment_names2 { - // Early return on fragment recursion, as it makes no sense. - // Fragment recursions are prevented by `no_fragment_cycles` validator. - if fragment_name == fragment_name2 { - return; - } - self.collect_conflicts_between_fields_and_fragment( - conflicts, - field_map, - fragment_name2, - mutually_exclusive, - ctx, - ); - } + Some(fragment_names2) } fn collect_conflicts_between( diff --git a/juniper/src/validation/rules/variables_in_allowed_position.rs b/juniper/src/validation/rules/variables_in_allowed_position.rs index 9e7e3fc3..ad1e9884 100644 --- a/juniper/src/validation/rules/variables_in_allowed_position.rs +++ b/juniper/src/validation/rules/variables_in_allowed_position.rs @@ -17,13 +17,6 @@ pub enum Scope<'a> { Fragment(&'a str), } -pub struct VariableInAllowedPosition<'a, S: Debug + 'a> { - spreads: HashMap<Scope<'a>, HashSet<&'a str>>, - variable_usages: HashMap<Scope<'a>, Vec<(Spanning<&'a String>, Type<'a>)>>, - variable_defs: HashMap<Scope<'a>, Vec<&'a (Spanning<&'a str>, VariableDefinition<'a, S>)>>, - current_scope: Option<Scope<'a>>, -} - pub fn factory<'a, S: Debug>() -> VariableInAllowedPosition<'a, S> { VariableInAllowedPosition { spreads: HashMap::new(), @@ -33,16 +26,54 @@ pub fn factory<'a, S: Debug>() -> VariableInAllowedPosition<'a, S> { } } +pub struct VariableInAllowedPosition<'a, S: Debug + 'a> { + spreads: HashMap<Scope<'a>, HashSet<&'a str>>, + variable_usages: HashMap<Scope<'a>, Vec<(Spanning<&'a String>, Type<'a>)>>, + #[allow(clippy::type_complexity)] + variable_defs: HashMap<Scope<'a>, Vec<&'a (Spanning<&'a str>, VariableDefinition<'a, S>)>>, + current_scope: Option<Scope<'a>>, +} + impl<'a, S: Debug> VariableInAllowedPosition<'a, S> { - fn collect_incorrect_usages( - &self, + fn collect_incorrect_usages<'me>( + &'me self, from: &Scope<'a>, var_defs: &[&'a (Spanning<&'a str>, VariableDefinition<S>)], ctx: &mut ValidatorContext<'a, S>, visited: &mut HashSet<Scope<'a>>, ) { + let mut to_visit = Vec::new(); + if let Some(spreads) = self.collect_incorrect_usages_inner(from, var_defs, ctx, visited) { + to_visit.push(spreads); + } + + while let Some(spreads) = to_visit.pop() { + for spread in spreads { + if let Some(spreads) = self.collect_incorrect_usages_inner( + &Scope::Fragment(spread), + var_defs, + ctx, + visited, + ) { + to_visit.push(spreads); + } + } + } + } + + /// This function should be called only inside + /// [`Self::collect_incorrect_usages()`], as it's a recursive function using + /// heap instead of a stack. So, instead of the recursive call, we return a + /// [`Vec`] that is visited inside [`Self::collect_incorrect_usages()`]. + fn collect_incorrect_usages_inner<'me>( + &'me self, + from: &Scope<'a>, + var_defs: &[&'a (Spanning<&'a str>, VariableDefinition<S>)], + ctx: &mut ValidatorContext<'a, S>, + visited: &mut HashSet<Scope<'a>>, + ) -> Option<&'me HashSet<&'a str>> { if visited.contains(from) { - return; + return None; } visited.insert(from.clone()); @@ -75,11 +106,7 @@ impl<'a, S: Debug> VariableInAllowedPosition<'a, S> { } } - if let Some(spreads) = self.spreads.get(from) { - for spread in spreads { - self.collect_incorrect_usages(&Scope::Fragment(spread), var_defs, ctx, visited); - } - } + self.spreads.get(from) } } From 6fd7a591cf43255712f102892a46c732ee193f9e Mon Sep 17 00:00:00 2001 From: tyranron <tyranron@gmail.com> Date: Thu, 28 Jul 2022 17:12:27 +0300 Subject: [PATCH 2/2] Release `juniper` 0.15.10 --- juniper/CHANGELOG.md | 2 +- juniper/Cargo.toml | 2 +- juniper/src/lib.rs | 2 +- juniper_actix/Cargo.toml | 4 ++-- juniper_codegen/Cargo.toml | 2 +- juniper_graphql_ws/Cargo.toml | 2 +- juniper_hyper/Cargo.toml | 4 ++-- juniper_iron/Cargo.toml | 4 ++-- juniper_rocket/Cargo.toml | 4 ++-- juniper_subscriptions/Cargo.toml | 2 +- juniper_warp/Cargo.toml | 4 ++-- 11 files changed, 16 insertions(+), 16 deletions(-) diff --git a/juniper/CHANGELOG.md b/juniper/CHANGELOG.md index 6ae5c2c8..1b9e8e80 100644 --- a/juniper/CHANGELOG.md +++ b/juniper/CHANGELOG.md @@ -1,4 +1,4 @@ -# master +# [[0.15.10] 2022-07-28](https://github.com/graphql-rust/juniper/releases/tag/juniper-v0.15.10) - Fix [CVE-2022-31173](https://github.com/graphql-rust/juniper/security/advisories/GHSA-4rx6-g5vg-5f3j). - Fix incorrect error when explicit `null` provided for `null`able list input parameter. ([#1086](https://github.com/graphql-rust/juniper/pull/1086)) diff --git a/juniper/Cargo.toml b/juniper/Cargo.toml index 3b083370..96c4b5ae 100644 --- a/juniper/Cargo.toml +++ b/juniper/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "juniper" -version = "0.15.9" +version = "0.15.10" authors = [ "Magnus Hallin <mhallin@fastmail.com>", "Christoph Herzog <chris@theduke.at>", diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs index 85b0bafb..f7c9452e 100644 --- a/juniper/src/lib.rs +++ b/juniper/src/lib.rs @@ -90,7 +90,7 @@ Juniper has not reached 1.0 yet, thus some API instability should be expected. [bson]: https://crates.io/crates/bson */ -#![doc(html_root_url = "https://docs.rs/juniper/0.15.9")] +#![doc(html_root_url = "https://docs.rs/juniper/0.15.10")] #![warn(missing_docs)] // Required for using `juniper_codegen` macros inside this crate to resolve absolute `::juniper` diff --git a/juniper_actix/Cargo.toml b/juniper_actix/Cargo.toml index c59e0dfa..e8aecb31 100644 --- a/juniper_actix/Cargo.toml +++ b/juniper_actix/Cargo.toml @@ -18,7 +18,7 @@ http = "0.2.4" actix-web = "4.0.0-beta.8" actix-web-actors = "4.0.0-beta.6" -juniper = { version = "0.15.9", path = "../juniper", default-features = false } +juniper = { version = "0.15.10", path = "../juniper", default-features = false } juniper_graphql_ws = { version = "0.2.6", path = "../juniper_graphql_ws", optional = true } anyhow = "1.0" @@ -35,7 +35,7 @@ tokio = "1" async-stream = "0.3" actix-test = "0.1.0-beta.3" -juniper = { version = "0.15.9", path = "../juniper", features = ["expose-test-schema"] } +juniper = { version = "0.15.10", path = "../juniper", features = ["expose-test-schema"] } bytes = "1.0" env_logger = "0.8" diff --git a/juniper_codegen/Cargo.toml b/juniper_codegen/Cargo.toml index dfe54e63..85449f41 100644 --- a/juniper_codegen/Cargo.toml +++ b/juniper_codegen/Cargo.toml @@ -26,4 +26,4 @@ syn = { version = "1.0.60", features = ["extra-traits", "full", "parsing"], defa [dev-dependencies] derive_more = "0.99.7" futures = "0.3" -juniper = { version = "0.15.9", path = "../juniper" } +juniper = { version = "0.15.10", path = "../juniper" } diff --git a/juniper_graphql_ws/Cargo.toml b/juniper_graphql_ws/Cargo.toml index e7f1ce34..b9b12b89 100644 --- a/juniper_graphql_ws/Cargo.toml +++ b/juniper_graphql_ws/Cargo.toml @@ -10,7 +10,7 @@ repository = "https://github.com/graphql-rust/juniper" keywords = ["apollo", "graphql", "graphql-ws", "juniper"] [dependencies] -juniper = { version = "0.15.9", path = "../juniper", default-features = false } +juniper = { version = "0.15.10", path = "../juniper", default-features = false } juniper_subscriptions = { version = "0.15.6", path = "../juniper_subscriptions" } serde = { version = "1.0.8", features = ["derive"], default-features = false } tokio = { version = "1", features = ["macros", "rt", "time"], default-features = false } diff --git a/juniper_hyper/Cargo.toml b/juniper_hyper/Cargo.toml index 6b0cffa4..3808b69d 100644 --- a/juniper_hyper/Cargo.toml +++ b/juniper_hyper/Cargo.toml @@ -10,14 +10,14 @@ repository = "https://github.com/graphql-rust/juniper" [dependencies] futures = "0.3.1" -juniper = { version = "0.15.9", path = "../juniper", default-features = false } +juniper = { version = "0.15.10", path = "../juniper", default-features = false } hyper = {version = "0.14", features = ["server", "runtime"]} serde_json = "1.0" tokio = "1" url = "2" [dev-dependencies] -juniper = { version = "0.15.9", path = "../juniper", features = ["expose-test-schema"] } +juniper = { version = "0.15.10", path = "../juniper", features = ["expose-test-schema"] } pretty_env_logger = "0.4" reqwest = { version = "0.11", features = ["blocking", "rustls-tls"] } tokio = { version = "1", features = ["macros", "rt-multi-thread"] } diff --git a/juniper_iron/Cargo.toml b/juniper_iron/Cargo.toml index 46239046..a98b04c7 100644 --- a/juniper_iron/Cargo.toml +++ b/juniper_iron/Cargo.toml @@ -13,13 +13,13 @@ repository = "https://github.com/graphql-rust/juniper" [dependencies] futures = "0.3.1" -juniper = { version = "0.15.9", path = "../juniper" } +juniper = { version = "0.15.10", path = "../juniper" } iron = ">= 0.5, < 0.7" serde_json = "1.0.2" urlencoded = ">= 0.5, < 0.7" [dev-dependencies] -juniper = { version = "0.15.9", path = "../juniper", features = ["expose-test-schema"] } +juniper = { version = "0.15.10", path = "../juniper", features = ["expose-test-schema"] } iron-test = "0.6" logger = "0.4" mount = "0.4" diff --git a/juniper_rocket/Cargo.toml b/juniper_rocket/Cargo.toml index 7b68c0a2..f92315ce 100644 --- a/juniper_rocket/Cargo.toml +++ b/juniper_rocket/Cargo.toml @@ -13,9 +13,9 @@ repository = "https://github.com/graphql-rust/juniper" [dependencies] futures = "0.3.1" -juniper = { version = "0.15.9", path = "../juniper", default-features = false } +juniper = { version = "0.15.10", path = "../juniper", default-features = false } rocket = { version = "0.5.0-rc.1", default-features = false } serde_json = "1.0.2" [dev-dependencies] -juniper = { version = "0.15.9", path = "../juniper", features = ["expose-test-schema"] } +juniper = { version = "0.15.10", path = "../juniper", features = ["expose-test-schema"] } diff --git a/juniper_subscriptions/Cargo.toml b/juniper_subscriptions/Cargo.toml index d7bb9781..623de98d 100644 --- a/juniper_subscriptions/Cargo.toml +++ b/juniper_subscriptions/Cargo.toml @@ -10,7 +10,7 @@ repository = "https://github.com/graphql-rust/juniper" [dependencies] futures = "0.3.1" -juniper = { version = "0.15.9", path = "../juniper", default-features = false } +juniper = { version = "0.15.10", path = "../juniper", default-features = false } [dev-dependencies] serde_json = "1.0" diff --git a/juniper_warp/Cargo.toml b/juniper_warp/Cargo.toml index 11c10449..59b0328a 100644 --- a/juniper_warp/Cargo.toml +++ b/juniper_warp/Cargo.toml @@ -14,7 +14,7 @@ subscriptions = ["juniper_graphql_ws"] [dependencies] anyhow = "1.0" futures = "0.3.1" -juniper = { version = "0.15.9", path = "../juniper", default-features = false } +juniper = { version = "0.15.10", path = "../juniper", default-features = false } juniper_graphql_ws = { version = "0.2.6", path = "../juniper_graphql_ws", optional = true } serde = { version = "1.0.75", features = ["derive"] } serde_json = "1.0.24" @@ -24,7 +24,7 @@ warp = "0.3" [dev-dependencies] env_logger = "0.8" -juniper = { version = "0.15.9", path = "../juniper", features = ["expose-test-schema"] } +juniper = { version = "0.15.10", path = "../juniper", features = ["expose-test-schema"] } log = "0.4" percent-encoding = "2.1" tokio = { version = "1", features = ["macros", "rt-multi-thread"] }