Bug 713980 - Log warnings about blocked cross-site requests to the Web Console. r=smaug

This commit is contained in:
Garrett Robinson 2014-01-07 09:51:05 -05:00
parent f98dd49b0f
commit d204ea98fa
5 changed files with 181 additions and 2 deletions

View File

@ -4444,6 +4444,7 @@ var Utils = {
case "Invalid HSTS Headers":
case "Insecure Password Field":
case "SSL":
case "CORS":
return CATEGORY_SECURITY;
default:

View File

@ -1,4 +1,4 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* -*- Mode: C++; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@ -26,6 +26,12 @@
#include "nsHashKeys.h"
#include "nsStreamUtils.h"
#include "mozilla/Preferences.h"
#include "nsIScriptError.h"
#include "nsILoadGroup.h"
#include "nsILoadContext.h"
#include "nsIConsoleService.h"
#include "nsIDOMWindowUtils.h"
#include "nsIDOMWindow.h"
#include <algorithm>
using namespace mozilla;
@ -35,6 +41,75 @@ using namespace mozilla;
static bool gDisableCORS = false;
static bool gDisableCORSPrivateData = false;
static nsresult
LogBlockedRequest(nsIRequest* aRequest)
{
nsresult rv = NS_OK;
// Get the innerWindowID associated with the XMLHTTPRequest
PRUint64 innerWindowID = 0;
nsCOMPtr<nsILoadGroup> loadGroup;
aRequest->GetLoadGroup(getter_AddRefs(loadGroup));
if (loadGroup) {
nsCOMPtr<nsIInterfaceRequestor> callbacks;
loadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
if (callbacks) {
nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
if(loadContext) {
nsCOMPtr<nsIDOMWindow> window;
loadContext->GetAssociatedWindow(getter_AddRefs(window));
if (window) {
nsCOMPtr<nsIDOMWindowUtils> du = do_GetInterface(window);
du->GetCurrentInnerWindowID(&innerWindowID);
}
}
}
}
if (!innerWindowID) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
nsCOMPtr<nsIURI> aUri;
channel->GetURI(getter_AddRefs(aUri));
nsAutoCString spec;
if (aUri) {
aUri->GetSpec(spec);
}
// Generate the error message
nsXPIDLString blockedMessage;
NS_ConvertUTF8toUTF16 specUTF16(spec);
const PRUnichar* params[] = { specUTF16.get() };
rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eSECURITY_PROPERTIES,
"CrossSiteRequestBlocked",
params,
blockedMessage);
// Build the error object and log it to the console
nsCOMPtr<nsIConsoleService> console(do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIScriptError> scriptError = do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoString msg(blockedMessage.get());
rv = scriptError->InitWithWindowID(msg,
NS_ConvertUTF8toUTF16(spec),
EmptyString(),
0,
0,
nsIScriptError::warningFlag,
"CORS",
innerWindowID);
NS_ENSURE_SUCCESS(rv, rv);
rv = console->LogMessage(scriptError);
return rv;
}
//////////////////////////////////////////////////////////////////////////
// Preflight cache
@ -398,8 +473,11 @@ NS_IMETHODIMP
nsCORSListenerProxy::OnStartRequest(nsIRequest* aRequest,
nsISupports* aContext)
{
mRequestApproved = NS_SUCCEEDED(CheckRequestApproved(aRequest));
nsresult rv = CheckRequestApproved(aRequest);
mRequestApproved = NS_SUCCEEDED(rv);
if (!mRequestApproved) {
rv = LogBlockedRequest(aRequest);
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to log blocked cross-site request");
if (sPreflightCache) {
nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
if (channel) {
@ -622,6 +700,9 @@ nsCORSListenerProxy::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
if (!NS_IsInternalSameURIRedirect(aOldChannel, aNewChannel, aFlags)) {
rv = CheckRequestApproved(aOldChannel);
if (NS_FAILED(rv)) {
rv = LogBlockedRequest(aOldChannel);
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to log blocked cross-site request");
if (sPreflightCache) {
nsCOMPtr<nsIURI> oldURI;
NS_GetFinalChannelURI(aOldChannel, getter_AddRefs(oldURI));

View File

@ -580,3 +580,4 @@ support-files =
[test_xhr_send_readystate.html]
[test_xhr_withCredentials.html]
[test_file_from_blob.html]
[test_warning_for_blocked_cross_site_request.html]

View File

@ -0,0 +1,92 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=713980
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 713980</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<!-- Load a cross-origin webfont without CORS (common pain point -->
<style>
@font-face {
font-family: "bad_cross_origin_webfont";
src: url('http://example.org/tests/content/base/test/csp/file_CSP.sjs?testid=font_bad&type=application/octet-stream');
}
div#bad_webfont { font-family: "bad_cross_origin_webfont"; }
</style>
</head>
<body>
<pre id="test">
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
var tests = {
font : {
uri_test : "font_bad",
result : null
},
xhr : {
uri_test : "http://invalid",
result : null
},
}
function testsComplete() {
for (var testName in tests) {
var test = tests[testName];
if (test.result == null)
return false;
}
return true;
}
SpecialPowers.registerConsoleListener(function CORSMsgListener(aMsg) {
if (!/Cross-Origin Request Blocked/.test(aMsg.message))
return;
for (var testName in tests) {
var test = tests[testName];
if (test.result != null)
continue;
var testRegexp = new RegExp(test.uri_test);
if (testRegexp.test(aMsg.message)) {
test.result = true;
ok(true, "Got \"Cross-site request blocked\" warning message for " + testName);
ok(aMsg.category == "CORS", "Got warning message with category \"" + aMsg.category + "\", expected \"CORS\"");
// Got the message we wanted - make sure it is destined for a valid inner window
ok(aMsg.windowID != 0, "Valid (non-zero) windowID for the cross-site request blocked message.");
break;
}
}
if (testsComplete()) {
SimpleTest.executeSoon(cleanup);
}
});
function cleanup() {
SpecialPowers.postConsoleSentinel();
SimpleTest.finish();
}
// Send a cross-origin XHR request without CORS
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://example.org/tests/content/base/test/file_CrossSiteXHR_server.sjs?allowOrigin=http://invalid", true);
xhr.send(null);
// Create a div that triggers a cross-origin webfont request
// We do this in Javascript in order to guarantee the console listener has
// already been registered; otherwise, there could be a race.
var badDiv = document.createElement('div');
badDiv.setAttribute('id', 'bad_webfont');
document.body.appendChild(badDiv);
</script>
</pre>
</body>
</html>

View File

@ -9,6 +9,10 @@ ReportOnlyCSPIgnored=Report-only CSP policy will be ignored because there are ot
OldCSPHeaderDeprecated=The X-Content-Security-Policy and X-Content-Security-Report-Only headers will be deprecated in the future. Please use the Content-Security-Policy and Content-Security-Report-Only headers with CSP spec compliant syntax instead.
# LOCALIZATION NOTE: Do not translate "X-Content-Security-Policy/Report-Only" or "Content-Security-Policy/Report-Only"
BothCSPHeadersPresent=This site specified both an X-Content-Security-Policy/Report-Only header and a Content-Security-Policy/Report-Only header. The X-Content-Security-Policy/Report-Only header(s) will be ignored.
# CORS
CrossSiteRequestBlocked=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. This can be fixed by moving the resource to the same domain or enabling CORS.
# LOCALIZATION NOTE: Do not translate "Strict-Transport-Security" or "HSTS"
InvalidSTSHeaders=The site specified an invalid Strict-Transport-Security header.
InsecurePasswordsPresentOnPage=Password fields present on an insecure (http://) page. This is a security risk that allows user login credentials to be stolen.