Skip to content

Commit

Permalink
Add a security prompt when user opens ZIM in SW mode for first time #974
Browse files Browse the repository at this point in the history
  • Loading branch information
Greeshmanth1909 authored Feb 21, 2024
1 parent 92e6177 commit feb8e6a
Show file tree
Hide file tree
Showing 9 changed files with 118 additions and 5 deletions.
6 changes: 6 additions & 0 deletions i18n/en.jsonp.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions i18n/es.jsonp.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions i18n/fr.jsonp.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions tests/e2e/spec/gutenberg_ro.e2e.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,13 @@ function runTests (driver, modes) {
console.log('\x1b[33m%s\x1b[0m', ' Skipping SW mode tests because browser does not support API');
await driver.quit();
}
// Disable source verification in SW mode as the dialogue box gave incosistent test results in automated tests
if (mode === 'serviceworker') {
const sourceVerificationCheckbox = await driver.findElement(By.id('enableSourceVerification'));
if (sourceVerificationCheckbox.isSelected()) {
await sourceVerificationCheckbox.click();
}
}
});

// Loads the ZIM archive for the mode if the mode is not skipped
Expand Down
9 changes: 8 additions & 1 deletion tests/e2e/spec/legacy-ray_charles.e2e.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -194,8 +194,14 @@ function runTests (driver, modes) {
console.log('\x1b[33m%s\x1b[0m', ' Skipping SW mode tests because browser does not support API');
await driver.quit();
}
// Disable source verification in SW mode as the dialogue box gave incosistent test results in automated tests
if (mode === 'serviceworker') {
const sourceVerificationCheckbox = await driver.findElement(By.id('enableSourceVerification'));
if (sourceVerificationCheckbox.isSelected()) {
await sourceVerificationCheckbox.click();
}
}
});

it('Load legacy Ray Charles and check index contains specified article', async function () {
if (!serviceWorkerAPI) {
console.log('\x1b[33m%s\x1b[0m', ' - Following test skipped:');
Expand Down Expand Up @@ -230,6 +236,7 @@ function runTests (driver, modes) {
console.log('\x1b[33m%s\x1b[0m', ' - Following test skipped:');
return;
}

// console.log('FilesLength outer: ' + filesLength);
// Switch to iframe and check that the index contains the specified article
await driver.switchTo().frame('articleContent');
Expand Down
3 changes: 2 additions & 1 deletion tests/unit/js/init.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
// Define global params needed for tests to run on existing app code
// eslint-disable-next-line no-unused-vars
var params = {};

// We need to turn off source verification so that the test files can be loaded normally without interruption
params['sourceVerification'] = false;
// Test if WebP is natively supported, and if not, load a webpMachine instance. This is used in uiUtils.js.
// eslint-disable-next-line no-unused-vars
var webpMachine = false;
Expand Down
6 changes: 6 additions & 0 deletions www/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -687,6 +687,12 @@ <h3 data-i18n="configure-expert-settings-title">Expert settings</h3>
<span data-i18n="configure-expert-disabledragdrop"><strong>Disable drag-and-drop</strong> (in case it is causing anomalies)</span>
</label>
</div>
<div class="checkbox" id="enableSourceVerificationCheckBox">
<label data-i18n-tip="configure-expert-enable-source-verification-tip" title="Warning: Some ZIM archives from untrusted sources could run malicious code in your browser. This can be prevented by using JQuery mode, which cannot run active content from the ZIM. Highly dynamic ZIMs will probably fail in JQuery mode, but ZIMs with largely static content should work. If you trust the source of all of your ZIMs, then disabling this option will use ServiceWorker mode by default, if available.">
<input type="checkbox" name="disableFileVerification" id="enableSourceVerification" >
<span data-i18n="configure-expert-enable-source-verification-check-box"><strong>Enable source verification of new files</strong> (<i>recommended</i>: you will only be prompted the first time you open a ZIM)</span>
</label>
</div>
<div class="checkbox" id="">
<label data-i18n-tip="configure-expert-useLibzim-tip" title="Uses the selected version of libzim to access the ZIM contents (ServiceWorker mode only).">
<input type="checkbox" name="useLibzim" id="useLibzim">
Expand Down
75 changes: 74 additions & 1 deletion www/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,33 @@ document.getElementById('disableDragAndDropCheck').addEventListener('change', fu
}
});
});
// Handle switching from jQuery to serviceWorker modes.
document.getElementById('serviceworkerModeRadio').addEventListener('click', async function () {
document.getElementById('enableSourceVerificationCheckBox').style.display = '';
if (selectedArchive.isReady() && !(settingsStore.getItem("trustedZimFiles").includes(selectedArchive.file.name)) && params.sourceVerification) {
await verifyLoadedArchive(selectedArchive);
}
});
document.getElementById('jqueryModeRadio').addEventListener('click', function () {
if (this.checked) {
document.getElementById('enableSourceVerificationCheckBox').style.display = 'none';
}
});
// Handle switching to serviceWorkerLocal mode for chrome-extension
document.getElementById('serviceworkerLocalModeRadio').addEventListener('click', async function () {
document.getElementById('enableSourceVerificationCheckBox').style.display = '';
if (selectedArchive.isReady() && !(settingsStore.getItem("trustedZimFiles").includes(selectedArchive.file.name)) && params.sourceVerification) {
await verifyLoadedArchive(selectedArchive);
}
});

