Skip to content

Commit 7bcd31d

Browse files
authored
Avoid infinite recursion in QueryDepth validator
1 parent dff1699 commit 7bcd31d

File tree

3 files changed

+21
-1
lines changed

3 files changed

+21
-1
lines changed

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,15 @@ You can find and compare releases at the [GitHub release page](https://github.co
99

1010
## Unreleased
1111

12+
### Fixed
13+
14+
- Avoid infinite recursion in `QueryDepth` validator https://github.com/webonyx/graphql-php/pull/1581
15+
1216
## v15.12.4
1317

1418
### Fixed
1519

16-
- Ensure unaliasedPath does not grow for each list item https://github.com/webonyx/graphql-php/pull/1579
20+
- Ensure `unaliasedPath` does not grow for each list item https://github.com/webonyx/graphql-php/pull/1579
1721

1822
## v15.12.3
1923

src/Validator/Rules/QueryDepth.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515

1616
class QueryDepth extends QuerySecurityRule
1717
{
18+
/** @var array<string, bool> Fragment names which are already calculated in recursion */
19+
protected array $calculatedFragments = [];
20+
1821
protected int $maxQueryDepth;
1922

2023
/** @throws \InvalidArgumentException */
@@ -82,7 +85,14 @@ protected function nodeDepth(Node $node, int $depth = 0, int $maxDepth = 0): int
8285
$fragment = $this->getFragment($node);
8386

8487
if ($fragment !== null) {
88+
$name = $fragment->name->value;
89+
if (isset($this->calculatedFragments[$name])) {
90+
return $this->maxQueryDepth + 1;
91+
}
92+
93+
$this->calculatedFragments[$name] = true;
8594
$maxDepth = $this->fieldDepth($fragment, $depth, $maxDepth);
95+
unset($this->calculatedFragments[$name]);
8696
}
8797

8898
break;

tests/Validator/QueryDepthTest.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,12 @@ public function testTypeNameMetaFieldQuery(): void
9393
$this->assertTypeNameMetaFieldQuery(1);
9494
}
9595

96+
public function testInfiniteRecursion(): void
97+
{
98+
$query = 'query MyQuery { human { ...F1 } } fragment F1 on Human { ...F1 }';
99+
$this->assertDocumentValidator($query, 7, [self::createFormattedError(7, 8)]);
100+
}
101+
96102
/** @return iterable<array{0: int, 1?: int, 2?: array<int, array<string, mixed>>}> */
97103
public static function queryDataProvider(): iterable
98104
{

0 commit comments

Comments
 (0)