diff --git a/CHANGELOG.md b/CHANGELOG.md index 421ea29a6..77cee0033 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,11 +9,15 @@ You can find and compare releases at the [GitHub release page](https://github.co ## Unreleased +### Fixed + +- Avoid infinite recursion in `QueryDepth` validator https://github.com/webonyx/graphql-php/pull/1581 + ## v15.12.4 ### Fixed -- Ensure unaliasedPath does not grow for each list item https://github.com/webonyx/graphql-php/pull/1579 +- Ensure `unaliasedPath` does not grow for each list item https://github.com/webonyx/graphql-php/pull/1579 ## v15.12.3 diff --git a/src/Validator/Rules/QueryDepth.php b/src/Validator/Rules/QueryDepth.php index 751348aa6..2ea3f17c5 100644 --- a/src/Validator/Rules/QueryDepth.php +++ b/src/Validator/Rules/QueryDepth.php @@ -15,6 +15,9 @@ class QueryDepth extends QuerySecurityRule { + /** @var array Fragment names which are already calculated in recursion */ + protected array $calculatedFragments = []; + protected int $maxQueryDepth; /** @throws \InvalidArgumentException */ @@ -82,7 +85,14 @@ protected function nodeDepth(Node $node, int $depth = 0, int $maxDepth = 0): int $fragment = $this->getFragment($node); if ($fragment !== null) { + $name = $fragment->name->value; + if (isset($this->calculatedFragments[$name])) { + return $this->maxQueryDepth + 1; + } + + $this->calculatedFragments[$name] = true; $maxDepth = $this->fieldDepth($fragment, $depth, $maxDepth); + unset($this->calculatedFragments[$name]); } break; diff --git a/tests/Validator/QueryDepthTest.php b/tests/Validator/QueryDepthTest.php index 0b5d06bdf..450c30db2 100644 --- a/tests/Validator/QueryDepthTest.php +++ b/tests/Validator/QueryDepthTest.php @@ -93,6 +93,12 @@ public function testTypeNameMetaFieldQuery(): void $this->assertTypeNameMetaFieldQuery(1); } + public function testInfiniteRecursion(): void + { + $query = 'query MyQuery { human { ...F1 } } fragment F1 on Human { ...F1 }'; + $this->assertDocumentValidator($query, 7, [self::createFormattedError(7, 8)]); + } + /** @return iterable>}> */ public static function queryDataProvider(): iterable {