diff --git a/api/src/controllers/files.ts b/api/src/controllers/files.ts index 521a78d075..64fd73bf15 100644 --- a/api/src/controllers/files.ts +++ b/api/src/controllers/files.ts @@ -4,7 +4,7 @@ import express from 'express'; import Joi from 'joi'; import path from 'path'; import env from '../env'; -import { ForbiddenException, InvalidPayloadException } from '../exceptions'; +import { ForbiddenException, InvalidPayloadException, UnsupportedMediaTypeException } from '../exceptions'; import { respond } from '../middleware/respond'; import useCollection from '../middleware/use-collection'; import { validateBatch } from '../middleware/validate-batch'; @@ -18,7 +18,8 @@ const router = express.Router(); router.use(useCollection('directus_files')); const multipartHandler = asyncHandler(async (req, res, next) => { - if (req.is('multipart/form-data') === false) return next(); + if (req.is('multipart/form-data') === false) + throw new UnsupportedMediaTypeException(`Unsupported Content-Type header`); let headers: BusboyHeaders; diff --git a/api/src/controllers/utils.ts b/api/src/controllers/utils.ts index d87e5269e3..d1e8bb5848 100644 --- a/api/src/controllers/utils.ts +++ b/api/src/controllers/utils.ts @@ -2,7 +2,12 @@ import argon2 from 'argon2'; import { Router } from 'express'; import Joi from 'joi'; import { nanoid } from 'nanoid'; -import { ForbiddenException, InvalidPayloadException, InvalidQueryException } from '../exceptions'; +import { + ForbiddenException, + InvalidPayloadException, + InvalidQueryException, + UnsupportedMediaTypeException, +} from '../exceptions'; import collectionExists from '../middleware/collection-exists'; import { respond } from '../middleware/respond'; import { RevisionsService, UtilsService, ImportService } from '../services'; @@ -94,6 +99,9 @@ router.post( '/import/:collection', collectionExists, asyncHandler(async (req, res, next) => { + if (req.is('multipart/form-data') === false) + throw new UnsupportedMediaTypeException(`Unsupported Content-Type header`); + const service = new ImportService({ accountability: req.accountability, schema: req.schema, diff --git a/api/src/exceptions/index.ts b/api/src/exceptions/index.ts index 3e48408054..2e3c445c0b 100644 --- a/api/src/exceptions/index.ts +++ b/api/src/exceptions/index.ts @@ -14,5 +14,6 @@ export * from './range-not-satisfiable'; export * from './route-not-found'; export * from './service-unavailable'; export * from './unprocessable-entity'; +export * from './unsupported-media-type'; export * from './user-suspended'; export * from './unexpected-response'; diff --git a/api/src/exceptions/unsupported-media-type.ts b/api/src/exceptions/unsupported-media-type.ts new file mode 100644 index 0000000000..26031cb96f --- /dev/null +++ b/api/src/exceptions/unsupported-media-type.ts @@ -0,0 +1,7 @@ +import { BaseException } from '@directus/shared/exceptions'; + +export class UnsupportedMediaTypeException extends BaseException { + constructor(message: string, extensions?: Record) { + super(message, 415, 'UNSUPPORTED_MEDIA_TYPE', extensions); + } +} diff --git a/api/src/services/import.ts b/api/src/services/import.ts index 6833265cdb..c0d5a9bfd9 100644 --- a/api/src/services/import.ts +++ b/api/src/services/import.ts @@ -2,7 +2,7 @@ import { Knex } from 'knex'; import getDatabase from '../database'; import { AbstractServiceOptions, SchemaOverview } from '../types'; import { Accountability } from '@directus/shared/types'; -import { ForbiddenException, InvalidPayloadException } from '../exceptions'; +import { ForbiddenException, InvalidPayloadException, UnsupportedMediaTypeException } from '../exceptions'; import StreamArray from 'stream-json/streamers/StreamArray'; import { ItemsService } from './items'; import { queue } from 'async'; @@ -42,7 +42,7 @@ export class ImportService { case 'text/csv': return await this.importCSV(collection, stream); default: - throw new InvalidPayloadException(`Can't import files of type "${mimetype}"`); + throw new UnsupportedMediaTypeException(`Can't import files of type "${mimetype}"`); } } diff --git a/docs/reference/introduction.md b/docs/reference/introduction.md index 8624988c27..803635134c 100644 --- a/docs/reference/introduction.md +++ b/docs/reference/introduction.md @@ -261,19 +261,20 @@ and system data. The only difference in the two endpoints are the root query and Below are the global error codes used within Directus, and what they mean. -| Error Code | HTTP Status | Description | -| ---------------------- | ----------- | ------------------------------------------------------------ | -| `FAILED_VALIDATION` | 400 | Validation for this particular item failed | -| `FORBIDDEN` | 403 | You are not allowed to do the current action | -| `INVALID_CREDENTIALS` | 401 | Username / password or access token is wrong | -| `INVALID_IP` | 401 | Your IP address isn't allow-listed to be used with this user | -| `INVALID_OTP` | 401 | Wrong OTP was provided | -| `INVALID_PAYLOAD` | 400 | Provided payload is invalid | -| `INVALID_QUERY` | 400 | The requested query parameters can not be used | -| `REQUESTS_EXCEEDED` | 429 | Hit the rate limit | -| `ROUTE_NOT_FOUND` | 404 | Endpoint does not exist | -| `SERVICE_UNAVAILABLE` | 503 | Could not use external service | -| `UNPROCESSABLE_ENTITY` | 422 | You tried doing something illegal | +| Error Code | HTTP Status | Description | +| ------------------------ | ----------- | --------------------------------------------------------------- | +| `FAILED_VALIDATION` | 400 | Validation for this particular item failed | +| `FORBIDDEN` | 403 | You are not allowed to do the current action | +| `INVALID_CREDENTIALS` | 401 | Username / password or access token is wrong | +| `INVALID_IP` | 401 | Your IP address isn't allow-listed to be used with this user | +| `INVALID_OTP` | 401 | Wrong OTP was provided | +| `INVALID_PAYLOAD` | 400 | Provided payload is invalid | +| `INVALID_QUERY` | 400 | The requested query parameters can not be used | +| `UNSUPPORTED_MEDIA_TYPE` | 415 | Provided payload format or `Content-Type` header is unsupported | +| `REQUESTS_EXCEEDED` | 429 | Hit the rate limit | +| `ROUTE_NOT_FOUND` | 404 | Endpoint does not exist | +| `SERVICE_UNAVAILABLE` | 503 | Could not use external service | +| `UNPROCESSABLE_ENTITY` | 422 | You tried doing something illegal | ::: warning Security