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;