Skip to content

Commit

Permalink
Merge pull request #73 from braintree/li-38822-fix
Browse files Browse the repository at this point in the history
LI-38822 Handle Whitespace Escapes
  • Loading branch information
ibooker authored May 14, 2024
2 parents 0d5140d + 284119e commit 6f474fc
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 2 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# CHANGELOG

## UNRELEASED

- Improve sanitization of whitespace escapes

## 7.0.1

- Improve sanitization of HTML entities

## 7.0.0
Expand Down
20 changes: 20 additions & 0 deletions src/__tests__/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,26 @@ 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\\%74t:alert('XSS')",
"javascrip%5c%72t:alert()",
"javascrip%5Ctt:alert()",
"javascrip%255Ctt:alert()",
"javascrip%25%35Ctt:alert()",
"javascrip%25%35%43tt:alert()",
"javascrip%25%32%35%25%33%35%25%34%33rt:alert()",
"javascrip%255Crt:alert('%25xss')",
];

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}`, () => {
Expand Down
2 changes: 2 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@ export const htmlCtrlEntityRegex = /&(newline|tab);/gi;
export const ctrlCharactersRegex =
/[\u0000-\u001F\u007F-\u009F\u2000-\u200D\uFEFF]/gim;
export const urlSchemeRegex = /^.+(:|:)/gim;
export const whitespaceEscapeCharsRegex =
/(\\|%5[cC])((%(6[eE]|72|74))|[nrt])/g;
export const relativeFirstCharacters = [".", "/"];
export const BLANK_URL = "about:blank";
22 changes: 20 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
invalidProtocolRegex,
relativeFirstCharacters,
urlSchemeRegex,
whitespaceEscapeCharsRegex,
} from "./constants";

function isRelativeUrlWithoutProtocol(url: string): boolean {
Expand All @@ -20,21 +21,38 @@ function decodeHtmlCharacters(str: string) {
});
}

function decodeURI(uri: string): string {
try {
return decodeURIComponent(uri);
} catch (e: unknown) {
// Ignoring error
// It is possible that the URI contains a `%` not associated
// with URI/URL-encoding.
return uri;
}
}

export function sanitizeUrl(url?: string): string {
if (!url) {
return BLANK_URL;
}
let charsToDecode;
let decodedUrl = url;
let decodedUrl = decodeURI(url);

do {
decodedUrl = decodeHtmlCharacters(decodedUrl)
.replace(htmlCtrlEntityRegex, "")
.replace(ctrlCharactersRegex, "")
.replace(whitespaceEscapeCharsRegex, "")
.trim();

decodedUrl = decodeURI(decodedUrl);

charsToDecode =
decodedUrl.match(ctrlCharactersRegex) ||
decodedUrl.match(htmlEntitiesRegex) ||
decodedUrl.match(htmlCtrlEntityRegex);
decodedUrl.match(htmlCtrlEntityRegex) ||
decodedUrl.match(whitespaceEscapeCharsRegex);
} while (charsToDecode && charsToDecode.length > 0);
const sanitizedUrl = decodedUrl;
if (!sanitizedUrl) {
Expand Down

0 comments on commit 6f474fc

Please sign in to comment.