mirror of
https://github.com/retailcrm/graphql-php.git
synced 2024-11-25 22:36:02 +03:00
Merge pull request #131 from n1ru4l/feature-disable-introspection-validation-rule
Add DisableIntrospection validation rule
This commit is contained in:
commit
141afc1cf7
18
README.md
18
README.md
@ -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.
|
||||
|
||||
|
@ -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
|
||||
];
|
||||
|
50
src/Validator/Rules/DisableIntrospection.php
Normal file
50
src/Validator/Rules/DisableIntrospection.php
Normal 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]
|
||||
));
|
||||
}
|
||||
}
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
128
tests/Validator/DisableIntrospectionTest.php
Normal file
128
tests/Validator/DisableIntrospectionTest.php
Normal 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) ]
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user