From c838e1f11557b35f2329f46fc95aa8b7beee5f9c Mon Sep 17 00:00:00 2001 From: jNullj <15849761+jNullj@users.noreply.github.com> Date: Sat, 4 Nov 2023 03:40:10 +0200 Subject: [PATCH] Fix: Page crash on half surrogate pair (#261) This commit fixes an issue where utf-16 encoded surrogated pair could crash the page when inserted as input. The commit adds endsWithHalfSurrogatePair to check for half pair and safeEncodeURIComponent to ignore half surrogated pair before passing it to encodeURIComponent. Fixes #260 --- .../theme/ApiDemoPanel/buildPostmanRequest.ts | 32 +++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/packages/docusaurus-theme-openapi/src/theme/ApiDemoPanel/buildPostmanRequest.ts b/packages/docusaurus-theme-openapi/src/theme/ApiDemoPanel/buildPostmanRequest.ts index 85e5b596..837eeee5 100644 --- a/packages/docusaurus-theme-openapi/src/theme/ApiDemoPanel/buildPostmanRequest.ts +++ b/packages/docusaurus-theme-openapi/src/theme/ApiDemoPanel/buildPostmanRequest.ts @@ -17,6 +17,30 @@ type Param = { value?: string | string[]; } & ParameterObject; +function endsWithHalfSurrogatePair(value: string) { + if (value.length === 0) { + return false; + } + const lastIndex = value.length - 1; + const charCode = value.charCodeAt(lastIndex); + // https://unicodebook.readthedocs.io/unicode_encodings.html#utf-16-surrogate-pairs + // If ends with high surrogates string is invalid and encodeURIComponent fails + // Only end checked for cases where surrogates input is sequential + // We assume the user inputs a valid string but sequential write might temporarily + // result in half a pair ending string + if (charCode < 0xd800 || charCode > 0xdbff) { + return false; // If any character is not a high surrogate, return false + } + return true; +} + +function safeEncodeURIComponent(value: string | number | boolean) { + if (typeof value === "string" && endsWithHalfSurrogatePair(value)) { + return encodeURIComponent(value.slice(0, -1)); + } + return encodeURIComponent(value); +} + export function openApiQueryParams2PostmanQueryParams( queryParams: Param[] ): sdk.QueryParam[] { @@ -46,7 +70,7 @@ export function openApiQueryParams2PostmanQueryParams( if (Array.isArray(param.value)) { return new sdk.QueryParam({ key: param.name, - value: param.value.map(encodeURIComponent).join(delimiter), + value: param.value.map(safeEncodeURIComponent).join(delimiter), }); } @@ -63,7 +87,7 @@ export function openApiQueryParams2PostmanQueryParams( return new sdk.QueryParam({ key: param.name, - value: encodeURIComponent(param.value), + value: safeEncodeURIComponent(param.value), }); }) .filter((item): item is sdk.QueryParam => item !== undefined); @@ -79,7 +103,9 @@ function setQueryParams(postman: sdk.Request, queryParams: Param[]) { function setPathParams(postman: sdk.Request, queryParams: Param[]) { const recursiveEncodeURIComponent = (c: string | string[]) => - Array.isArray(c) ? c.map(encodeURIComponent) : encodeURIComponent(c); + Array.isArray(c) + ? c.map(safeEncodeURIComponent) + : safeEncodeURIComponent(c); const source = queryParams.map((param) => { return new sdk.Variable({