From 1c0be2b876f66e3c23c27ffe79f30cfa17191de0 Mon Sep 17 00:00:00 2001 From: Iris Booker Date: Mon, 6 May 2024 17:42:08 -0500 Subject: [PATCH] li-38822 fix: handle whitespace escapes --- src/__tests__/index.test.ts | 13 +++++++++++++ src/constants.ts | 1 + src/index.ts | 5 ++++- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/__tests__/index.test.ts b/src/__tests__/index.test.ts index c6a4e26..3b70efc 100644 --- a/src/__tests__/index.test.ts +++ b/src/__tests__/index.test.ts @@ -127,6 +127,19 @@ describe("sanitizeUrl", () => { ).toBe("https://example.com/javascript:alert('XSS')"); }); + it("removes whitespace escape sequences", () => { + const attackVectors = [ + "javascri\npt:alert('xss')", + "javascri\rpt:alert('xss')", + "javascri\tpt:alert('xss')", + "javascrip\x74t:alert('XSS')", + ]; + + attackVectors.forEach((vector) => { + expect(sanitizeUrl(vector)).toBe(BLANK_URL); + }); + }); + describe("invalid protocols", () => { describe.each(["javascript", "data", "vbscript"])("%s", (protocol) => { it(`replaces ${protocol} urls with ${BLANK_URL}`, () => { diff --git a/src/constants.ts b/src/constants.ts index ef18d43..f87cd63 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -4,5 +4,6 @@ export const htmlCtrlEntityRegex = /&(newline|tab);/gi; export const ctrlCharactersRegex = /[\u0000-\u001F\u007F-\u009F\u2000-\u200D\uFEFF]/gim; export const urlSchemeRegex = /^.+(:|:)/gim; +export const whitespaceEscapeChars = /\\[nrt]/g; export const relativeFirstCharacters = [".", "/"]; export const BLANK_URL = "about:blank"; diff --git a/src/index.ts b/src/index.ts index dd04813..3560f0f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,6 +6,7 @@ import { invalidProtocolRegex, relativeFirstCharacters, urlSchemeRegex, + whitespaceEscapeChars, } from "./constants"; function isRelativeUrlWithoutProtocol(url: string): boolean { @@ -30,11 +31,13 @@ export function sanitizeUrl(url?: string): string { decodedUrl = decodeHtmlCharacters(decodedUrl) .replace(htmlCtrlEntityRegex, "") .replace(ctrlCharactersRegex, "") + .replace(whitespaceEscapeChars, "") .trim(); charsToDecode = decodedUrl.match(ctrlCharactersRegex) || decodedUrl.match(htmlEntitiesRegex) || - decodedUrl.match(htmlCtrlEntityRegex); + decodedUrl.match(htmlCtrlEntityRegex) || + decodedUrl.match(whitespaceEscapeChars); } while (charsToDecode && charsToDecode.length > 0); const sanitizedUrl = decodedUrl; if (!sanitizedUrl) {