• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

wp-graphql / wp-graphql / 17335997908

29 Aug 2025 11:06PM UTC coverage: 84.593%. Remained the same
17335997908

push

github

actions-user
chore: update changeset for PR #3407

15884 of 18777 relevant lines covered (84.59%)

260.51 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

39.29
/src/Server/ValidationRules/QueryDepth.php
1
<?php
2

3
namespace WPGraphQL\Server\ValidationRules;
4

5
use GraphQL\Error\Error;
6
use GraphQL\Language\AST\FieldNode;
7
use GraphQL\Language\AST\FragmentSpreadNode;
8
use GraphQL\Language\AST\InlineFragmentNode;
9
use GraphQL\Language\AST\Node;
10
use GraphQL\Language\AST\NodeKind;
11
use GraphQL\Language\AST\OperationDefinitionNode;
12
use GraphQL\Language\AST\SelectionSetNode;
13
use GraphQL\Validator\Rules\QuerySecurityRule;
14

15
/**
16
 * Class QueryDepth
17
 *
18
 * @package WPGraphQL\Server\ValidationRules
19
 */
20
class QueryDepth extends QuerySecurityRule {
21

22
        /**
23
         * The max query depth allowed.
24
         */
25
        private int $maxQueryDepth;
26

27
        /**
28
         * QueryDepth constructor.
29
         */
30
        public function __construct() {
762✔
31
                $max_query_depth = get_graphql_setting( 'query_depth_max', 10 );
762✔
32
                $max_query_depth = absint( $max_query_depth ) ?? 10;
762✔
33
                $this->setMaxQueryDepth( $max_query_depth );
762✔
34
        }
35

36
        /**
37
         * {@inheritDoc}
38
         */
39
        public function getVisitor( \GraphQL\Validator\QueryValidationContext $context ): array {
754✔
40
                return $this->invokeIfNeeded(
754✔
41
                        $context,
754✔
42
                        [
754✔
43
                                NodeKind::OPERATION_DEFINITION => [
754✔
44
                                        'leave' => function ( Node $node ) use ( $context ): void {
754✔
45
                                                if ( ! $node instanceof OperationDefinitionNode ) {
×
46
                                                        return;
×
47
                                                }
48

49
                                                $maxDepth = $this->fieldDepth( $node );
×
50

51
                                                if ( $maxDepth <= $this->getMaxQueryDepth() ) {
×
52
                                                        return;
×
53
                                                }
54

55
                                                $context->reportError(
×
56
                                                        new Error( $this->errorMessage( $this->getMaxQueryDepth(), $maxDepth ) )
×
57
                                                );
×
58
                                        },
754✔
59
                                ],
754✔
60
                        ]
754✔
61
                );
754✔
62
        }
63

64
        /**
65
         * Determine field depth
66
         *
67
         * @param \GraphQL\Language\AST\Node $node The node being analyzed
68
         * @param int                        $depth The depth of the field. Default is 0
69
         * @param int                        $maxDepth The max depth allowed. Default is 0
70
         */
71
        private function fieldDepth( Node $node, int $depth = 0, int $maxDepth = 0 ): int {
×
72
                if ( isset( $node->selectionSet ) && $node->selectionSet instanceof SelectionSetNode ) {
×
73
                        foreach ( $node->selectionSet->selections as $childNode ) {
×
74
                                $maxDepth = $this->nodeDepth( $childNode, $depth, $maxDepth );
×
75
                        }
76
                }
77

78
                return $maxDepth;
×
79
        }
80

81
        /**
82
         * Determine node depth
83
         *
84
         * @param \GraphQL\Language\AST\Node $node The node being analyzed in the operation
85
         * @param int                        $depth The depth of the operation
86
         * @param int                        $maxDepth The Max Depth of the operation
87
         */
88
        private function nodeDepth( Node $node, int $depth, int $maxDepth ): int {
×
89
                switch ( true ) {
90
                        case $node instanceof FieldNode:
×
91
                                // node has children?
92
                                if ( isset( $node->selectionSet ) ) {
×
93
                                        // update maxDepth if needed
94
                                        if ( $depth > $maxDepth ) {
×
95
                                                $maxDepth = $depth;
×
96
                                        }
97
                                        $maxDepth = $this->fieldDepth( $node, $depth + 1, $maxDepth );
×
98
                                }
99
                                break;
×
100

101
                        case $node instanceof InlineFragmentNode:
×
102
                                // node has children?
103
                                $maxDepth = $this->fieldDepth( $node, $depth, $maxDepth );
×
104
                                break;
×
105

106
                        case $node instanceof FragmentSpreadNode:
×
107
                                $fragment = $this->getFragment( $node );
×
108

109
                                if ( null !== $fragment ) {
×
110
                                        $maxDepth = $this->fieldDepth( $fragment, $depth, $maxDepth );
×
111
                                }
112
                                break;
×
113
                }
114

115
                return $maxDepth;
×
116
        }
117

118
        /**
119
         * Return the maxQueryDepth allowed
120
         *
121
         * @return int
122
         */
123
        public function getMaxQueryDepth() {
×
124
                return $this->maxQueryDepth;
×
125
        }
126

127
        /**
128
         * Set max query depth. If equal to 0 no check is done. Must be greater or equal to 0.
129
         *
130
         * @param int $maxQueryDepth The max query depth to allow for GraphQL operations
131
         *
132
         * @return void
133
         */
134
        public function setMaxQueryDepth( int $maxQueryDepth ) {
762✔
135
                $this->checkIfGreaterOrEqualToZero( 'maxQueryDepth', $maxQueryDepth );
762✔
136

137
                $this->maxQueryDepth = $maxQueryDepth;
762✔
138
        }
139

140
        /**
141
         * Return the max query depth error message
142
         *
143
         * @param int $max The max number of levels to allow in GraphQL operation
144
         * @param int $count The number of levels in the current operation
145
         *
146
         * @return string
147
         */
148
        public function errorMessage( $max, $count ) {
×
149
                return sprintf( 'The server administrator has limited the max query depth to %d, but the requested query has %d levels.', $max, $count );
×
150
        }
151

152
        /**
153
         * Determine whether the rule should be enabled
154
         */
155
        protected function isEnabled(): bool {
754✔
156
                $is_enabled = false;
754✔
157

158
                $enabled = get_graphql_setting( 'query_depth_enabled', 'off' );
754✔
159

160
                if ( 'on' === $enabled && absint( $this->getMaxQueryDepth() ) && 1 <= $this->getMaxQueryDepth() ) {
754✔
161
                        $is_enabled = true;
×
162
                }
163

164
                return $is_enabled;
754✔
165
        }
166
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc