Bug 1877195: Expand mixed-content download protection to all http downloads, r=freddyb,Gijs,anti-tracking-reviewers,pbz
Differential Revision: https://phabricator.services.mozilla.com/D200267
This commit is contained in:
@@ -14,7 +14,7 @@ function triggerSave(aWindow, aCallback) {
|
|||||||
let testBrowser = aWindow.gBrowser.selectedBrowser;
|
let testBrowser = aWindow.gBrowser.selectedBrowser;
|
||||||
// This page sets a cookie if and only if a cookie does not exist yet
|
// This page sets a cookie if and only if a cookie does not exist yet
|
||||||
let testURI =
|
let testURI =
|
||||||
"http://mochi.test:8888/browser/browser/base/content/test/general/bug792517-2.html";
|
"https://example.com/browser/browser/base/content/test/general/bug792517-2.html";
|
||||||
BrowserTestUtils.startLoadingURIString(testBrowser, testURI);
|
BrowserTestUtils.startLoadingURIString(testBrowser, testURI);
|
||||||
BrowserTestUtils.browserLoaded(testBrowser, false, testURI).then(() => {
|
BrowserTestUtils.browserLoaded(testBrowser, false, testURI).then(() => {
|
||||||
waitForFocus(function () {
|
waitForFocus(function () {
|
||||||
@@ -132,7 +132,7 @@ function test() {
|
|||||||
info("onExamineResponse with " + channel.URI.spec);
|
info("onExamineResponse with " + channel.URI.spec);
|
||||||
if (
|
if (
|
||||||
channel.URI.spec !=
|
channel.URI.spec !=
|
||||||
"http://mochi.test:8888/browser/browser/base/content/test/general/bug792517.sjs"
|
"https://example.com/browser/browser/base/content/test/general/bug792517.sjs"
|
||||||
) {
|
) {
|
||||||
info("returning");
|
info("returning");
|
||||||
return;
|
return;
|
||||||
@@ -158,7 +158,7 @@ function test() {
|
|||||||
info("onModifyRequest with " + channel.URI.spec);
|
info("onModifyRequest with " + channel.URI.spec);
|
||||||
if (
|
if (
|
||||||
channel.URI.spec !=
|
channel.URI.spec !=
|
||||||
"http://mochi.test:8888/browser/browser/base/content/test/general/bug792517.sjs"
|
"https://example.com/browser/browser/base/content/test/general/bug792517.sjs"
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ function triggerSave(aWindow, aCallback) {
|
|||||||
var fileName;
|
var fileName;
|
||||||
let testBrowser = aWindow.gBrowser.selectedBrowser;
|
let testBrowser = aWindow.gBrowser.selectedBrowser;
|
||||||
let testURI =
|
let testURI =
|
||||||
"http://mochi.test:8888/browser/browser/base/content/test/general/navigating_window_with_download.html";
|
"https://example.com/browser/browser/base/content/test/general/navigating_window_with_download.html";
|
||||||
|
|
||||||
// Only observe the UTC dialog if it's enabled by pref
|
// Only observe the UTC dialog if it's enabled by pref
|
||||||
if (Services.prefs.getBoolPref(ALWAYS_ASK_PREF)) {
|
if (Services.prefs.getBoolPref(ALWAYS_ASK_PREF)) {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ add_task(async function () {
|
|||||||
let loadPromise = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
|
let loadPromise = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
|
||||||
BrowserTestUtils.startLoadingURIString(
|
BrowserTestUtils.startLoadingURIString(
|
||||||
gBrowser,
|
gBrowser,
|
||||||
"http://mochi.test:8888/browser/browser/base/content/test/general/web_video.html"
|
"https://example.com/browser/browser/base/content/test/general/web_video.html"
|
||||||
);
|
);
|
||||||
await loadPromise;
|
await loadPromise;
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,6 @@
|
|||||||
<html>
|
<html>
|
||||||
<head><title>This window will navigate while you're downloading something</title></head>
|
<head><title>This window will navigate while you're downloading something</title></head>
|
||||||
<body>
|
<body>
|
||||||
<iframe src="http://mochi.test:8888/browser/browser/base/content/test/general/unknownContentType_file.pif"></iframe>
|
<iframe src="https://example.com/browser/browser/base/content/test/general/unknownContentType_file.pif"></iframe>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -696,7 +696,7 @@ export var DownloadsCommon = {
|
|||||||
message = s.unblockTypePotentiallyUnwanted2;
|
message = s.unblockTypePotentiallyUnwanted2;
|
||||||
break;
|
break;
|
||||||
case lazy.Downloads.Error.BLOCK_VERDICT_INSECURE:
|
case lazy.Downloads.Error.BLOCK_VERDICT_INSECURE:
|
||||||
message = s.unblockInsecure2;
|
message = s.unblockInsecure3;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// Assume Downloads.Error.BLOCK_VERDICT_MALWARE
|
// Assume Downloads.Error.BLOCK_VERDICT_MALWARE
|
||||||
|
|||||||
@@ -888,7 +888,7 @@ DownloadsViewUI.DownloadElementShell.prototype = {
|
|||||||
case lazy.Downloads.Error.BLOCK_VERDICT_INSECURE:
|
case lazy.Downloads.Error.BLOCK_VERDICT_INSECURE:
|
||||||
return [
|
return [
|
||||||
s.blockedPotentiallyInsecure,
|
s.blockedPotentiallyInsecure,
|
||||||
[s.unblockInsecure2, s.unblockTip2],
|
[s.unblockInsecure3, s.unblockTip2],
|
||||||
];
|
];
|
||||||
case lazy.Downloads.Error.BLOCK_VERDICT_POTENTIALLY_UNWANTED:
|
case lazy.Downloads.Error.BLOCK_VERDICT_POTENTIALLY_UNWANTED:
|
||||||
return [
|
return [
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
const TEST_ROOT = getRootDirectory(gTestPath).replace(
|
const TEST_ROOT = getRootDirectory(gTestPath).replace(
|
||||||
"chrome://mochitests/content",
|
"chrome://mochitests/content",
|
||||||
"http://example.com"
|
"https://example.com"
|
||||||
);
|
);
|
||||||
|
|
||||||
var MockFilePicker = SpecialPowers.MockFilePicker;
|
var MockFilePicker = SpecialPowers.MockFilePicker;
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ add_setup(async function () {
|
|||||||
set: [
|
set: [
|
||||||
["privacy.firstparty.isolate", true],
|
["privacy.firstparty.isolate", true],
|
||||||
["dom.security.https_first", false],
|
["dom.security.https_first", false],
|
||||||
|
["dom.block_download_insecure", false],
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ fileDeleted=File deleted
|
|||||||
# LOCALIZATION NOTE (unblockHeaderUnblock, unblockHeaderOpen,
|
# LOCALIZATION NOTE (unblockHeaderUnblock, unblockHeaderOpen,
|
||||||
# unblockTypeMalware, unblockTypePotentiallyUnwanted2,
|
# unblockTypeMalware, unblockTypePotentiallyUnwanted2,
|
||||||
# unblockTypeUncommon2, unblockTip2, unblockButtonOpen,
|
# unblockTypeUncommon2, unblockTip2, unblockButtonOpen,
|
||||||
# unblockButtonUnblock, unblockButtonConfirmBlock, unblockInsecure2):
|
# unblockButtonUnblock, unblockButtonConfirmBlock, unblockInsecure3):
|
||||||
# These strings are displayed in the dialog shown when the user asks a blocked
|
# These strings are displayed in the dialog shown when the user asks a blocked
|
||||||
# download to be unblocked. The severity of the threat is expressed in
|
# download to be unblocked. The severity of the threat is expressed in
|
||||||
# descending order by the unblockType strings, it is higher for files detected
|
# descending order by the unblockType strings, it is higher for files detected
|
||||||
@@ -57,7 +57,7 @@ unblockHeaderOpen=Are you sure you want to open this file?
|
|||||||
unblockTypeMalware=This file contains a virus or other malware that will harm your computer.
|
unblockTypeMalware=This file contains a virus or other malware that will harm your computer.
|
||||||
unblockTypePotentiallyUnwanted2=This file is disguised as a helpful download, but it can make unexpected changes to your programs and settings.
|
unblockTypePotentiallyUnwanted2=This file is disguised as a helpful download, but it can make unexpected changes to your programs and settings.
|
||||||
unblockTypeUncommon2=This file is not commonly downloaded and may not be safe to open. It may contain a virus or make unexpected changes to your programs and settings.
|
unblockTypeUncommon2=This file is not commonly downloaded and may not be safe to open. It may contain a virus or make unexpected changes to your programs and settings.
|
||||||
unblockInsecure2=The download is offered over HTTP even though the current document was delivered over a secure HTTPS connection. If you proceed, the download may be corrupted or tampered with during the download process.
|
unblockInsecure3=You are trying to download this file on a connection that’s not secure. If you continue, the file might be changed, used to steal your info or harm your device.
|
||||||
unblockTip2=You can search for an alternate download source or try again later.
|
unblockTip2=You can search for an alternate download source or try again later.
|
||||||
unblockButtonOpen=Open
|
unblockButtonOpen=Open
|
||||||
unblockButtonUnblock=Allow download
|
unblockButtonUnblock=Allow download
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ LoadingMixedActiveContent2=Loading mixed (insecure) active content “%1$S” on
|
|||||||
LoadingMixedDisplayContent2=Loading mixed (insecure) display content “%1$S” on a secure page
|
LoadingMixedDisplayContent2=Loading mixed (insecure) display content “%1$S” on a secure page
|
||||||
LoadingMixedDisplayObjectSubrequestDeprecation=Loading mixed (insecure) content “%1$S” within a plugin on a secure page is discouraged and will be blocked soon.
|
LoadingMixedDisplayObjectSubrequestDeprecation=Loading mixed (insecure) content “%1$S” within a plugin on a secure page is discouraged and will be blocked soon.
|
||||||
# LOCALIZATION NOTE: "%S" is the URI of the insecure mixed content download
|
# LOCALIZATION NOTE: "%S" is the URI of the insecure mixed content download
|
||||||
MixedContentBlockedDownload = Blocked downloading insecure content “%S”.
|
BlockedInsecureDownload = We blocked a download that’s not secure: “%S”.
|
||||||
|
|
||||||
# LOCALIZATION NOTE: Do not translate "allow-scripts", "allow-same-origin", "sandbox" or "iframe"
|
# LOCALIZATION NOTE: Do not translate "allow-scripts", "allow-same-origin", "sandbox" or "iframe"
|
||||||
BothAllowScriptsAndSameOriginPresent=An iframe which has both allow-scripts and allow-same-origin for its sandbox attribute can remove its sandboxing.
|
BothAllowScriptsAndSameOriginPresent=An iframe which has both allow-scripts and allow-same-origin for its sandbox attribute can remove its sandboxing.
|
||||||
|
|||||||
@@ -1670,37 +1670,25 @@ long nsContentSecurityUtils::ClassifyDownload(
|
|||||||
nsCOMPtr<nsIURI> contentLocation;
|
nsCOMPtr<nsIURI> contentLocation;
|
||||||
aChannel->GetURI(getter_AddRefs(contentLocation));
|
aChannel->GetURI(getter_AddRefs(contentLocation));
|
||||||
|
|
||||||
nsCOMPtr<nsIPrincipal> loadingPrincipal = loadInfo->GetLoadingPrincipal();
|
if (StaticPrefs::dom_block_download_insecure()) {
|
||||||
if (!loadingPrincipal) {
|
// If we are not dealing with a potentially trustworthy origin, or a URI
|
||||||
loadingPrincipal = loadInfo->TriggeringPrincipal();
|
// that is safe to be loaded like e.g. data:, then we block the load.
|
||||||
}
|
bool isInsecureDownload =
|
||||||
// Creating a fake Loadinfo that is just used for the MCB check.
|
!nsMixedContentBlocker::IsPotentiallyTrustworthyOrigin(
|
||||||
nsCOMPtr<nsILoadInfo> secCheckLoadInfo = new mozilla::net::LoadInfo(
|
contentLocation) &&
|
||||||
loadingPrincipal, loadInfo->TriggeringPrincipal(), nullptr,
|
!nsMixedContentBlocker::URISafeToBeLoadedInSecureContext(
|
||||||
nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK,
|
contentLocation);
|
||||||
nsIContentPolicy::TYPE_FETCH);
|
|
||||||
// Disable HTTPS-Only checks for that loadinfo. This is required because
|
|
||||||
// otherwise nsMixedContentBlocker::ShouldLoad would assume that the request
|
|
||||||
// is safe, because HTTPS-Only is handling it.
|
|
||||||
secCheckLoadInfo->SetHttpsOnlyStatus(nsILoadInfo::HTTPS_ONLY_EXEMPT);
|
|
||||||
|
|
||||||
int16_t decission = nsIContentPolicy::ACCEPT;
|
Telemetry::Accumulate(mozilla::Telemetry::INSECURE_DOWNLOADS,
|
||||||
nsMixedContentBlocker::ShouldLoad(false, // aHadInsecureImageRedirect
|
isInsecureDownload);
|
||||||
contentLocation, // aContentLocation,
|
|
||||||
secCheckLoadInfo, // aLoadinfo
|
|
||||||
false, // aReportError
|
|
||||||
&decission // aDecision
|
|
||||||
);
|
|
||||||
Telemetry::Accumulate(mozilla::Telemetry::MIXED_CONTENT_DOWNLOADS,
|
|
||||||
decission != nsIContentPolicy::ACCEPT);
|
|
||||||
|
|
||||||
if (StaticPrefs::dom_block_download_insecure() &&
|
if (isInsecureDownload) {
|
||||||
decission != nsIContentPolicy::ACCEPT) {
|
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
|
||||||
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
|
if (httpChannel) {
|
||||||
if (httpChannel) {
|
LogMessageToConsole(httpChannel, "BlockedInsecureDownload");
|
||||||
LogMessageToConsole(httpChannel, "MixedContentBlockedDownload");
|
}
|
||||||
|
return nsITransfer::DOWNLOAD_POTENTIALLY_UNSAFE;
|
||||||
}
|
}
|
||||||
return nsITransfer::DOWNLOAD_POTENTIALLY_UNSAFE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loadInfo->TriggeringPrincipal()->IsSystemPrincipal()) {
|
if (loadInfo->TriggeringPrincipal()->IsSystemPrincipal()) {
|
||||||
|
|||||||
@@ -48,6 +48,16 @@ support-files = [
|
|||||||
"file_gpc_server.sjs",
|
"file_gpc_server.sjs",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
["browser_test_http_download.js"]
|
||||||
|
skip-if = [
|
||||||
|
"win11_2009", # Bug 1784764
|
||||||
|
"os == 'linux' && !debug",
|
||||||
|
]
|
||||||
|
support-files = [
|
||||||
|
"http_download_page.html",
|
||||||
|
"http_download_server.sjs"
|
||||||
|
]
|
||||||
|
|
||||||
["browser_test_referrer_loadInOtherProcess.js"]
|
["browser_test_referrer_loadInOtherProcess.js"]
|
||||||
|
|
||||||
["browser_test_report_blocking.js"]
|
["browser_test_report_blocking.js"]
|
||||||
|
|||||||
275
dom/security/test/general/browser_test_http_download.js
Normal file
275
dom/security/test/general/browser_test_http_download.js
Normal file
@@ -0,0 +1,275 @@
|
|||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
* https://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
ChromeUtils.defineESModuleGetters(this, {
|
||||||
|
Downloads: "resource://gre/modules/Downloads.sys.mjs",
|
||||||
|
DownloadsCommon: "resource:///modules/DownloadsCommon.sys.mjs",
|
||||||
|
});
|
||||||
|
|
||||||
|
const HandlerService = Cc[
|
||||||
|
"@mozilla.org/uriloader/handler-service;1"
|
||||||
|
].getService(Ci.nsIHandlerService);
|
||||||
|
|
||||||
|
const MIMEService = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService);
|
||||||
|
|
||||||
|
// Using insecure HTTP URL for a test cases around HTTP downloads
|
||||||
|
let INSECURE_BASE_URL =
|
||||||
|
getRootDirectory(gTestPath).replace(
|
||||||
|
"chrome://mochitests/content/",
|
||||||
|
// eslint-disable-next-line @microsoft/sdl/no-insecure-url
|
||||||
|
"http://example.com/"
|
||||||
|
) + "http_download_page.html";
|
||||||
|
|
||||||
|
function promiseFocus() {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
waitForFocus(resolve);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function task_openPanel() {
|
||||||
|
await promiseFocus();
|
||||||
|
|
||||||
|
let promise = BrowserTestUtils.waitForPopupEvent(
|
||||||
|
DownloadsPanel.panel,
|
||||||
|
"shown"
|
||||||
|
);
|
||||||
|
DownloadsPanel.showPanel();
|
||||||
|
await promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
const downloadMonitoringView = {
|
||||||
|
_listeners: [],
|
||||||
|
onDownloadAdded(download) {
|
||||||
|
for (let listener of this._listeners) {
|
||||||
|
listener(download);
|
||||||
|
}
|
||||||
|
this._listeners = [];
|
||||||
|
},
|
||||||
|
waitForDownload(listener) {
|
||||||
|
this._listeners.push(listener);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Waits until a download is triggered.
|
||||||
|
* Unless the always_ask_before_handling_new_types pref is true, the download
|
||||||
|
* will simply be saved, so resolve when the view is notified of the new
|
||||||
|
* download. Otherwise, it waits until a prompt is shown, selects the choosen
|
||||||
|
* <action>, then accepts the dialog
|
||||||
|
* @param [action] Which action to select, either:
|
||||||
|
* "handleInternally", "save" or "open".
|
||||||
|
* @returns {Promise} Resolved once done.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function shouldTriggerDownload(action = "save") {
|
||||||
|
if (
|
||||||
|
Services.prefs.getBoolPref(
|
||||||
|
"browser.download.always_ask_before_handling_new_types"
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
Services.wm.addListener({
|
||||||
|
onOpenWindow(xulWin) {
|
||||||
|
Services.wm.removeListener(this);
|
||||||
|
let win = xulWin.docShell.domWindow;
|
||||||
|
waitForFocus(() => {
|
||||||
|
if (
|
||||||
|
win.location ==
|
||||||
|
"chrome://mozapps/content/downloads/unknownContentType.xhtml"
|
||||||
|
) {
|
||||||
|
let dialog = win.document.getElementById("unknownContentType");
|
||||||
|
let button = dialog.getButton("accept");
|
||||||
|
let actionRadio = win.document.getElementById(action);
|
||||||
|
actionRadio.click();
|
||||||
|
button.disabled = false;
|
||||||
|
dialog.acceptDialog();
|
||||||
|
resolve();
|
||||||
|
} else {
|
||||||
|
reject();
|
||||||
|
}
|
||||||
|
}, win);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return new Promise(res => {
|
||||||
|
downloadMonitoringView.waitForDownload(res);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const CONSOLE_ERROR_MESSAGE = "We blocked a download that’s not secure";
|
||||||
|
|
||||||
|
function shouldConsoleError() {
|
||||||
|
// Waits until CONSOLE_ERROR_MESSAGE was logged
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
function listener(msgObj) {
|
||||||
|
let text = msgObj.message;
|
||||||
|
if (text.includes(CONSOLE_ERROR_MESSAGE)) {
|
||||||
|
Services.console.unregisterListener(listener);
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Services.console.registerListener(listener);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function resetDownloads() {
|
||||||
|
// Removes all downloads from the download List
|
||||||
|
const types = new Set();
|
||||||
|
let publicList = await Downloads.getList(Downloads.PUBLIC);
|
||||||
|
let downloads = await publicList.getAll();
|
||||||
|
for (let download of downloads) {
|
||||||
|
if (download.contentType) {
|
||||||
|
types.add(download.contentType);
|
||||||
|
}
|
||||||
|
publicList.remove(download);
|
||||||
|
await download.finalize(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (types.size) {
|
||||||
|
// reset handlers for the contentTypes of any files previously downloaded
|
||||||
|
for (let type of types) {
|
||||||
|
const mimeInfo = MIMEService.getFromTypeAndExtension(type, "");
|
||||||
|
info("resetting handler for type: " + type);
|
||||||
|
HandlerService.remove(mimeInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function shouldNotifyDownloadUI() {
|
||||||
|
return new Promise(res => {
|
||||||
|
downloadMonitoringView.waitForDownload(async aDownload => {
|
||||||
|
let { error } = aDownload;
|
||||||
|
if (
|
||||||
|
error.becauseBlockedByReputationCheck &&
|
||||||
|
error.reputationCheckVerdict == Downloads.Error.BLOCK_VERDICT_INSECURE
|
||||||
|
) {
|
||||||
|
// It's an insecure Download, now Check that it has been cleaned up properly
|
||||||
|
if ((await IOUtils.stat(aDownload.target.path)).size != 0) {
|
||||||
|
throw new Error(`Download target is not empty!`);
|
||||||
|
}
|
||||||
|
if ((await IOUtils.stat(aDownload.target.path)).size != 0) {
|
||||||
|
throw new Error(`Download partFile was not cleaned up properly`);
|
||||||
|
}
|
||||||
|
// Assert that the Referrer is presnt
|
||||||
|
if (!aDownload.source.referrerInfo) {
|
||||||
|
throw new Error("The Blocked download is missing the ReferrerInfo");
|
||||||
|
}
|
||||||
|
|
||||||
|
res(aDownload);
|
||||||
|
} else {
|
||||||
|
ok(false, "No error for download that was expected to error!");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runTest(url, link, checkFunction, description) {
|
||||||
|
await SpecialPowers.pushPrefEnv({
|
||||||
|
set: [["dom.block_download_insecure", true]],
|
||||||
|
});
|
||||||
|
await resetDownloads();
|
||||||
|
|
||||||
|
let tab = BrowserTestUtils.addTab(gBrowser, url);
|
||||||
|
gBrowser.selectedTab = tab;
|
||||||
|
|
||||||
|
let browser = gBrowser.getBrowserForTab(tab);
|
||||||
|
await BrowserTestUtils.browserLoaded(browser);
|
||||||
|
|
||||||
|
info("Checking: " + description);
|
||||||
|
|
||||||
|
let checkPromise = checkFunction();
|
||||||
|
// Click the Link to trigger the download
|
||||||
|
SpecialPowers.spawn(gBrowser.selectedBrowser, [link], contentLink => {
|
||||||
|
content.document.getElementById(contentLink).click();
|
||||||
|
});
|
||||||
|
|
||||||
|
await checkPromise;
|
||||||
|
|
||||||
|
ok(true, description);
|
||||||
|
BrowserTestUtils.removeTab(tab);
|
||||||
|
|
||||||
|
await SpecialPowers.popPrefEnv();
|
||||||
|
}
|
||||||
|
|
||||||
|
add_setup(async () => {
|
||||||
|
let list = await Downloads.getList(Downloads.ALL);
|
||||||
|
list.addView(downloadMonitoringView);
|
||||||
|
registerCleanupFunction(() => list.removeView(downloadMonitoringView));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test Blocking
|
||||||
|
add_task(async function test_blocking() {
|
||||||
|
for (let prefVal of [true, false]) {
|
||||||
|
await SpecialPowers.pushPrefEnv({
|
||||||
|
set: [["browser.download.always_ask_before_handling_new_types", prefVal]],
|
||||||
|
});
|
||||||
|
await runTest(
|
||||||
|
INSECURE_BASE_URL,
|
||||||
|
"http-link",
|
||||||
|
() =>
|
||||||
|
Promise.all([
|
||||||
|
shouldTriggerDownload(),
|
||||||
|
shouldNotifyDownloadUI(),
|
||||||
|
shouldConsoleError(),
|
||||||
|
]),
|
||||||
|
"Insecure (HTTP) toplevel -> Insecure (HTTP) download should Error"
|
||||||
|
);
|
||||||
|
await SpecialPowers.popPrefEnv();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test Manual Unblocking
|
||||||
|
add_task(async function test_manual_unblocking() {
|
||||||
|
for (let prefVal of [true, false]) {
|
||||||
|
await SpecialPowers.pushPrefEnv({
|
||||||
|
set: [["browser.download.always_ask_before_handling_new_types", prefVal]],
|
||||||
|
});
|
||||||
|
await runTest(
|
||||||
|
INSECURE_BASE_URL,
|
||||||
|
"http-link",
|
||||||
|
async () => {
|
||||||
|
let [, download] = await Promise.all([
|
||||||
|
shouldTriggerDownload(),
|
||||||
|
shouldNotifyDownloadUI(),
|
||||||
|
]);
|
||||||
|
await download.unblock();
|
||||||
|
Assert.equal(
|
||||||
|
download.error,
|
||||||
|
null,
|
||||||
|
"There should be no error after unblocking"
|
||||||
|
);
|
||||||
|
},
|
||||||
|
"A blocked download should succeed to download after a manual unblock"
|
||||||
|
);
|
||||||
|
await SpecialPowers.popPrefEnv();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test Unblock Download Visible
|
||||||
|
add_task(async function test_unblock_download_visible() {
|
||||||
|
for (let prefVal of [true, false]) {
|
||||||
|
await SpecialPowers.pushPrefEnv({
|
||||||
|
set: [["browser.download.always_ask_before_handling_new_types", prefVal]],
|
||||||
|
});
|
||||||
|
await promiseFocus();
|
||||||
|
await runTest(
|
||||||
|
INSECURE_BASE_URL,
|
||||||
|
"http-link",
|
||||||
|
async () => {
|
||||||
|
let panelHasOpened = BrowserTestUtils.waitForPopupEvent(
|
||||||
|
DownloadsPanel.panel,
|
||||||
|
"shown"
|
||||||
|
);
|
||||||
|
info("awaiting that the download is triggered and added to the list");
|
||||||
|
await Promise.all([shouldTriggerDownload(), shouldNotifyDownloadUI()]);
|
||||||
|
info("awaiting that the Download list shows itself");
|
||||||
|
await panelHasOpened;
|
||||||
|
DownloadsPanel.hidePanel();
|
||||||
|
ok(true, "The Download Panel should have opened on blocked download");
|
||||||
|
},
|
||||||
|
"A blocked download should open the download panel"
|
||||||
|
);
|
||||||
|
await SpecialPowers.popPrefEnv();
|
||||||
|
}
|
||||||
|
});
|
||||||
23
dom/security/test/general/http_download_page.html
Normal file
23
dom/security/test/general/http_download_page.html
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Test for the download attribute</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
hi
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const host = window.location.host;
|
||||||
|
const path = location.pathname.replace("http_download_page.html","http_download_server.sjs");
|
||||||
|
|
||||||
|
const insecureLink = document.createElement("a");
|
||||||
|
// eslint-disable-next-line @microsoft/sdl/no-insecure-url
|
||||||
|
insecureLink.href=`http://${host}/${path}`;
|
||||||
|
insecureLink.download="true";
|
||||||
|
insecureLink.id="http-link";
|
||||||
|
insecureLink.textContent="Not secure Link";
|
||||||
|
|
||||||
|
document.body.append(insecureLink);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
20
dom/security/test/general/http_download_server.sjs
Normal file
20
dom/security/test/general/http_download_server.sjs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
// force the Browser to Show a Download Prompt
|
||||||
|
|
||||||
|
function handleRequest(request, response) {
|
||||||
|
let type = "image/png";
|
||||||
|
let filename = "hello.png";
|
||||||
|
request.queryString.split("&").forEach(val => {
|
||||||
|
var [key, value] = val.split("=");
|
||||||
|
if (key == "type") {
|
||||||
|
type = value;
|
||||||
|
}
|
||||||
|
if (key == "name") {
|
||||||
|
filename = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
response.setHeader("Cache-Control", "no-cache", false);
|
||||||
|
response.setHeader("Content-Disposition", `attachment; filename=${filename}`);
|
||||||
|
response.setHeader("Content-Type", type);
|
||||||
|
response.write("🙈🙊🐵🙊");
|
||||||
|
}
|
||||||
@@ -155,7 +155,11 @@ async function setHttpsFirstAndOnlyPrefs(httpsFirst, httpsOnly) {
|
|||||||
add_task(async function testBaseline() {
|
add_task(async function testBaseline() {
|
||||||
// Run with HTTPS-First and HTTPS-Only disabled
|
// Run with HTTPS-First and HTTPS-Only disabled
|
||||||
await setHttpsFirstAndOnlyPrefs(false, false);
|
await setHttpsFirstAndOnlyPrefs(false, false);
|
||||||
await runTest("#insecure-link", HTTP_LINK, undefined);
|
await runTest(
|
||||||
|
"#insecure-link",
|
||||||
|
HTTP_LINK,
|
||||||
|
"We blocked a download that’s not secure: “http://example.org/”."
|
||||||
|
);
|
||||||
await runTest("#secure-link", HTTPS_LINK, undefined);
|
await runTest("#secure-link", HTTPS_LINK, undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -169,7 +173,7 @@ add_task(async function testHttpsFirst() {
|
|||||||
await runTest(
|
await runTest(
|
||||||
"#insecure-link",
|
"#insecure-link",
|
||||||
HTTP_LINK,
|
HTTP_LINK,
|
||||||
"Blocked downloading insecure content “http://example.org/”."
|
"We blocked a download that’s not secure: “http://example.org/”."
|
||||||
);
|
);
|
||||||
await runTest("#secure-link", HTTPS_LINK, undefined);
|
await runTest("#secure-link", HTTPS_LINK, undefined);
|
||||||
});
|
});
|
||||||
@@ -181,7 +185,7 @@ add_task(async function testHttpsOnly() {
|
|||||||
await runTest(
|
await runTest(
|
||||||
"#insecure-link",
|
"#insecure-link",
|
||||||
HTTP_LINK,
|
HTTP_LINK,
|
||||||
"Blocked downloading insecure content “http://example.org/”."
|
"We blocked a download that’s not secure: “http://example.org/”."
|
||||||
);
|
);
|
||||||
await runTest("#secure-link", HTTPS_LINK, undefined);
|
await runTest("#secure-link", HTTPS_LINK, undefined);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ function shouldTriggerDownload(action = "save") {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const CONSOLE_ERROR_MESSAGE = "Blocked downloading insecure content";
|
const CONSOLE_ERROR_MESSAGE = "We blocked a download that’s not secure";
|
||||||
|
|
||||||
function shouldConsoleError() {
|
function shouldConsoleError() {
|
||||||
// Waits until CONSOLE_ERROR_MESSAGE was logged
|
// Waits until CONSOLE_ERROR_MESSAGE was logged
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
var gTestRoot = getRootDirectory(gTestPath).replace(
|
var gTestRoot = getRootDirectory(gTestPath).replace(
|
||||||
"chrome://mochitests/content/",
|
"chrome://mochitests/content/",
|
||||||
"http://mochi.test:8888/"
|
"https://example.com/"
|
||||||
);
|
);
|
||||||
|
|
||||||
function getFile(aFilename) {
|
function getFile(aFilename) {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ skip-if = [
|
|||||||
prefs = [
|
prefs = [
|
||||||
"security.mixed_content.upgrade_display_content=false",
|
"security.mixed_content.upgrade_display_content=false",
|
||||||
"dom.security.https_first=false",
|
"dom.security.https_first=false",
|
||||||
|
"dom.block_download_insecure=false",
|
||||||
]
|
]
|
||||||
support-files = [
|
support-files = [
|
||||||
"alloworigin.sjs",
|
"alloworigin.sjs",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
[iframe_sandbox_navigation_download_allow_downloads.sub.tentative.html]
|
[iframe_sandbox_navigation_download_allow_downloads.sub.tentative.https.html]
|
||||||
expected:
|
expected:
|
||||||
if (os == "linux") and not fission: [OK, TIMEOUT]
|
if (os == "linux") and not fission: [OK, TIMEOUT]
|
||||||
if (os == "android") and fission: [TIMEOUT, OK]
|
if (os == "android") and fission: [TIMEOUT, OK]
|
||||||
@@ -102,6 +102,7 @@ add_task(async function testContextMenuSaveImage() {
|
|||||||
set: [
|
set: [
|
||||||
["privacy.partition.network_state", networkIsolation],
|
["privacy.partition.network_state", networkIsolation],
|
||||||
["privacy.dynamic_firstparty.use_site", partitionPerSite],
|
["privacy.dynamic_firstparty.use_site", partitionPerSite],
|
||||||
|
["dom.block_download_insecure", false],
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -197,6 +198,7 @@ add_task(async function testContextMenuSaveVideo() {
|
|||||||
set: [
|
set: [
|
||||||
["privacy.partition.network_state", networkIsolation],
|
["privacy.partition.network_state", networkIsolation],
|
||||||
["privacy.dynamic_firstparty.use_site", partitionPerSite],
|
["privacy.dynamic_firstparty.use_site", partitionPerSite],
|
||||||
|
["dom.block_download_insecure", false],
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<html>
|
<html>
|
||||||
<body>
|
<body>
|
||||||
<img src="http://example.net/browser/toolkit/components/antitracking/test/browser/raptor.jpg" id="image1">
|
<img src="https://example.net/browser/toolkit/components/antitracking/test/browser/raptor.jpg" id="image1">
|
||||||
<video src="http://example.net/browser/toolkit/components/antitracking/test/browser/file_video.ogv" id="video1"> </video>
|
<video src="https://example.net/browser/toolkit/components/antitracking/test/browser/file_video.ogv" id="video1"> </video>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -11,6 +11,8 @@
|
|||||||
|
|
||||||
// Execution of common tests
|
// Execution of common tests
|
||||||
|
|
||||||
|
Services.prefs.setBoolPref("dom.block_download_insecure", false);
|
||||||
|
|
||||||
// This is used in common_test_Download.js
|
// This is used in common_test_Download.js
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
var gUseLegacySaver = true;
|
var gUseLegacySaver = true;
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const URL_PATH = "browser/toolkit/components/extensions/test/browser/data";
|
const URL_PATH = "browser/toolkit/components/extensions/test/browser/data";
|
||||||
const TEST_URL = `http://example.com/${URL_PATH}/test_downloads_referrer.html`;
|
const TEST_URL = `https://example.com/${URL_PATH}/test_downloads_referrer.html`;
|
||||||
const DOWNLOAD_URL = `http://example.com/${URL_PATH}/test-download.txt`;
|
const DOWNLOAD_URL = `https://example.com/${URL_PATH}/test-download.txt`;
|
||||||
|
|
||||||
async function triggerSaveAs({ selector }) {
|
async function triggerSaveAs({ selector }) {
|
||||||
const contextMenu = window.document.getElementById("contentAreaContextMenu");
|
const contextMenu = window.document.getElementById("contentAreaContextMenu");
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
const TESTROOT = getRootDirectory(gTestPath).replace(
|
const TESTROOT = getRootDirectory(gTestPath).replace(
|
||||||
"chrome://mochitests/content/",
|
"chrome://mochitests/content/",
|
||||||
"http://mochi.test:8888/"
|
"https://example.com/"
|
||||||
);
|
);
|
||||||
|
|
||||||
// Get a ref to the pdf we want to open.
|
// Get a ref to the pdf we want to open.
|
||||||
|
|||||||
@@ -12347,14 +12347,14 @@
|
|||||||
"n_values": 10,
|
"n_values": 10,
|
||||||
"description": "How often would blocked mixed content be allowed if HSTS upgrades were allowed? 0=display/no-HSTS, 1=display/HSTS, 2=active/no-HSTS, 3=active/HSTS"
|
"description": "How often would blocked mixed content be allowed if HSTS upgrades were allowed? 0=display/no-HSTS, 1=display/HSTS, 2=active/no-HSTS, 3=active/HSTS"
|
||||||
},
|
},
|
||||||
"MIXED_CONTENT_DOWNLOADS": {
|
"INSECURE_DOWNLOADS": {
|
||||||
"record_in_processes": ["main", "content"],
|
"record_in_processes": ["main", "content"],
|
||||||
"products": ["firefox"],
|
"products": ["firefox"],
|
||||||
"alert_emails": ["seceng-telemetry@mozilla.com", "sstreich@mozilla.com"],
|
"alert_emails": ["seceng-telemetry@mozilla.com", "ckerschb@mozilla.com"],
|
||||||
"bug_numbers": [1646768],
|
"bug_numbers": [1877195],
|
||||||
"expires_in_version": "90",
|
"expires_in_version": "130",
|
||||||
"kind": "boolean",
|
"kind": "boolean",
|
||||||
"description": "Accumulates how many downloads are mixed-content (True = The download is MixedContent, False= is not MixedContent)"
|
"description": "Accumulates how many downloads are insecure (True = The download is insecure, False= The download is secure)"
|
||||||
},
|
},
|
||||||
"MIXED_CONTENT_IMAGES": {
|
"MIXED_CONTENT_IMAGES": {
|
||||||
"record_in_processes": ["main", "content"],
|
"record_in_processes": ["main", "content"],
|
||||||
|
|||||||
@@ -24,7 +24,10 @@ server.registerFile(`/${encodeURIComponent(TEST_FILE)}`, file);
|
|||||||
*/
|
*/
|
||||||
add_task(async function test_idn_blocklisted_char_not_escaped() {
|
add_task(async function test_idn_blocklisted_char_not_escaped() {
|
||||||
await SpecialPowers.pushPrefEnv({
|
await SpecialPowers.pushPrefEnv({
|
||||||
set: [["browser.download.always_ask_before_handling_new_types", false]],
|
set: [
|
||||||
|
["browser.download.always_ask_before_handling_new_types", false],
|
||||||
|
["dom.block_download_insecure", false],
|
||||||
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
info("Testing with " + TEST_URL);
|
info("Testing with " + TEST_URL);
|
||||||
|
|||||||
Reference in New Issue
Block a user