From aba9bee0dae442da8364c327bd3d2e560e7de4cc Mon Sep 17 00:00:00 2001 From: Chelsea Hohmann <33431856+chohmann@users.noreply.github.com> Date: Mon, 18 Sep 2023 14:59:14 -0500 Subject: [PATCH] fix: keep encoded value if uri decoding fails. (#2387) --- .../validators/__tests__/body.spec.ts | 19 ++++++++++++++++++- .../http/src/validator/validators/body.ts | 9 ++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/packages/http/src/validator/validators/__tests__/body.spec.ts b/packages/http/src/validator/validators/__tests__/body.spec.ts index 32df86039..570a55dda 100644 --- a/packages/http/src/validator/validators/__tests__/body.spec.ts +++ b/packages/http/src/validator/validators/__tests__/body.spec.ts @@ -1,6 +1,6 @@ import { HttpParamStyles, IMediaTypeContent } from '@stoplight/types'; import { JSONSchema } from '../../..'; -import { validate, findContentByMediaTypeOrFirst } from '../body'; +import { validate, findContentByMediaTypeOrFirst, decodeUriEntities } from '../body'; import { assertRight, assertLeft, assertSome } from '@stoplight/prism-core/src/__tests__/utils'; import { ValidationContext } from '../types'; import * as faker from '@faker-js/faker/locale/en'; @@ -279,3 +279,20 @@ describe('findContentByMediaTypeOrFirst()', () => { }); }); }); + +describe('decodeUriEntities', () => { + it('should decode both key and value', () => { + const target = { 'profile%2DImage': 'outer%20space' }; + const results = decodeUriEntities(target); + expect(results).toEqual({ 'profile-Image': 'outer space' }); + }); + + it('should decode the key but leave the value as encoded if decoding fails', () => { + const target = { + 'profile%2DImage': + '�PNG\r\n\u001a\n\u0000\u0000\u0000\rIHDR\u0000\u0000\u0000\u0001\u0000\u0000\u0000\u0001\u0001\u0003\u0000\u0000\u0000%�V�\u0000\u0000\u0000\u0003PLTE\u0000\u0000\u0000�z=�\u0000\u0000\u0000\u0001tRNS\u0000@��f\u0000\u0000\u0000\nIDAT\b�c`\u0000\u0000\u0000\u0002\u0000\u0001�!�3\u0000\u0000\u0000\u0000IEND�B`�', + }; + const results = decodeUriEntities(target); + expect(results).toEqual({ 'profile-Image': target['profile%2DImage'] }); + }); +}); diff --git a/packages/http/src/validator/validators/body.ts b/packages/http/src/validator/validators/body.ts index 30c54b6dd..cd1f97bd1 100644 --- a/packages/http/src/validator/validators/body.ts +++ b/packages/http/src/validator/validators/body.ts @@ -87,7 +87,14 @@ export function parseMultipartFormDataParams( export function decodeUriEntities(target: Dictionary) { return Object.entries(target).reduce((result, [k, v]) => { - result[decodeURIComponent(k)] = decodeURIComponent(v); + try { + // NOTE: this will decode the value even if it shouldn't (i.e when text/plain mime type). + // the decision to decode or not should be made before calling this function + result[decodeURIComponent(k)] = decodeURIComponent(v); + } catch (e) { + // when the data is binary, for example, uri decoding will fail so leave value as-is + result[decodeURIComponent(k)] = v; + } return result; }, {}); }