From 9da00496682c086b05f2879797246f5083c8ca79 Mon Sep 17 00:00:00 2001 From: Shubhendu Madhukar Date: Tue, 25 Oct 2022 16:31:39 +0530 Subject: [PATCH] feat: Merge dev to main (#198) * fix: move @types/js-yaml to dev deps * fix: upgrade convert-csv-to-json * deps: bump express to 4.18.1 * deps: bump fs-extra to latest * deps: bump logform * deps: bump moment * deps: bump node-cron * deps: bump pg * fix: pass grpc metadata to helpers. Closes #189 * fix: generic parsing of express requests to string * feat: implemented initial version of schema validation. Closes #195 (#196) * feat: implemented initial version of schema validation #195 * chore: tweaks after review #195 * doc: added a validation section in the docs #195 * doc: added a validation section in the docs #195 * fix/ignore lint errors Co-authored-by: Richard Ruiter --- README.md | 7 + config.yml | 5 + docs/MockValidation.png | Bin 0 -> 22822 bytes docs/index.md | 2 + docs/validation.md | 181 ++++ mkdocs.yml | 1 + mocks/pets/GET.mock | 9 + package-lock.json | 1525 +++++++++++++++++++-------- package.json | 22 +- src/ConfigLoader/LoaderInterface.ts | 87 +- src/handlebar/RequestHelper.ts | 2 + src/index.ts | 21 +- src/parser/GrpcParser.ts | 8 +- src/parser/HttpParser.ts | 107 +- src/protocols/GRPC.ts | 8 +- src/protocols/Thrift.ts | 4 + src/routes/GlobalController.ts | 56 +- src/validation/OpenApiAdapter.ts | 152 +++ src/validation/ValidationAdapter.ts | 29 + src/validation/index.ts | 86 ++ tsconfig.json | 35 +- 21 files changed, 1759 insertions(+), 588 deletions(-) create mode 100644 docs/MockValidation.png create mode 100644 docs/validation.md create mode 100644 mocks/pets/GET.mock create mode 100644 src/validation/OpenApiAdapter.ts create mode 100644 src/validation/ValidationAdapter.ts create mode 100644 src/validation/index.ts diff --git a/README.md b/README.md index b0c237e..4f3c3a1 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,8 @@ Camouflage is a service virtualization tool inspired by [namshi/mockserver](http 📁 Comes with a file explorer UI that allows modification of mock files hosted remotely. 📁 +✅ Validation of requests and responses using your OpenApi schema's. ✅ + # Getting Started 1. Camouflage is an NPM package, therefore to install Camouflage, you'd need to install NodeJS (>v14) first, if you haven't already done so. @@ -111,6 +113,11 @@ cache: ttl_seconds: 300 injection: enable: false +validation: + enable: true + schemas: + - type: OpenApi + url: https://raw.githubusercontent.com/OAI/OpenAPI-Specification/main/examples/v3.0/petstore.json origins: - http://localhost:3000 - http://localhost:3001 diff --git a/config.yml b/config.yml index 004f8de..5ad4520 100644 --- a/config.yml +++ b/config.yml @@ -50,6 +50,11 @@ origins: - http://localhost:3000 - http://localhost:3001 - http://localhost:5000 +validation: + enable: true + schemas: + - type: OpenApi + url: https://raw.githubusercontent.com/OAI/OpenAPI-Specification/main/examples/v3.0/petstore.json # ext_helpers: "./custom_handlebar.json" # ext_data_source: # pg: diff --git a/docs/MockValidation.png b/docs/MockValidation.png new file mode 100644 index 0000000000000000000000000000000000000000..5f3af4c75877096e5c43f308a11680cae0cf643f GIT binary patch literal 22822 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 literal 0 HcmV?d00001 diff --git a/docs/index.md b/docs/index.md index 9bbca8e..2abd796 100644 --- a/docs/index.md +++ b/docs/index.md @@ -48,3 +48,5 @@ Camouflage is a service virtualization tool inspired by [namshi/mockserver](http 🎊 Deployable on standalone VMs, Dockers and Kubernetes. 🎊 📁 Comes with a file explorer UI that allows modification of mock files hosted remotely. 📁 + +✅ Validation of requests and responses using your OpenApi schema's. ✅ diff --git a/docs/validation.md b/docs/validation.md new file mode 100644 index 0000000..56238dc --- /dev/null +++ b/docs/validation.md @@ -0,0 +1,181 @@ +When testing software using a mock server it's important that your mocks are correct/valid to avoid false assumptions. + +Contract testing is a grest methodology for ensuring that two separate systems are compatible and can communicate with one other.​ + +What sets this form of testing apart from other approaches that aim to achieve the same thing, is that each system can be tested independently from the other and that the contract is generated by the code itself, meaning the contract is always kept up to date with reality.​ + +![Mock validation using Contract Testing](MockValidation.png) + +## OpenApi schema validation + +Camouflage server support OpenApi schema's for request and response validation. When enabled the configured schema's are loaded in memory and each request and response simply need to adhere to the rules in schema. + +### Configuration Options + +By default, validation is disabled. To specify any of these optional configurations, modify config.yml in following way. + +```yaml +validation: + enable: true + schemas: + - type: OpenApi + url: https://raw.githubusercontent.com/OAI/OpenAPI-Specification/main/examples/v3.0/petstore.json +``` + +### Example + +Now when you have a mock for the supported endpoint `/pets` requesting it would result in a proper response. + +``` +HTTP/1.1 200 OK +Content-Type: application/json + +[ + { "id": 1, "name": "Rabbit" }, + { "id": 2, "name": "Dog" }, + { "id": 3, "name": "Cat" }, + { "id": 4, "name": "Bird" } +] +``` + +#### Request validation + +Given this schema for `/pets` we see that the only support parameter is the integer `limit` + +```json +{ + "openapi": "3.0.0", + "paths": { + "/pets": { + "get": { + "summary": "List all pets", + "operationId": "listPets", + "parameters": [ + { + "name": "limit", + "in": "query", + "description": "How many items to return at one time (max 100)", + "required": false, + "schema": { + "type": "integer", + "format": "int32" + } + } + ] + ... + } + } +} +``` + +Called with an unsupported paraemeter like `/pets?unsupported=1` will result in the following 400 error. + +```json +[ + { + "path": "page", + "errorCode": "type.openapi.requestValidation", + "message": "unknown query parameter 'unsupported'", + "location": "query" + } +] +``` + +Called with a wrong type like `/pets?limit=abc` will also result in the following 400 error. + +```json +[ + { + "path": "limit", + "errorCode": "type.openapi.requestValidation", + "message": "must be integer", + "location": "query" + } +] +``` + +#### Response validation + +Given this schema for `/pets` we see that a pet has two required properties `id` and `name`. + +```json +{ + "openapi": "3.0.0", + "paths": { + "/pets": { + "get": { + "summary": "List all pets", + "operationId": "listPets", + ... + "responses": { + "200": { + "description": "A paged array of pets", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pets" + } + } + } + }, + ... + } + } +"components": { + "schemas": { + "Pet": { + "type": "object", + "required": [ + "id", + "name" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "Pets": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Pet" + } + }, + ... + } + } +} +``` + +In case previously your backend api did only had a required property `id` your assumptions in the tests are false. + +``` +HTTP/1.1 200 OK +Content-Type: application/json + +[ + { "id": 1, "name": "Rabbit" }, + { "id": 2, "name": "Dog" }, + { "id": 3, "name": "Cat" }, + { "id": 4 } +] +``` + +Responses with the following mock will be blocked with a 409 response helping you to avoid mistakes. + +```json +[ + { + "path": "3", + "errorCode": "required.openapi.responseValidation", + "message": "must have required property 'name'" + } +] +``` diff --git a/mkdocs.yml b/mkdocs.yml index 8535d76..595b76d 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -44,6 +44,7 @@ nav: - tests.md - back-up-and-restore.md - openAPI-conversion.md + - validation.md - configuring-cache.md - using-with-docker.md - using-with-kubernetes.md diff --git a/mocks/pets/GET.mock b/mocks/pets/GET.mock new file mode 100644 index 0000000..2231dc8 --- /dev/null +++ b/mocks/pets/GET.mock @@ -0,0 +1,9 @@ +HTTP/1.1 200 OK +Content-Type: application/json + +[ + { "id": 1, "name": "Rabbit" }, + { "id": 2, "name": "Dog" }, + { "id": 3, "name": "Cat" }, + { "id": 4, "name": "Bird" } +] diff --git a/package-lock.json b/package-lock.json index 6a56fb2..fc322b6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,24 +9,28 @@ "version": "0.0.0-development", "license": "MIT", "dependencies": { + "@apidevtools/swagger-parser": "^10.1.0", "@grpc/grpc-js": "^1.4.6", "@grpc/proto-loader": "^0.6.8", - "@types/js-yaml": "^4.0.5", "apicache": "^1.6.3", "compression": "^1.7.4", - "convert-csv-to-json": "^1.3.1", + "convert-csv-to-json": "^1.3.3", "cors": "^2.8.5", - "express": "^4.17.2", + "express": "^4.18.1", + "express-query-parser": "^1.3.3", "express-winston": "^4.2.0", - "fs-extra": "^9.1.0", + "fs-extra": "^10.1.0", "handlebars": "^4.7.7", + "http-errors": "^2.0.0", "http-proxy": "^1.18.1", "js-yaml": "^4.1.0", "jsonpath": "^1.1.1", - "logform": "^2.3.2", - "moment": "^2.29.1", - "node-cron": "^3.0.0", - "pg": "^8.7.1", + "logform": "^2.4.2", + "moment": "^2.29.4", + "node-cron": "^3.0.2", + "openapi-request-validator": "^12.0.2", + "openapi-response-validator": "^12.0.2", + "pg": "^8.8.0", "prom-client": "^13.2.0", "promised-handlebars": "^2.0.1", "q": "^1.5.1", @@ -44,6 +48,8 @@ }, "devDependencies": { "@types/express": "^4.17.11", + "@types/http-errors": "^1.8.2", + "@types/js-yaml": "^4.0.5", "@types/node": "^14.14.37", "@types/q": "^1.5.5", "@types/redis": "^2.8.32", @@ -62,6 +68,111 @@ "typescript": "^4.4.4" } }, + "node_modules/@apidevtools/json-schema-ref-parser": { + "version": "9.0.6", + "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.0.6.tgz", + "integrity": "sha512-M3YgsLjI0lZxvrpeGVk9Ap032W6TPQkH6pRAZz81Ac3WUNF79VQooAFnp8umjvVzUmD93NkogxEwbSce7qMsUg==", + "dependencies": { + "@jsdevtools/ono": "^7.1.3", + "call-me-maybe": "^1.0.1", + "js-yaml": "^3.13.1" + } + }, + "node_modules/@apidevtools/json-schema-ref-parser/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@apidevtools/json-schema-ref-parser/node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@apidevtools/json-schema-ref-parser/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@apidevtools/openapi-schemas": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz", + "integrity": "sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/@apidevtools/swagger-methods": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz", + "integrity": "sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==" + }, + "node_modules/@apidevtools/swagger-parser": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-10.1.0.tgz", + "integrity": "sha512-9Kt7EuS/7WbMAUv2gSziqjvxwDbFSg3Xeyfuj5laUODX8o/k/CpsAKiQ8W7/R88eXFTMbJYg6+7uAmOWNKmwnw==", + "dependencies": { + "@apidevtools/json-schema-ref-parser": "9.0.6", + "@apidevtools/openapi-schemas": "^2.1.0", + "@apidevtools/swagger-methods": "^3.0.2", + "@jsdevtools/ono": "^7.1.3", + "ajv": "^8.6.3", + "ajv-draft-04": "^1.0.0", + "call-me-maybe": "^1.0.1" + }, + "peerDependencies": { + "openapi-types": ">=7" + } + }, + "node_modules/@apidevtools/swagger-parser/node_modules/ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@apidevtools/swagger-parser/node_modules/ajv-draft-04": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", + "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", + "peerDependencies": { + "ajv": "^8.5.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/@apidevtools/swagger-parser/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, "node_modules/@babel/code-frame": { "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", @@ -100,6 +211,14 @@ "node": ">=6.0.0" } }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "engines": { + "node": ">=0.1.90" + } + }, "node_modules/@dabh/diagnostics": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz", @@ -310,6 +429,11 @@ "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==", "dev": true }, + "node_modules/@jsdevtools/ono": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", + "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==" + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz", @@ -646,32 +770,6 @@ } } }, - "node_modules/@semantic-release/github/node_modules/fs-extra": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", - "integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@semantic-release/github/node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, "node_modules/@semantic-release/github/node_modules/mime": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz", @@ -690,15 +788,6 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "node_modules/@semantic-release/github/node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true, - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/@semantic-release/npm": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/@semantic-release/npm/-/npm-7.1.3.tgz", @@ -726,41 +815,6 @@ "semantic-release": ">=16.0.0 <18.0.0" } }, - "node_modules/@semantic-release/npm/node_modules/fs-extra": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", - "integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@semantic-release/npm/node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/@semantic-release/npm/node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true, - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/@semantic-release/release-notes-generator": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/@semantic-release/release-notes-generator/-/release-notes-generator-9.0.2.tgz", @@ -907,10 +961,17 @@ "@types/range-parser": "*" } }, + "node_modules/@types/http-errors": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-1.8.2.tgz", + "integrity": "sha512-EqX+YQxINb+MeXaIqYDASb6U6FCHbWjkj4a1CKDBks3d/QiB2+PqBLyO72vLDgAO1wUI4O+9gweRcQK11bTL/w==", + "dev": true + }, "node_modules/@types/js-yaml": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.5.tgz", - "integrity": "sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==" + "integrity": "sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==", + "dev": true }, "node_modules/@types/json-schema": { "version": "7.0.9", @@ -1301,12 +1362,12 @@ "dev": true }, "node_modules/accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "dependencies": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" + "mime-types": "~2.1.34", + "negotiator": "0.6.3" }, "engines": { "node": ">= 0.6" @@ -1396,6 +1457,42 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, "node_modules/ansi-align": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", @@ -1656,14 +1753,6 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, - "node_modules/at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "engines": { - "node": ">= 4.0.0" - } - }, "node_modules/aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", @@ -1734,37 +1823,71 @@ "dev": true }, "node_modules/body-parser": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.1.tgz", - "integrity": "sha512-8ljfQi5eBk8EJfECMrgqNGWPEY5jWP+1IzkzkGdFFEwFQZZyaZ21UqdaHktgiMlH0xLHqIFtE/u2OYE5dOtViA==", + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", + "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", "dependencies": { - "bytes": "3.1.1", + "bytes": "3.1.2", "content-type": "~1.0.4", "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.8.1", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.9.6", - "raw-body": "2.4.2", - "type-is": "~1.6.18" + "on-finished": "2.4.1", + "qs": "6.10.3", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" }, "engines": { - "node": ">= 0.8" + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, "node_modules/body-parser/node_modules/bytes": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz", - "integrity": "sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser/node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser/node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, "engines": { "node": ">= 0.8" } }, "node_modules/body-parser/node_modules/qs": { - "version": "6.9.6", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", - "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==", + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", + "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "dependencies": { + "side-channel": "^1.0.4" + }, "engines": { "node": ">=0.6" }, @@ -1961,6 +2084,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/call-me-maybe": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", + "integrity": "sha512-wCyFsDQkKPwwF8BDwOiWNx/9K45L/hvggQiDbve+viMNMQnWhrlYIuBk09offfwCRtCO9P6XwUttufzU11WCVw==" + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -2311,14 +2439,6 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, - "node_modules/colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "engines": { - "node": ">=0.1.90" - } - }, "node_modules/colorspace": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz", @@ -2536,14 +2656,14 @@ } }, "node_modules/convert-csv-to-json": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/convert-csv-to-json/-/convert-csv-to-json-1.3.1.tgz", - "integrity": "sha512-cKugcLmLHIdgxk1zSiq9ImPMszskp4zR3uJi8Ty9qx2/QhCozPVTk8A9v3b9ZEYnkbY6fbk7jEKyJzQZ49/p7A==" + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/convert-csv-to-json/-/convert-csv-to-json-1.3.3.tgz", + "integrity": "sha512-lIQrCmEw0GCywtVWeJ/AGv5hhSTPsMFdqOf9GcMtActqO2Gt8z1RZgfw9NBOll479cIPM6BqDOFC8R3W7bSwtA==" }, "node_modules/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", "engines": { "node": ">= 0.6" } @@ -3611,37 +3731,38 @@ } }, "node_modules/express": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.2.tgz", - "integrity": "sha512-oxlxJxcQlYwqPWKVJJtvQiwHgosH/LrLSPA+H4UxpyvSS6jC5aH+5MoHFM+KABgTOt0APue4w66Ha8jCUo9QGg==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz", + "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==", "dependencies": { - "accepts": "~1.3.7", + "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.19.1", + "body-parser": "1.20.0", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.4.1", + "cookie": "0.5.0", "cookie-signature": "1.0.6", "debug": "2.6.9", - "depd": "~1.1.2", + "depd": "2.0.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "~1.1.2", + "finalhandler": "1.2.0", "fresh": "0.5.2", + "http-errors": "2.0.0", "merge-descriptors": "1.0.1", "methods": "~1.1.2", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", "proxy-addr": "~2.0.7", - "qs": "6.9.6", + "qs": "6.10.3", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", - "send": "0.17.2", - "serve-static": "1.14.2", + "send": "0.18.0", + "serve-static": "1.15.0", "setprototypeof": "1.2.0", - "statuses": "~1.5.0", + "statuses": "2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" @@ -3650,6 +3771,14 @@ "node": ">= 0.10.0" } }, + "node_modules/express-query-parser": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/express-query-parser/-/express-query-parser-1.3.3.tgz", + "integrity": "sha512-sL4G5D3e1WVNQv28KmwWVpBPtUaqdhZV62nPNe409terbS4RPvTHCHqEKgYH4uyxeBQWcK0kYJ2KABxOt0GbDA==", + "dependencies": { + "express": "^4.17.1" + } + }, "node_modules/express-winston": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/express-winston/-/express-winston-4.2.0.tgz", @@ -3665,27 +3794,46 @@ "winston": ">=3.x <4" } }, - "node_modules/express/node_modules/finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "node_modules/express/node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/express/node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/express/node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" + "ee-first": "1.1.1" }, "engines": { "node": ">= 0.8" } }, "node_modules/express/node_modules/qs": { - "version": "6.9.6", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", - "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==", + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", + "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "dependencies": { + "side-channel": "^1.0.4" + }, "engines": { "node": ">=0.6" }, @@ -3712,6 +3860,37 @@ } ] }, + "node_modules/express/node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/express/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -3823,6 +4002,42 @@ "node": ">=8" } }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -4059,17 +4274,16 @@ } }, "node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dependencies": { - "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" }, "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/fs-extra/node_modules/jsonfile": { @@ -5108,18 +5322,34 @@ "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=" }, "node_modules/http-errors": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", - "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "dependencies": { - "depd": "~1.1.2", + "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", - "statuses": ">= 1.5.0 < 2", + "statuses": "2.0.1", "toidentifier": "1.0.1" }, "engines": { - "node": ">= 0.6" + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" } }, "node_modules/http-proxy": { @@ -6177,14 +6407,14 @@ } }, "node_modules/logform": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.3.2.tgz", - "integrity": "sha512-V6JiPThZzTsbVRspNO6TmHkR99oqYTs8fivMBYQkjZj6rxW92KxtDCPE6IkAk1DNBnYKNkjm4jYBm6JDUcyhOA==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.4.2.tgz", + "integrity": "sha512-W4c9himeAwXEdZ05dQNerhFz2XG80P9Oj0loPUMV23VC2it0orMHQhJm4hdnnor3rd1HsGf6a2lPwBM1zeXHGw==", "dependencies": { - "colors": "1.4.0", + "@colors/colors": "1.5.0", "fecha": "^4.2.0", "ms": "^2.1.1", - "safe-stable-stringify": "^1.1.0", + "safe-stable-stringify": "^2.3.1", "triple-beam": "^1.3.0" } }, @@ -6467,7 +6697,7 @@ "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "engines": { "node": ">= 0.6" } @@ -6562,19 +6792,19 @@ } }, "node_modules/mime-db": { - "version": "1.47.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.47.0.tgz", - "integrity": "sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw==", + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "2.1.30", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.30.tgz", - "integrity": "sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg==", + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dependencies": { - "mime-db": "1.47.0" + "mime-db": "1.52.0" }, "engines": { "node": ">= 0.6" @@ -6812,20 +7042,9 @@ } }, "node_modules/moment": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", - "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==", - "engines": { - "node": "*" - } - }, - "node_modules/moment-timezone": { - "version": "0.5.33", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.33.tgz", - "integrity": "sha512-PTc2vcT8K9J5/9rDEPe5czSIKgLoGsH8UNpA4qZTVw0Vd/Uz19geE9abbIOQKaAQFcnQ3v5YEXrbSc5BpshH+w==", - "dependencies": { - "moment": ">= 2.9.0" - }, + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", "engines": { "node": "*" } @@ -6854,9 +7073,9 @@ "dev": true }, "node_modules/negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "engines": { "node": ">= 0.6" } @@ -6873,11 +7092,11 @@ "dev": true }, "node_modules/node-cron": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/node-cron/-/node-cron-3.0.0.tgz", - "integrity": "sha512-DDwIvvuCwrNiaU7HEivFDULcaQualDv7KoNlB/UU1wPW0n1tDEmBJKhEIE6DlF2FuoOHcNbLJ8ITL2Iv/3AWmA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/node-cron/-/node-cron-3.0.2.tgz", + "integrity": "sha512-iP8l0yGlNpE0e6q1o185yOApANRe47UPbLf4YxfbiNHt/RU5eBcGB/e0oudruheSf+LQeDMezqC5BVAb5wwRcQ==", "dependencies": { - "moment-timezone": "^0.5.31" + "uuid": "8.3.2" }, "engines": { "node": ">=6.0.0" @@ -9974,6 +10193,81 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/openapi-jsonschema-parameters": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/openapi-jsonschema-parameters/-/openapi-jsonschema-parameters-12.0.2.tgz", + "integrity": "sha512-JzPqPBNSO4SItVJwhmBBKQfNEXkfavgz7lToOHNwTl6MEskWDUMm0jl7s+eLZM3jxz6Vj/o0/VlcecmFJcX88w==", + "dependencies": { + "openapi-types": "^12.0.2" + } + }, + "node_modules/openapi-request-validator": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/openapi-request-validator/-/openapi-request-validator-12.0.2.tgz", + "integrity": "sha512-ak2R28YkNoeTHat5i4EP8xQ6X4z1FJO6jfL8oFXElcMVyHAwQVWooqYYj7MDutMO5cT0fyROapcAFBlmI8U7NQ==", + "dependencies": { + "ajv": "^8.3.0", + "ajv-formats": "^2.1.0", + "content-type": "^1.0.4", + "openapi-jsonschema-parameters": "^12.0.2", + "openapi-types": "^12.0.2", + "ts-log": "^2.1.4" + } + }, + "node_modules/openapi-request-validator/node_modules/ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/openapi-request-validator/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/openapi-response-validator": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/openapi-response-validator/-/openapi-response-validator-12.0.2.tgz", + "integrity": "sha512-lzPo6c/rZGC30u6GOH2e9JP7mknCMd411mkgZAdjQGuY30l8hw8zFAXU4OrBSR6qGL+j0nlI7qfu5rTbwLOVqA==", + "dependencies": { + "ajv": "^8.4.0", + "openapi-types": "^12.0.2" + } + }, + "node_modules/openapi-response-validator/node_modules/ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/openapi-response-validator/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/openapi-types": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.0.2.tgz", + "integrity": "sha512-GuTo7FyZjOIWVhIhQSWJVaws6A82sWIGyQogxxYBYKZ0NBdyP2CYSIgOwFfSB+UVoPExk/YzFpyYitHS8KVZtA==" + }, "node_modules/optionator": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", @@ -10309,14 +10603,14 @@ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, "node_modules/pg": { - "version": "8.7.1", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.7.1.tgz", - "integrity": "sha512-7bdYcv7V6U3KAtWjpQJJBww0UEsWuh4yQ/EjNf2HeO/NnvKjpvhEIe/A/TleP6wtmSKnUnghs5A9jUoK6iDdkA==", + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.8.0.tgz", + "integrity": "sha512-UXYN0ziKj+AeNNP7VDMwrehpACThH7LUl/p8TDFpEUuSejCUIwGSfxpHsPvtM6/WXFy6SU4E5RG4IJV/TZAGjw==", "dependencies": { "buffer-writer": "2.0.0", "packet-reader": "1.0.0", "pg-connection-string": "^2.5.0", - "pg-pool": "^3.4.1", + "pg-pool": "^3.5.2", "pg-protocol": "^1.5.0", "pg-types": "^2.1.0", "pgpass": "1.x" @@ -10325,7 +10619,7 @@ "node": ">= 8.0.0" }, "peerDependencies": { - "pg-native": ">=2.0.0" + "pg-native": ">=3.0.1" }, "peerDependenciesMeta": { "pg-native": { @@ -10347,9 +10641,9 @@ } }, "node_modules/pg-pool": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.4.1.tgz", - "integrity": "sha512-TVHxR/gf3MeJRvchgNHxsYsTCHQ+4wm3VIHSS19z8NC0+gioEhq1okDY1sm/TYbfoP6JLFx01s0ShvZ3puP/iQ==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.5.2.tgz", + "integrity": "sha512-His3Fh17Z4eg7oANLob6ZvH8xIVen3phEZh2QuyrIl4dQSDVEabNducv6ysROKpDNPSD+12tONZVWfSgMvDD9w==", "peerDependencies": { "pg": ">=8.0" } @@ -10760,12 +11054,12 @@ } }, "node_modules/raw-body": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.2.tgz", - "integrity": "sha512-RPMAFUJP19WIet/99ngh6Iv8fzAbqum4Li7AD6DtGaW2RpMB/11xDoalPiJMTbu6I3hkbMVkATvZrqb9EEqeeQ==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", "dependencies": { - "bytes": "3.1.1", - "http-errors": "1.8.1", + "bytes": "3.1.2", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" }, @@ -10774,9 +11068,9 @@ } }, "node_modules/raw-body/node_modules/bytes": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz", - "integrity": "sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "engines": { "node": ">= 0.8" } @@ -11147,7 +11441,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -11273,9 +11566,12 @@ "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" }, "node_modules/safe-stable-stringify": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-1.1.1.tgz", - "integrity": "sha512-ERq4hUjKDbJfE4+XtZLFPCDi8Vb1JqaxAPTxWFLBx8XcAlf9Bda/ZJdVezs/NAfsMQScyIlUMx+Yeu7P7rx5jw==" + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.3.1.tgz", + "integrity": "sha512-kYBSfT+troD9cDA85VDnHZ1rpHC50O0g1e6WlGHVCz/g+JS+9WKLj+XwFYyR8UbrZN8ll9HUpDAAddY58MGisg==", + "engines": { + "node": ">=10" + } }, "node_modules/safer-buffer": { "version": "2.1.2", @@ -11565,6 +11861,21 @@ "node": ">= 0.8.0" } }, + "node_modules/send/node_modules/http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/send/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -11580,76 +11891,83 @@ } }, "node_modules/serve-static": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz", - "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", "dependencies": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.17.2" + "send": "0.18.0" }, "engines": { "node": ">= 0.8.0" } }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, - "node_modules/shebang-command": { + "node_modules/serve-static/node_modules/depd": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "1.8.1", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "~2.3.0", - "range-parser": "~1.2.1", - "statuses": "~1.5.0" - }, + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "engines": { - "node": ">= 0.8.0" + "node": ">= 0.8" } }, - "node_modules/send/node_modules/ms": { + "node_modules/serve-static/node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/serve-static/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, - "node_modules/serialize-javascript": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", - "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", - "dev": true, + "node_modules/serve-static/node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "dependencies": { - "randombytes": "^2.1.0" + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" } }, - "node_modules/serve-static": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz", - "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==", + "node_modules/serve-static/node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.17.2" + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" }, "engines": { "node": ">= 0.8.0" } }, + "node_modules/serve-static/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -11920,8 +12238,7 @@ "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" }, "node_modules/sshpk": { "version": "1.16.1", @@ -12492,6 +12809,11 @@ "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" }, + "node_modules/ts-log": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/ts-log/-/ts-log-2.2.5.tgz", + "integrity": "sha512-PGcnJoTBnVGy6yYNFxWVNkdcAuAMstvutN9MgDJIV6L0oG8fB+ZNNy1T+wJzah8RPGor1mZuPQkVfXNDpy9eHA==" + }, "node_modules/tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", @@ -12675,7 +12997,7 @@ "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "engines": { "node": ">= 0.8" } @@ -13193,9 +13515,91 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } - } - }, - "dependencies": { + } + }, + "dependencies": { + "@apidevtools/json-schema-ref-parser": { + "version": "9.0.6", + "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.0.6.tgz", + "integrity": "sha512-M3YgsLjI0lZxvrpeGVk9Ap032W6TPQkH6pRAZz81Ac3WUNF79VQooAFnp8umjvVzUmD93NkogxEwbSce7qMsUg==", + "requires": { + "@jsdevtools/ono": "^7.1.3", + "call-me-maybe": "^1.0.1", + "js-yaml": "^3.13.1" + }, + "dependencies": { + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + } + } + }, + "@apidevtools/openapi-schemas": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz", + "integrity": "sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==" + }, + "@apidevtools/swagger-methods": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz", + "integrity": "sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==" + }, + "@apidevtools/swagger-parser": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-10.1.0.tgz", + "integrity": "sha512-9Kt7EuS/7WbMAUv2gSziqjvxwDbFSg3Xeyfuj5laUODX8o/k/CpsAKiQ8W7/R88eXFTMbJYg6+7uAmOWNKmwnw==", + "requires": { + "@apidevtools/json-schema-ref-parser": "9.0.6", + "@apidevtools/openapi-schemas": "^2.1.0", + "@apidevtools/swagger-methods": "^3.0.2", + "@jsdevtools/ono": "^7.1.3", + "ajv": "^8.6.3", + "ajv-draft-04": "^1.0.0", + "call-me-maybe": "^1.0.1" + }, + "dependencies": { + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-draft-04": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", + "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", + "requires": {} + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + } + } + }, "@babel/code-frame": { "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", @@ -13228,6 +13632,11 @@ "integrity": "sha512-ArliyUsWDUqEGfWcmzpGUzNfLxTdTp6WU4IuP6QFSp9gGfWS6boxFCkJSJ/L4+RG8z/FnIU3WxCk6hPL9SSWeA==", "dev": true }, + "@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==" + }, "@dabh/diagnostics": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz", @@ -13398,6 +13807,11 @@ "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==", "dev": true }, + "@jsdevtools/ono": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", + "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==" + }, "@nodelib/fs.scandir": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz", @@ -13689,27 +14103,6 @@ "ms": "2.1.2" } }, - "fs-extra": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", - "integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, - "jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" - } - }, "mime": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz", @@ -13721,12 +14114,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true - }, - "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true } } }, @@ -13749,35 +14136,6 @@ "registry-auth-token": "^4.0.0", "semver": "^7.1.2", "tempy": "^1.0.0" - }, - "dependencies": { - "fs-extra": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", - "integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, - "jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" - } - }, - "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true - } } }, "@semantic-release/release-notes-generator": { @@ -13899,10 +14257,17 @@ "@types/range-parser": "*" } }, + "@types/http-errors": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-1.8.2.tgz", + "integrity": "sha512-EqX+YQxINb+MeXaIqYDASb6U6FCHbWjkj4a1CKDBks3d/QiB2+PqBLyO72vLDgAO1wUI4O+9gweRcQK11bTL/w==", + "dev": true + }, "@types/js-yaml": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.5.tgz", - "integrity": "sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==" + "integrity": "sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==", + "dev": true }, "@types/json-schema": { "version": "7.0.9", @@ -14193,12 +14558,12 @@ "dev": true }, "accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "requires": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" + "mime-types": "~2.1.34", + "negotiator": "0.6.3" } }, "acorn": { @@ -14261,6 +14626,32 @@ "uri-js": "^4.2.2" } }, + "ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "requires": { + "ajv": "^8.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + } + } + }, "ansi-align": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", @@ -14461,11 +14852,6 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, - "at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" - }, "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", @@ -14529,31 +14915,54 @@ "dev": true }, "body-parser": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.1.tgz", - "integrity": "sha512-8ljfQi5eBk8EJfECMrgqNGWPEY5jWP+1IzkzkGdFFEwFQZZyaZ21UqdaHktgiMlH0xLHqIFtE/u2OYE5dOtViA==", + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", + "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", "requires": { - "bytes": "3.1.1", + "bytes": "3.1.2", "content-type": "~1.0.4", "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.8.1", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.9.6", - "raw-body": "2.4.2", - "type-is": "~1.6.18" + "on-finished": "2.4.1", + "qs": "6.10.3", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" }, "dependencies": { "bytes": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz", - "integrity": "sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==" + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" + }, + "on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "requires": { + "ee-first": "1.1.1" + } }, "qs": { - "version": "6.9.6", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", - "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==" + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", + "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "requires": { + "side-channel": "^1.0.4" + } } } }, @@ -14705,6 +15114,11 @@ "get-intrinsic": "^1.0.2" } }, + "call-me-maybe": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", + "integrity": "sha512-wCyFsDQkKPwwF8BDwOiWNx/9K45L/hvggQiDbve+viMNMQnWhrlYIuBk09offfwCRtCO9P6XwUttufzU11WCVw==" + }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -14993,11 +15407,6 @@ "simple-swizzle": "^0.2.2" } }, - "colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" - }, "colorspace": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz", @@ -15168,14 +15577,14 @@ } }, "convert-csv-to-json": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/convert-csv-to-json/-/convert-csv-to-json-1.3.1.tgz", - "integrity": "sha512-cKugcLmLHIdgxk1zSiq9ImPMszskp4zR3uJi8Ty9qx2/QhCozPVTk8A9v3b9ZEYnkbY6fbk7jEKyJzQZ49/p7A==" + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/convert-csv-to-json/-/convert-csv-to-json-1.3.3.tgz", + "integrity": "sha512-lIQrCmEw0GCywtVWeJ/AGv5hhSTPsMFdqOf9GcMtActqO2Gt8z1RZgfw9NBOll479cIPM6BqDOFC8R3W7bSwtA==" }, "cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" }, "cookie-signature": { "version": "1.0.6", @@ -16000,68 +16409,114 @@ } }, "express": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.2.tgz", - "integrity": "sha512-oxlxJxcQlYwqPWKVJJtvQiwHgosH/LrLSPA+H4UxpyvSS6jC5aH+5MoHFM+KABgTOt0APue4w66Ha8jCUo9QGg==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz", + "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==", "requires": { - "accepts": "~1.3.7", + "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.19.1", + "body-parser": "1.20.0", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.4.1", + "cookie": "0.5.0", "cookie-signature": "1.0.6", "debug": "2.6.9", - "depd": "~1.1.2", + "depd": "2.0.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "~1.1.2", + "finalhandler": "1.2.0", "fresh": "0.5.2", + "http-errors": "2.0.0", "merge-descriptors": "1.0.1", "methods": "~1.1.2", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", "proxy-addr": "~2.0.7", - "qs": "6.9.6", + "qs": "6.10.3", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", - "send": "0.17.2", - "serve-static": "1.14.2", + "send": "0.18.0", + "serve-static": "1.15.0", "setprototypeof": "1.2.0", - "statuses": "~1.5.0", + "statuses": "2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" }, "dependencies": { - "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" + "ee-first": "1.1.1" } }, "qs": { - "version": "6.9.6", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", - "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==" + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", + "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "requires": { + "side-channel": "^1.0.4" + } }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "requires": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + } + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" } } }, + "express-query-parser": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/express-query-parser/-/express-query-parser-1.3.3.tgz", + "integrity": "sha512-sL4G5D3e1WVNQv28KmwWVpBPtUaqdhZV62nPNe409terbS4RPvTHCHqEKgYH4uyxeBQWcK0kYJ2KABxOt0GbDA==", + "requires": { + "express": "^4.17.1" + } + }, "express-winston": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/express-winston/-/express-winston-4.2.0.tgz", @@ -16166,6 +16621,35 @@ "to-regex-range": "^5.0.1" } }, + "finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "dependencies": { + "on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "requires": { + "ee-first": "1.1.1" + } + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" + } + } + }, "find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -16339,11 +16823,10 @@ } }, "fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "requires": { - "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" @@ -17146,15 +17629,27 @@ "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=" }, "http-errors": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", - "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "requires": { - "depd": "~1.1.2", + "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", - "statuses": ">= 1.5.0 < 2", + "statuses": "2.0.1", "toidentifier": "1.0.1" + }, + "dependencies": { + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" + } } }, "http-proxy": { @@ -18008,14 +18503,14 @@ } }, "logform": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.3.2.tgz", - "integrity": "sha512-V6JiPThZzTsbVRspNO6TmHkR99oqYTs8fivMBYQkjZj6rxW92KxtDCPE6IkAk1DNBnYKNkjm4jYBm6JDUcyhOA==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.4.2.tgz", + "integrity": "sha512-W4c9himeAwXEdZ05dQNerhFz2XG80P9Oj0loPUMV23VC2it0orMHQhJm4hdnnor3rd1HsGf6a2lPwBM1zeXHGw==", "requires": { - "colors": "1.4.0", + "@colors/colors": "1.5.0", "fecha": "^4.2.0", "ms": "^2.1.1", - "safe-stable-stringify": "^1.1.0", + "safe-stable-stringify": "^2.3.1", "triple-beam": "^1.3.0" }, "dependencies": { @@ -18237,7 +18732,7 @@ "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" }, "meow": { "version": "8.1.2", @@ -18304,16 +18799,16 @@ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" }, "mime-db": { - "version": "1.47.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.47.0.tgz", - "integrity": "sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw==" + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" }, "mime-types": { - "version": "2.1.30", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.30.tgz", - "integrity": "sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg==", + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "requires": { - "mime-db": "1.47.0" + "mime-db": "1.52.0" } }, "mimic-fn": { @@ -18487,17 +18982,9 @@ "dev": true }, "moment": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", - "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" - }, - "moment-timezone": { - "version": "0.5.33", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.33.tgz", - "integrity": "sha512-PTc2vcT8K9J5/9rDEPe5czSIKgLoGsH8UNpA4qZTVw0Vd/Uz19geE9abbIOQKaAQFcnQ3v5YEXrbSc5BpshH+w==", - "requires": { - "moment": ">= 2.9.0" - } + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==" }, "ms": { "version": "2.0.0", @@ -18517,9 +19004,9 @@ "dev": true }, "negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" }, "neo-async": { "version": "2.6.2", @@ -18533,11 +19020,11 @@ "dev": true }, "node-cron": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/node-cron/-/node-cron-3.0.0.tgz", - "integrity": "sha512-DDwIvvuCwrNiaU7HEivFDULcaQualDv7KoNlB/UU1wPW0n1tDEmBJKhEIE6DlF2FuoOHcNbLJ8ITL2Iv/3AWmA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/node-cron/-/node-cron-3.0.2.tgz", + "integrity": "sha512-iP8l0yGlNpE0e6q1o185yOApANRe47UPbLf4YxfbiNHt/RU5eBcGB/e0oudruheSf+LQeDMezqC5BVAb5wwRcQ==", "requires": { - "moment-timezone": "^0.5.31" + "uuid": "8.3.2" } }, "node-emoji": { @@ -20771,6 +21258,77 @@ "mimic-fn": "^2.1.0" } }, + "openapi-jsonschema-parameters": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/openapi-jsonschema-parameters/-/openapi-jsonschema-parameters-12.0.2.tgz", + "integrity": "sha512-JzPqPBNSO4SItVJwhmBBKQfNEXkfavgz7lToOHNwTl6MEskWDUMm0jl7s+eLZM3jxz6Vj/o0/VlcecmFJcX88w==", + "requires": { + "openapi-types": "^12.0.2" + } + }, + "openapi-request-validator": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/openapi-request-validator/-/openapi-request-validator-12.0.2.tgz", + "integrity": "sha512-ak2R28YkNoeTHat5i4EP8xQ6X4z1FJO6jfL8oFXElcMVyHAwQVWooqYYj7MDutMO5cT0fyROapcAFBlmI8U7NQ==", + "requires": { + "ajv": "^8.3.0", + "ajv-formats": "^2.1.0", + "content-type": "^1.0.4", + "openapi-jsonschema-parameters": "^12.0.2", + "openapi-types": "^12.0.2", + "ts-log": "^2.1.4" + }, + "dependencies": { + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + } + } + }, + "openapi-response-validator": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/openapi-response-validator/-/openapi-response-validator-12.0.2.tgz", + "integrity": "sha512-lzPo6c/rZGC30u6GOH2e9JP7mknCMd411mkgZAdjQGuY30l8hw8zFAXU4OrBSR6qGL+j0nlI7qfu5rTbwLOVqA==", + "requires": { + "ajv": "^8.4.0", + "openapi-types": "^12.0.2" + }, + "dependencies": { + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + } + } + }, + "openapi-types": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.0.2.tgz", + "integrity": "sha512-GuTo7FyZjOIWVhIhQSWJVaws6A82sWIGyQogxxYBYKZ0NBdyP2CYSIgOwFfSB+UVoPExk/YzFpyYitHS8KVZtA==" + }, "optionator": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", @@ -21015,14 +21573,14 @@ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, "pg": { - "version": "8.7.1", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.7.1.tgz", - "integrity": "sha512-7bdYcv7V6U3KAtWjpQJJBww0UEsWuh4yQ/EjNf2HeO/NnvKjpvhEIe/A/TleP6wtmSKnUnghs5A9jUoK6iDdkA==", + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.8.0.tgz", + "integrity": "sha512-UXYN0ziKj+AeNNP7VDMwrehpACThH7LUl/p8TDFpEUuSejCUIwGSfxpHsPvtM6/WXFy6SU4E5RG4IJV/TZAGjw==", "requires": { "buffer-writer": "2.0.0", "packet-reader": "1.0.0", "pg-connection-string": "^2.5.0", - "pg-pool": "^3.4.1", + "pg-pool": "^3.5.2", "pg-protocol": "^1.5.0", "pg-types": "^2.1.0", "pgpass": "1.x" @@ -21039,9 +21597,9 @@ "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==" }, "pg-pool": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.4.1.tgz", - "integrity": "sha512-TVHxR/gf3MeJRvchgNHxsYsTCHQ+4wm3VIHSS19z8NC0+gioEhq1okDY1sm/TYbfoP6JLFx01s0ShvZ3puP/iQ==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.5.2.tgz", + "integrity": "sha512-His3Fh17Z4eg7oANLob6ZvH8xIVen3phEZh2QuyrIl4dQSDVEabNducv6ysROKpDNPSD+12tONZVWfSgMvDD9w==", "requires": {} }, "pg-protocol": { @@ -21342,20 +21900,20 @@ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" }, "raw-body": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.2.tgz", - "integrity": "sha512-RPMAFUJP19WIet/99ngh6Iv8fzAbqum4Li7AD6DtGaW2RpMB/11xDoalPiJMTbu6I3hkbMVkATvZrqb9EEqeeQ==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", "requires": { - "bytes": "3.1.1", - "http-errors": "1.8.1", + "bytes": "3.1.2", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" }, "dependencies": { "bytes": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz", - "integrity": "sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==" + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" } } }, @@ -21632,8 +22190,7 @@ "require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" }, "requires-port": { "version": "1.0.0", @@ -21720,9 +22277,9 @@ "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" }, "safe-stable-stringify": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-1.1.1.tgz", - "integrity": "sha512-ERq4hUjKDbJfE4+XtZLFPCDi8Vb1JqaxAPTxWFLBx8XcAlf9Bda/ZJdVezs/NAfsMQScyIlUMx+Yeu7P7rx5jw==" + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.3.1.tgz", + "integrity": "sha512-kYBSfT+troD9cDA85VDnHZ1rpHC50O0g1e6WlGHVCz/g+JS+9WKLj+XwFYyR8UbrZN8ll9HUpDAAddY58MGisg==" }, "safer-buffer": { "version": "2.1.2", @@ -21945,6 +22502,18 @@ "statuses": "~1.5.0" }, "dependencies": { + "http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + } + }, "ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -21962,14 +22531,64 @@ } }, "serve-static": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz", - "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", "requires": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.17.2" + "send": "0.18.0" + }, + "dependencies": { + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "requires": { + "ee-first": "1.1.1" + } + }, + "send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "requires": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + } + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" + } } }, "setprototypeof": { @@ -22195,8 +22814,7 @@ "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" }, "sshpk": { "version": "1.16.1", @@ -22647,6 +23265,11 @@ "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" }, + "ts-log": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/ts-log/-/ts-log-2.2.5.tgz", + "integrity": "sha512-PGcnJoTBnVGy6yYNFxWVNkdcAuAMstvutN9MgDJIV6L0oG8fB+ZNNy1T+wJzah8RPGor1mZuPQkVfXNDpy9eHA==" + }, "tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", @@ -22784,7 +23407,7 @@ "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" }, "update-notifier": { "version": "4.1.3", diff --git a/package.json b/package.json index 1a62ffa..70bb5f1 100644 --- a/package.json +++ b/package.json @@ -41,24 +41,28 @@ "license": "MIT", "preferGlobal": true, "dependencies": { + "@apidevtools/swagger-parser": "^10.1.0", "@grpc/grpc-js": "^1.4.6", "@grpc/proto-loader": "^0.6.8", - "@types/js-yaml": "^4.0.5", "apicache": "^1.6.3", "compression": "^1.7.4", - "convert-csv-to-json": "^1.3.1", + "convert-csv-to-json": "^1.3.3", "cors": "^2.8.5", - "express": "^4.17.2", + "express": "^4.18.1", + "express-query-parser": "^1.3.3", "express-winston": "^4.2.0", - "fs-extra": "^9.1.0", + "fs-extra": "^10.1.0", "handlebars": "^4.7.7", + "http-errors": "^2.0.0", "http-proxy": "^1.18.1", "js-yaml": "^4.1.0", "jsonpath": "^1.1.1", - "logform": "^2.3.2", - "moment": "^2.29.1", - "node-cron": "^3.0.0", - "pg": "^8.7.1", + "logform": "^2.4.2", + "moment": "^2.29.4", + "node-cron": "^3.0.2", + "openapi-request-validator": "^12.0.2", + "openapi-response-validator": "^12.0.2", + "pg": "^8.8.0", "prom-client": "^13.2.0", "promised-handlebars": "^2.0.1", "q": "^1.5.1", @@ -73,6 +77,8 @@ }, "devDependencies": { "@types/express": "^4.17.11", + "@types/http-errors": "^1.8.2", + "@types/js-yaml": "^4.0.5", "@types/node": "^14.14.37", "@types/q": "^1.5.5", "@types/redis": "^2.8.32", diff --git a/src/ConfigLoader/LoaderInterface.ts b/src/ConfigLoader/LoaderInterface.ts index 851b596..73e4212 100644 --- a/src/ConfigLoader/LoaderInterface.ts +++ b/src/ConfigLoader/LoaderInterface.ts @@ -1,63 +1,78 @@ import { ThriftConfig } from "../protocols/Thrift"; export interface CamouflageConfig { - loglevel: "debug" | "info" | "warning" | "error"; - cpus: number; - monitoring: MonitoringConfig; - ssl: SSLConfig; - protocols: Protocols; - backup: BackupConfig; - cache: CacheConfig; - injection: InjectionConfig; - ext_helpers?: string; - origins?: string[]; + loglevel: "debug" | "info" | "warning" | "error"; + cpus: number; + monitoring: MonitoringConfig; + ssl: SSLConfig; + protocols: Protocols; + backup: BackupConfig; + cache: CacheConfig; + injection: InjectionConfig; + ext_helpers?: string; + origins?: string[]; + validation?: ValidationConfig; } interface MonitoringConfig { - port: number; + port: number; } interface SSLConfig { - cert: string; - key: string; - root_cert?: string; + cert: string; + key: string; + root_cert?: string; } interface Protocols { - http: ProtocolConfig; - https: ProtocolConfig; - http2: ProtocolConfig; - ws: ProtocolConfig; - grpc: ProtocolConfig; - thrift: ThriftProtocolConfig; + http: ProtocolConfig; + https: ProtocolConfig; + http2: ProtocolConfig; + ws: ProtocolConfig; + grpc: ProtocolConfig; + thrift: ThriftProtocolConfig; } interface ProtocolConfig { - enable: boolean; - port: number - mocks_dir?: string; - host?: string; - protos_dir?: string; - grpc_tls?: boolean; + enable: boolean; + port: number; + mocks_dir?: string; + host?: string; + protos_dir?: string; + grpc_tls?: boolean; } interface ThriftProtocolConfig { - enable: boolean; - mocks_dir: string; - services: ThriftConfig[]; + enable: boolean; + mocks_dir: string; + services: ThriftConfig[]; } interface BackupConfig { - enable: boolean - cron: string + enable: boolean; + cron: string; } interface CacheConfig { - enable: boolean; - ttl_seconds: number; - cache_options?: Record; + enable: boolean; + ttl_seconds: number; + cache_options?: Record; } interface InjectionConfig { - enable: boolean; -} \ No newline at end of file + enable: boolean; +} + +export interface ValidationConfig { + enable: boolean; + schemas: ValidationSchema[]; +} + +export enum ValidationSchemaType { + OpenApi = "OpenApi", +} + +export interface ValidationSchema { + type: ValidationSchemaType; + url: string; +} diff --git a/src/handlebar/RequestHelper.ts b/src/handlebar/RequestHelper.ts index a9bdd6d..bff3b81 100644 --- a/src/handlebar/RequestHelper.ts +++ b/src/handlebar/RequestHelper.ts @@ -79,6 +79,8 @@ export class RequestHelper { return null; } } + case "metadata": + return context.data.root.metadata.get(context.hash.key); default: if ( typeof context.hash.using === "undefined" || diff --git a/src/index.ts b/src/index.ts index 53888c6..f63e569 100644 --- a/src/index.ts +++ b/src/index.ts @@ -18,11 +18,13 @@ import redis from "redis"; import swStats from "swagger-stats"; import cors from "cors"; import compression from "compression"; +import { queryParser } from "express-query-parser"; import ConfigLoader, { getLoaderInstance, setLoaderInstance, } from "./ConfigLoader"; import { CamouflageConfig } from "./ConfigLoader/LoaderInterface"; +import { Validation } from "./validation"; const app = express(); // Configure logging for express requests @@ -44,8 +46,24 @@ app.use( // Configure express to understand json/url encoded request body app.use(express.json()); app.use(express.urlencoded({ extended: true })); +app.use( + express.text({ + type: () => { + return true; + }, + }) +); // Configure express to compress responses - FUTURE IMPROVEMENT - Allow compression options app.use(compression()); +// parse query paramters to do stricter type checking +app.use( + queryParser({ + parseNull: true, + parseUndefined: true, + parseBoolean: true, + parseNumber: true, + }) +); /** * Initializes required variables and starts a 1 master X workers configuration - FUTURE IMPROVEMENT - Pass a single config object * @param {string[]} protoIgnore array of files to be ignored during loading proto files @@ -61,6 +79,7 @@ const start = ( configLoader.validateAndLoad(); setLoaderInstance(configLoader); const config: CamouflageConfig = getLoaderInstance().getConfig(); + const validation = Validation.create(config.validation); // Set log level to the configured level from config.yaml setLogLevel(config.loglevel); logger.debug(JSON.stringify(config, null, 2)); @@ -136,7 +155,7 @@ const start = ( ); backupScheduler.schedule(); } - let middlewareConfigFile: string = path.join( + const middlewareConfigFile: string = path.join( path.dirname(path.resolve(configFilePath)), "middleware.js" ); diff --git a/src/parser/GrpcParser.ts b/src/parser/GrpcParser.ts index b33889b..b18932e 100644 --- a/src/parser/GrpcParser.ts +++ b/src/parser/GrpcParser.ts @@ -35,8 +35,8 @@ export default class GrpcParser { const mockFilePath = path.join(this.config.protocols.grpc.mocks_dir, mockFile + ".mock"); if (fs.existsSync(mockFilePath)) { const template = Handlebars.compile(fs.readFileSync(mockFilePath, "utf-8").toString()); - logger.debug(`Unary Request: ${JSON.stringify(call.request)}`) - const fileContent = await template({ request: call.request }); + logger.debug(`Unary Request: ${JSON.stringify(call.request)}. Metadata: ${JSON.stringify(call.metadata)}`) + const fileContent = await template({ request: call.request, metadata: call.metadata }); logger.debug(`Mock file path: ${mockFilePath}`); logger.debug(`Response: ${fileContent}`); const response = JSON.parse(fileContent); @@ -72,8 +72,8 @@ export default class GrpcParser { if (fs.existsSync(mockFilePath)) { try { const template = Handlebars.compile(fs.readFileSync(mockFilePath, "utf-8").toString()); - logger.debug(`Server Stream Request: ${JSON.stringify(call.request)}`) - const fileContent = await template({ request: call.request }); + logger.debug(`Server Stream Request: ${JSON.stringify(call.request)}. Metadata ${JSON.stringify(call.metadata)}`) + const fileContent = await template({ request: call.request, metadata: call.metadata }); logger.debug(`Mock file path: ${mockFilePath}`); const streamArr = fileContent.split("===="); let delay = 0; diff --git a/src/parser/HttpParser.ts b/src/parser/HttpParser.ts index a97a619..e9b924b 100644 --- a/src/parser/HttpParser.ts +++ b/src/parser/HttpParser.ts @@ -2,18 +2,24 @@ import express from "express"; import path from "path"; import fs from "fs"; import os from "os"; -import { getHandlebars } from '../handlebar' +import { getHandlebars } from "../handlebar"; import logger from "../logger"; import { ProxyResponse } from "../handlebar/ProxyHelper"; import * as httpProxy from "http-proxy"; const proxy = httpProxy.createProxyServer({}); let DELAY = 0; -const Handlebars = getHandlebars() +const Handlebars = getHandlebars(); /** * Create a parser class which defines methods to parse * 1. Request URL to get a matching directory * 2. From matched directory get .mock file content and generate a response */ + +export interface HttpParserResponse { + status: number; + body: string; + headers: { [key: string]: string }; +} export class HttpParser { private req: express.Request; private mockDir: string; @@ -53,9 +59,9 @@ export class HttpParser { * @param {string} mockFile location of of the closest mached mockfile for incoming request * @returns {string} matchedDir for a given incoming request */ - getResponse = (mockFile: string) => { + getResponse = async (mockFile: string): Promise => { // Default response - const response = { + let response: HttpParserResponse = { status: 404, body: '{"error": "Not Found"}', headers: { @@ -64,20 +70,26 @@ export class HttpParser { }; // Check if mock file exists if (fs.existsSync(mockFile)) { - this.prepareResponse(mockFile); + response = await this.prepareResponse(mockFile); } else { logger.error(`No suitable mock file found: ${mockFile}`); if (fs.existsSync(path.join(this.mockDir, "__", "GET.mock"))) { - logger.debug(`Found a custom global override for default response. Sending custom default response.`); - this.prepareResponse(path.join(this.mockDir, "__", "GET.mock")); + logger.debug( + `Found a custom global override for default response. Sending custom default response.` + ); + response = await this.prepareResponse( + path.join(this.mockDir, "__", "GET.mock") + ); } else { //If no mockFile is found, return default response - logger.debug(`No custom global override for default response. Sending default Camouflage response.`); - this.res.statusCode = response.status; - this.res.set(response.headers) - this.res.send(response.body); + logger.debug( + `No custom global override for default response. Sending default Camouflage response.` + ); } } + + response.status = parseFloat((response.status) as string); + return response; }; /** * - Since response file contains headers and body both, a PARSE_BODY flag is required to tell the logic if it's currently parsing headers or body @@ -104,10 +116,12 @@ export class HttpParser { * - Send the generated Response, from a timeout set to send the response after a DELAY value * @param {string} mockFile location of of the closest mached mockfile for incoming request */ - private prepareResponse = async (mockFile: string) => { + private prepareResponse = async ( + mockFile: string + ): Promise => { let PARSE_BODY = false; let responseBody = ""; - const response = { + const response: HttpParserResponse = { status: 404, body: '{"error": "Not Found"}', headers: { @@ -118,7 +132,8 @@ export class HttpParser { let fileResponse = await template({ request: this.req, logger: logger }); if (fileResponse.includes("====")) { const fileContentArray = removeBlanks(fileResponse.split("====")); - fileResponse = fileContentArray[Math.floor(Math.random() * fileContentArray.length)]; + fileResponse = + fileContentArray[Math.floor(Math.random() * fileContentArray.length)]; } const newLine = getNewLine(fileResponse); const fileContent = fileResponse.trim().split(newLine); @@ -143,6 +158,7 @@ export class HttpParser { logger.debug(`Delay Set ${headerValue}`); } else { this.res.setHeader(headerKey, headerValue); + response.headers[headerKey] = headerValue; logger.debug(`Headers Set ${headerKey}: ${headerValue}`); } } @@ -151,10 +167,9 @@ export class HttpParser { responseBody = responseBody + line; } if (index == fileContent.length - 1) { - this.res.statusCode = response.status; if (responseBody.includes("camouflage_file_helper")) { const fileResponse = responseBody.split(";")[1]; - if (!fs.existsSync(fileResponse)) this.res.status(404) + if (!fs.existsSync(fileResponse)) this.res.status(404); setTimeout(() => { this.res.sendFile(fileResponse); }, DELAY); @@ -164,16 +179,21 @@ export class HttpParser { responseBody = responseBody.replace(/}}}/, "}} }"); const template = Handlebars.compile(responseBody); try { - const codeResponse = JSON.parse(responseBody.replace(/"/g, "\"")); + const codeResponse = JSON.parse( + responseBody.replace(/"/g, '"') + ); switch (codeResponse["CamouflageResponseType"]) { case "code": - this.res.statusCode = codeResponse["status"] || this.res.statusCode; + response.status = codeResponse["status"]; if (codeResponse["headers"]) { - this.res.set(codeResponse["headers"]) + response.headers = { + ...response.headers, + ...codeResponse["headers"], + }; } setTimeout(() => { logger.debug(`Generated Response ${codeResponse["body"]}`); - this.res.send(codeResponse["body"]); + response.body = codeResponse["body"]; }); break; case "proxy": @@ -186,19 +206,19 @@ export class HttpParser { const faultType = codeResponse["FaultType"]; switch (faultType) { case "ERR_EMPTY_RESPONSE": - this.res.socket.destroy() + this.res.socket.destroy(); break; case "ERR_INCOMPLETE_CHUNKED_ENCODING": this.res.writeHead(200); - this.res.write('123sdlyndb;aie10-)(&2*2++1dnb/vlaj'); + this.res.write("123sdlyndb;aie10-)(&2*2++1dnb/vlaj"); setTimeout(() => { this.res.socket.destroy(); }, 100); break; case "ERR_CONTENT_LENGTH_MISMATCH": - this.res.setHeader('Content-Length', 100); + this.res.setHeader("Content-Length", 100); this.res.writeHead(200); - this.res.write('123sdlyndb;aie10-)(&2*2++1dnb/vlaj'); + this.res.write("123sdlyndb;aie10-)(&2*2++1dnb/vlaj"); setTimeout(() => { this.res.socket.destroy(); }, 100); @@ -209,24 +229,43 @@ export class HttpParser { break; default: setTimeout(async () => { - logger.debug(`Generated Response ${await template({ request: this.req, logger: logger })}`); - this.res.send(await template({ request: this.req, logger: logger })); + logger.debug( + `Generated Response ${await template({ + request: this.req, + logger: logger, + })}` + ); + response.body = await template({ + request: this.req, + logger: logger, + }); }, DELAY); break; } } catch (error) { logger.warn(error.message); setTimeout(async () => { - logger.debug(`Generated Response ${await template({ request: this.req, logger: logger })}`); - this.res.send(await template({ request: this.req, logger: logger })); + logger.debug( + `Generated Response ${await template({ + request: this.req, + logger: logger, + })}` + ); + response.body = await template({ + request: this.req, + logger: logger, + }); + console.log("yo"); }, DELAY); } + response.body = responseBody; } PARSE_BODY = false; responseBody = ""; DELAY = 0; } }); + return response; }; } @@ -264,20 +303,22 @@ const getNewLine = (source: string) => { const crlf = source.split("\r\n").length; if (cr + lf === 0) { - logger.warn(`No valid new line found in the mock file. Using OS default: ${os.EOL}`); + logger.warn( + `No valid new line found in the mock file. Using OS default: ${os.EOL}` + ); return os.EOL; } if (crlf === cr && crlf === lf) { - logger.debug("Using new line as \\r\\n") + logger.debug("Using new line as \\r\\n"); return "\r\n"; } if (cr > lf) { - logger.debug("Using new line as \\r") + logger.debug("Using new line as \\r"); return "\r"; } else { - logger.debug("Using new line as \\n") + logger.debug("Using new line as \\n"); return "\n"; } -} \ No newline at end of file +}; diff --git a/src/protocols/GRPC.ts b/src/protocols/GRPC.ts index cd17988..501fdd0 100644 --- a/src/protocols/GRPC.ts +++ b/src/protocols/GRPC.ts @@ -120,16 +120,16 @@ const fromDir = function (startPath: string, filter: string, protoIgnore: string return availableFiles; } -const serverCredentials = function(enableTls: boolean, certPath: string, keyPath: string, rootCert?: string): grpc.ServerCredentials { - if(!enableTls) { +const serverCredentials = function (enableTls: boolean, certPath: string, keyPath: string, rootCert?: string): grpc.ServerCredentials { + if (!enableTls) { logger.debug(`Using insecure gRPC server credentials.`); return grpc.ServerCredentials.createInsecure() } - var keyCertPairs = [{ + const keyCertPairs = [{ private_key: fs.readFileSync(keyPath), cert_chain: fs.readFileSync(certPath) }] - if(fs.existsSync(rootCert)) { + if (fs.existsSync(rootCert)) { logger.debug(`Using SSL gRPC server credentials with client authentication.`); return grpc.ServerCredentials.createSsl(fs.readFileSync(rootCert), keyCertPairs, true) } else { diff --git a/src/protocols/Thrift.ts b/src/protocols/Thrift.ts index 5144c55..5c9e7f0 100644 --- a/src/protocols/Thrift.ts +++ b/src/protocols/Thrift.ts @@ -13,7 +13,9 @@ export default class ThriftSetup { this.config = getLoaderInstance().getConfig() } initThrift = () => { + /*eslint-disable */ const handlers: Record = {} + /*eslint-enable */ this.config.protocols.thrift.services.forEach((service: ThriftConfig) => { service.handlers.forEach((handler: string) => { handlers[handler] = async (request: any, response: any) => { @@ -35,9 +37,11 @@ export default class ThriftSetup { } }); try { + /*eslint-disable */ thrift.createServer(require(service.service), handlers).listen(service.port, () => { logger.info(`Worker sharing Thrift server for ${service.service} on ${service.port} ⛳`) }); + /*eslint-enable */ } catch (err) { logger.error(`Failed to start thrift server for ${service.service}. ${err.message}`) } diff --git a/src/routes/GlobalController.ts b/src/routes/GlobalController.ts index 91b8a33..a5bb61a 100644 --- a/src/routes/GlobalController.ts +++ b/src/routes/GlobalController.ts @@ -1,7 +1,9 @@ import { Router, Request, Response } from "express"; +import { Validation } from "../validation"; import { getLoaderInstance } from "../ConfigLoader"; import { CamouflageConfig } from "../ConfigLoader/LoaderInterface"; import { HttpParser } from "../parser/HttpParser"; +import logger from "../logger"; /** * Defines and registers global contoller which will handle any request not handled by admin/management endpoints */ @@ -25,42 +27,32 @@ export default class GlobalController { * @returns {void} */ register = (): Router => { - let router: Router = Router(); + const router: Router = Router(); router.all("*", (req: Request, res: Response) => { this.handler(req, res, req.method.toUpperCase()); }); - // router.get("*", (req: Request, res: Response) => { - // this.handler(req, res, "GET"); - // }); - // router.post("*", (req: Request, res: Response) => { - // this.handler(req, res, "POST"); - // }); - // router.put("*", (req: Request, res: Response) => { - // this.handler(req, res, "PUT"); - // }); - // router.delete("*", (req: Request, res: Response) => { - // this.handler(req, res, "DELETE"); - // }); - // router.head("*", (req: Request, res: Response) => { - // this.handler(req, res, "HEAD"); - // }); - // router.connect("*", (req: Request, res: Response) => { - // this.handler(req, res, "CONNECT"); - // }); - // router.options("*", (req: Request, res: Response) => { - // this.handler(req, res, "OPTIONS"); - // }); - // router.trace("*", (req: Request, res: Response) => { - // this.handler(req, res, "TRACE"); - // }); - // router.patch("*", (req: Request, res: Response) => { - // this.handler(req, res, "PATCH"); - // }); return router; }; - private handler = (req: Request, res: Response, verb: string) => { - const parser = new HttpParser(req, res, this.mocksDir); - const mockFile = parser.getMatchedDir() + `/${verb}.mock`; - parser.getResponse(mockFile); + private handler = async (req: Request, res: Response, verb: string) => { + const validator = Validation.getInstance(); + const request = validator.validateRequest(req); + if (request.valid) { + const parser = new HttpParser(req, res, this.mocksDir); + const mockFile = parser.getMatchedDir() + `/${verb}.mock`; + const response = await parser.getResponse(mockFile); + if (!res.headersSent) { + const responseValidation = validator.validateResponse(req, response); + if (responseValidation.valid) { + const { headers, status, body } = response; + res.set(headers).status(status).send(body); + } else { + res.status(409).json(JSON.parse(responseValidation.error.message)); + } + } else { + logger.warn("Headers already sent."); + } + } else { + res.status(400).json(JSON.parse(request.error.message)); + } }; } diff --git a/src/validation/OpenApiAdapter.ts b/src/validation/OpenApiAdapter.ts new file mode 100644 index 0000000..883f36b --- /dev/null +++ b/src/validation/OpenApiAdapter.ts @@ -0,0 +1,152 @@ +import { Request } from "express"; +import SwaggerParser from "@apidevtools/swagger-parser"; +import OpenAPIRequestValidator from "openapi-request-validator"; +import OpenAPIResponseValidator from "openapi-response-validator"; +import createHttpError from "http-errors"; +import type { OpenAPI } from "openapi-types"; + +import logger from "../logger"; +import { ValidationAdapter, ValidationResult } from "./ValidationAdapter"; +import type { HttpParserResponse } from "../parser/HttpParser"; + +const expressifyPath = (path: string) => { + /* eslint-disable */ + const params = path.match(/(\{(?:\{.*\}|[^\{])*\})/g); + /* eslint-enable */ + if (params?.length > 0) { + for (let x = 0; x < params.length; x++) { + const param = params[x]; + path = path.replace(param, param.replace("{", ":").replace("}", "")); + } + } + return path; +}; + +export default class OpenApiAdapter extends ValidationAdapter { + private document: OpenAPI.Document; + + private findRoute(req: Request) { + const { paths } = this.document; + const route = Object.entries(paths).find(([p]) => { + const expressPath = expressifyPath(p); + const url = req.url.split("?")[0]; + return ( + expressPath === req.route.path || + expressPath === url || + expressPath.slice(0, -1) === url || + expressPath === url.slice(0, -1) + ); + }); + return route && route[1]; + } + + async load(): Promise { + const parser = new SwaggerParser(); + try { + this.document = await parser.dereference(this.config.url); + logger.info( + `OpenApi ${this.document.info.title} (v${this.document.info.version}) validation schema loaded from ${this.config.url}.` + ); + } catch (error) { + logger.error( + `Couldn't load OpenApi validation schema ${this.config.url}, because: ${error.message}` + ); + } + } + + supportsRequest(req: Request): boolean { + return this.findRoute(req) ? true : false; + } + + verifyRequest(req: Request) { + const method = req.method.toLowerCase(); + const currentRoute = this.findRoute(req); + + if (currentRoute && currentRoute[method]) { + // check required params + const requestValidator = new OpenAPIRequestValidator( + currentRoute[method] + ); + const result = requestValidator.validateRequest(req); + + if (result?.errors.length > 0) { + const error = new createHttpError.BadRequest( + JSON.stringify(result.errors, null, 2) + ); + return { + valid: false, + error, + }; + } + + const queryParams: any[] = []; + const parameters = currentRoute[method]?.parameters; + parameters.forEach((parameter: any) => { + if (parameter.in === "query") { + queryParams.push(parameter); + } + }); + + for (let x = 0; x < Object.keys(req.query).length; x++) { + const k = Object.keys(req.query)[x]; + const param = queryParams.find((p) => p.name === k); + if (!param) { + const error = new createHttpError.BadRequest( + JSON.stringify( + [ + { + path: "page", + errorCode: "type.openapi.requestValidation", + message: `unknown query parameter '${k}'`, + location: "query", + }, + ], + null, + 2 + ) + ); + return { + valid: false, + error, + }; + } + } + } + + return { + valid: true, + }; + } + + verifyResponse(req: Request, response: HttpParserResponse): ValidationResult { + const method = req.method.toLowerCase(); + const currentRoute = this.findRoute(req); + + if (currentRoute && currentRoute[method]) { + // check response + const responseValidator = new OpenAPIResponseValidator( + currentRoute[method] + ); + + const result = responseValidator.validateResponse( + response.status, + JSON.parse(response.body) + ); + + if (result?.errors.length > 0) { + const error = createHttpError( + 409, + new Error(JSON.stringify(result.errors, null, 2)) + ); + return { + valid: false, + error, + }; + } + } + + return { + valid: true, + }; + } +} diff --git a/src/validation/ValidationAdapter.ts b/src/validation/ValidationAdapter.ts new file mode 100644 index 0000000..155cf30 --- /dev/null +++ b/src/validation/ValidationAdapter.ts @@ -0,0 +1,29 @@ +import { Request } from "express"; + +import { HttpParserResponse } from "../parser/HttpParser"; +import { ValidationSchema } from "../ConfigLoader/LoaderInterface"; + +export interface ValidationResult { + valid: boolean; + error?: Error; +} + +export abstract class ValidationAdapter { + config: ValidationSchema; + + constructor(config: ValidationSchema) { + this.config = config; + this.load(); + } + + abstract load(): Promise; + + abstract supportsRequest(req: Request): boolean; + + abstract verifyRequest(req: Request): ValidationResult; + + abstract verifyResponse( + req: Request, + response: HttpParserResponse + ): ValidationResult; +} diff --git a/src/validation/index.ts b/src/validation/index.ts new file mode 100644 index 0000000..733ff92 --- /dev/null +++ b/src/validation/index.ts @@ -0,0 +1,86 @@ +import { Request } from "express"; + +import { + ValidationConfig, + ValidationSchemaType, + ValidationSchema, +} from "../ConfigLoader/LoaderInterface"; +import { HttpParserResponse } from "../parser/HttpParser"; +import logger from "../logger"; +import OpenApiAdapter from "./OpenApiAdapter"; +import { ValidationResult, ValidationAdapter } from "./ValidationAdapter"; + +export class Validation { + private static instance: Validation; + private config: ValidationConfig; + private adapters: ValidationAdapter[] = []; + + public static create(config: ValidationConfig): Validation { + this.instance = new Validation(config); + return Validation.getInstance(); + } + + public static getInstance(): Validation { + return Validation.instance; + } + + private constructor(config: ValidationConfig) { + this.config = config; + if (this.config.enable) { + this.loadSchemas(this.config.schemas); + } else { + logger.warn( + "Validation is disabled. Request and responses are not validated." + ); + } + } + + private loadSchemas(schemas: ValidationSchema[]) { + for (let x = 0; x < schemas.length; x++) { + const schema = schemas[x]; + // for now only OpenApi json schemas are supported + // in the future more types can be added like xml-rpc + switch (schema.type) { + case ValidationSchemaType.OpenApi: + this.adapters.push(new OpenApiAdapter(schema)); + break; + default: + logger.warn( + `Unsupported schema type ${schema.type} for ${schema.url}` + ); + break; + } + } + } + + validateRequest(req: Request): ValidationResult { + const adapters = this.adapters.filter((adapter) => + adapter.supportsRequest(req) + ); + for (let x = 0; x < adapters.length; x++) { + const adapter = adapters[x]; + const result = adapter.verifyRequest(req); + if (!result.valid) { + return result; + } + } + return { valid: true }; + } + + validateResponse( + req: Request, + response: HttpParserResponse + ): ValidationResult { + const adapters = this.adapters.filter((adapter) => + adapter.supportsRequest(req) + ); + for (let x = 0; x < adapters.length; x++) { + const adapter = adapters[x]; + const result = adapter.verifyResponse(req, response); + if (!result.valid) { + return result; + } + } + return { valid: true }; + } +} diff --git a/tsconfig.json b/tsconfig.json index d2b22e9..cc7f843 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,20 +1,17 @@ { - "compilerOptions": { - "module": "commonjs", - "esModuleInterop": true, - "target": "es6", - "noImplicitAny": true, - "moduleResolution": "node", - "sourceMap": true, - "outDir": "dist", - "baseUrl": ".", - "paths": { - "*": [ - "node_modules/*" - ] - } - }, - "include": [ - "src/**/*" - ] -} \ No newline at end of file + "compilerOptions": { + "module": "commonjs", + "esModuleInterop": true, + "target": "es6", + "noImplicitAny": true, + "moduleResolution": "node", + "sourceMap": true, + "outDir": "dist", + "skipLibCheck": true, + "baseUrl": ".", + "paths": { + "*": ["node_modules/*"] + } + }, + "include": ["src/**/*"] +}