From 44cc1194e2e4a1dcd4b54d61a813cbb02b39b96e Mon Sep 17 00:00:00 2001 From: Oleksandr Strakhov Date: Sun, 7 Mar 2021 19:25:59 +0200 Subject: [PATCH] Fix issue with navigation when operationId contains backslash --- package-lock.json | 9 +++++++-- package.json | 2 ++ src/services/MenuStore.ts | 8 ++++---- src/utils/dom.ts | 9 +++++++++ 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index b6195280..11a81b12 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1869,6 +1869,12 @@ "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==" }, + "@types/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-4mBnOrTpVKn+tYzlnMO7cwDkDa6wlQ2bBXW+79/6ahMd36GF216kxWYxgz+S4d5Ev1ByFbnQbPGxV4P5BSL8MA==", + "dev": true + }, "@types/dompurify": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-2.0.2.tgz", @@ -5212,8 +5218,7 @@ "cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==" }, "cssom": { "version": "0.4.4", diff --git a/package.json b/package.json index 28e788a5..84b2d77c 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,7 @@ "@cypress/webpack-preprocessor": "^5.4.2", "@hot-loader/react-dom": "^16.12.0", "@types/chai": "^4.2.12", + "@types/cssesc": "^3.0.0", "@types/dompurify": "^2.0.2", "@types/enzyme": "^3.10.5", "@types/enzyme-to-json": "^1.5.3", @@ -137,6 +138,7 @@ "@redocly/react-dropdown-aria": "^2.0.11", "@types/node": "^13.11.1", "classnames": "^2.2.6", + "cssesc": "^3.0.0", "decko": "^1.2.0", "dompurify": "^2.0.12", "eventemitter3": "^4.0.4", diff --git a/src/services/MenuStore.ts b/src/services/MenuStore.ts index ac60fee8..aa9ab366 100644 --- a/src/services/MenuStore.ts +++ b/src/services/MenuStore.ts @@ -1,5 +1,5 @@ import { action, observable, makeObservable } from 'mobx'; -import { querySelector } from '../utils/dom'; +import { escapeSelectorValue, querySelector } from '../utils/dom'; import { SpecStore } from './models'; import { history as historyInst, HistoryService } from './HistoryService'; @@ -162,7 +162,7 @@ export class MenuStore { */ getElementAt(idx: number): Element | null { const item = this.flatItems[idx]; - return (item && querySelector(`[${SECTION_ATTR}="${item.id}"]`)) || null; + return (item && querySelector(`[${SECTION_ATTR}="${escapeSelectorValue(item.id)}"]`)) || null; } /** @@ -174,7 +174,7 @@ export class MenuStore { if (item && item.type === 'group') { item = item.items[0]; } - return (item && querySelector(`[${SECTION_ATTR}="${item.id}"]`)) || null; + return (item && querySelector(`[${SECTION_ATTR}="${escapeSelectorValue(item.id)}"]`)) || null; } /** @@ -222,7 +222,7 @@ export class MenuStore { this.activeItemIdx = item.absoluteIdx!; if (updateLocation) { - this.history.replace(item.id, rewriteHistory); + this.history.replace(encodeURI(item.id), rewriteHistory); } item.activate(); diff --git a/src/utils/dom.ts b/src/utils/dom.ts index ce91a33a..92d64601 100644 --- a/src/utils/dom.ts +++ b/src/utils/dom.ts @@ -1,3 +1,5 @@ +import * as cssesc from 'cssesc'; + export const IS_BROWSER = typeof window !== 'undefined' && 'HTMLElement' in window; export function querySelector(selector: string): Element | null { @@ -7,6 +9,13 @@ export function querySelector(selector: string): Element | null { return null; } +export function escapeSelectorValue( + value: string, + options: Readonly> = { quotes: 'double' }, +): string { + return cssesc(value, options); +} + /** * Drop everything inside <...> (i.e., tags/elements), and keep the text. * Unlike browser innerText, this removes newlines; it also doesn't handle