CVE-2024-39895 (published 2024-07-08T17:15Z)

Directus is a real-time API and App dashboard for managing SQL database content. A denial of service (DoS) attack by field duplication in GraphQL is a type of attack where an attacker exploits the flexibility of GraphQL to overwhelm a server by requesting the same field multiple times in a single query. This can cause the server to perform redundant computations and consume excessive resources, leading to a denial of service for legitimate users. Request to the endpoint /graphql are sent when visualizing graphs generated at a dashboard. By modifying the data sent and duplicating many times the fields a DoS attack is possible. This vulnerability is fixed in 10.12.0.


Weakness information:
cwe_id: NVD-CWE-noinfo
cwe_name: Insufficient Information
description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
extended_description: Insufficient Information

Fix information:
repo_url: https://github.com/directus/directus
commit_details: repo_url: https://github.com/directus/directus
author: Brainslug
author_date: 2024-06-04 15:46:50+02:00
msg: Merge pull request from GHSA-7hmh-pfrp-vcx4

* Add gql parser limit

* Add changeset

* Format docs

---------

Co-authored-by: Pascal Jufer <pascal-jufer@bluewin.ch>
repository: repo_url: https://github.com/directus/directus
repo_name: directus/directus
description: The Modern Data Stack 🐰 — Directus is an instant REST+GraphQL API and intuitive no-code data collaboration app for any SQL database.
repo_language: TypeScript
owner: directus
file_changes: filename: flat-kids-jump.md
change_type: ModificationType.ADD
diff: @@ -0,0 +1,6 @@
+---
+'@directus/env': patch
+'@directus/api': patch
+---
+
+Added a configurable max token limit for GraphQL queries

programming_language: SQL
filename: graphql.ts
change_type: ModificationType.MODIFY
diff: @@ -6,6 +6,7 @@ import { InvalidPayloadError, InvalidQueryError, MethodNotAllowedError } from '@
 import { GraphQLValidationError } from '../services/graphql/errors/validation.js';
 import type { GraphQLParams } from '../types/index.js';
 import asyncHandler from '../utils/async-handler.js';
+import { useEnv } from '@directus/env';
 
 export const parseGraphQL: RequestHandler = asyncHandler(async (req, res, next) => {
 	if (req.method !== 'GET' && req.method !== 'POST') {
@@ -42,7 +43,11 @@ export const parseGraphQL: RequestHandler = asyncHandler(async (req, res, next)
 	}
 
 	try {
-		document = parse(new Source(query));
+		const env = useEnv();
+
+		document = parse(new Source(query), {
+			maxTokens: Number(env['GRAPHQL_QUERY_TOKEN_LIMIT']),
+		});
 	} catch (err: any) {
 		throw new GraphQLValidationError({
 			errors: [err],

programming_language: TypeScript
filename: config-options.md
change_type: ModificationType.MODIFY
diff: @@ -1047,10 +1047,11 @@ Directus collects little and anonymized data about your environment.
 
 Allows you to configure hard technical limits, to prevent abuse and optimize for your particular server environment.
 
-| Variable                | Description                                                                               | Default Value |
-| ----------------------- | ----------------------------------------------------------------------------------------- | ------------- |
-| `RELATIONAL_BATCH_SIZE` | How many rows are read into memory at a time when constructing nested relational datasets | 25000         |
-| `EXPORT_BATCH_SIZE`     | How many rows are read into memory at a time when constructing exports                    | 5000          |
+| Variable                    | Description                                                                                                                     | Default Value |
+| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | ------------- |
+| `RELATIONAL_BATCH_SIZE`     | How many rows are read into memory at a time when constructing nested relational datasets                                       | 25000         |
+| `EXPORT_BATCH_SIZE`         | How many rows are read into memory at a time when constructing exports                                                          | 5000          |
+| `GRAPHQL_QUERY_TOKEN_LIMIT` | How many GraphQL query tokens will be parsed. [More details here](https://graphql-js.org/api/interface/parseoptions/#maxTokens) | 5000          |
 
 ## WebSockets
 

programming_language: Markdown
filename: defaults.ts
change_type: ModificationType.MODIFY
diff: @@ -120,6 +120,7 @@ export const DEFAULTS = {
 	FILE_METADATA_ALLOW_LIST: 'ifd0.Make,ifd0.Model,exif.FNumber,exif.ExposureTime,exif.FocalLength,exif.ISOSpeedRatings',
 
 	GRAPHQL_INTROSPECTION: true,
+	GRAPHQL_QUERY_TOKEN_LIMIT: 5000,
 
 	WEBSOCKETS_ENABLED: false,
 	WEBSOCKETS_REST_ENABLED: true,

programming_language: JavaScript
filename: directus-variables.ts
change_type: ModificationType.MODIFY
diff: @@ -15,6 +15,7 @@ export const DIRECTUS_VARIABLES = [
 	'SERVE_APP',
 	'GRAPHQL_INTROSPECTION',
 	'GRAPHQL_SCHEMA_CACHE_CAPACITY',
+	'GRAPHQL_QUERY_TOKEN_LIMIT',
 	'MAX_BATCH_MUTATION',
 	'LOGGER_.+',
 	'QUERY_LIMIT_MAX',

programming_language: PHP