зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1614969 - Check download with MixedContentBlocker r=ckerschb
Differential Revision: https://phabricator.services.mozilla.com/D73302
This commit is contained in:
Родитель
147fdbb684
Коммит
5bb89780fa
|
@ -45,6 +45,9 @@ InsecurePasswordsPresentOnIframe=Password fields present on an insecure (http://
|
|||
LoadingMixedActiveContent2=Loading mixed (insecure) active 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.
|
||||
# LOCALIZATION NOTE: "%S" is the URI of the insecure mixed content download
|
||||
MixedContentBlockedDownload = Blocked downloading insecure content “%S”.
|
||||
|
||||
# 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.
|
||||
# LOCALIZATION NOTE: Do not translate "allow-top-navigation-by-user-activation", "allow-top-navigation", "sandbox" or "iframe"
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "mozilla/ExtensionPolicyService.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "mozilla/dom/Document.h"
|
||||
#include "LoadInfo.h"
|
||||
#include "mozilla/StaticPrefs_extensions.h"
|
||||
#include "mozilla/StaticPrefs_dom.h"
|
||||
|
||||
|
@ -1039,3 +1040,77 @@ bool nsContentSecurityUtils::ValidateScriptFilename(const char* aFilename,
|
|||
// builds and return false to prevent execution in non-debug builds.
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */
|
||||
void nsContentSecurityUtils::LogMessageToConsole(nsIHttpChannel* aChannel,
|
||||
const char* aMsg) {
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsresult rv = aChannel->GetURI(getter_AddRefs(uri));
|
||||
if (NS_FAILED(rv)) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t windowID = 0;
|
||||
rv = aChannel->GetTopLevelContentWindowId(&windowID);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
if (!windowID) {
|
||||
nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
|
||||
loadInfo->GetInnerWindowID(&windowID);
|
||||
}
|
||||
|
||||
nsAutoString localizedMsg;
|
||||
nsAutoCString spec;
|
||||
uri->GetSpec(spec);
|
||||
AutoTArray<nsString, 1> params = {NS_ConvertUTF8toUTF16(spec)};
|
||||
rv = nsContentUtils::FormatLocalizedString(
|
||||
nsContentUtils::eSECURITY_PROPERTIES, aMsg, params, localizedMsg);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsContentUtils::ReportToConsoleByWindowID(
|
||||
localizedMsg, nsIScriptError::warningFlag, NS_LITERAL_CSTRING("Security"),
|
||||
windowID, uri);
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool nsContentSecurityUtils::IsDownloadAllowed(
|
||||
nsIChannel* aChannel, const nsAutoCString& aMimeTypeGuess) {
|
||||
MOZ_ASSERT(aChannel, "IsDownloadAllowed without channel?");
|
||||
if (!StaticPrefs::dom_block_download_insecure()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
|
||||
if (loadInfo->TriggeringPrincipal()->IsSystemPrincipal()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> contentLocation;
|
||||
aChannel->GetURI(getter_AddRefs(contentLocation));
|
||||
|
||||
nsCOMPtr<nsIPrincipal> loadingPrincipal = loadInfo->GetLoadingPrincipal();
|
||||
// Creating a fake Loadinfo that is just used for the MCB check.
|
||||
nsCOMPtr<nsILoadInfo> secCheckLoadInfo =
|
||||
new LoadInfo(loadingPrincipal, loadInfo->TriggeringPrincipal(), nullptr,
|
||||
nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK,
|
||||
nsIContentPolicy::TYPE_OTHER);
|
||||
|
||||
int16_t decission = nsIContentPolicy::ACCEPT;
|
||||
nsMixedContentBlocker::ShouldLoad(false, // aHadInsecureImageRedirect
|
||||
contentLocation, // aContentLocation,
|
||||
secCheckLoadInfo, // aLoadinfo
|
||||
aMimeTypeGuess, // aMimeGuess,
|
||||
&decission // aDecision
|
||||
);
|
||||
if (decission == nsIContentPolicy::ACCEPT) {
|
||||
return true;
|
||||
}
|
||||
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
|
||||
if (httpChannel) {
|
||||
LogMessageToConsole(httpChannel, "MixedContentBlockedDownload");
|
||||
}
|
||||
return false;
|
||||
}
|
|
@ -50,6 +50,12 @@ class nsContentSecurityUtils {
|
|||
// If any of the two disallows framing, the channel will be cancelled.
|
||||
static void PerformCSPFrameAncestorAndXFOCheck(nsIChannel* aChannel);
|
||||
|
||||
// Helper function to Check if a Download is allowed;
|
||||
static bool IsDownloadAllowed(nsIChannel* aChannel,
|
||||
const nsAutoCString& aMimeTypeGuess);
|
||||
// Logs an Error Message to the Console
|
||||
static void LogMessageToConsole(nsIHttpChannel* aChannel, const char* aMsg);
|
||||
|
||||
#if defined(DEBUG)
|
||||
static void AssertAboutPageHasCSP(mozilla::dom::Document* aDocument);
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
[DEFAULT]
|
||||
support-files =
|
||||
download_page.html
|
||||
download_server.sjs
|
||||
|
||||
[browser_test_mixed_content_download.js]
|
|
@ -0,0 +1,98 @@
|
|||
let INSECURE_BASE_URL =
|
||||
getRootDirectory(gTestPath).replace(
|
||||
"chrome://mochitests/content/",
|
||||
"http://example.com/"
|
||||
) + "download_page.html";
|
||||
let SECURE_BASE_URL =
|
||||
getRootDirectory(gTestPath).replace(
|
||||
"chrome://mochitests/content/",
|
||||
"https://example.com/"
|
||||
) + "download_page.html";
|
||||
|
||||
function shouldPromptDownload() {
|
||||
// Waits until the download Prompt is shown
|
||||
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"
|
||||
) {
|
||||
resolve();
|
||||
info("Trying to close window");
|
||||
win.close();
|
||||
} else {
|
||||
reject();
|
||||
}
|
||||
}, win);
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const CONSOLE_ERROR_MESSAGE = "was blocked because it was insecure.";
|
||||
|
||||
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 runTest(url, link, check, decscription) {
|
||||
let tab = BrowserTestUtils.addTab(gBrowser, url);
|
||||
gBrowser.selectedTab = tab;
|
||||
|
||||
let browser = gBrowser.getBrowserForTab(tab);
|
||||
await BrowserTestUtils.browserLoaded(browser);
|
||||
|
||||
info("Checking: " + decscription);
|
||||
|
||||
let checkPromise = check();
|
||||
// Click the Link to trigger the download
|
||||
SpecialPowers.spawn(gBrowser.selectedBrowser, [link], contentLink => {
|
||||
content.document.getElementById(contentLink).click();
|
||||
});
|
||||
|
||||
await checkPromise;
|
||||
|
||||
ok(true, decscription);
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
}
|
||||
|
||||
add_task(async function() {
|
||||
await runTest(
|
||||
INSECURE_BASE_URL,
|
||||
"insecure",
|
||||
shouldPromptDownload,
|
||||
"Insecure -> Insecure should download"
|
||||
);
|
||||
await runTest(
|
||||
INSECURE_BASE_URL,
|
||||
"secure",
|
||||
shouldPromptDownload,
|
||||
"Insecure -> Secure should download"
|
||||
);
|
||||
await runTest(
|
||||
SECURE_BASE_URL,
|
||||
"insecure",
|
||||
shouldConsoleError,
|
||||
"Secure -> Insecure should Error"
|
||||
);
|
||||
await runTest(
|
||||
SECURE_BASE_URL,
|
||||
"secure",
|
||||
shouldPromptDownload,
|
||||
"Secure -> Secure should Download"
|
||||
);
|
||||
});
|
|
@ -0,0 +1,35 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=676619
|
||||
-->
|
||||
<head>
|
||||
|
||||
<title>Test for the download attribute</title>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
hi
|
||||
|
||||
<script>
|
||||
const host = window.location.host;
|
||||
const path = location.pathname.replace("download_page.html","download_server.sjs");
|
||||
|
||||
const secureLink = document.createElement("a");
|
||||
secureLink.href=`https://${host}/${path}`;
|
||||
secureLink.download="true";
|
||||
secureLink.textContent="Secure Link";
|
||||
|
||||
secureLink.id="secure";
|
||||
|
||||
const insecureLink = document.createElement("a");
|
||||
insecureLink.href=`http://${host}/${path}`;
|
||||
insecureLink.download="true";
|
||||
insecureLink.id="insecure";
|
||||
insecureLink.textContent="Not secure Link";
|
||||
|
||||
document.body.append(secureLink);
|
||||
document.body.append(insecureLink);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,9 @@
|
|||
// force the Browser to Show a Download Prompt
|
||||
|
||||
function handleRequest(request, response)
|
||||
{
|
||||
response.setHeader("Cache-Control", "no-cache", false);
|
||||
response.setHeader("Content-Disposition", "attachment");
|
||||
response.setHeader("Content-Type", "application/octet-stream");
|
||||
response.write('🙈🙊🐵🙊');
|
||||
}
|
|
@ -34,4 +34,5 @@ BROWSER_CHROME_MANIFESTS += [
|
|||
'csp/browser.ini',
|
||||
'general/browser.ini',
|
||||
'https-only/browser.ini',
|
||||
'mixedcontentblocker/browser.ini'
|
||||
]
|
||||
|
|
|
@ -1506,6 +1506,12 @@
|
|||
value: true
|
||||
mirror: always
|
||||
|
||||
# Block Insecure downloads from Secure Origins
|
||||
- name: dom.block_download_insecure
|
||||
type: bool
|
||||
value: @IS_NIGHTLY_BUILD@
|
||||
mirror: always
|
||||
|
||||
# Block multiple window.open() per single event.
|
||||
- name: dom.block_multiple_popups
|
||||
type: bool
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
#include "nsIRedirectHistoryEntry.h"
|
||||
#include "nsOSHelperAppService.h"
|
||||
#include "nsOSHelperAppServiceChild.h"
|
||||
#include "nsContentSecurityUtils.h"
|
||||
|
||||
// used to access our datastore of user-configured helper applications
|
||||
#include "nsIHandlerService.h"
|
||||
|
@ -1557,6 +1558,14 @@ NS_IMETHODIMP nsExternalAppHandler::OnStartRequest(nsIRequest* request) {
|
|||
nsCOMPtr<nsIChannel> aChannel = do_QueryInterface(request);
|
||||
|
||||
nsresult rv;
|
||||
nsAutoCString MIMEType;
|
||||
mMimeInfo->GetMIMEType(MIMEType);
|
||||
|
||||
if (!nsContentSecurityUtils::IsDownloadAllowed(aChannel, MIMEType)) {
|
||||
mCanceled = true;
|
||||
request->Cancel(NS_ERROR_ABORT);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIFileChannel> fileChan(do_QueryInterface(request));
|
||||
mIsFileChannel = fileChan != nullptr;
|
||||
|
@ -1577,7 +1586,6 @@ NS_IMETHODIMP nsExternalAppHandler::OnStartRequest(nsIRequest* request) {
|
|||
if (mBrowsingContext) {
|
||||
mMaybeCloseWindowHelper = new MaybeCloseWindowHelper(mBrowsingContext);
|
||||
mMaybeCloseWindowHelper->SetShouldCloseWindow(mShouldCloseWindow);
|
||||
|
||||
nsCOMPtr<nsIPropertyBag2> props(do_QueryInterface(request, &rv));
|
||||
// Determine whether a new window was opened specifically for this request
|
||||
if (props) {
|
||||
|
@ -1666,8 +1674,6 @@ NS_IMETHODIMP nsExternalAppHandler::OnStartRequest(nsIRequest* request) {
|
|||
|
||||
bool alwaysAsk = true;
|
||||
mMimeInfo->GetAlwaysAskBeforeHandling(&alwaysAsk);
|
||||
nsAutoCString MIMEType;
|
||||
mMimeInfo->GetMIMEType(MIMEType);
|
||||
if (alwaysAsk) {
|
||||
// But we *don't* ask if this mimeInfo didn't come from
|
||||
// our user configuration datastore and the user has said
|
||||
|
|
Загрузка…
Ссылка в новой задаче