mirror of
https://github.com/go-gitea/gitea.git
synced 2026-03-05 03:55:24 +01:00
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>
58 lines
2.2 KiB
TypeScript
58 lines
2.2 KiB
TypeScript
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') {
|
|
iframe.style.height = `${e.data.iframeHeight}px`;
|
|
} else if (cmd === 'open-link') {
|
|
navigateToIframeLink(e.data.openLink, e.data.anchorTarget);
|
|
} else {
|
|
throw new Error(`Unknown gitea iframe cmd: ${cmd}`);
|
|
}
|
|
});
|
|
|
|
const u = new URL(iframeSrcUrl, window.location.origin);
|
|
u.searchParams.set('gitea-is-dark-theme', String(isDarkTheme()));
|
|
u.searchParams.set('gitea-iframe-id', iframe.id);
|
|
iframe.src = u.href;
|
|
}
|
|
|
|
export function initMarkupRenderIframe(el: HTMLElement) {
|
|
queryElemChildren(el, 'iframe.external-render-iframe', loadRenderIframeContent);
|
|
}
|