From 5738db8fce6ebb7dbba03426c40decabbf9828f7 Mon Sep 17 00:00:00 2001 From: Nikhil Shahi Date: Thu, 1 Sep 2022 23:45:02 -0500 Subject: [PATCH] (feature) add additional parameters errors for response body --- backend/package.json | 4 +- backend/src/services/spec/index.ts | 109 +++++++++++++++----------- backend/src/services/spec/utils.ts | 118 ++++++++++++++++++++++++----- backend/yarn.lock | 87 ++++++++++++++++++--- 4 files changed, 245 insertions(+), 73 deletions(-) diff --git a/backend/package.json b/backend/package.json index dd927f82..a2d23a8c 100644 --- a/backend/package.json +++ b/backend/package.json @@ -19,9 +19,11 @@ "author": "", "license": "ISC", "dependencies": { + "@apidevtools/swagger-parser": "^10.1.0", "@aws-sdk/client-ec2": "^3.142.0", "@aws-sdk/client-pricing": "^3.142.0", "@google-cloud/compute": "^3.4.0", + "@leoscope/openapi-response-validator": "^12.0.3", "@types/async-retry": "^1.4.4", "@types/newman": "^5.3.0", "@types/ssh2": "^1.11.5", @@ -39,8 +41,8 @@ "node-schedule": "^2.1.0", "node-ssh": "^13.0.0", "openapi-request-validator": "^12.0.0", - "openapi-response-validator": "^12.0.0", "openapi-schema-validator": "^12.0.0", + "openapi-types": "^12.0.0", "pg": "^8.7.3", "pg-protocol": "^1.5.0", "reflect-metadata": "^0.1.13", diff --git a/backend/src/services/spec/index.ts b/backend/src/services/spec/index.ts index c228c998..a63d2edb 100644 --- a/backend/src/services/spec/index.ts +++ b/backend/src/services/spec/index.ts @@ -1,5 +1,6 @@ import { v4 as uuidv4 } from "uuid" import { Not } from "typeorm" +import SwaggerParser from "@apidevtools/swagger-parser" import yaml from "js-yaml" import OpenAPIRequestValidator, { OpenAPIRequestValidatorError, @@ -7,7 +8,7 @@ import OpenAPIRequestValidator, { import OpenAPIResponseValidator, { OpenAPIResponseValidatorError, OpenAPIResponseValidatorValidationError, -} from "openapi-response-validator" +} from "@leoscope/openapi-response-validator" import { AlertType, RestMethod, SpecExtension } from "@common/enums" import { ApiEndpoint, ApiTrace, DataField, OpenApiSpec, Alert } from "models" import Error400BadRequest from "errors/error-400-bad-request" @@ -25,10 +26,12 @@ import { generateAlertMessageFromReqErrors, generateAlertMessageFromRespErrors, getHostsFromServer, + getOpenAPISpecVersion, getSpecRequestParameters, getSpecResponses, parsePathParameter, SpecValue, + AjvError, validateSpecSchema, } from "./utils" import { AlertService } from "services/alert" @@ -55,8 +58,14 @@ export class SpecService { extension: SpecExtension, specString: string, ): Promise { + const specVersion = getOpenAPISpecVersion(specObject) + if (!specVersion) { + throw new Error422UnprocessableEntity( + "Invalid OpenAPI Spec: No 'swagger' or 'openapi' field defined.", + ) + } const validationErrors = validateSpecSchema(specObject) - if (validationErrors.length > 0) { + if (validationErrors?.length > 0) { throw new Error422UnprocessableEntity("Invalid OpenAPI Spec", { message: "Invalid OpenAPI Spec", errors: validationErrors, @@ -88,7 +97,10 @@ export class SpecService { const endpoint = specEndpoints[i] endpoint.openapiSpecName = null } - await DatabaseService.executeTransactions([[...specEndpoints]], [[openApiSpec]]) + await DatabaseService.executeTransactions( + [[...specEndpoints]], + [[openApiSpec]], + ) } static async uploadNewSpec( @@ -97,8 +109,14 @@ export class SpecService { extension: SpecExtension, specString: string, ): Promise { - const validationErrors = validateSpecSchema(specObject) - if (validationErrors.length > 0) { + const specVersion = getOpenAPISpecVersion(specObject) + if (!specVersion) { + throw new Error422UnprocessableEntity( + "Invalid OpenAPI Spec: No 'swagger' or 'openapi' field defined.", + ) + } + const validationErrors = validateSpecSchema(specObject, specVersion) + if (validationErrors?.length > 0) { throw new Error422UnprocessableEntity("Invalid OpenAPI Spec", { message: "Invalid OpenAPI Spec", errors: validationErrors, @@ -209,35 +227,37 @@ export class SpecService { riskScore: "ASC", }, }) - similarEndpoints.forEach(async endpoint => { - apiEndpoint.totalCalls += endpoint.totalCalls - apiEndpoint.riskScore = endpoint.riskScore - const traces = await apiTraceRepository.findBy({ - apiEndpointUuid: endpoint.uuid, - }) - endpoint.dataFields.forEach(dataField => { - dataField.apiEndpointUuid = apiEndpoint.uuid - }) - traces.forEach(trace => { - trace.apiEndpointUuid = apiEndpoint.uuid - }) - endpoint.alerts.forEach(alert => { - switch (alert.type) { - case AlertType.NEW_ENDPOINT: - case AlertType.OPEN_API_SPEC_DIFF: - endpoints.alertsToRemove.push(alert) - break - case AlertType.PII_DATA_DETECTED: - case AlertType.UNDOCUMENTED_ENDPOINT: - default: - alert.apiEndpointUuid = apiEndpoint.uuid - endpoints.alertsToKeep.push(alert) - } - }) - endpoints.traces.push(...traces) - endpoints.dataFields.push(...endpoint.dataFields) - }) - endpoints.similarEndpoints.push(...similarEndpoints) + if (similarEndpoints) { + for (const endpoint of similarEndpoints) { + apiEndpoint.totalCalls += endpoint.totalCalls + apiEndpoint.riskScore = endpoint.riskScore + const traces = await apiTraceRepository.findBy({ + apiEndpointUuid: endpoint.uuid, + }) + endpoint.dataFields.forEach(dataField => { + dataField.apiEndpointUuid = apiEndpoint.uuid + }) + traces.forEach(trace => { + trace.apiEndpointUuid = apiEndpoint.uuid + }) + endpoint.alerts.forEach(alert => { + switch (alert.type) { + case AlertType.NEW_ENDPOINT: + case AlertType.OPEN_API_SPEC_DIFF: + endpoints.alertsToRemove.push(alert) + break + case AlertType.PII_DATA_DETECTED: + case AlertType.UNDOCUMENTED_ENDPOINT: + default: + alert.apiEndpointUuid = apiEndpoint.uuid + endpoints.alertsToKeep.push(alert) + } + }) + endpoints.traces.push(...traces) + endpoints.dataFields.push(...endpoint.dataFields) + } + endpoints.similarEndpoints.push(...similarEndpoints) + } } } } @@ -269,8 +289,9 @@ export class SpecService { return [] } const specObject: JSONValue = yaml.load(openApiSpec.spec) as JSONValue - const specPath: JSONValue = - specObject["paths"][endpoint.path][endpoint.method.toLowerCase()] + const parsedSpec = await SwaggerParser.dereference(specObject as any) + const specPath = + parsedSpec.paths?.[endpoint.path][endpoint.method.toLowerCase()] // Validate request info const specRequestParameters = getSpecRequestParameters( @@ -291,7 +312,7 @@ export class SpecService { const requestValidator = new OpenAPIRequestValidator({ parameters: specRequestParameters?.value, requestBody: specRequestBody?.value, - schemas: specObject?.["components"]?.["schemas"] ?? {}, + schemas: parsedSpec?.["components"]?.["schemas"] ?? {}, errorTransformer: (error, ajvError) => { if (ajvError.params["additionalProperty"]) { return { ...error, path: ajvError.params["additionalProperty"] } @@ -338,24 +359,21 @@ export class SpecService { ) // Validate response info - const responses = getSpecResponses(specObject, endpoint) + const responses = getSpecResponses(parsedSpec, endpoint) const responseValidator = new OpenAPIResponseValidator({ components: specObject["components"], responses: responses?.value, errorTransformer: (error, ajvError) => { - if (ajvError.params) { - const keys = Object.keys(ajvError.params) - return { ...error, path: ajvError.params[keys[0]] } - } - return error + return ajvError }, }) const traceStatusCode = trace.responseStatus const traceResponseBody = parsedJsonNonNull(trace.responseBody, true) - const responseErrors: OpenAPIResponseValidatorValidationError = + const responseValidationItems: OpenAPIResponseValidatorValidationError = responseValidator.validateResponse(traceStatusCode, traceResponseBody) + const responseErrors = responseValidationItems.errors const respErrorItems = generateAlertMessageFromRespErrors( - responseErrors?.errors as OpenAPIResponseValidatorError[], + responseErrors as AjvError[], responses?.path, ) @@ -369,6 +387,7 @@ export class SpecService { ) } catch (err) { console.error(`Error finding OpenAPI Spec diff: ${err}`) + return [] } } } diff --git a/backend/src/services/spec/utils.ts b/backend/src/services/spec/utils.ts index f0bf84d4..ca9968b2 100644 --- a/backend/src/services/spec/utils.ts +++ b/backend/src/services/spec/utils.ts @@ -1,8 +1,11 @@ +import { ErrorObject } from "ajv/dist/2019" +import { OpenAPI, OpenAPIV2, OpenAPIV3 } from "openapi-types" import OpenAPISchemaValidator from "openapi-schema-validator" import { OpenAPIRequestValidatorError } from "openapi-request-validator" -import { OpenAPIResponseValidatorError } from "openapi-response-validator" import { ApiEndpoint } from "models" import { JSONValue } from "@common/types" +import { getDataType } from "utils" +import { DataType } from "@common/enums" export interface VariableObject { enum?: string[] @@ -37,6 +40,17 @@ enum Location { BODY = "body", } +export type AjvError = ErrorObject, unknown> + +export const getOpenAPISpecVersion = (specObject: any): number | null => { + if (specObject["swagger"]) { + return 2 + } else if (specObject["openapi"]) { + return 3 + } + return null +} + const getPathToRequestLocation = ( parameters: Parameter[], location: Location, @@ -57,9 +71,9 @@ export const parsePathParameter = (parameterValue: string) => { return Number(parameterValue) ?? parameterValue } -export const validateSpecSchema = (schema: any, version?: number) => { +export const validateSpecSchema = (schema: any, version?: number): string[] => { if (!schema) { - return [] + return ["No schema defined in OpenAPI spec"] } const res: string[] = [] const schemaValidator = new OpenAPISchemaValidator({ @@ -75,13 +89,13 @@ export const validateSpecSchema = (schema: any, version?: number) => { let message = "" switch (error.keyword) { case "required": - message = `Required property '${error.params?.["missingProperty"]}' is missing from ${field}` + message = `Required property '${error.params?.["missingProperty"]}' is missing from ${field}.` break case "additionalProperties": - message = `Must not have additional property '${error.params?.["additionalProperty"]}' for ${field}` + message = `Must not have additional property '${error.params?.["additionalProperty"]}' for ${field}.` break default: - message = `${message} of ${field}` + message = `${message} of ${field}.` } res.push(message) } @@ -109,7 +123,7 @@ export const generateAlertMessageFromReqErrors = ( const basePath = error.location === Location.BODY ? pathToRequestBody : pathToParameters if (!error.path) { - errorMessage = `${error.message} for request ${error.location}` + errorMessage = `${error.message} for request ${error.location}.` } else { switch (error.errorCode.split(".")[0]) { case "required": @@ -135,7 +149,7 @@ export const generateAlertMessageFromReqErrors = ( } export const generateAlertMessageFromRespErrors = ( - errors: OpenAPIResponseValidatorError[], + errors: AjvError[], pathToResponseBody: string[], ): Record => { const res = {} @@ -143,24 +157,53 @@ export const generateAlertMessageFromRespErrors = ( return res } errors?.forEach(error => { - let errorMessage = "" - switch (error.errorCode.split(".")[0]) { + let pathArray = error.instancePath.split("/")?.slice(2) + const defaultErrorMessage = + error.message[0].toUpperCase() + error.message.slice(1) + let errorMessage = `${defaultErrorMessage} in response body.` + let path = pathArray.length > 0 ? pathArray.join("/") : "" + switch (error.keyword) { case "required": - errorMessage = `Required property '${error.path}' is missing from response body.` + if (error.params?.missingProperty) { + path = path + ? `${path}/${error.params.missingProperty}` + : error.params.missingProperty + } + errorMessage = `Required property '${path}' is missing from response body.` break case "type": - errorMessage = `Property '${error.path}' ${error.message} in response body.` + errorMessage = `Property '${path}' ${error.message} in response body.` break case "additionalProperties": - errorMessage = `Property '${error.path}' is present in response body without being defined in OpenAPI Spec.` + if (error.params?.additionalProperty) { + path = path + ? `${path}/${error.params.additionalProperty}` + : error.params.additionalProperty + } + errorMessage = + path && + `Property '${path}' is present in response body without matching any schemas/definitions in the OpenAPI Spec.` + break + case "unevaluatedProperties": + if (error.params?.unevaluatedProperty) { + path = path + ? `${path}/${error.params.unevaluatedProperty}` + : error.params.unevaluatedProperty + } + errorMessage = + path && + `Property '${path}' is present in response body without matching any schemas/definitions in the OpenAPI Spec.` break case "format": - errorMessage = `Property '${error.path}' ${error.message} in response body.` + errorMessage = `Property '${path}' ${error.message} in response body.` break default: - errorMessage = `${error.message}: '${error.path}' in response body.` + errorMessage = `${defaultErrorMessage}: '${path}' in response body.` break } + if (!path) { + errorMessage = `${defaultErrorMessage} in response body.` + } res[errorMessage] = pathToResponseBody }) return res @@ -192,8 +235,36 @@ export const getSpecRequestParameters = ( return { path: pathToParameters ?? [], value: parameters ?? [] } } +export const recursiveTransformSpec = (schema: any) => { + const combineKeywords = ["anyOf", "allOf", "oneOf"] + for (const keyword of combineKeywords) { + if (schema[keyword]) { + schema["unevaluatedProperties"] = false + for (let i = 0; i < schema[keyword]?.length; i++) { + if (schema[keyword][i]) { + const properties = schema[keyword][i]["properties"] + if (properties && getDataType(properties) === DataType.OBJECT) { + for (const property in properties) { + recursiveTransformSpec(schema[keyword][i]["properties"][property]) + } + } + } + } + } + } + if (schema["type"] === "object") { + schema["additionalProperties"] = false + const properties = schema["properties"] + if (properties && getDataType(properties) === DataType.OBJECT) { + for (const property in properties) { + recursiveTransformSpec(schema["properties"][property]) + } + } + } +} + export const getSpecResponses = ( - specObject: JSONValue, + specObject: OpenAPI.Document, endpoint: ApiEndpoint, ): SpecValue => { const operation = @@ -210,7 +281,20 @@ export const getSpecResponses = ( responses = specObject["components"]["responses"] pathToResponses = responses ? ["components", "responses"] : [] } - return { path: pathToResponses ?? [], value: responses ?? [] } + if (responses) { + for (const responseStatus in responses) { + const content = responses[responseStatus]["content"] + if (content) { + for (const contentType in content) { + const schema = content[contentType]["schema"] + if (schema) { + recursiveTransformSpec(schema) + } + } + } + } + } + return { path: pathToResponses ?? [], value: responses ?? {} } } export const getHostFromServer = (server: ServerObject): Set => { diff --git a/backend/yarn.lock b/backend/yarn.lock index 74131c15..8804bb56 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -2,6 +2,38 @@ # yarn lockfile v1 +"@apidevtools/json-schema-ref-parser@9.0.6": + version "9.0.6" + resolved "https://registry.yarnpkg.com/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.0.6.tgz#5d9000a3ac1fd25404da886da6b266adcd99cf1c" + integrity sha512-M3YgsLjI0lZxvrpeGVk9Ap032W6TPQkH6pRAZz81Ac3WUNF79VQooAFnp8umjvVzUmD93NkogxEwbSce7qMsUg== + dependencies: + "@jsdevtools/ono" "^7.1.3" + call-me-maybe "^1.0.1" + js-yaml "^3.13.1" + +"@apidevtools/openapi-schemas@^2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz#9fa08017fb59d80538812f03fc7cac5992caaa17" + integrity sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ== + +"@apidevtools/swagger-methods@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz#b789a362e055b0340d04712eafe7027ddc1ac267" + integrity sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg== + +"@apidevtools/swagger-parser@^10.1.0": + version "10.1.0" + resolved "https://registry.yarnpkg.com/@apidevtools/swagger-parser/-/swagger-parser-10.1.0.tgz#a987d71e5be61feb623203be0c96e5985b192ab6" + 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" + "@aws-crypto/ie11-detection@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@aws-crypto/ie11-detection/-/ie11-detection-2.0.0.tgz#bb6c2facf8f03457e949dcf0921477397ffa4c6e" @@ -767,6 +799,19 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" +"@jsdevtools/ono@^7.1.3": + version "7.1.3" + resolved "https://registry.yarnpkg.com/@jsdevtools/ono/-/ono-7.1.3.tgz#9df03bbd7c696a5c58885c34aa06da41c8543796" + integrity sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg== + +"@leoscope/openapi-response-validator@^12.0.3": + version "12.0.3" + resolved "https://registry.yarnpkg.com/@leoscope/openapi-response-validator/-/openapi-response-validator-12.0.3.tgz#1900f2bc81d03c9c08a7a0b3d4457f638df28586" + integrity sha512-sVD6TqTt5OTJNLryMARIRBh3DlPpXezkswuxxIamHt+PBQzBRHJ8G3OcHpLeL9sSodRIZVJmVpvDONHLeaCoFw== + dependencies: + ajv "^8.3.0" + openapi-types "^12.0.0" + "@postman/form-data@~3.1.1": version "3.1.1" resolved "https://registry.yarnpkg.com/@postman/form-data/-/form-data-3.1.1.tgz#d0446d0d3639a291f5e800e89fa1d0d3723f9414" @@ -1073,6 +1118,11 @@ agent-base@6: dependencies: debug "4" +ajv-draft-04@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz#3b64761b268ba0b9e668f0b41ba53fce0ad77fc8" + integrity sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw== + ajv-formats@^2.0.2, ajv-formats@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" @@ -1090,7 +1140,7 @@ ajv@^6.12.3: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^8.0.0, ajv@^8.1.0, ajv@^8.3.0, ajv@^8.4.0: +ajv@^8.0.0, ajv@^8.1.0, ajv@^8.3.0, ajv@^8.6.3: version "8.11.0" resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.11.0.tgz#977e91dd96ca669f54a11e23e378e33b884a565f" integrity sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg== @@ -1140,6 +1190,13 @@ arg@^4.1.0: resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + argparse@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" @@ -1361,6 +1418,11 @@ call-bind@^1.0.0, call-bind@^1.0.2: function-bind "^1.1.1" get-intrinsic "^1.0.2" +call-me-maybe@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b" + integrity sha512-wCyFsDQkKPwwF8BDwOiWNx/9K45L/hvggQiDbve+viMNMQnWhrlYIuBk09offfwCRtCO9P6XwUttufzU11WCVw== + caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" @@ -1782,7 +1844,7 @@ espree@^9.0.0: acorn-jsx "^5.3.2" eslint-visitor-keys "^3.3.0" -esprima@^4.0.1: +esprima@^4.0.0, esprima@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== @@ -2504,6 +2566,14 @@ js-sha512@0.8.0: resolved "https://registry.yarnpkg.com/js-sha512/-/js-sha512-0.8.0.tgz#dd22db8d02756faccf19f218e3ed61ec8249f7d4" integrity sha512-PWsmefG6Jkodqt+ePTvBZCSMFgN7Clckjd0O7su3I0+BW2QWUTJNzjktHsztGLhncP2h8mcF9V9Y2Ha59pAViQ== +js-yaml@^3.13.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + js-yaml@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" @@ -3025,14 +3095,6 @@ openapi-request-validator@^12.0.0: openapi-types "^12.0.0" ts-log "^2.1.4" -openapi-response-validator@^12.0.0: - version "12.0.0" - resolved "https://registry.yarnpkg.com/openapi-response-validator/-/openapi-response-validator-12.0.0.tgz#5bbe4c015b33a636572786de63a61b0782dc7894" - integrity sha512-02zzantcJFYBeBeDJVLLNUushxMYkdM4tD9+V1BM7U/witfEc++IX0C/GRuEa1KJ/TTLvnzsXbyR/o/9DipUKg== - dependencies: - ajv "^8.4.0" - openapi-types "^12.0.0" - openapi-schema-validator@^12.0.0: version "12.0.0" resolved "https://registry.yarnpkg.com/openapi-schema-validator/-/openapi-schema-validator-12.0.0.tgz#c5ebd357970fa5c515f77368598e81f31001b5be" @@ -3653,6 +3715,11 @@ split2@^4.1.0: resolved "https://registry.yarnpkg.com/split2/-/split2-4.1.0.tgz#101907a24370f85bb782f08adaabe4e281ecf809" integrity sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ== +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== + ssh2@^1.5.0: version "1.11.0" resolved "https://registry.yarnpkg.com/ssh2/-/ssh2-1.11.0.tgz#ce60186216971e12f6deb553dcf82322498fe2e4"