From 2866ce6b57cad277758482148fcf536ed32355fa Mon Sep 17 00:00:00 2001 From: Arno Hilke Date: Sun, 14 Feb 2021 21:54:11 +0100 Subject: [PATCH] Fix Mutation of API Spec (#537) * test: Add Failing Test Case for Issue 535. Add failing test case for https://github.com/cdimascio/express-openapi-validator/issues/535. The problem manifests itself when calling `middleware()` with an object multiple times, as the object itself is mutated which might cause errors on subsequent calls. In this specific example, the parameters for GET /ping/{value} are modified in such a way that a second call will cause a validation error. This happens in `preprocessPathLevelParameters`. * fix: Clone API Spec to avoid Mutation of Parameter Clone the API specification passed to the `middleware()` function. This avoids mutating the `apiSpec` parameter passed to that function, which can lead to unexpected behaviour. Fixes https://github.com/cdimascio/express-openapi-validator/issues/535. --- src/index.ts | 3 ++- test/535.spec.ts | 67 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 test/535.spec.ts diff --git a/src/index.ts b/src/index.ts index e839b59a..3a6b3f7d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,4 @@ +import * as cloneDeep from 'lodash.clonedeep'; import * as res from './resolvers'; import { OpenApiValidator, OpenApiValidatorOpts } from './openapi.validator'; import { OpenApiSpecLoader } from './framework/openapi.spec.loader'; @@ -34,7 +35,7 @@ function openapiValidator(options: OpenApiValidatorOpts) { return oav.installMiddleware( new OpenApiSpecLoader({ - apiDoc: options.apiSpec, + apiDoc: cloneDeep(options.apiSpec), validateApiSpec: options.validateApiSpec, $refParser: options.$refParser, }).load(), diff --git a/test/535.spec.ts b/test/535.spec.ts new file mode 100644 index 00000000..c07beee8 --- /dev/null +++ b/test/535.spec.ts @@ -0,0 +1,67 @@ +import * as express from 'express'; +import { Server } from 'http'; +import * as request from 'supertest'; +import * as OpenApiValidator from '../src'; +import { OpenAPIV3 } from '../src/framework/types'; +import { startServer } from './common/app.common'; +import { deepStrictEqual } from 'assert'; + +describe('#535 - calling `middleware()` multiple times', () => { + it('does not mutate the API specification', async () => { + const apiSpec = createApiSpec(); + + const app = await createApp(apiSpec); + await request(app).get('/ping/GNU Sir Terry').expect(200, 'GNU Sir Terry'); + app.server.close(); + + deepStrictEqual(apiSpec, createApiSpec()); + }); +}); + +async function createApp( + apiSpec: OpenAPIV3.Document, +): Promise { + const app = express(); + + app.use( + OpenApiValidator.middleware({ + apiSpec, + validateRequests: true, + }), + ); + app.use( + express.Router().get('/ping/:value', (req, res) => { + res.status(200).send(req.params.value); + }), + ); + + await startServer(app, 3001); + return app; +} + +function createApiSpec(): OpenAPIV3.Document { + return { + openapi: '3.0.3', + info: { + title: 'Ping API', + version: '1.0.0', + }, + paths: { + '/ping/{value}': { + parameters: [ + { + in: 'path', + name: 'value', + required: true, + schema: { type: 'string' }, + }, + ], + get: { + responses: { + '200': { description: 'pong!' }, + }, + }, + }, + }, + }; +}