Bug 949488 - postMessage's targetOrigin argument should accept /, r=bholley

This commit is contained in:
Andrea Marchesini 2014-01-07 19:53:31 +01:00
parent 03c8eceaa0
commit 202e31a90e
11 changed files with 208 additions and 27 deletions

View File

@ -15,6 +15,7 @@
#include "nsIScriptContext.h"
#include "nsContentUtils.h"
#include "nsTArray.h"
#include "nsJSUtils.h"
namespace mozilla {
namespace dom {
@ -92,6 +93,25 @@ void DestroyScriptSettings()
delete ptr;
}
// This mostly gets the entry global, but doesn't entirely match the spec in
// certain edge cases. It's good enough for some purposes, but not others. If
// you want to call this function, ping bholley and describe your use-case.
nsIGlobalObject*
BrokenGetEntryGlobal()
{
// We need the current JSContext in order to check the JS for
// scripted frames that may have appeared since anyone last
// manipulated the stack. If it's null, that means that there
// must be no entry point on the stack.
JSContext *cx = nsContentUtils::GetCurrentJSContextForThread();
if (!cx) {
MOZ_ASSERT(ScriptSettingsStack::Ref().EntryPoint() == nullptr);
return nullptr;
}
return nsJSUtils::GetDynamicScriptGlobal(cx);
}
// Note: When we're ready to expose it, GetEntryGlobal will look similar to
// GetIncumbentGlobal below.

View File

@ -27,6 +27,11 @@ namespace dom {
void InitScriptSettings();
void DestroyScriptSettings();
// This mostly gets the entry global, but doesn't entirely match the spec in
// certain edge cases. It's good enough for some purposes, but not others. If
// you want to call this function, ping bholley and describe your use-case.
nsIGlobalObject* BrokenGetEntryGlobal();
// Note: We don't yet expose GetEntryGlobal, because in order for it to be
// correct, we first need to replace a bunch of explicit cx pushing in the
// browser with AutoEntryScript. But GetIncumbentGlobal is simpler, because it

View File

@ -84,6 +84,7 @@ UNIFIED_SOURCES += [
'nsFocusManager.cpp',
'nsGlobalWindowCommands.cpp',
'nsHistory.cpp',
'nsIGlobalObject.cpp',
'nsJSTimeoutHandler.cpp',
'nsJSUtils.cpp',
'nsLocation.cpp',

View File

@ -7493,14 +7493,14 @@ class PostMessageEvent : public nsRunnable
PostMessageEvent(nsGlobalWindow* aSource,
const nsAString& aCallerOrigin,
nsGlobalWindow* aTargetWindow,
nsIURI* aProvidedOrigin,
nsIPrincipal* aProvidedPrincipal,
bool aTrustedCaller)
: mSource(aSource),
mCallerOrigin(aCallerOrigin),
mMessage(nullptr),
mMessageLen(0),
mTargetWindow(aTargetWindow),
mProvidedOrigin(aProvidedOrigin),
mProvidedPrincipal(aProvidedPrincipal),
mTrustedCaller(aTrustedCaller)
{
MOZ_COUNT_CTOR(PostMessageEvent);
@ -7530,7 +7530,7 @@ class PostMessageEvent : public nsRunnable
uint64_t* mMessage;
size_t mMessageLen;
nsRefPtr<nsGlobalWindow> mTargetWindow;
nsCOMPtr<nsIURI> mProvidedOrigin;
nsCOMPtr<nsIPrincipal> mProvidedPrincipal;
bool mTrustedCaller;
nsTArray<nsCOMPtr<nsISupports> > mSupportsArray;
};
@ -7699,32 +7699,22 @@ PostMessageEvent::Run()
// intercept messages intended for another site by carefully timing navigation
// of the target window so it changed location after postMessage but before
// now.
if (mProvidedOrigin) {
if (mProvidedPrincipal) {
// Get the target's origin either from its principal or, in the case the
// principal doesn't carry a URI (e.g. the system principal), the target's
// document.
nsIPrincipal* targetPrin = targetWindow->GetPrincipal();
if (!targetPrin)
if (NS_WARN_IF(!targetPrin))
return NS_OK;
nsCOMPtr<nsIURI> targetURI;
if (NS_FAILED(targetPrin->GetURI(getter_AddRefs(targetURI))))
return NS_OK;
if (!targetURI) {
targetURI = targetWindow->mDoc->GetDocumentURI();
if (!targetURI)
return NS_OK;
}
// Note: This is contrary to the spec with respect to file: URLs, which
// the spec groups into a single origin, but given we intentionally
// don't do that in other places it seems better to hold the line for
// now. Long-term, we want HTML5 to address this so that we can
// be compliant while being safer.
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
nsresult rv =
ssm->CheckSameOriginURI(mProvidedOrigin, targetURI, true);
if (NS_FAILED(rv))
if (!targetPrin->EqualsIgnoringDomain(mProvidedPrincipal)) {
return NS_OK;
}
}
// Deserialize the structured clone data
@ -7857,15 +7847,47 @@ nsGlobalWindow::PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
}
// Convert the provided origin string into a URI for comparison purposes.
nsCOMPtr<nsIPrincipal> providedPrincipal;
if (aTargetOrigin.EqualsASCII("/")) {
providedPrincipal = BrokenGetEntryGlobal()->PrincipalOrNull();
if (NS_WARN_IF(!providedPrincipal))
return;
}
// "*" indicates no specific origin is required.
nsCOMPtr<nsIURI> providedOrigin;
if (!aTargetOrigin.EqualsASCII("*")) {
if (NS_FAILED(NS_NewURI(getter_AddRefs(providedOrigin), aTargetOrigin))) {
else if (!aTargetOrigin.EqualsASCII("*")) {
nsCOMPtr<nsIURI> originURI;
if (NS_FAILED(NS_NewURI(getter_AddRefs(originURI), aTargetOrigin))) {
aError.Throw(NS_ERROR_DOM_SYNTAX_ERR);
return;
}
if (NS_FAILED(providedOrigin->SetUserPass(EmptyCString())) ||
NS_FAILED(providedOrigin->SetPath(EmptyCString()))) {
if (NS_FAILED(originURI->SetUserPass(EmptyCString())) ||
NS_FAILED(originURI->SetPath(EmptyCString()))) {
return;
}
nsCOMPtr<nsIScriptSecurityManager> ssm =
nsContentUtils::GetSecurityManager();
MOZ_ASSERT(ssm);
nsCOMPtr<nsIPrincipal> principal = nsContentUtils::GetSubjectPrincipal();
MOZ_ASSERT(principal);
uint32_t appId;
if (NS_WARN_IF(NS_FAILED(principal->GetAppId(&appId))))
return;
bool isInBrowser;
if (NS_WARN_IF(NS_FAILED(principal->GetIsInBrowserElement(&isInBrowser))))
return;
// Create a nsIPrincipal inheriting the app/browser attributes from the
// caller.
nsresult rv = ssm->GetAppCodebasePrincipal(originURI, appId, isInBrowser,
getter_AddRefs(providedPrincipal));
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
}
@ -7878,7 +7900,7 @@ nsGlobalWindow::PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
: callerInnerWin->GetOuterWindowInternal(),
origin,
this,
providedOrigin,
providedPrincipal,
nsContentUtils::IsCallerChrome());
// We *must* clone the data here, or the JS::Value could be modified

View File

@ -0,0 +1,18 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
#include "nsIGlobalObject.h"
#include "nsContentUtils.h"
nsIPrincipal*
nsIGlobalObject::PrincipalOrNull()
{
JSObject *global = GetGlobalJSObject();
if (NS_WARN_IF(!global))
return nullptr;
return nsContentUtils::GetObjectPrincipal(global);
}

View File

@ -13,12 +13,17 @@
{ 0xe2538ded, 0x13ef, 0x4f4d, \
{ 0x94, 0x6b, 0x65, 0xd3, 0x33, 0xb4, 0xf0, 0x3c } }
class nsIPrincipal;
class nsIGlobalObject : public nsISupports
{
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_IGLOBALOBJECT_IID)
virtual JSObject* GetGlobalJSObject() = 0;
// This method is not meant to be overridden.
nsIPrincipal* PrincipalOrNull();
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsIGlobalObject,

View File

@ -0,0 +1,15 @@
<!DOCTYPE HTML>
<html>
<body>
<script type="application/javascript">
window.addEventListener('message', receiveMessage, false);
function receiveMessage(evt) {
window.parent.postMessage(evt.data, '*');
}
</script>
</body>
</html>

View File

@ -4,6 +4,7 @@ support-files =
iframe_messageChannel_pingpong.html
iframe_messageChannel_post.html
file_empty.html
iframe_postMessage_solidus.html
[test_Image_constructor.html]
[test_appname_override.html]
@ -45,4 +46,5 @@ support-files =
[test_openDialogChromeOnly.html]
[test_messagemanager_targetchain.html]
[test_url_empty_port.html]
[test_postMessage_solidus.html]
[test_urlSearchParams.html]

View File

@ -0,0 +1,93 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=949488
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 949488 - basic support</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=949488">Mozilla Bug 949488</a>
<div id="content"></div>
<script type="application/javascript">
function selfMessage() {
addEventListener('message', receiveMessage);
function receiveMessage(evt) {
is(evt.data, 1, "Message received");
removeEventListener('message', receiveMessage);
runTest();
}
postMessage(1, '/');
}
function frameOk() {
var ifr = document.createElement("iframe");
ifr.addEventListener("load", iframeLoaded, false);
ifr.setAttribute('src', "iframe_postMessage_solidus.html");
var div = document.getElementById("content");
div.appendChild(ifr);
function iframeLoaded() {
addEventListener('message', receiveMessage);
function receiveMessage(evt) {
is(evt.data, 2, "Message received");
removeEventListener('message', receiveMessage);
runTest();
}
ifr.contentWindow.postMessage(2, '/');
}
}
function frameWrong() {
var ifr = document.createElement("iframe");
ifr.addEventListener("load", iframeLoaded, false);
ifr.setAttribute('src', "http://www.example.com/tests/dom/base/test/iframe_postMessage_solidus.html");
var div = document.getElementById("content");
div.appendChild(ifr);
function iframeLoaded() {
addEventListener('message', receiveMessage);
function receiveMessage(evt) {
ok(evt.data, 3, "Message received");
removeEventListener('message', receiveMessage);
runTest();
}
ifr.contentWindow.postMessage(4, '/');
SimpleTest.executeSoon(function() {
ifr.contentWindow.postMessage(3, '*');
});
}
}
var tests = [
selfMessage,
frameOk,
frameWrong
];
function runTest() {
if (!tests.length) {
SimpleTest.finish();
return;
}
var test = tests.shift();
test();
}
SimpleTest.waitForExplicitFinish();
runTest();
</script>
</body>
</html>

View File

@ -191,7 +191,7 @@ function browserLoadEvent() {
setupStorage(gBrowserStorage.localStorage);
setupStorage(gBrowserStorage.sessionStorage);
frames[1].postMessage("clear", "http://www.example.com");
frames[1].postMessage("clear", "*");
waitForClearBrowserData();
};

View File

@ -28,11 +28,11 @@ function onMessageReceived(event)
// Indication of successfully finished step of a test
case "perf":
if (callMasterFrame)
masterFrame.postMessage("step", masterFrameOrigin);
masterFrame.postMessage("step", "*");
else if (slaveFrame)
slaveFrame.postMessage("step", slaveFrameOrigin);
slaveFrame.postMessage("step", "*");
else if (SpecialPowers.wrap(masterFrame).slaveFrame)
SpecialPowers.wrap(masterFrame).slaveFrame.postMessage("step", slaveFrameOrigin);
SpecialPowers.wrap(masterFrame).slaveFrame.postMessage("step", "*");
callMasterFrame = !callMasterFrame;
break;