Harden render iframe open-link handling (#36811)

This PR hardens the handling of the “open-link” action in render iframes
(external rendering iframes). It prevents iframes from triggering unsafe
or unintended redirects or opening new windows via postMessage.

Additionally, it improves iframe height reporting to reduce scrollbar
and height mismatch issues, and adds unit test coverage.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
2026-03-03 23:15:33 -08:00
committed by GitHub
parent b874e0d8e5
commit 315b947740
3 changed files with 92 additions and 13 deletions

View File

@@ -1,23 +1,46 @@
import {generateElemId, queryElemChildren} from '../utils/dom.ts';
import {isDarkTheme} from '../utils.ts';
function safeRenderIframeLink(link: any): string | null {
try {
const url = new URL(`${link}`, window.location.href);
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
console.error(`Unsupported link protocol: ${link}`);
return null;
}
return url.href;
} catch (e) {
console.error(`Failed to parse link: ${link}, error: ${e}`);
return null;
}
}
// This function is only designed for "open-link" command from iframe, is not suitable for other contexts.
// Because other link protocols are directly handled by the iframe, but not here.
// Arguments can be any type & any value, they are from "message" event's data which is not controlled by us.
export function navigateToIframeLink(unsafeLink: any, target: any) {
const linkHref = safeRenderIframeLink(unsafeLink);
if (linkHref === null) return;
if (target === '_blank') {
window.open(linkHref, '_blank', 'noopener,noreferrer');
return;
}
// treat all other targets including ("_top", "_self", etc.) as same tab navigation
window.location.assign(linkHref);
}
async function loadRenderIframeContent(iframe: HTMLIFrameElement) {
const iframeSrcUrl = iframe.getAttribute('data-src')!;
if (!iframe.id) iframe.id = generateElemId('gitea-iframe-');
window.addEventListener('message', (e) => {
if (e.source !== iframe.contentWindow) return;
if (!e.data?.giteaIframeCmd || e.data?.giteaIframeId !== iframe.id) return;
const cmd = e.data.giteaIframeCmd;
if (cmd === 'resize') {
// TODO: sometimes the reported iframeHeight is not the size we need, need to figure why. Example: openapi swagger.
// As a workaround, add some pixels here.
iframe.style.height = `${e.data.iframeHeight + 2}px`;
iframe.style.height = `${e.data.iframeHeight}px`;
} else if (cmd === 'open-link') {
if (e.data.anchorTarget === '_blank') {
window.open(e.data.openLink, '_blank');
} else {
window.location.href = e.data.openLink;
}
navigateToIframeLink(e.data.openLink, e.data.anchorTarget);
} else {
throw new Error(`Unknown gitea iframe cmd: ${cmd}`);
}