зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1331730 - Log CORS messages from the content process r=bz,mayhemer
In e10s, a channel created by parent does not have a reliable reference to the inner window ID that initiated the request. Without that, the channel must request that the content process log and blocked messages to the web console. This patch creates a new ipdl interface to pass the message from the parent to the child process. The nsCORSListenerProxy also needs to keep a reference to the nsIHttpChannel that created it so it can find its way back to the child. Additionally, the HttpChannelParent needs to be propagated when creating a new channel for CORS. MozReview-Commit-ID: 8CUhlVCTWxt --HG-- extra : rebase_source : 350f39ad6f7ada39e88dfcc69c4f2c470e2be0de
This commit is contained in:
Родитель
4d5da90a53
Коммит
84d5adef43
|
@ -0,0 +1,8 @@
|
|||
[DEFAULT]
|
||||
support-files =
|
||||
file_CrossSiteXHR_server.sjs
|
||||
file_CrossSiteXHR_inner.html
|
||||
file_cors_logging_test.html
|
||||
head.js
|
||||
|
||||
[browser_CORS-console-warnings.js]
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Description of the test:
|
||||
* Ensure that CORS warnings are printed to the web console.
|
||||
*
|
||||
* This test uses the same tests as the plain mochitest, but needs access to
|
||||
* the console.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
function console_observer(subject, topic, data) {
|
||||
var message = subject.wrappedJSObject.arguments[0];
|
||||
ok(false, message);
|
||||
};
|
||||
|
||||
var webconsole = null;
|
||||
var messages_seen = 0;
|
||||
var expected_messages = 50;
|
||||
|
||||
function on_new_message(event, new_messages) {
|
||||
for (let message of new_messages) {
|
||||
let elem = message.node;
|
||||
let text = elem.textContent;
|
||||
if (text.match('Cross-Origin Request Blocked:')) {
|
||||
ok(true, "message is: " + text);
|
||||
messages_seen++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function do_cleanup() {
|
||||
if (webconsole) {
|
||||
webconsole.ui.off("new-messages", on_new_message);
|
||||
}
|
||||
yield unsetCookiePref();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set e10s related preferences in the test environment.
|
||||
* @return {Promise} promise that resolves when preferences are set.
|
||||
*/
|
||||
function setCookiePref() {
|
||||
return new Promise(resolve =>
|
||||
// accept all cookies so that the CORS requests will send the right cookies
|
||||
SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["network.cookie.cookieBehavior", 0],
|
||||
]
|
||||
}, resolve));
|
||||
}
|
||||
|
||||
/**
|
||||
* Unset e10s related preferences in the test environment.
|
||||
* @return {Promise} promise that resolves when preferences are unset.
|
||||
*/
|
||||
function unsetCookiePref() {
|
||||
return new Promise(resolve => {
|
||||
SpecialPowers.popPrefEnv(resolve);
|
||||
});
|
||||
}
|
||||
|
||||
//jscs:disable
|
||||
add_task(function*() {
|
||||
//jscs:enable
|
||||
// A longer timeout is necessary for this test than the plain mochitests
|
||||
// due to opening a new tab with the web console.
|
||||
requestLongerTimeout(4);
|
||||
registerCleanupFunction(do_cleanup);
|
||||
yield setCookiePref();
|
||||
|
||||
let test_uri = "http://mochi.test:8888/browser/dom/security/test/cors/file_cors_logging_test.html";
|
||||
|
||||
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
|
||||
|
||||
let toolbox = yield openToolboxForTab(tab, "webconsole");
|
||||
ok(toolbox, "Got toolbox");
|
||||
let hud = toolbox.getCurrentPanel().hud;
|
||||
ok(hud, "Got hud");
|
||||
|
||||
if (!webconsole) {
|
||||
registerCleanupFunction(do_cleanup);
|
||||
hud.ui.on("new-messages", on_new_message);
|
||||
webconsole = hud;
|
||||
}
|
||||
|
||||
BrowserTestUtils.loadURI(gBrowser, test_uri);
|
||||
|
||||
yield BrowserTestUtils.waitForLocationChange(gBrowser, test_uri+"#finished");
|
||||
|
||||
// Different OS combinations
|
||||
ok(messages_seen > 0, "Saw " + messages_seen + " messages.");
|
||||
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
});
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,69 @@
|
|||
'use strict';
|
||||
|
||||
var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
|
||||
function scopedCuImport(path) {
|
||||
const scope = {};
|
||||
Cu.import(path, scope);
|
||||
return scope;
|
||||
}
|
||||
const {loader, require} = scopedCuImport("resource://devtools/shared/Loader.jsm");
|
||||
const {TargetFactory} = require("devtools/client/framework/target");
|
||||
const {Utils: WebConsoleUtils} =
|
||||
require("devtools/client/webconsole/utils");
|
||||
let { gDevTools } = require("devtools/client/framework/devtools");
|
||||
loader.lazyGetter(this, "HUDService", () => require("devtools/client/webconsole/webconsole"));
|
||||
loader.lazyGetter(this, "HUDService", () => require("devtools/client/webconsole/hudservice"));
|
||||
let promise = require("promise");
|
||||
|
||||
/**
|
||||
* Open the toolbox in a given tab.
|
||||
* @param {XULNode} tab The tab the toolbox should be opened in.
|
||||
* @param {String} toolId Optional. The ID of the tool to be selected.
|
||||
* @param {String} hostType Optional. The type of toolbox host to be used.
|
||||
* @return {Promise} Resolves with the toolbox, when it has been opened.
|
||||
*/
|
||||
var openToolboxForTab = Task.async(function* (tab, toolId, hostType) {
|
||||
info("Opening the toolbox");
|
||||
|
||||
let toolbox;
|
||||
let target = TargetFactory.forTab(tab);
|
||||
yield target.makeRemote();
|
||||
|
||||
// Check if the toolbox is already loaded.
|
||||
toolbox = gDevTools.getToolbox(target);
|
||||
if (toolbox) {
|
||||
if (!toolId || (toolId && toolbox.getPanel(toolId))) {
|
||||
info("Toolbox is already opened");
|
||||
return toolbox;
|
||||
}
|
||||
}
|
||||
|
||||
// If not, load it now.
|
||||
toolbox = yield gDevTools.showToolbox(target, toolId, hostType);
|
||||
|
||||
// Make sure that the toolbox frame is focused.
|
||||
yield new Promise(resolve => waitForFocus(resolve, toolbox.win));
|
||||
|
||||
info("Toolbox opened and focused");
|
||||
|
||||
return toolbox;
|
||||
});
|
||||
|
||||
/**
|
||||
* Find multiple messages in the output.
|
||||
*
|
||||
* @param object hud
|
||||
* The web console.
|
||||
* @param string text
|
||||
* A substring that can be found in the message.
|
||||
* @param selector [optional]
|
||||
* The selector to use in finding the message.
|
||||
*/
|
||||
function findMessages(hud, text, selector = ".message") {
|
||||
const messages = hud.ui.experimentalOutputNode.querySelectorAll(selector);
|
||||
const elements = Array.prototype.filter.call(
|
||||
messages,
|
||||
(el) => el.textContent.includes(text)
|
||||
);
|
||||
return elements;
|
||||
}
|
|
@ -28,6 +28,7 @@ MOCHITEST_CHROME_MANIFESTS += [
|
|||
]
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += [
|
||||
'cors/browser.ini',
|
||||
'csp/browser.ini',
|
||||
'general/browser.ini',
|
||||
'hsts/browser.ini',
|
||||
|
|
|
@ -54,6 +54,7 @@
|
|||
#include "nsSocketTransportService2.h"
|
||||
#include "nsStreamUtils.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsCORSListenerProxy.h"
|
||||
|
||||
#ifdef MOZ_TASK_TRACER
|
||||
#include "GeckoTaskTracer.h"
|
||||
|
@ -3598,5 +3599,22 @@ HttpChannelChild::ActorDestroy(ActorDestroyReason aWhy)
|
|||
}
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
HttpChannelChild::RecvLogBlockedCORSRequest(const nsString& aMessage)
|
||||
{
|
||||
Unused << LogBlockedCORSRequest(aMessage);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HttpChannelChild::LogBlockedCORSRequest(const nsAString & aMessage)
|
||||
{
|
||||
if (mLoadInfo) {
|
||||
uint64_t innerWindowID = mLoadInfo->GetInnerWindowID();
|
||||
nsCORSListenerProxy::LogBlockedCORSRequest(innerWindowID, aMessage);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -179,6 +179,9 @@ protected:
|
|||
// Get event target for processing network events.
|
||||
already_AddRefed<nsIEventTarget> GetNeckoTarget() override;
|
||||
|
||||
virtual mozilla::ipc::IPCResult RecvLogBlockedCORSRequest(const nsString& aMessage) override;
|
||||
NS_IMETHOD LogBlockedCORSRequest(const nsAString & aMessage) override;
|
||||
|
||||
private:
|
||||
// this section is for main-thread-only object
|
||||
// all the references need to be proxy released on main thread.
|
||||
|
|
|
@ -232,6 +232,12 @@ HttpChannelParent::CleanupBackgroundChannel()
|
|||
return;
|
||||
}
|
||||
|
||||
// The nsHttpChannel may have a reference to this parent, release it
|
||||
// to avoid circular references.
|
||||
if (mChannel) {
|
||||
mChannel->SetWarningReporter(nullptr);
|
||||
}
|
||||
|
||||
if (!mPromise.IsEmpty()) {
|
||||
mRequest.DisconnectIfExists();
|
||||
mPromise.Reject(NS_ERROR_FAILURE, __func__);
|
||||
|
@ -805,6 +811,8 @@ HttpChannelParent::ConnectChannel(const uint32_t& registrarId, const bool& shoul
|
|||
return true;
|
||||
}
|
||||
|
||||
mChannel->SetWarningReporter(this);
|
||||
|
||||
nsCOMPtr<nsINetworkInterceptController> controller;
|
||||
NS_QueryNotificationCallbacks(channel, controller);
|
||||
RefPtr<HttpChannelParentListener> parentListener = do_QueryObject(controller);
|
||||
|
@ -1544,6 +1552,8 @@ HttpChannelParent::OnStopRequest(nsIRequest *aRequest,
|
|||
mChannel->GetCacheReadStart(&timing.cacheReadStart);
|
||||
mChannel->GetCacheReadEnd(&timing.cacheReadEnd);
|
||||
|
||||
mChannel->SetWarningReporter(nullptr);
|
||||
|
||||
// Either IPC channel is closed or background channel
|
||||
// is ready to send OnStopRequest.
|
||||
MOZ_ASSERT(mIPCClosed || mBgParent);
|
||||
|
@ -2227,5 +2237,15 @@ HttpChannelParent::DoSendSetPriority(int16_t aValue)
|
|||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
HttpChannelParent::LogBlockedCORSRequest(const nsAString& aMessage)
|
||||
{
|
||||
if (mIPCClosed ||
|
||||
NS_WARN_IF(!SendLogBlockedCORSRequest(nsString(aMessage)))) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -214,6 +214,7 @@ protected:
|
|||
MOZ_MUST_USE nsresult
|
||||
ReportSecurityMessage(const nsAString& aMessageTag,
|
||||
const nsAString& aMessageCategory) override;
|
||||
nsresult LogBlockedCORSRequest(const nsAString& aMessage) override;
|
||||
|
||||
// Calls SendDeleteSelf and sets mIPCClosed to true because we should not
|
||||
// send any more messages after that. Bug 1274886
|
||||
|
|
|
@ -854,6 +854,12 @@ NullHttpChannel::SetIsMainDocumentChannel(bool aValue)
|
|||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::LogBlockedCORSRequest(const nsAString& aMessage)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
#define IMPL_TIMING_ATTR(name) \
|
||||
NS_IMETHODIMP \
|
||||
NullHttpChannel::Get##name##Time(PRTime* _retval) { \
|
||||
|
|
|
@ -141,6 +141,11 @@ child:
|
|||
// Tell the child to issue a deprecation warning.
|
||||
async IssueDeprecationWarning(uint32_t warning, bool asError);
|
||||
|
||||
// When CORS blocks the request in the parent process, it doesn't have the
|
||||
// correct window ID, so send the message to the child for logging to the web
|
||||
// console.
|
||||
async LogBlockedCORSRequest(nsString message);
|
||||
|
||||
both:
|
||||
// After receiving this message, the parent also calls
|
||||
// SendFinishInterceptedRedirect, and makes sure not to send any more messages
|
||||
|
|
|
@ -42,8 +42,10 @@
|
|||
#include "NullPrincipal.h"
|
||||
#include "nsICorsPreflightCallback.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "nsHttpChannel.h"
|
||||
#include "mozilla/LoadInfo.h"
|
||||
#include "nsIHttpHeaderVisitor.h"
|
||||
#include "nsQueryObject.h"
|
||||
#include <algorithm>
|
||||
|
||||
using namespace mozilla;
|
||||
|
@ -56,24 +58,11 @@ static bool gDisableCORSPrivateData = false;
|
|||
static void
|
||||
LogBlockedRequest(nsIRequest* aRequest,
|
||||
const char* aProperty,
|
||||
const char16_t* aParam)
|
||||
const char16_t* aParam,
|
||||
nsIHttpChannel* aCreatingChannel)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
// Build the error object and log it to the console
|
||||
nsCOMPtr<nsIConsoleService> console(do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv));
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to log blocked cross-site request (no console)");
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIScriptError> scriptError =
|
||||
do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to log blocked cross-site request (no scriptError)");
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
|
||||
nsCOMPtr<nsIURI> aUri;
|
||||
channel->GetURI(getter_AddRefs(aUri));
|
||||
|
@ -98,34 +87,19 @@ LogBlockedRequest(nsIRequest* aRequest,
|
|||
|
||||
nsAutoString msg(blockedMessage.get());
|
||||
|
||||
// query innerWindowID and log to web console, otherwise log to
|
||||
// the error to the browser console.
|
||||
uint64_t innerWindowID = nsContentUtils::GetInnerWindowID(aRequest);
|
||||
if (XRE_IsParentProcess()) {
|
||||
if (aCreatingChannel) {
|
||||
rv = aCreatingChannel->LogBlockedCORSRequest(msg);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
NS_WARNING("Failed to log blocked cross-site request to web console from parent->child, falling back to browser console");
|
||||
}
|
||||
|
||||
if (innerWindowID > 0) {
|
||||
rv = scriptError->InitWithWindowID(msg,
|
||||
EmptyString(), // sourceName
|
||||
EmptyString(), // sourceLine
|
||||
0, // lineNumber
|
||||
0, // columnNumber
|
||||
nsIScriptError::warningFlag,
|
||||
"CORS",
|
||||
innerWindowID);
|
||||
}
|
||||
else {
|
||||
rv = scriptError->Init(msg,
|
||||
EmptyString(), // sourceName
|
||||
EmptyString(), // sourceLine
|
||||
0, // lineNumber
|
||||
0, // columnNumber
|
||||
nsIScriptError::warningFlag,
|
||||
"CORS");
|
||||
}
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to log blocked cross-site request (scriptError init failed)");
|
||||
return;
|
||||
}
|
||||
console->LogMessage(scriptError);
|
||||
// log message ourselves
|
||||
uint64_t innerWindowID = nsContentUtils::GetInnerWindowID(aRequest);
|
||||
nsCORSListenerProxy::LogBlockedCORSRequest(innerWindowID, msg);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
@ -451,6 +425,7 @@ nsCORSListenerProxy::Init(nsIChannel* aChannel, DataURIHandling aAllowDataURI)
|
|||
mRequestingPrincipal = nullptr;
|
||||
mOriginHeaderPrincipal = nullptr;
|
||||
mOuterNotificationCallbacks = nullptr;
|
||||
mHttpChannel = nullptr;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
mInited = true;
|
||||
|
@ -540,9 +515,11 @@ nsCORSListenerProxy::CheckRequestApproved(nsIRequest* aRequest)
|
|||
if (!mHasBeenCrossSite) {
|
||||
return NS_OK;
|
||||
}
|
||||
nsCOMPtr<nsIHttpChannel> topChannel;
|
||||
topChannel.swap(mHttpChannel);
|
||||
|
||||
if (gDisableCORS) {
|
||||
LogBlockedRequest(aRequest, "CORSDisabled", nullptr);
|
||||
LogBlockedRequest(aRequest, "CORSDisabled", nullptr, topChannel);
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
|
||||
|
@ -560,7 +537,7 @@ nsCORSListenerProxy::CheckRequestApproved(nsIRequest* aRequest)
|
|||
// Test that things worked on a HTTP level
|
||||
nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(aRequest);
|
||||
if (!http) {
|
||||
LogBlockedRequest(aRequest, "CORSRequestNotHttp", nullptr);
|
||||
LogBlockedRequest(aRequest, "CORSRequestNotHttp", nullptr, topChannel);
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
|
||||
|
@ -582,14 +559,14 @@ nsCORSListenerProxy::CheckRequestApproved(nsIRequest* aRequest)
|
|||
// check for duplicate headers
|
||||
rv = http->VisitOriginalResponseHeaders(visitor);
|
||||
if (NS_FAILED(rv)) {
|
||||
LogBlockedRequest(aRequest, "CORSAllowOriginNotMatchingOrigin", nullptr);
|
||||
LogBlockedRequest(aRequest, "CORSAllowOriginNotMatchingOrigin", nullptr, topChannel);
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = http->GetResponseHeader(
|
||||
NS_LITERAL_CSTRING("Access-Control-Allow-Origin"), allowedOriginHeader);
|
||||
if (NS_FAILED(rv)) {
|
||||
LogBlockedRequest(aRequest, "CORSMissingAllowOrigin", nullptr);
|
||||
LogBlockedRequest(aRequest, "CORSMissingAllowOrigin", nullptr, topChannel);
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
@ -602,7 +579,7 @@ nsCORSListenerProxy::CheckRequestApproved(nsIRequest* aRequest)
|
|||
// below since "if (A && B)" is included in "if (A || !B)".
|
||||
//
|
||||
if (mWithCredentials && allowedOriginHeader.EqualsLiteral("*")) {
|
||||
LogBlockedRequest(aRequest, "CORSNotSupportingCredentials", nullptr);
|
||||
LogBlockedRequest(aRequest, "CORSNotSupportingCredentials", nullptr, topChannel);
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
|
||||
|
@ -613,7 +590,7 @@ nsCORSListenerProxy::CheckRequestApproved(nsIRequest* aRequest)
|
|||
|
||||
if (!allowedOriginHeader.Equals(origin)) {
|
||||
LogBlockedRequest(aRequest, "CORSAllowOriginNotMatchingOrigin",
|
||||
NS_ConvertUTF8toUTF16(allowedOriginHeader).get());
|
||||
NS_ConvertUTF8toUTF16(allowedOriginHeader).get(), topChannel);
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
}
|
||||
|
@ -625,7 +602,7 @@ nsCORSListenerProxy::CheckRequestApproved(nsIRequest* aRequest)
|
|||
NS_LITERAL_CSTRING("Access-Control-Allow-Credentials"), allowCredentialsHeader);
|
||||
|
||||
if (!allowCredentialsHeader.EqualsLiteral("true")) {
|
||||
LogBlockedRequest(aRequest, "CORSMissingAllowCredentials", nullptr);
|
||||
LogBlockedRequest(aRequest, "CORSMissingAllowCredentials", nullptr, topChannel);
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
}
|
||||
|
@ -642,6 +619,7 @@ nsCORSListenerProxy::OnStopRequest(nsIRequest* aRequest,
|
|||
nsresult rv = mOuterListener->OnStopRequest(aRequest, aContext, aStatusCode);
|
||||
mOuterListener = nullptr;
|
||||
mOuterNotificationCallbacks = nullptr;
|
||||
mHttpChannel = nullptr;
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
@ -994,6 +972,8 @@ nsCORSListenerProxy::UpdateChannel(nsIChannel* aChannel,
|
|||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
mHttpChannel = http;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -1310,11 +1290,12 @@ nsCORSPreflightListener::CheckPreflightRequestApproved(nsIRequest* aRequest)
|
|||
nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(aRequest);
|
||||
nsCOMPtr<nsIHttpChannelInternal> internal = do_QueryInterface(aRequest);
|
||||
NS_ENSURE_STATE(internal);
|
||||
nsCOMPtr<nsIHttpChannel> parentHttpChannel = do_QueryInterface(mCallback);
|
||||
|
||||
bool succeedded;
|
||||
rv = http->GetRequestSucceeded(&succeedded);
|
||||
if (NS_FAILED(rv) || !succeedded) {
|
||||
LogBlockedRequest(aRequest, "CORSPreflightDidNotSucceed", nullptr);
|
||||
LogBlockedRequest(aRequest, "CORSPreflightDidNotSucceed", nullptr, parentHttpChannel);
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
|
||||
|
@ -1334,13 +1315,13 @@ nsCORSPreflightListener::CheckPreflightRequestApproved(nsIRequest* aRequest)
|
|||
}
|
||||
if (!NS_IsValidHTTPToken(method)) {
|
||||
LogBlockedRequest(aRequest, "CORSInvalidAllowMethod",
|
||||
NS_ConvertUTF8toUTF16(method).get());
|
||||
NS_ConvertUTF8toUTF16(method).get(), parentHttpChannel);
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
foundMethod |= mPreflightMethod.Equals(method);
|
||||
}
|
||||
if (!foundMethod) {
|
||||
LogBlockedRequest(aRequest, "CORSMethodNotFound", nullptr);
|
||||
LogBlockedRequest(aRequest, "CORSMethodNotFound", nullptr, parentHttpChannel);
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
|
||||
|
@ -1357,7 +1338,7 @@ nsCORSPreflightListener::CheckPreflightRequestApproved(nsIRequest* aRequest)
|
|||
}
|
||||
if (!NS_IsValidHTTPToken(header)) {
|
||||
LogBlockedRequest(aRequest, "CORSInvalidAllowHeader",
|
||||
NS_ConvertUTF8toUTF16(header).get());
|
||||
NS_ConvertUTF8toUTF16(header).get(), parentHttpChannel);
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
headers.AppendElement(header);
|
||||
|
@ -1366,7 +1347,7 @@ nsCORSPreflightListener::CheckPreflightRequestApproved(nsIRequest* aRequest)
|
|||
if (!headers.Contains(mPreflightHeaders[i],
|
||||
nsCaseInsensitiveCStringArrayComparator())) {
|
||||
LogBlockedRequest(aRequest, "CORSMissingAllowHeaderFromPreflight",
|
||||
NS_ConvertUTF8toUTF16(mPreflightHeaders[i]).get());
|
||||
NS_ConvertUTF8toUTF16(mPreflightHeaders[i]).get(), parentHttpChannel);
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
}
|
||||
|
@ -1396,6 +1377,7 @@ nsCORSListenerProxy::RemoveFromCorsPreflightCache(nsIURI* aURI,
|
|||
}
|
||||
}
|
||||
|
||||
// static
|
||||
nsresult
|
||||
nsCORSListenerProxy::StartCORSPreflight(nsIChannel* aRequestChannel,
|
||||
nsICorsPreflightCallback* aCallback,
|
||||
|
@ -1405,7 +1387,8 @@ nsCORSListenerProxy::StartCORSPreflight(nsIChannel* aRequestChannel,
|
|||
*aPreflightChannel = nullptr;
|
||||
|
||||
if (gDisableCORS) {
|
||||
LogBlockedRequest(aRequestChannel, "CORSDisabled", nullptr);
|
||||
nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(aRequestChannel);
|
||||
LogBlockedRequest(aRequestChannel, "CORSDisabled", nullptr, http);
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
|
||||
|
@ -1501,6 +1484,13 @@ nsCORSListenerProxy::StartCORSPreflight(nsIChannel* aRequestChannel,
|
|||
method, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Set the CORS preflight channel's warning reporter to be the same as the
|
||||
// requesting channel so that all log messages are able to be reported through
|
||||
// the warning reporter.
|
||||
RefPtr<nsHttpChannel> reqCh = do_QueryObject(aRequestChannel);
|
||||
RefPtr<nsHttpChannel> preCh = do_QueryObject(preHttp);
|
||||
preCh->SetWarningReporter(reqCh->GetWarningReporter());
|
||||
|
||||
nsTArray<nsCString> preflightHeaders;
|
||||
if (!aUnsafeHeaders.IsEmpty()) {
|
||||
for (uint32_t i = 0; i < aUnsafeHeaders.Length(); ++i) {
|
||||
|
@ -1538,3 +1528,52 @@ nsCORSListenerProxy::StartCORSPreflight(nsIChannel* aRequestChannel,
|
|||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// static
|
||||
void
|
||||
nsCORSListenerProxy::LogBlockedCORSRequest(uint64_t aInnerWindowID,
|
||||
const nsAString& aMessage)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
// Build the error object and log it to the console
|
||||
nsCOMPtr<nsIConsoleService> console(do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv));
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to log blocked cross-site request (no console)");
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIScriptError> scriptError =
|
||||
do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to log blocked cross-site request (no scriptError)");
|
||||
return;
|
||||
}
|
||||
|
||||
// query innerWindowID and log to web console, otherwise log to
|
||||
// the error to the browser console.
|
||||
if (aInnerWindowID > 0) {
|
||||
rv = scriptError->InitWithWindowID(aMessage,
|
||||
EmptyString(), // sourceName
|
||||
EmptyString(), // sourceLine
|
||||
0, // lineNumber
|
||||
0, // columnNumber
|
||||
nsIScriptError::warningFlag,
|
||||
"CORS",
|
||||
aInnerWindowID);
|
||||
}
|
||||
else {
|
||||
rv = scriptError->Init(aMessage,
|
||||
EmptyString(), // sourceName
|
||||
EmptyString(), // sourceLine
|
||||
0, // lineNumber
|
||||
0, // columnNumber
|
||||
nsIScriptError::warningFlag,
|
||||
"CORS");
|
||||
}
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to log blocked cross-site request (scriptError init failed)");
|
||||
return;
|
||||
}
|
||||
console->LogMessage(scriptError);
|
||||
}
|
||||
|
|
|
@ -70,6 +70,10 @@ public:
|
|||
|
||||
void SetInterceptController(nsINetworkInterceptController* aInterceptController);
|
||||
|
||||
// When CORS blocks a request, log the message to the web console, or the
|
||||
// browser console if no valid inner window ID is found.
|
||||
static void LogBlockedCORSRequest(uint64_t aInnerWindowID,
|
||||
const nsAString& aMessage);
|
||||
private:
|
||||
// Only HttpChannelParent can call RemoveFromCorsPreflightCache
|
||||
friend class mozilla::net::HttpChannelParent;
|
||||
|
@ -108,6 +112,10 @@ private:
|
|||
// an http: request to https: in nsHttpChannel::Connect() and hence
|
||||
// a request might not be marked as cross site request based on that promise.
|
||||
bool mHasBeenCrossSite;
|
||||
// Under e10s, logging happens in the child process. Keep a reference to the
|
||||
// creator nsIHttpChannel in order to find the way back to the child. Released
|
||||
// in OnStopRequest().
|
||||
nsCOMPtr<nsIHttpChannel> mHttpChannel;
|
||||
#ifdef DEBUG
|
||||
bool mInited;
|
||||
#endif
|
||||
|
|
|
@ -416,6 +416,15 @@ nsHttpChannel::AddSecurityMessage(const nsAString& aMessageTag,
|
|||
aMessageCategory);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHttpChannel::LogBlockedCORSRequest(const nsAString& aMessage)
|
||||
{
|
||||
if (mWarningReporter) {
|
||||
return mWarningReporter->LogBlockedCORSRequest(aMessage);
|
||||
}
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsHttpChannel <private>
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -858,6 +867,7 @@ nsHttpChannel::ReleaseListeners()
|
|||
{
|
||||
HttpBaseChannel::ReleaseListeners();
|
||||
mChannelClassifier = nullptr;
|
||||
mWarningReporter = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -5922,6 +5932,7 @@ nsHttpChannel::Cancel(nsresult status)
|
|||
LOG((" ignoring; already canceled\n"));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (mWaitingForRedirectCallback) {
|
||||
LOG(("channel canceled during wait for redirect callback"));
|
||||
}
|
||||
|
@ -9250,5 +9261,19 @@ nsHttpChannel::Notify(nsITimer *aTimer)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpChannel::SetWarningReporter(HttpChannelSecurityWarningReporter *aReporter)
|
||||
{
|
||||
LOG(("nsHttpChannel [this=%p] SetWarningReporter [%p]", this, aReporter));
|
||||
mWarningReporter = aReporter;
|
||||
}
|
||||
|
||||
HttpChannelSecurityWarningReporter*
|
||||
nsHttpChannel::GetWarningReporter()
|
||||
{
|
||||
LOG(("nsHttpChannel [this=%p] GetWarningReporter [%p]", this, mWarningReporter.get()));
|
||||
return mWarningReporter.get();
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -43,12 +43,13 @@ namespace mozilla { namespace net {
|
|||
class nsChannelClassifier;
|
||||
class Http2PushedStream;
|
||||
|
||||
class HttpChannelSecurityWarningReporter
|
||||
class HttpChannelSecurityWarningReporter : public nsISupports
|
||||
{
|
||||
public:
|
||||
virtual MOZ_MUST_USE nsresult
|
||||
ReportSecurityMessage(const nsAString& aMessageTag,
|
||||
const nsAString& aMessageCategory) = 0;
|
||||
virtual nsresult LogBlockedCORSRequest(const nsAString& aMessage) = 0;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -190,10 +191,10 @@ public:
|
|||
MOZ_MUST_USE nsresult
|
||||
AddSecurityMessage(const nsAString& aMessageTag,
|
||||
const nsAString& aMessageCategory) override;
|
||||
NS_IMETHOD LogBlockedCORSRequest(const nsAString& aMessage) override;
|
||||
|
||||
void SetWarningReporter(HttpChannelSecurityWarningReporter* aReporter)
|
||||
{ mWarningReporter = aReporter; }
|
||||
|
||||
void SetWarningReporter(HttpChannelSecurityWarningReporter *aReporter);
|
||||
HttpChannelSecurityWarningReporter* GetWarningReporter();
|
||||
public: /* internal necko use only */
|
||||
|
||||
using InitLocalBlockListCallback = std::function<void(bool)>;
|
||||
|
@ -672,7 +673,7 @@ private:
|
|||
nsCString mUsername;
|
||||
|
||||
// If non-null, warnings should be reported to this object.
|
||||
HttpChannelSecurityWarningReporter* mWarningReporter;
|
||||
RefPtr<HttpChannelSecurityWarningReporter> mWarningReporter;
|
||||
|
||||
RefPtr<ADivertableParentChannel> mParentChannel;
|
||||
|
||||
|
|
|
@ -478,4 +478,14 @@ interface nsIHttpChannel : nsIChannel
|
|||
* Don't alter it otherwise.
|
||||
*/
|
||||
[must_use] attribute uint64_t topLevelOuterContentWindowId;
|
||||
|
||||
/**
|
||||
* In e10s, the information that the CORS response blocks the load is in the
|
||||
* parent, which doesn't know the true window id of the request, so we may
|
||||
* need to proxy the request to the child.
|
||||
*
|
||||
* @param aMessage
|
||||
* The message to print in the console.
|
||||
*/
|
||||
void logBlockedCORSRequest(in AString aMessage);
|
||||
};
|
||||
|
|
|
@ -1044,3 +1044,13 @@ nsViewSourceChannel::SetCorsPreflightParameters(const nsTArray<nsCString>& aUnsa
|
|||
{
|
||||
mHttpChannelInternal->SetCorsPreflightParameters(aUnsafeHeaders);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsViewSourceChannel::LogBlockedCORSRequest(const nsAString& aMessage)
|
||||
{
|
||||
if (!mHttpChannel) {
|
||||
NS_WARNING("nsViewSourceChannel::LogBlockedCORSRequest mHttpChannel is null");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
return mHttpChannel->LogBlockedCORSRequest(aMessage);
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче