Skip to content

Commit 0d07207

Browse files
authored
feat(server): Add configurable validation security rules for introspection and query complexity (#1244)
1 parent 61b005f commit 0d07207

File tree

3 files changed

+144
-3
lines changed

3 files changed

+144
-3
lines changed

Diff for: config/schema/graphql.schema.yml

+9
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,15 @@ graphql.graphql_servers.*:
2323
batching:
2424
type: boolean
2525
label: 'Batching'
26+
disable_introspection:
27+
type: boolean
28+
label: 'Disable Introspection'
29+
query_depth:
30+
type: integer
31+
label: 'Max query depth'
32+
query_complexity:
33+
type: integer
34+
label: 'Max query complexity'
2635
schema_configuration:
2736
type: 'graphql.schema.[%parent.schema]'
2837
persisted_queries_settings:

Diff for: src/Entity/Server.php

+109-3
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@
2525
use GraphQL\Server\Helper;
2626
use GraphQL\Type\Definition\ResolveInfo;
2727
use GraphQL\Validator\DocumentValidator;
28+
use GraphQL\Validator\Rules\DisableIntrospection;
29+
use GraphQL\Validator\Rules\QueryComplexity;
30+
use GraphQL\Validator\Rules\QueryDepth;
2831

2932
/**
3033
* The main GraphQL configuration and request entry point.
@@ -59,7 +62,10 @@
5962
* "endpoint",
6063
* "debug_flag",
6164
* "caching",
62-
* "batching"
65+
* "batching",
66+
* "disable_introspection",
67+
* "query_depth",
68+
* "query_complexity"
6369
* },
6470
* links = {
6571
* "collection" = "/admin/config/graphql/servers",
@@ -123,6 +129,27 @@ class Server extends ConfigEntityBase implements ServerInterface {
123129
*/
124130
public $batching = TRUE;
125131

132+
/**
133+
* Whether to disable query introspection.
134+
*
135+
* @var bool
136+
*/
137+
public $disable_introspection = FALSE;
138+
139+
/**
140+
* The query complexity.
141+
*
142+
* @var int|null
143+
*/
144+
public $query_complexity = NULL;
145+
146+
/**
147+
* The query depth.
148+
*
149+
* @var int|null
150+
*/
151+
public $query_depth = NULL;
152+
126153
/**
127154
* The server's endpoint.
128155
*
@@ -137,7 +164,6 @@ class Server extends ConfigEntityBase implements ServerInterface {
137164
*/
138165
public $persisted_queries_settings = [];
139166

140-
141167
/**
142168
* Persisted query plugin instances available on this server.
143169
*
@@ -498,10 +524,90 @@ protected function getValidationRules() {
498524
return [];
499525
}
500526

501-
return array_values(DocumentValidator::defaultRules());
527+
$rules = array_values(DocumentValidator::defaultRules());
528+
if ($this->getDisableIntrospection()) {
529+
$rules[] = new DisableIntrospection();
530+
}
531+
if ($this->getQueryDepth()) {
532+
$rules[] = new QueryDepth($this->getQueryDepth());
533+
}
534+
if ($this->getQueryComplexity()) {
535+
$rules[] = new QueryComplexity($this->getQueryComplexity());
536+
}
537+
538+
return $rules;
502539
};
503540
}
504541

542+
/**
543+
* Gets disable introspection config.
544+
*
545+
* @return bool
546+
* The disable introspection config, FALSE otherwise.
547+
*/
548+
public function getDisableIntrospection(): bool {
549+
return (bool) $this->disable_introspection;
550+
}
551+
552+
/**
553+
* Sets disable introspection config.
554+
*
555+
* @param bool $introspection
556+
* The value for the disable introspection config.
557+
*
558+
* @return $this
559+
*/
560+
public function setDisableIntrospection(bool $introspection) {
561+
$this->disable_introspection = $introspection;
562+
return $this;
563+
}
564+
565+
/**
566+
* Gets query depth config.
567+
*
568+
* @return int|null
569+
* The query depth, NULL otherwise.
570+
*/
571+
public function getQueryDepth(): ?int {
572+
return (int) $this->query_depth;
573+
}
574+
575+
/**
576+
* Sets query depth config.
577+
*
578+
* @param int|null $depth
579+
* The value for the query depth config.
580+
*
581+
* @return $this
582+
*/
583+
public function setQueryDepth(?int $depth) {
584+
$this->query_depth = $depth;
585+
return $this;
586+
}
587+
588+
/**
589+
* Gets query complexity config.
590+
*
591+
* @return int|null
592+
* The query complexity, NULL otherwise.
593+
*/
594+
public function getQueryComplexity(): ?int {
595+
return (int) $this->query_complexity;
596+
}
597+
598+
/**
599+
* Sets query complexity config.
600+
*
601+
* @param int|null $complexity
602+
* The value for the query complexity config.
603+
*
604+
* @return $this
605+
*/
606+
public function setQueryComplexity(?int $complexity) {
607+
$this->query_complexity = $complexity;
608+
return $this;
609+
}
610+
505611
/**
506612
* {@inheritDoc}
507613
*/

Diff for: src/Form/ServerForm.php

+26
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,32 @@ public function form(array $form, FormStateInterface $formState): array {
186186
'#description' => $this->t('Whether caching of queries and partial results is enabled.'),
187187
];
188188

189+
$form['validation'] = [
190+
'#title' => $this->t('Validation rules'),
191+
'#type' => 'fieldset',
192+
];
193+
194+
$form['validation']['disable_introspection'] = [
195+
'#title' => $this->t('Disable introspection'),
196+
'#type' => 'checkbox',
197+
'#default_value' => $server->get('disable_introspection'),
198+
'#description' => $this->t('Security rule: Whether introspection should be disabled.'),
199+
];
200+
201+
$form['validation']['query_depth'] = [
202+
'#title' => $this->t('Max query depth'),
203+
'#type' => 'number',
204+
'#default_value' => $server->get('query_depth'),
205+
'#description' => $this->t('Security rule: The maximum allowed depth of nested queries. Leave empty to set unlimited.'),
206+
];
207+
208+
$form['validation']['query_complexity'] = [
209+
'#title' => $this->t('Max query complexity'),
210+
'#default_value' => $server->get('query_complexity'),
211+
'#type' => 'number',
212+
'#description' => $this->t('Security rule: The maximum allowed complexity of a query. Leave empty to set unlimited.'),
213+
];
214+
189215
$debug_flags = $server->get('debug_flag') ?? 0;
190216
$form['debug_flag'] = [
191217
'#title' => $this->t('Debug settings'),

0 commit comments

Comments
 (0)