diff --git a/package.json b/package.json index 2d5be7933c..5ebdb9689c 100644 --- a/package.json +++ b/package.json @@ -111,6 +111,7 @@ "response-time": "^2.3.2", "serve-favicon": "^2.5.0", "stoppable": "^1.1.0", + "type-is": "^1.6.18", "unleash-frontend": "4.6.0-beta.6", "uuid": "^8.3.2" }, @@ -130,6 +131,7 @@ "@types/owasp-password-strength-test": "1.3.0", "@types/stoppable": "1.1.1", "@types/supertest": "2.0.11", + "@types/type-is": "^1.6.3", "@types/uuid": "8.3.4", "@typescript-eslint/eslint-plugin": "5.10.0", "@typescript-eslint/parser": "5.10.0", diff --git a/src/lib/middleware/content_type_checker.test.ts b/src/lib/middleware/content_type_checker.test.ts index 9ba7865501..86782735e5 100644 --- a/src/lib/middleware/content_type_checker.test.ts +++ b/src/lib/middleware/content_type_checker.test.ts @@ -38,6 +38,20 @@ test('Content-type middleware should by default only support application/json', expect(fail).toHaveBeenCalledTimes(0); }); +test('Content-type middleware should by default only support application/json with charset', () => { + const middleware = requireContentType(); + const t = jest.fn(); + const fail = jest.fn(); + middleware( + mockRequest('application/json; charset=UTF-8'), + expectNoCall(fail), + t, + ); + middleware(mockRequest('text/plain'), returns415(t), fail); + expect(t).toHaveBeenCalledTimes(2); + expect(fail).toHaveBeenCalledTimes(0); +}); + test('Content-type middleware should allow adding custom supported types', () => { const middleware = requireContentType('application/yaml'); const t = jest.fn(); diff --git a/src/lib/middleware/content_type_checker.ts b/src/lib/middleware/content_type_checker.ts index 2a45bd9856..c5be859aff 100644 --- a/src/lib/middleware/content_type_checker.ts +++ b/src/lib/middleware/content_type_checker.ts @@ -1,4 +1,5 @@ import { RequestHandler } from 'express'; +import { is } from 'type-is'; const DEFAULT_ACCEPTED_CONTENT_TYPE = 'application/json'; @@ -12,23 +13,15 @@ const DEFAULT_ACCEPTED_CONTENT_TYPE = 'application/json'; export default function requireContentType( ...acceptedContentTypes: string[] ): RequestHandler { + if (acceptedContentTypes.length === 0) { + acceptedContentTypes.push(DEFAULT_ACCEPTED_CONTENT_TYPE); + } return (req, res, next) => { const contentType = req.header('Content-Type'); - if ( - Array.isArray(acceptedContentTypes) && - acceptedContentTypes.length > 0 - ) { - if (acceptedContentTypes.includes(contentType)) { - next(); - } else { - res.status(415).end(); - } - } else if (DEFAULT_ACCEPTED_CONTENT_TYPE === contentType) { + if (is(contentType, acceptedContentTypes)) { next(); } else { res.status(415).end(); } }; } - -module.exports = requireContentType; diff --git a/yarn.lock b/yarn.lock index 438e651958..cdec8cb5b0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1127,6 +1127,13 @@ dependencies: "@types/superagent" "*" +"@types/type-is@^1.6.3": + version "1.6.3" + resolved "https://registry.yarnpkg.com/@types/type-is/-/type-is-1.6.3.tgz#45285b3be846a4afc9d488910a8e4b7bc2e8a169" + integrity sha512-PNs5wHaNcBgCQG5nAeeZ7OvosrEsI9O4W2jAOO9BCCg4ux9ZZvH2+0iSCOIDBiKuQsiNS8CBlmfX9f5YBQ22cA== + dependencies: + "@types/node" "*" + "@types/uuid@8.3.4": version "8.3.4" resolved "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz" @@ -7118,7 +7125,7 @@ type-fest@^1.0.1, type-fest@^1.2.1, type-fest@^1.2.2: resolved "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz" integrity sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA== -type-is@^1.6.4, type-is@~1.6.17, type-is@~1.6.18: +type-is@^1.6.18, type-is@^1.6.4, type-is@~1.6.17, type-is@~1.6.18: version "1.6.18" resolved "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz" integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==