1
0
Fork 0
mirror of synced 2024-06-28 19:20:25 +12:00

Add abuse test

This commit is contained in:
Jake Barnby 2022-07-14 20:11:39 +12:00
parent 13ff5ca45e
commit 221e7b59c4
5 changed files with 118 additions and 12 deletions

View file

@ -34,7 +34,7 @@ App::get('/v1/graphql')
->inject('register')
->inject('dbForProject')
->inject('promiseAdapter')
->inject('gqlSchema')
->inject('schema')
->action(Closure::fromCallable('graphqlRequest'));
App::post('/v1/graphql')
@ -54,7 +54,7 @@ App::post('/v1/graphql')
->inject('request')
->inject('response')
->inject('promiseAdapter')
->inject('gqlSchema')
->inject('schema')
->action(Closure::fromCallable('graphqlRequest'));
/**
@ -66,7 +66,7 @@ function graphqlRequest(
Appwrite\Utopia\Request $request,
Appwrite\Utopia\Response $response,
CoroutinePromiseAdapter $promiseAdapter,
Type\Schema $gqlSchema
Type\Schema $schema
): void {
$contentType = $request->getHeader('content-type');
$maxBatchSize = App::getEnv('_APP_GRAPHQL_MAX_BATCH_SIZE', 50);
@ -108,7 +108,7 @@ function graphqlRequest(
foreach ($query as $indexed) {
$promises[] = GraphQL::promiseToExecute(
$promiseAdapter,
$gqlSchema,
$schema,
$indexed['query'],
variableValues: $indexed['variables'] ?? null,
operationName: $indexed['operationName'] ?? null,
@ -121,12 +121,11 @@ function graphqlRequest(
$wg->add();
$promiseAdapter->all($promises)->then(
function (array $results) use (&$output, &$wg, $debugFlags) {
processResult($results, $output, $debugFlags);
$wg->done();
},
function ($error) use (&$output, $wg) {
$output = ['errors' => [$error]];
$wg->done();
try {
processResult($results, $output, $debugFlags);
} finally {
$wg->done();
}
}
);
$wg->wait();

View file

@ -1006,6 +1006,6 @@ App::setResource('promiseAdapter', function ($register) {
return $register->get('promiseAdapter');
}, ['register']);
App::setResource('gqlSchema', function ($utopia, $dbForProject) {
App::setResource('schema', function ($utopia, $dbForProject) {
return SchemaBuilder::buildSchema($utopia, $dbForProject);
}, ['utopia', 'dbForProject']);

View file

@ -0,0 +1,68 @@
<?php
namespace Tests\E2E\Services\GraphQL;
use Tests\E2E\Client;
use Tests\E2E\Scopes\ProjectCustom;
use Tests\E2E\Scopes\Scope;
use Tests\E2E\Scopes\SideServer;
use Utopia\App;
class GraphQLAbuseTest extends Scope
{
use ProjectCustom;
use SideServer;
use GraphQLBase;
public function testComplexQueryBlocked()
{
$projectId = $this->getProject()['$id'];
$query = $this->getQuery(self::$COMPLEX_QUERY);
$graphQLPayload = [
'query' => $query,
'variables' => [
'userId' => 'user',
'email' => 'user@appwrite.io',
'password' => 'password',
'databaseId' => 'database',
'databaseName' => 'database',
'collectionId' => 'collection',
'collectionName' => 'collection',
'collectionPermission' => 'collection',
'collectionRead' => ['role:member'],
'collectionWrite' => ['role:member'],
'documentId' => 'document',
'documentData' => ['name' => 'foobar'],
'documentRead' => ['role:member'],
'documentWrite' => ['role:member'],
],
];
$response = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $projectId,
], $this->getHeaders()), $graphQLPayload);
\var_dump($response);
$this->assertEquals('Too many queries.', $response['body']['message']);
}
public function testTooManyQueriesBlocked()
{
$projectId = $this->getProject()['$id'];
$maxQueries = App::getEnv('_APP_GRAPHQL_MAX_QUERIES', 50);
$query = [];
for ($i = 0; $i <= $maxQueries + 1; $i++) {
$query[] = ['query' => $this->getQuery(self::$LIST_COUNTRIES)];
}
$response = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $projectId,
], $this->getHeaders()), $query);
$this->assertEquals('Too many queries.', $response['body']['message']);
}
}

View file

@ -174,6 +174,8 @@ trait GraphQLBase
public static string $GET_QRCODE = 'get_qrcode';
public static string $GET_USER_INITIALS = 'get_user_initials';
public static string $COMPLEX_QUERY = 'complex_query';
public function getQuery(string $name): string
{
switch ($name) {
@ -1346,6 +1348,18 @@ trait GraphQLBase
status
}
}';
case self::$COMPLEX_QUERY:
return 'mutation complex($databaseId: String!, $databaseName: String!, $collectionId: String!, $collectionName: String!, $collectionPermission: String!, $collectionRead: [String!]!, $collectionWrite: [String!]!) {
databasesCreate(databaseId: $databaseId, name: $databaseName) {
_id
name
}
databasesCreateCollection(databaseId: $databaseId, collectionId: $collectionId, name: $collectionName, permission: $collectionPermission, read: $collectionRead, write: $collectionWrite) {
_id
name
permission
}
}';
}
throw new \InvalidArgumentException('Invalid query type');

View file

@ -46,7 +46,7 @@ class GraphQLContentTypeTest extends Scope
$this->assertEquals(194, $response['total']);
}
public function testBatchedJSONContentType()
public function testArrayBatchedJSONContentType()
{
$projectId = $this->getProject()['$id'];
$query1 = 'query { localeGetCountries { total countries { code } } }';
@ -68,6 +68,31 @@ class GraphQLContentTypeTest extends Scope
$this->assertEquals(7, $response['body']['data']['localeGetContinents']['total']);
}
public function testQueryBatchedJSONContentType()
{
$projectId = $this->getProject()['$id'];
$query = '
query {
localeGetCountries { total countries { code } }
localeGetContinents { total continents { code } }
}
';
$graphQLPayload = [
['query' => $query],
];
$response = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $projectId,
], $this->getHeaders()), $graphQLPayload);
$this->assertIsArray($response['body']['data']);
$this->assertArrayNotHasKey('errors', $response['body']);
$this->assertArrayHasKey('localeGetCountries', $response['body']['data']);
$this->assertArrayHasKey('localeGetContinents', $response['body']['data']);
$this->assertEquals(194, $response['body']['data']['localeGetCountries']['total']);
$this->assertEquals(7, $response['body']['data']['localeGetContinents']['total']);
}
public function testMultipartFormDataContentType()
{
$projectId = $this->getProject()['$id'];