From 642a87c0a3f62ede923b090b0c944dec5b5cf712 Mon Sep 17 00:00:00 2001 From: Oliwia Rogala Date: Wed, 8 May 2024 15:25:44 +0200 Subject: [PATCH] fix(security): replace regular expressions in path builders (#3504) Refs #3503 --------- Co-authored-by: Vladimir Gorej --- config/webpack/browser.config.babel.js | 2 +- package-lock.json | 17 ++++++++++ package.json | 1 + src/execute/index.js | 1 + src/execute/oas3/parameter-builders.js | 39 ++++++++++++++-------- src/execute/swagger2/parameter-builders.js | 8 +++-- 6 files changed, 52 insertions(+), 16 deletions(-) diff --git a/config/webpack/browser.config.babel.js b/config/webpack/browser.config.babel.js index 68deb58a6..0a6b7683e 100644 --- a/config/webpack/browser.config.babel.js +++ b/config/webpack/browser.config.babel.js @@ -63,7 +63,7 @@ const browserMin = { devtool: 'source-map', performance: { hints: 'error', - maxEntrypointSize: 440000, + maxEntrypointSize: 460000, maxAssetSize: 50000000, }, output: { diff --git a/package-lock.json b/package-lock.json index e36f364ee..3e95bd36b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "js-yaml": "^4.1.0", "node-abort-controller": "^3.1.1", "node-fetch-commonjs": "^3.3.2", + "openapi-path-templating": "^1.5.1", "qs": "^6.10.2", "traverse": "=0.6.8" }, @@ -4844,6 +4845,11 @@ "node": ">= 8" } }, + "node_modules/apg-lite": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/apg-lite/-/apg-lite-1.0.3.tgz", + "integrity": "sha512-lOoNkL7vN7PGdyQMFPey1aok2oVVqvs3n7UMFBRvQ9FoELSbKhgPc3rd7JptaGwCmo4125gLX9Cqb8ElvLCFaQ==" + }, "node_modules/arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", @@ -12406,6 +12412,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/openapi-path-templating": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/openapi-path-templating/-/openapi-path-templating-1.5.1.tgz", + "integrity": "sha512-kgRHToVP571U1YzUnaZnWaUIygon2itg5g96kwaFIi8bnpsw4oXYOk7k59Ivn+ley1iQnMENe/1HSovpPVZuXA==", + "dependencies": { + "apg-lite": "^1.0.3" + }, + "engines": { + "node": ">=12.20.0" + } + }, "node_modules/optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", diff --git a/package.json b/package.json index 08cdb6a5e..fe2a947b2 100644 --- a/package.json +++ b/package.json @@ -122,6 +122,7 @@ "js-yaml": "^4.1.0", "node-abort-controller": "^3.1.1", "node-fetch-commonjs": "^3.3.2", + "openapi-path-templating": "^1.5.1", "qs": "^6.10.2", "traverse": "=0.6.8" }, diff --git a/src/execute/index.js b/src/execute/index.js index f92024c0f..9ca9882f7 100755 --- a/src/execute/index.js +++ b/src/execute/index.js @@ -265,6 +265,7 @@ export function buildRequest(options) { value, operation, spec, + pathName, }); } }); diff --git a/src/execute/oas3/parameter-builders.js b/src/execute/oas3/parameter-builders.js index fe5193a9e..0e5e7e8db 100644 --- a/src/execute/oas3/parameter-builders.js +++ b/src/execute/oas3/parameter-builders.js @@ -1,28 +1,41 @@ +import { resolve as resolvePathTemplate } from 'openapi-path-templating'; + import stylize, { encodeCharacters } from './style-serializer.js'; import serialize from './content-serializer.js'; -export function path({ req, value, parameter }) { +export function path({ req, value, parameter, pathName }) { const { name, style, explode, content } = parameter; if (value === undefined) return; + let resolvedPathname; + if (content) { const effectiveMediaType = Object.keys(content)[0]; - req.url = req.url - .split(`{${name}}`) - .join(encodeCharacters(serialize(value, effectiveMediaType))); + resolvedPathname = resolvePathTemplate( + pathName, + { [name]: value }, + { encoder: (val) => encodeCharacters(serialize(val, effectiveMediaType)) } + ); } else { - const styledValue = stylize({ - key: parameter.name, - value, - style: style || 'simple', - explode: explode || false, - escape: 'reserved', - }); - - req.url = req.url.replace(new RegExp(`{${name}}`, 'g'), styledValue); + resolvedPathname = resolvePathTemplate( + pathName, + { [name]: value }, + { + encoder: (val) => + stylize({ + key: parameter.name, + value: val, + style: style || 'simple', + explode: explode || false, + escape: 'reserved', + }), + } + ); } + + req.url = req.url.replace(pathName, resolvedPathname); } export function query({ req, value, parameter }) { diff --git a/src/execute/swagger2/parameter-builders.js b/src/execute/swagger2/parameter-builders.js index 1e7aefead..125c4d7fe 100644 --- a/src/execute/swagger2/parameter-builders.js +++ b/src/execute/swagger2/parameter-builders.js @@ -1,3 +1,5 @@ +import { resolve as resolvePathTemplate } from 'openapi-path-templating'; + // These functions will update the request. // They'll be given {req, value, paramter, spec, operation}. @@ -49,9 +51,11 @@ function headerBuilder({ req, parameter, value }) { } // Replace path paramters, with values ( ie: the URL ) -function pathBuilder({ req, value, parameter }) { +function pathBuilder({ req, value, parameter, pathName }) { if (value !== undefined) { - req.url = req.url.replace(new RegExp(`{${parameter.name}}`, 'g'), encodeURIComponent(value)); + const resolvedPathname = resolvePathTemplate(pathName, { [parameter.name]: value }); + + req.url = req.url.replace(pathName, resolvedPathname); } }