Skip to content

Commit

Permalink
Merge pull request #131 from n1ru4l/feature-disable-introspection-val…
Browse files Browse the repository at this point in the history
…idation-rule

Add DisableIntrospection validation rule
  • Loading branch information
vladar committed Jun 19, 2017
2 parents 393a741 + beaf91d commit 141afc1
Show file tree
Hide file tree
Showing 4 changed files with 198 additions and 0 deletions.
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
2 changes: 2 additions & 0 deletions src/Validator/DocumentValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
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;
Expand Down Expand Up @@ -100,6 +101,7 @@ public static function defaultRules()
'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
];
Expand Down
50 changes: 50 additions & 0 deletions src/Validator/Rules/DisableIntrospection.php
Original file line number Diff line number Diff line change
@@ -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]
));
}
}
]
);
}
}
128 changes: 128 additions & 0 deletions tests/Validator/DisableIntrospectionTest.php
Original file line number Diff line number Diff line change
@@ -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) ]
);
}
}

0 comments on commit 141afc1

Please sign in to comment.