From 2b3a8c320a8e547abd3cd77b03d3a17058f50d4b Mon Sep 17 00:00:00 2001 From: Jaifroid Date: Sat, 15 Jun 2024 12:33:52 +0100 Subject: [PATCH] Handle custom protocols and URI schemata to avoid CSP violations #615 (#616) --- www/js/app.js | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/www/js/app.js b/www/js/app.js index 2885e461..e27f4ee5 100644 --- a/www/js/app.js +++ b/www/js/app.js @@ -5253,8 +5253,8 @@ function filterClickEvent (event) { } var href = clickedAnchor.getAttribute('href'); // We assume that, if an absolute http(s) link is hardcoded inside an HTML string, it means it's a link to an external website. - // We also do it for ftp even if it's not supported any more by recent browsers... - if (/^(?:http|ftp)/i.test(href)) { + // By comparing the protocols, we can filter out links such as `mailto:`, `tel:`, `skype:`, etc. (these should open in a new window). + if (/^(?:http|ftp)/i.test(href) || clickedAnchor.protocol && articleWindow.location.protocol !== clickedAnchor.protocol) { console.debug('filterClickEvent opening external link in new tab'); clickedAnchor.newcontainer = true; uiUtil.warnAndOpenExternalLinkInNewTab(event, clickedAnchor); @@ -5407,10 +5407,20 @@ function handleClickOnReplayLink (ev, anchor) { var pseudoNamespace = appstate.selectedArchive.zimitPseudoContentNamespace; var pseudoDomainPath = (anchor.hostname === window.location.hostname ? appstate.selectedArchive.zimitPrefix.replace(/\/$/, '') : anchor.hostname) + anchor.pathname; var containingDocDomainPath = anchor.ownerDocument.location.hostname + anchor.ownerDocument.location.pathname; - // If it's for a different protocol (e.g. javascript:) we should let Replay handle that, or if the paths are identical, then we are dealing - // with a link to an anchor in the same document, or if the user has pressed the ctrl or command key, the document will open in a new window - // anyway, so we can return. Note that some PDFs are served with a protocol of http: instead of https:, so we need to account for that. - if (anchor.protocol.replace(/s:/, ':') !== document.location.protocol.replace(/s:/, ':') || pseudoDomainPath === containingDocDomainPath) return; + // If the paths are identical, then we are dealing with a link to an anchor in the same document + if (pseudoDomainPath === containingDocDomainPath) return; + // If it's for a different protocol (e.g. javascript:) we may need to handle that, or if the user has pressed the ctrl or command key, the document + // will open in a new window anyway, so we can return. Note that some PDFs are served with a protocol of http: instead of https:, so we need to account for that. + if (anchor.protocol && anchor.protocol.replace(/s:/, ':') !== document.location.protocol.replace(/s:/, ':')) { + // DEV: Monitor whether you need to handle /blob:|data:|file:/ as well (probably not, as they would be blocked by the sandbox if loaded into iframe) + if (/about:|javascript:/i.test(anchor.protocol) || ev.ctrlKey || ev.metaKey || ev.button === 1) return; + // So it's probably a URI scheme or protocol like mailto: that would violate the CSP, so we need to open it explicitly in a new taba + ev.preventDefault(); + ev.stopPropagation(); + console.debug('handleClickOnReplayLink opening custom protocol ' + anchor.protocol + ' in new tab'); + uiUtil.warnAndOpenExternalLinkInNewTab(ev, anchor); + return; + } var zimUrl; // If it starts with the path to the ZIM file, then we are dealing with an untransformed absolute local ZIM link if (!anchor.href.indexOf(pathToZim)) { @@ -5548,6 +5558,8 @@ function handleClickOnReplayLink (ev, anchor) { console.error('Error getting dirEntry for ' + zimUrl, err); uiUtil.systemAlert('There was an error looking up ' + zimUrl, 'Error reading direcotry entry!'); }); + } else { + } }