// Source verification is only makes sense in SW mode as doing the same in jQuery mode is redundant.
document.getElementById('enableSourceVerificationCheckBox').style.display = params.contentInjectionMode === ('serviceworker' || 'serviceworkerlocal') ? 'block' : 'none';

document.getElementById('enableSourceVerification').addEventListener('change', function () {
params.sourceVerification = this.checked;
settingsStore.setItem('sourceVerification', this.checked, Infinity);
});
document.querySelectorAll('input[type="checkbox"][name=hideActiveContentWarning]').forEach(function (element) {
element.addEventListener('change', function () {
params.hideActiveContentWarning = !!this.checked;
Expand Down Expand Up @@ -601,6 +628,29 @@ function focusPrefixOnHomeKey (event) {
}, 0);
}
}
/**
* Verifies the given archive and switches contentInjectionMode accourdingly
* @param {archive} the archive that needs verification
* */
async function verifyLoadedArchive (archive) {
const response = await uiUtil.systemAlert(translateUI.t('dialog-sourceverification-alert') || "Is this ZIM archive from a trusted source?\n If not, you can still read the ZIM file in Safe Mode (aka JQuery mode). Closing this window also opens the file in Safe Mode. This option can be disabled in Expert Settings", translateUI.t('dialog-sourceverification-title') || "Security alert!", true, translateUI.t('dialog-sourceverification-safe-mode-button') || 'Open in Safe Mode', translateUI.t('dialog-sourceverification-trust-button')|| 'Trust Source');
if (response) {
params.contentInjectionMode = 'serviceworker';
var trustedZimFiles = settingsStore.getItem('trustedZimFiles');
var updatedTrustedZimFiles = trustedZimFiles + archive.file.name + '|';
settingsStore.setItem('trustedZimFiles', updatedTrustedZimFiles, Infinity);
// Change radio buttons accordingly
if (params.serviceWorkerLocal) {
document.getElementById('serviceworkerLocalModeRadio').checked = true;
} else {
document.getElementById('serviceworkerModeRadio').checked = true;
}
} else {
// Switch to Safe mode
params.contentInjectionMode = 'jquery';
document.getElementById('jqueryModeRadio').checked = true;
}
}
// switch on/off the feature to use Home Key to focus search bar
function switchHomeKeyToFocusSearchBar () {
var iframeContentWindow = document.getElementById('articleContent').contentWindow;
Expand Down Expand Up @@ -1640,7 +1690,7 @@ function setLocalArchiveFromFileList (files) {
*
* @param {ZIMArchive} archive The ZIM archive
*/
function archiveReadyCallback (archive) {
async function archiveReadyCallback (archive) {
selectedArchive = archive;
// A css cache significantly speeds up the loading of CSS files (used by default in jQuery mode)
selectedArchive.cssCache = new Map();
Expand All @@ -1650,6 +1700,28 @@ function archiveReadyCallback (archive) {
params.originalContentInjectionMode = null;
}
}
// Set contentInjectionMode to serviceWorker when opening a new archive in case the user switched to Safe Mode/jquery Mode when opening the previous archive
if (params.contentInjectionMode === 'jquery') {
params.contentInjectionMode = settingsStore.getItem('contentInjectionMode');
// Change the radio buttons accordingly
switch (settingsStore.getItem('contentInjectionMode')) {
case 'serviceworker':
document.getElementById('serviceworkerModeRadio').checked = true;
break;
case 'serviceworkerlocal':
document.getElementById('serviceworkerLocalModeRadio').checked = true;
break;
}
}
if (settingsStore.getItem('trustedZimFiles') === null) {
settingsStore.setItem('trustedZimFiles', '', Infinity);
}
if (params.sourceVerification && (params.contentInjectionMode === 'serviceworker' || params.contentInjectionMode === 'serviceworkerlocal')) {
// Check if source of the zim file can be trusted.
if (!(settingsStore.getItem('trustedZimFiles').includes(archive.file.name))) {
await verifyLoadedArchive(archive);
}
}
// When a new ZIM is loaded, we turn this flag to null, so that we don't get false positive attempts to use the Worker
// It will be defined as false or true when the first article is loaded
appstate.isReplayWorkerAvailable = null;
Expand Down Expand Up @@ -1875,6 +1947,7 @@ function readArticle (dirEntry) {
uiUtil.spinnerDisplay(false);
return;
}

// Reset search prefix to allow users to search the same string again if they want to
appstate.search.prefix = '';
// Only update for expectedArticleURLToBeDisplayed.
Expand Down
Loading

0 comments on commit feb8e6a

Please sign in to comment.