Compare commits

..

2 Commits

Author SHA1 Message Date
Veloman Yunkan
7d0ea2b074 Guaranteed activation of external link blocking
This is a quickfix for the problem observed with external link blocking
during certain history navigation actions (when the cached iframe content is
loaded/restored before the viewer setup is completed).

Since external link blocking doesn't depend on the translations (that
are asynchronously loaded during the viewer setup) it can be performed
unconditionally. However, the current dependence of `on_content_load()`
on viewer setup has to be addressed too.
2023-05-20 15:48:45 +04:00
Veloman Yunkan
f674336cc1 Fixed external links in the viewer iframe
Before this fix clicking an external link in the viewer iframe had no
effect (other than an error being reported in the browser dev tools
console) because the attempt to navigate the top browser context was
suppressed due to sandboxing.

This commit works around that limitation by providing message-based
API for top context navigation. Now malicious pages can break out of
the viewer iframe as soon as they learn about that API :)
2023-05-16 18:47:38 +04:00
2 changed files with 20 additions and 36 deletions

View File

@@ -23,7 +23,7 @@ function userUrl2IframeUrl(url) {
}
function getBookFromUserUrl(url) {
if ( url == '' || url.startsWith('catch/external?') ) {
if ( url == '' ) {
return null;
}
@@ -131,10 +131,6 @@ function iframeUrl2UserUrl(url, query) {
return '';
}
if ( url == `${root}/catch/external` ) {
return `catch/external${query}`;
}
if ( url == `${root}/search` ) {
return `search${query}`;
}
@@ -224,16 +220,12 @@ function handle_location_hash_change() {
function handle_content_url_change() {
const iframeLocation = contentIframe.contentWindow.location;
console.log('handle_content_url_change: ' + iframeLocation.href);
document.title = contentIframe.contentDocument.title;
const iframeContentUrl = iframeLocation.pathname;
const iframeContentQuery = iframeLocation.search;
const newHash = iframeUrl2UserUrl(iframeContentUrl, iframeContentQuery);
if ( newHash.startsWith('catch/external?') ) {
handleInterceptedExternalLink(newHash);
} else {
document.title = contentIframe.contentDocument.title;
history.replaceState(viewerState, null, makeURL(location.search, newHash));
updateCurrentBookIfNeeded(newHash);
}
history.replaceState(viewerState, null, makeURL(location.search, newHash));
updateCurrentBookIfNeeded(newHash);
};
////////////////////////////////////////////////////////////////////////////////
@@ -251,9 +243,10 @@ function matchingAncestorElement(el, context, selector) {
const block_path = `${root}/catch/external`;
function blockLink(target) {
const encodedHref = encodeURIComponent(target.href);
target.setAttribute("href", block_path + "?source=" + encodedHref);
function blockLink(url) {
return viewerSettings.linkBlockingEnabled
? block_path + "?source=" + encodeURIComponent(url)
: url;
}
function isExternalUrl(url) {
@@ -270,7 +263,8 @@ function onClickEvent(e) {
const target = matchingAncestorElement(e.target, iframeDocument, "a");
if (target !== null && "href" in target) {
if ( isExternalUrl(target.href) ) {
return blockLink(target);
target.setAttribute("href", blockLink(target.href));
contentIframe.contentWindow.parent.postMessage({ externalURL : target.href }, "*");
}
}
}
@@ -309,24 +303,6 @@ function setup_external_link_blocker() {
setupEventHandler(contentIframe.contentDocument, 'a', 'click', onClickEvent);
}
function getBlockedUrl(catchExternalUrl) {
const p = new URLSearchParams(catchExternalUrl.split('?')[1]);
return p.get('source');
}
function handleInterceptedExternalLink(catchExternalUrl) {
// The external link blocking page was loaded in the viewer iframe.
// We need to get rid of the viewer taskbar and load in the top frame either
// the external resource or, if running in --blockexternal mode, the
// confirmation page
const url = viewerSettings.linkBlockingEnabled
? `${root}/` + catchExternalUrl
: getBlockedUrl(catchExternalUrl);
history.back(); // drop from the browsing history the state where the
// external link catcher page is loaded in the iframe ...
window.location = url; // ... and load the target in the top frame instead
}
////////////////////////////////////////////////////////////////////////////////
// End of external link blocking
////////////////////////////////////////////////////////////////////////////////
@@ -469,6 +445,13 @@ function changeUILanguage() {
});
}
function handleMessage(event) {
console.log("handleMessage");
if ( event.data.externalURL ) {
window.location = event.data.externalURL;
}
}
function setupViewer() {
// Defer the call of handle_visual_viewport_change() until after the
// presence or absence of the taskbar as determined by this function
@@ -476,6 +459,7 @@ function setupViewer() {
setTimeout(handle_visual_viewport_change, 0);
window.onresize = handle_visual_viewport_change;
window.addEventListener("message", handleMessage);
const kiwixToolBarWrapper = document.getElementById('kiwixtoolbarwrapper');
if ( ! viewerSettings.toolbarEnabled ) {

View File

@@ -73,7 +73,7 @@ const ResourceCollection resources200Compressible{
{ DYNAMIC_CONTENT, "/ROOT%23%3F/skin/taskbar.css" },
{ STATIC_CONTENT, "/ROOT%23%3F/skin/taskbar.css?cacheid=bbdaf425" },
{ DYNAMIC_CONTENT, "/ROOT%23%3F/skin/viewer.js" },
{ STATIC_CONTENT, "/ROOT%23%3F/skin/viewer.js?cacheid=db02f59d" },
{ STATIC_CONTENT, "/ROOT%23%3F/skin/viewer.js?cacheid=d407a38a" },
{ DYNAMIC_CONTENT, "/ROOT%23%3F/skin/fonts/Poppins.ttf" },
{ STATIC_CONTENT, "/ROOT%23%3F/skin/fonts/Poppins.ttf?cacheid=af705837" },
{ DYNAMIC_CONTENT, "/ROOT%23%3F/skin/fonts/Roboto.ttf" },
@@ -312,7 +312,7 @@ R"EXPECTEDRESULT( <link type="text/css" href="./skin/taskbar.css?cacheid=bbda
<link type="text/css" href="./skin/css/autoComplete.css?cacheid=08951e06" rel="Stylesheet" />
<script type="module" src="./skin/i18n.js?cacheid=2cf0f8c5" defer></script>
<script type="text/javascript" src="./skin/languages.js?cacheid=b00b12db" defer></script>
<script type="text/javascript" src="./skin/viewer.js?cacheid=db02f59d" defer></script>
<script type="text/javascript" src="./skin/viewer.js?cacheid=d407a38a" defer></script>
<script type="text/javascript" src="./skin/autoComplete.min.js?cacheid=1191aaaf"></script>
const blankPageUrl = root + "/skin/blank.html?cacheid=6b1fa032";
<img src="./skin/langSelector.svg?cacheid=00b59961">