Skip to content

Commit 589acb5

Browse files
committed
Merge security fixes from 0.15 juniper releases
2 parents 17d474e + 8276173 commit 589acb5

File tree

30 files changed

+227
-98
lines changed

30 files changed

+227
-98
lines changed

Makefile.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ CARGO_MAKE_WORKSPACE_SKIP_MEMBERS = "integration_tests/*;examples/*;juniper_benc
1212
[tasks.release]
1313
condition = { env_set = [ "RELEASE_LEVEL" ] }
1414
command = "cargo-release"
15-
args = ["release", "--config", "${CARGO_MAKE_WORKING_DIRECTORY}/../_build/release.toml", "${RELEASE_LEVEL}"]
15+
args = ["release", "--config", "${CARGO_MAKE_WORKING_DIRECTORY}/../_build/release.toml", "--execute", "${RELEASE_LEVEL}"]
1616

1717

1818
# Run `RELEASE_LEVEL=(patch|major|minor) cargo make release-dry-run` to do a dry run.
@@ -23,7 +23,7 @@ args = ["release", "--config", "${CARGO_MAKE_WORKING_DIRECTORY}/../_build/releas
2323
condition = { env_set = [ "RELEASE_LEVEL" ] }
2424
description = "Run `cargo-release --dry-run` for every crate"
2525
command = "cargo-release"
26-
args = ["release", "--config", "${CARGO_MAKE_WORKING_DIRECTORY}/../_build/release.toml", "--dry-run", "${RELEASE_LEVEL}"]
26+
args = ["release", "--config", "${CARGO_MAKE_WORKING_DIRECTORY}/../_build/release.toml", "--no-confirm", "--no-publish", "--no-push", "--no-tag", "${RELEASE_LEVEL}"]
2727

2828

2929
# Run `RELEASE_LEVEL=(patch|major|minor) cargo make release-local-test` to actually make changes locally but
@@ -35,4 +35,4 @@ args = ["release", "--config", "${CARGO_MAKE_WORKING_DIRECTORY}/../_build/releas
3535
condition = { env_set = [ "RELEASE_LEVEL" ] }
3636
description = "Run `cargo-release` for every crate, but only make changes locally"
3737
command = "cargo-release"
38-
args = ["release", "--config", "${CARGO_MAKE_WORKING_DIRECTORY}/../_build/release.toml", "--no-confirm", "--skip-publish", "--skip-push", "--skip-tag", "${RELEASE_LEVEL}"]
38+
args = ["release", "--config", "${CARGO_MAKE_WORKING_DIRECTORY}/../_build/release.toml", "--no-confirm", "${RELEASE_LEVEL}"]

_build/release.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
no-dev-version = true
21
pre-release-commit-message = "Release {{crate_name}} {{version}}"
3-
pro-release-commit-message = "Bump {{crate_name}} version to {{next_version}}"
2+
post-release-commit-message = "Bump {{crate_name}} version to {{next_version}}"
43
tag-message = "Release {{crate_name}} {{version}}"
54
pre-release-replacements = [
65
{file="CHANGELOG.md", min=0, search="# master", replace="# master\n\n- Compatibility with the latest `juniper`.\n\n# [[{{version}}] {{date}}](https://github.com/graphql-rust/juniper/releases/tag/{{crate_name}}-{{version}})"},

juniper/CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,14 @@
4242
- Support expressions in `graphql_value!` macro. ([#996](https://github.com/graphql-rust/juniper/pull/996), [#503](https://github.com/graphql-rust/juniper/issues/503))
4343
- List coercion rules: `null` cannot be coerced to an `[Int!]!` or `[Int]!`. ([#1004](https://github.com/graphql-rust/juniper/pull/1004))
4444

45+
# [[0.15.9] 2022-02-02](https://github.com/graphql-rust/juniper/releases/tag/juniper-v0.15.9)
46+
47+
- Fix infinite recursion on malformed queries with nested 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.
48+
49+
# [[0.15.8] 2022-01-26](https://github.com/graphql-rust/juniper/releases/tag/juniper-v0.15.8)
50+
51+
- 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.
52+
4553
# [[0.15.7] 2021-07-08](https://github.com/graphql-rust/juniper/releases/tag/juniper-v0.15.7)
4654

4755
- Fix panic on spreading untyped union fragments ([#945](https://github.com/graphql-rust/juniper/issues/945))

juniper/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "juniper"
3-
version = "0.15.7"
3+
version = "0.15.9"
44
authors = [
55
"Magnus Hallin <[email protected]>",
66
"Christoph Herzog <[email protected]>",
@@ -32,7 +32,7 @@ scalar-naivetime = []
3232
schema-language = ["graphql-parser-integration"]
3333

3434
[dependencies]
35-
juniper_codegen = { version = "0.15.7", path = "../juniper_codegen" }
35+
juniper_codegen = { version = "0.15.9", path = "../juniper_codegen" }
3636

3737
anyhow = { version = "1.0.32", optional = true, default-features = false }
3838
async-trait = "0.1.39"

juniper/Makefile.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@
44
CARGO_MAKE_CARGO_ALL_FEATURES = ""
55

66
[tasks.release]
7-
args = ["release", "--config", "${CARGO_MAKE_WORKING_DIRECTORY}/release.toml", "${RELEASE_LEVEL}"]
7+
args = ["release", "--config", "${CARGO_MAKE_WORKING_DIRECTORY}/release.toml", "--execute", "${RELEASE_LEVEL}"]
88

99
[tasks.release-dry-run]
10-
args = ["release", "--config", "${CARGO_MAKE_WORKING_DIRECTORY}/release.toml", "--dry-run", "${RELEASE_LEVEL}"]
10+
args = ["release", "--config", "${CARGO_MAKE_WORKING_DIRECTORY}/release.toml", "--no-confirm", "--no-publish", "--no-push", "--no-tag", "${RELEASE_LEVEL}"]
1111

1212
[tasks.release-local-test]
13-
args = ["release", "--config", "${CARGO_MAKE_WORKING_DIRECTORY}/release.toml", "--no-confirm", "--skip-publish", "--skip-push", "--skip-tag", "${RELEASE_LEVEL}"]
13+
args = ["release", "--config", "${CARGO_MAKE_WORKING_DIRECTORY}/release.toml", "--no-confirm", "${RELEASE_LEVEL}"]
1414

1515
[tasks.test]
1616
args = ["test", "--all-features"]

juniper/release.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
no-dev-version = true
21
pre-release-commit-message = "Release {{crate_name}} {{version}}"
3-
pro-release-commit-message = "Bump {{crate_name}} version to {{next_version}}"
2+
post-release-commit-message = "Bump {{crate_name}} version to {{next_version}}"
43
tag-message = "Release {{crate_name}} {{version}}"
54
pre-release-replacements = [
65
# Juniper's changelog

juniper/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ Juniper has not reached 1.0 yet, thus some API instability should be expected.
9494
*/
9595
// Due to `schema_introspection` test.
9696
#![cfg_attr(test, recursion_limit = "256")]
97-
#![doc(html_root_url = "https://docs.rs/juniper/0.15.7")]
97+
#![doc(html_root_url = "https://docs.rs/juniper/0.15.9")]
9898
#![warn(missing_docs)]
9999

100100
// Required for using `juniper_codegen` macros inside this crate to resolve absolute `::juniper`

juniper/src/validation/context.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,10 @@ impl<'a, S: Debug> ValidatorContext<'a, S> {
9797
self.errors.push(RuleError::new(message, locations))
9898
}
9999

100+
pub(crate) fn has_errors(&self) -> bool {
101+
!self.errors.is_empty()
102+
}
103+
100104
#[doc(hidden)]
101105
pub fn into_errors(mut self) -> Vec<RuleError> {
102106
self.errors.sort();

juniper/src/validation/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ pub use self::{
2121

2222
#[cfg(test)]
2323
pub use self::test_harness::{
24-
expect_fails_rule, expect_fails_rule_with_schema, expect_passes_rule,
24+
expect_fails_fn, expect_fails_fn_with_schema, expect_fails_rule, expect_fails_rule_with_schema,
25+
expect_passes_fn, expect_passes_fn_with_schema, expect_passes_rule,
2526
expect_passes_rule_with_schema,
2627
};

juniper/src/validation/rules/mod.rs

Lines changed: 65 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,14 @@ pub fn visit_all_rules<'a, S: Debug>(ctx: &mut ValidatorContext<'a, S>, doc: &'a
3535
where
3636
S: ScalarValue,
3737
{
38-
let mut mv = MultiVisitorNil
38+
// Some validators are depending on the results of other ones.
39+
// For example, validators checking fragments usually rely on the fact that
40+
// they have no cycles (`no_fragment_cycles`), otherwise may stall in an
41+
// infinite recursion. So, we should run validators in stages, moving to the
42+
// next stage only once the previous succeeds. This is better than making
43+
// every single validator being aware of fragments cycles and/or other
44+
// assumptions.
45+
let mut stage1 = MultiVisitorNil
3946
.with(self::arguments_of_correct_type::factory())
4047
.with(self::default_values_of_correct_type::factory())
4148
.with(self::fields_on_correct_type::factory())
@@ -49,7 +56,6 @@ where
4956
.with(self::no_undefined_variables::factory())
5057
.with(self::no_unused_fragments::factory())
5158
.with(self::no_unused_variables::factory())
52-
.with(self::overlapping_fields_can_be_merged::factory())
5359
.with(self::possible_fragment_spreads::factory())
5460
.with(self::provided_non_null_arguments::factory())
5561
.with(self::scalar_leafs::factory())
@@ -60,6 +66,62 @@ where
6066
.with(self::unique_variable_names::factory())
6167
.with(self::variables_are_input_types::factory())
6268
.with(self::variables_in_allowed_position::factory());
69+
visit(&mut stage1, ctx, doc);
70+
if ctx.has_errors() {
71+
return;
72+
}
6373

64-
visit(&mut mv, ctx, doc)
74+
let mut stage2 = MultiVisitorNil.with(self::overlapping_fields_can_be_merged::factory());
75+
visit(&mut stage2, ctx, doc);
76+
}
77+
78+
#[cfg(test)]
79+
mod tests {
80+
use crate::{parser::SourcePosition, DefaultScalarValue};
81+
82+
use crate::validation::{expect_fails_fn, RuleError};
83+
84+
#[test]
85+
fn handles_recursive_fragments() {
86+
expect_fails_fn::<_, DefaultScalarValue>(
87+
super::visit_all_rules,
88+
"fragment f on QueryRoot { ...f }",
89+
&[
90+
RuleError::new(
91+
"Fragment \"f\" is never used",
92+
&[SourcePosition::new(0, 0, 0)],
93+
),
94+
RuleError::new(
95+
"Cannot spread fragment \"f\"",
96+
&[SourcePosition::new(26, 0, 26)],
97+
),
98+
],
99+
);
100+
}
101+
102+
#[test]
103+
fn handles_nested_recursive_fragments() {
104+
expect_fails_fn::<_, DefaultScalarValue>(
105+
super::visit_all_rules,
106+
"fragment f on QueryRoot { a { ...f a { ...f } } }",
107+
&[
108+
RuleError::new(
109+
"Fragment \"f\" is never used",
110+
&[SourcePosition::new(0, 0, 0)],
111+
),
112+
RuleError::new(
113+
r#"Unknown field "a" on type "QueryRoot""#,
114+
&[SourcePosition::new(26, 0, 26)],
115+
),
116+
RuleError::new(
117+
"Cannot spread fragment \"f\"",
118+
&[SourcePosition::new(30, 0, 30)],
119+
),
120+
RuleError::new(
121+
"Cannot spread fragment \"f\"",
122+
&[SourcePosition::new(39, 0, 39)],
123+
),
124+
],
125+
);
126+
}
65127
}

0 commit comments

Comments
 (0)