From b1fa933868d352ac0bbccdea054f93b9e8219aa3 Mon Sep 17 00:00:00 2001 From: DingDongSoLong4 <99329275+DingDongSoLong4@users.noreply.github.com> Date: Fri, 28 Oct 2022 08:46:39 +0200 Subject: [PATCH] Fix URL encoding again (#3044) Co-authored-by: WithoutPants <53250216+WithoutPants@users.noreply.github.com> --- ui/v2.5/src/models/list-filter/filter.ts | 80 +++++++++++++++++++----- 1 file changed, 66 insertions(+), 14 deletions(-) diff --git a/ui/v2.5/src/models/list-filter/filter.ts b/ui/v2.5/src/models/list-filter/filter.ts index 114e04594..c913f9328 100644 --- a/ui/v2.5/src/models/list-filter/filter.ts +++ b/ui/v2.5/src/models/list-filter/filter.ts @@ -145,18 +145,69 @@ export class ListFilterModel { jsonParameters = [params.c!]; } params.c = jsonParameters.map((jsonString) => { - let decodedJson = jsonString; - // replace () back to {} - decodedJson = decodedJson.replaceAll("(", "{"); - decodedJson = decodedJson.replaceAll(")", "}"); - // decode all other characters - decodedJson = decodeURIComponent(decodedJson); - return decodedJson; + const decoding = true; + return ListFilterModel.translateSpecialCharacters( + decodeURIComponent(jsonString), + decoding + ); }); } return params; } + private static translateSpecialCharacters(input: string, decoding: boolean) { + let inString = false; + let escape = false; + return [...input] + .map((c) => { + if (escape) { + // this character has been escaped, skip + escape = false; + return c; + } + + switch (c) { + case "\\": + // escape the next character if in a string + if (inString) { + escape = true; + } + break; + case '"': + // unescaped quote, toggle inString + inString = !inString; + break; + case "(": + // decode only: restore ( to { if not in a string + if (decoding && !inString) { + return "{"; + } + break; + case ")": + // decode only: restore ) to } if not in a string + if (decoding && !inString) { + return "}"; + } + break; + case "{": + // encode only: replace { with ( if not in a string + if (!decoding && !inString) { + return "("; + } + break; + case "}": + // encode only: replace } with ) if not in a string + if (!decoding && !inString) { + return ")"; + } + break; + } + + return c; + }) + .join(""); + } + public configureFromQueryString(query: string) { const parsed = queryString.parse(query, { decode: false }); const decoded = ListFilterModel.decodeQueryParameters(parsed); @@ -192,15 +243,15 @@ export class ListFilterModel { // Returns query parameters with necessary parts encoded public getQueryParameters(): IQueryParameters { const encodedCriteria: string[] = this.criteria.map((criterion) => { - let str = criterion.toJSON(); + const decoding = false; + let str = ListFilterModel.translateSpecialCharacters( + criterion.toJSON(), + decoding + ); + // URL-encode other characters str = encodeURI(str); - // force URL-encode existing () - str = str.replaceAll("(", "%28"); - str = str.replaceAll(")", "%29"); - // replace JSON '{'(%7B) '}'(%7D) with explicitly unreserved () - str = str.replaceAll("%7B", "("); - str = str.replaceAll("%7D", ")"); + // only the reserved characters ?#&;=+ need to be URL-encoded // as they have special meaning in query strings str = str.replaceAll("?", encodeURIComponent("?")); @@ -209,6 +260,7 @@ export class ListFilterModel { str = str.replaceAll(";", encodeURIComponent(";")); str = str.replaceAll("=", encodeURIComponent("=")); str = str.replaceAll("+", encodeURIComponent("+")); + return str; });