Merge pull request #131 from n1ru4l/feature-disable-introspection-validation-rule

Add DisableIntrospection validation rule
This commit is contained in:
Vladimir Razuvaev 2017-06-19 13:35:23 +07:00 committed by GitHub
commit 141afc1cf7
4 changed files with 198 additions and 0 deletions

View File

@ -559,6 +559,24 @@ $queryDepth->setMaxQueryDepth($maxQueryDepth = 10);
GraphQL::execute(/*...*/);
```
#### Disabling Introspection
This is a PHP port of [graphql-disable-introspection](https://github.com/helfer/graphql-disable-introspection).
The rule prohibits queries that contain `__type` or `__schema` fields.
This document validator rule is disabled by default. Here an example to enable it:
```php
use GraphQL\GraphQL;
use GraphQL\Validator\Rules\DisableIntrospection;
/** @var \GraphQL\Validator\Rules\DisableIntrospection $disableIntrospection */
$disableIntrospection = DocumentValidator::getRule('DisableIntrospection');
$disableIntrospection->setEnabled(DisableIntrospection::ENABLED);
GraphQL::execute(/*...*/);
```
### More Examples
Make sure to check [tests](https://github.com/webonyx/graphql-php/tree/master/tests) for more usage examples.

View File

@ -25,6 +25,7 @@ use GraphQL\Utils;
use GraphQL\Utils\TypeInfo;
use GraphQL\Validator\Rules\ArgumentsOfCorrectType;
use GraphQL\Validator\Rules\DefaultValuesOfCorrectType;
use GraphQL\Validator\Rules\DisableIntrospection;
use GraphQL\Validator\Rules\FieldsOnCorrectType;
use GraphQL\Validator\Rules\FragmentsOnCompositeTypes;
use GraphQL\Validator\Rules\KnownArgumentNames;
@ -100,6 +101,7 @@ class DocumentValidator
'UniqueInputFieldNames' => new UniqueInputFieldNames(),
// Query Security
'DisableIntrospection' => new DisableIntrospection(DisableIntrospection::DISABLED), // DEFAULT DISABLED
'QueryDepth' => new QueryDepth(QueryDepth::DISABLED), // default disabled
'QueryComplexity' => new QueryComplexity(QueryComplexity::DISABLED), // default disabled
];

View File

@ -0,0 +1,50 @@
<?php
namespace GraphQL\Validator\Rules;
use GraphQL\Error\Error;
use GraphQL\Language\AST\FieldNode;
use GraphQL\Language\AST\NodeKind;
use GraphQL\Validator\ValidationContext;
class DisableIntrospection extends AbstractQuerySecurity
{
const ENABLED = 1;
private $isEnabled;
public function __construct($enabled)
{
$this->setEnabled($enabled);
}
public function setEnabled($enabled)
{
$this->isEnabled = $enabled;
}
static function introspectionDisabledMessage()
{
return 'GraphQL introspection is not allowed, but the query contained __schema or __type';
}
protected function isEnabled()
{
return $this->isEnabled !== static::DISABLED;
}
public function __invoke(ValidationContext $context)
{
return $this->invokeIfNeeded(
$context,
[
NodeKind::FIELD => function (FieldNode $node) use ($context) {
if ($node->name->value === '__type' || $node->name->value === '__schema') {
$context->reportError(new Error(
static::introspectionDisabledMessage(),
[$node]
));
}
}
]
);
}
}

View File

@ -0,0 +1,128 @@
<?php
namespace GraphQL\Tests\Validator;
use GraphQL\Error\FormattedError;
use GraphQL\Language\SourceLocation;
use GraphQL\Validator\Rules\DisableIntrospection;
class DisableIntrospectionTest extends TestCase
{
// Validate: Disable Introspection
/**
* @it fails if the query contains __schema
*/
public function testQueryContainsSchema()
{
$this->expectFailsRule(new DisableIntrospection(DisableIntrospection::ENABLED), '
query {
__schema {
queryType {
name
}
}
}
',
[$this->error(3, 9)]
);
}
/**
* @it fails if the query contains __type
*/
public function testQueryContainsType()
{
$this->expectFailsRule(new DisableIntrospection(DisableIntrospection::ENABLED), '
query {
__type(
name: "Query"
){
name
}
}
',
[$this->error(3, 9)]
);
}
/**
* @it does not fail on a query that does not contain __type
*/
public function testValidQuery()
{
$this->expectPassesRule(new DisableIntrospection(DisableIntrospection::ENABLED), '
query {
user {
name
email
friends {
name
}
}
}
');
}
/**
* @it does not fail when not enabled
*/
public function testQueryWhenDisabled()
{
$this->expectPassesRule(new DisableIntrospection(DisableIntrospection::DISABLED), '
query {
__type(
name: "Query"
){
name
}
}
');
}
/**
* @it has a public interface for enabeling the rule
*/
public function testPublicEnableInterface()
{
$disableIntrospection = new DisableIntrospection(DisableIntrospection::DISABLED);
$disableIntrospection->setEnabled(DisableIntrospection::ENABLED);
$this->expectFailsRule($disableIntrospection, '
query {
__type(
name: "Query"
){
name
}
}
',
[$this->error(3, 9)]
);
}
/**
* @it has a public interface for disableing the rule
*/
public function testPublicDisableInterface()
{
$disableIntrospection = new DisableIntrospection(DisableIntrospection::ENABLED);
$disableIntrospection->setEnabled(DisableIntrospection::DISABLED);
$this->expectPassesRule($disableIntrospection, '
query {
__type(
name: "Query"
){
name
}
}
');
}
private function error($line, $column)
{
return FormattedError::create(
DisableIntrospection::introspectionDisabledMessage(),
[ new SourceLocation($line, $column) ]
);
}
}