Skip to content

Commit 141afc1

Browse files
authored
Merge pull request #131 from n1ru4l/feature-disable-introspection-validation-rule
Add DisableIntrospection validation rule
2 parents 393a741 + beaf91d commit 141afc1

File tree

4 files changed

+198
-0
lines changed

4 files changed

+198
-0
lines changed

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,24 @@ $queryDepth->setMaxQueryDepth($maxQueryDepth = 10);
559559
GraphQL::execute(/*...*/);
560560
```
561561

562+
#### Disabling Introspection
563+
564+
This is a PHP port of [graphql-disable-introspection](https://github.com/helfer/graphql-disable-introspection).
565+
The rule prohibits queries that contain `__type` or `__schema` fields.
566+
567+
This document validator rule is disabled by default. Here an example to enable it:
568+
569+
```php
570+
use GraphQL\GraphQL;
571+
use GraphQL\Validator\Rules\DisableIntrospection;
572+
573+
/** @var \GraphQL\Validator\Rules\DisableIntrospection $disableIntrospection */
574+
$disableIntrospection = DocumentValidator::getRule('DisableIntrospection');
575+
$disableIntrospection->setEnabled(DisableIntrospection::ENABLED);
576+
577+
GraphQL::execute(/*...*/);
578+
```
579+
562580
### More Examples
563581
Make sure to check [tests](https://github.com/webonyx/graphql-php/tree/master/tests) for more usage examples.
564582

src/Validator/DocumentValidator.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
use GraphQL\Utils\TypeInfo;
2626
use GraphQL\Validator\Rules\ArgumentsOfCorrectType;
2727
use GraphQL\Validator\Rules\DefaultValuesOfCorrectType;
28+
use GraphQL\Validator\Rules\DisableIntrospection;
2829
use GraphQL\Validator\Rules\FieldsOnCorrectType;
2930
use GraphQL\Validator\Rules\FragmentsOnCompositeTypes;
3031
use GraphQL\Validator\Rules\KnownArgumentNames;
@@ -100,6 +101,7 @@ public static function defaultRules()
100101
'UniqueInputFieldNames' => new UniqueInputFieldNames(),
101102

102103
// Query Security
104+
'DisableIntrospection' => new DisableIntrospection(DisableIntrospection::DISABLED), // DEFAULT DISABLED
103105
'QueryDepth' => new QueryDepth(QueryDepth::DISABLED), // default disabled
104106
'QueryComplexity' => new QueryComplexity(QueryComplexity::DISABLED), // default disabled
105107
];
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php
2+
namespace GraphQL\Validator\Rules;
3+
4+
use GraphQL\Error\Error;
5+
use GraphQL\Language\AST\FieldNode;
6+
use GraphQL\Language\AST\NodeKind;
7+
use GraphQL\Validator\ValidationContext;
8+
9+
class DisableIntrospection extends AbstractQuerySecurity
10+
{
11+
const ENABLED = 1;
12+
private $isEnabled;
13+
14+
public function __construct($enabled)
15+
{
16+
$this->setEnabled($enabled);
17+
}
18+
19+
public function setEnabled($enabled)
20+
{
21+
$this->isEnabled = $enabled;
22+
}
23+
24+
static function introspectionDisabledMessage()
25+
{
26+
return 'GraphQL introspection is not allowed, but the query contained __schema or __type';
27+
}
28+
29+
protected function isEnabled()
30+
{
31+
return $this->isEnabled !== static::DISABLED;
32+
}
33+
34+
public function __invoke(ValidationContext $context)
35+
{
36+
return $this->invokeIfNeeded(
37+
$context,
38+
[
39+
NodeKind::FIELD => function (FieldNode $node) use ($context) {
40+
if ($node->name->value === '__type' || $node->name->value === '__schema') {
41+
$context->reportError(new Error(
42+
static::introspectionDisabledMessage(),
43+
[$node]
44+
));
45+
}
46+
}
47+
]
48+
);
49+
}
50+
}
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
<?php
2+
namespace GraphQL\Tests\Validator;
3+
4+
use GraphQL\Error\FormattedError;
5+
use GraphQL\Language\SourceLocation;
6+
use GraphQL\Validator\Rules\DisableIntrospection;
7+
8+
class DisableIntrospectionTest extends TestCase
9+
{
10+
// Validate: Disable Introspection
11+
12+
/**
13+
* @it fails if the query contains __schema
14+
*/
15+
public function testQueryContainsSchema()
16+
{
17+
$this->expectFailsRule(new DisableIntrospection(DisableIntrospection::ENABLED), '
18+
query {
19+
__schema {
20+
queryType {
21+
name
22+
}
23+
}
24+
}
25+
',
26+
[$this->error(3, 9)]
27+
);
28+
}
29+
30+
/**
31+
* @it fails if the query contains __type
32+
*/
33+
public function testQueryContainsType()
34+
{
35+
$this->expectFailsRule(new DisableIntrospection(DisableIntrospection::ENABLED), '
36+
query {
37+
__type(
38+
name: "Query"
39+
){
40+
name
41+
}
42+
}
43+
',
44+
[$this->error(3, 9)]
45+
);
46+
}
47+
48+
/**
49+
* @it does not fail on a query that does not contain __type
50+
*/
51+
public function testValidQuery()
52+
{
53+
$this->expectPassesRule(new DisableIntrospection(DisableIntrospection::ENABLED), '
54+
query {
55+
user {
56+
name
57+
email
58+
friends {
59+
name
60+
}
61+
}
62+
}
63+
');
64+
}
65+
66+
/**
67+
* @it does not fail when not enabled
68+
*/
69+
public function testQueryWhenDisabled()
70+
{
71+
$this->expectPassesRule(new DisableIntrospection(DisableIntrospection::DISABLED), '
72+
query {
73+
__type(
74+
name: "Query"
75+
){
76+
name
77+
}
78+
}
79+
');
80+
}
81+
82+
/**
83+
* @it has a public interface for enabeling the rule
84+
*/
85+
public function testPublicEnableInterface()
86+
{
87+
$disableIntrospection = new DisableIntrospection(DisableIntrospection::DISABLED);
88+
$disableIntrospection->setEnabled(DisableIntrospection::ENABLED);
89+
$this->expectFailsRule($disableIntrospection, '
90+
query {
91+
__type(
92+
name: "Query"
93+
){
94+
name
95+
}
96+
}
97+
',
98+
[$this->error(3, 9)]
99+
);
100+
}
101+
102+
/**
103+
* @it has a public interface for disableing the rule
104+
*/
105+
public function testPublicDisableInterface()
106+
{
107+
$disableIntrospection = new DisableIntrospection(DisableIntrospection::ENABLED);
108+
$disableIntrospection->setEnabled(DisableIntrospection::DISABLED);
109+
$this->expectPassesRule($disableIntrospection, '
110+
query {
111+
__type(
112+
name: "Query"
113+
){
114+
name
115+
}
116+
}
117+
');
118+
}
119+
120+
121+
private function error($line, $column)
122+
{
123+
return FormattedError::create(
124+
DisableIntrospection::introspectionDisabledMessage(),
125+
[ new SourceLocation($line, $column) ]
126+
);
127+
}
128+
}

0 commit comments

Comments
 (0)