mirror of
https://github.com/kevinveenbirkenbach/homepage.veen.world.git
synced 2026-09-23 19:03:18 +00:00
fix(iframe): hand the sinks the validated URL, not the parameter
The scheme and origin checks lived in a boolean guard in another function, so the raw query parameter still reached the iframe src, the history entry and window.open. The validator now returns the normalised href or null, and every sink consumes only that. Which URLs are accepted does not change: openIframe still checks the scheme alone, because the modal opens configured targets that are not among the page's iframe links. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -21,6 +21,7 @@ const SHARED = {
|
||||
openDynamicPopup: 'readonly',
|
||||
closeAllModals: 'readonly',
|
||||
isSafeUrl: 'readonly',
|
||||
safeUrl: 'readonly',
|
||||
openIframe: 'readonly',
|
||||
enterFullscreen: 'readonly',
|
||||
exitFullscreen: 'readonly',
|
||||
|
||||
@@ -2,19 +2,20 @@
|
||||
let mainElement, originalContent, originalMainStyle, container, customScrollbar, scrollbarContainer;
|
||||
let currentIframeUrl = null;
|
||||
|
||||
function isAllowedIframeUrl(url) {
|
||||
if (!isSafeUrl(url)) {
|
||||
return false;
|
||||
function allowedIframeUrl(url) {
|
||||
const candidate = safeUrl(url);
|
||||
if (candidate === null) {
|
||||
return null;
|
||||
}
|
||||
const allowedOrigins = new Set([window.location.origin]);
|
||||
document.querySelectorAll('a.iframe-link[href]').forEach((link) => allowedOrigins.add(link.origin));
|
||||
return allowedOrigins.has(new URL(url, window.location.href).origin);
|
||||
return allowedOrigins.has(new URL(candidate).origin) ? candidate : null;
|
||||
}
|
||||
|
||||
// === Auto-open iframe if URL parameter is present ===
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
const paramUrl = new URLSearchParams(window.location.search).get('iframe');
|
||||
if (paramUrl && isAllowedIframeUrl(paramUrl)) {
|
||||
const paramUrl = allowedIframeUrl(new URLSearchParams(window.location.search).get('iframe'));
|
||||
if (paramUrl) {
|
||||
currentIframeUrl = paramUrl;
|
||||
enterFullscreen();
|
||||
openIframe(paramUrl);
|
||||
@@ -43,7 +44,8 @@ function syncIframeHeight() {
|
||||
|
||||
// Function to open a URL in an iframe (jQuery version mit 1500 ms Fade)
|
||||
function openIframe(url) {
|
||||
if (!isSafeUrl(url)) {
|
||||
const target = safeUrl(url);
|
||||
if (target === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -73,7 +75,7 @@ function openIframe(url) {
|
||||
|
||||
// Quelle setzen und mit 1500 ms einblenden
|
||||
$iframe
|
||||
.attr('src', url)
|
||||
.attr('src', target)
|
||||
.fadeIn(1500, function() {
|
||||
syncIframeHeight();
|
||||
observeIframeNavigation();
|
||||
@@ -81,8 +83,8 @@ function openIframe(url) {
|
||||
|
||||
// URL-State pushen
|
||||
var newUrl = new URL(window.location);
|
||||
newUrl.searchParams.set('iframe', url);
|
||||
window.history.pushState({ iframe: url }, '', newUrl);
|
||||
newUrl.searchParams.set('iframe', target);
|
||||
window.history.pushState({ iframe: target }, '', newUrl);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -148,8 +150,8 @@ document.addEventListener("DOMContentLoaded", function() {
|
||||
*/
|
||||
function openIframeInNewTab() {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const iframeUrl = params.get('iframe');
|
||||
if (iframeUrl && isAllowedIframeUrl(iframeUrl)) {
|
||||
const iframeUrl = allowedIframeUrl(params.get('iframe'));
|
||||
if (iframeUrl) {
|
||||
window.open(iframeUrl, '_blank');
|
||||
} else {
|
||||
alert('No iframe is currently open.');
|
||||
|
||||
@@ -7,15 +7,19 @@ function t(source) {
|
||||
|
||||
const SAFE_URL_SCHEMES = ['http:', 'https:', 'mailto:'];
|
||||
|
||||
function isSafeUrl(url) {
|
||||
function safeUrl(url) {
|
||||
try {
|
||||
const parsed = new URL(String(url == null ? '' : url), window.location.href);
|
||||
return SAFE_URL_SCHEMES.includes(parsed.protocol);
|
||||
return SAFE_URL_SCHEMES.includes(parsed.protocol) ? parsed.href : null;
|
||||
} catch (error) {
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function isSafeUrl(url) {
|
||||
return safeUrl(url) !== null;
|
||||
}
|
||||
|
||||
function iconAndName(item) {
|
||||
const nodes = [];
|
||||
if (item.icon && item.icon.class) {
|
||||
|
||||
Reference in New Issue
Block a user