@@ -15,14 +15,34 @@ export function MaxIntrospectionDepthRule(
15
15
* Counts the depth of list fields in "__Type" recursively and
16
16
* returns `true` if the limit has been reached.
17
17
*/
18
- function checkDepth ( node : ASTNode , depth : number = 0 ) : boolean {
18
+ function checkDepth (
19
+ node : ASTNode ,
20
+ visitedFragments : Record < string , true > ,
21
+ depth : number = 0 ,
22
+ ) : boolean {
19
23
if ( node . kind === Kind . FRAGMENT_SPREAD ) {
20
- const fragment = context . getFragment ( node . name . value ) ;
24
+ const fragmentName = node . name . value ;
25
+ if ( visitedFragments [ fragmentName ] ) {
26
+ // Fragment cycles are handled by `NoFragmentCyclesRule`.
27
+ return false ;
28
+ }
29
+ const fragment = context . getFragment ( fragmentName ) ;
21
30
if ( ! fragment ) {
22
- // missing fragments checks are handled by the `KnownFragmentNamesRule`
31
+ // Missing fragments checks are handled by `KnownFragmentNamesRule`.
23
32
return false ;
24
33
}
25
- return checkDepth ( fragment , depth ) ;
34
+
35
+ // Rather than following an immutable programming pattern which has
36
+ // significant memory and garbage collection overhead, we've opted to
37
+ // take a mutable approach for efficiency's sake. Importantly visiting a
38
+ // fragment twice is fine, so long as you don't do one visit inside the
39
+ // other.
40
+ try {
41
+ visitedFragments [ fragmentName ] = true ;
42
+ return checkDepth ( fragment , visitedFragments , depth ) ;
43
+ } finally {
44
+ delete visitedFragments [ fragmentName ] ;
45
+ }
26
46
}
27
47
28
48
if (
@@ -43,7 +63,7 @@ export function MaxIntrospectionDepthRule(
43
63
// handles fields and inline fragments
44
64
if ( 'selectionSet' in node && node . selectionSet ) {
45
65
for ( const child of node . selectionSet . selections ) {
46
- if ( checkDepth ( child , depth ) ) {
66
+ if ( checkDepth ( child , visitedFragments , depth ) ) {
47
67
return true ;
48
68
}
49
69
}
@@ -55,7 +75,7 @@ export function MaxIntrospectionDepthRule(
55
75
return {
56
76
Field ( node ) {
57
77
if ( node . name . value === '__schema' || node . name . value === '__type' ) {
58
- if ( checkDepth ( node ) ) {
78
+ if ( checkDepth ( node , Object . create ( null ) ) ) {
59
79
context . reportError (
60
80
new GraphQLError ( 'Maximum introspection depth exceeded' , {
61
81
nodes : [ node ] ,
0 commit comments