From 5637ebed924ef04c23cb7e8cfd67f79724d17053 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 10 Feb 2023 13:28:42 +0100 Subject: [PATCH] core-components: global override for window.open MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw Signed-off-by: Patrik Oldsberg --- .changeset/gorgeous-ligers-burn.md | 5 +++++ .../src/components/Link/Link.test.tsx | 11 ++++++++++ .../src/components/Link/Link.tsx | 21 +++++++++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 .changeset/gorgeous-ligers-burn.md diff --git a/.changeset/gorgeous-ligers-burn.md b/.changeset/gorgeous-ligers-burn.md new file mode 100644 index 0000000000..f1d8f2cf5f --- /dev/null +++ b/.changeset/gorgeous-ligers-burn.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-components': patch +--- + +Added a global override for `window.open` that helps prevent security vulnerabilities. diff --git a/packages/core-components/src/components/Link/Link.test.tsx b/packages/core-components/src/components/Link/Link.test.tsx index 09e1293726..682c4bea8a 100644 --- a/packages/core-components/src/components/Link/Link.test.tsx +++ b/packages/core-components/src/components/Link/Link.test.tsx @@ -183,3 +183,14 @@ describe('', () => { ); }); }); + +describe('window.open', () => { + it('throws an error when attempting to open script code', () => { + expect(() => + // eslint-disable-next-line no-script-url + window.open("javascript:alert('hello')"), + ).toThrowErrorMatchingInlineSnapshot( + `"Rejected window.open() with a javascript: URL as a security precaution"`, + ); + }); +}); diff --git a/packages/core-components/src/components/Link/Link.tsx b/packages/core-components/src/components/Link/Link.tsx index bb753e10bd..be91753ea3 100644 --- a/packages/core-components/src/components/Link/Link.tsx +++ b/packages/core-components/src/components/Link/Link.tsx @@ -60,6 +60,27 @@ const scriptProtocolPattern = // eslint-disable-next-line no-control-regex /^[\u0000-\u001F ]*j[\r\n\t]*a[\r\n\t]*v[\r\n\t]*a[\r\n\t]*s[\r\n\t]*c[\r\n\t]*r[\r\n\t]*i[\r\n\t]*p[\r\n\t]*t[\r\n\t]*\:/i; +// We install this globally in order to prevent javascript: URL XSS attacks via window.open +const originalWindowOpen = window.open as typeof window.open & { + __backstage?: true; +}; +if (originalWindowOpen && !originalWindowOpen.__backstage) { + const newOpen = function open( + this: Window, + ...args: Parameters + ) { + const url = String(args[0]); + if (scriptProtocolPattern.test(url)) { + throw new Error( + 'Rejected window.open() with a javascript: URL as a security precaution', + ); + } + return originalWindowOpen.apply(this, args); + }; + newOpen.__backstage = true; + window.open = newOpen; +} + export type LinkProps = Omit & Omit & { to: string;