Bug 1139849 - postMessage to incorrect target domain should print a console security error
--- a/dom/base/PostMessageEvent.cpp
+++ b/dom/base/PostMessageEvent.cpp
@@ -23,23 +23,25 @@
namespace mozilla {
namespace dom {
PostMessageEvent::PostMessageEvent(nsGlobalWindow* aSource,
const nsAString& aCallerOrigin,
nsGlobalWindow* aTargetWindow,
nsIPrincipal* aProvidedPrincipal,
+ nsIDocument* aSourceDocument,
bool aTrustedCaller)
: StructuredCloneHolder(CloningSupported, TransferringSupported,
SameProcessSameThread),
mSource(aSource),
mCallerOrigin(aCallerOrigin),
mTargetWindow(aTargetWindow),
mProvidedPrincipal(aProvidedPrincipal),
+ mSourceDocument(aSourceDocument),
mTrustedCaller(aTrustedCaller)
{
MOZ_COUNT_CTOR(PostMessageEvent);
}
PostMessageEvent::~PostMessageEvent()
{
MOZ_COUNT_DTOR(PostMessageEvent);
@@ -87,20 +89,40 @@ PostMessageEvent::Run()
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.
if (!targetPrin->Equals(mProvidedPrincipal)) {
+ nsAutoCString origin, targetOrigin;
+ nsresult rv = targetPrin->GetOrigin(targetOrigin);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = mProvidedPrincipal->GetOrigin(origin);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_ConvertUTF8toUTF16 origin16(origin);
+ NS_ConvertUTF8toUTF16 targetOrigin16(targetOrigin);
+ const char16_t* params[] = { origin16.get(), targetOrigin16.get() };
+
+ nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
+ NS_LITERAL_CSTRING("DOM Window"), mSourceDocument,
+ nsContentUtils::eDOM_PROPERTIES,
+ "TargetPrincipalDoesNotMatch",
+ params, ArrayLength(params));
+
return NS_OK;
}
}
+ // The document is just used for the principal mismatch check above, now it is
+ // no longer needed.
+ mSourceDocument = nullptr;
+
ErrorResult rv;
JS::Rooted<JS::Value> messageData(cx);
nsCOMPtr<nsPIDOMWindow> window = targetWindow.get();
Read(window, cx, &messageData, rv);
if (NS_WARN_IF(rv.Failed())) {
return rv.StealNSResult();
}
--- a/dom/base/PostMessageEvent.h
+++ b/dom/base/PostMessageEvent.h
@@ -29,24 +29,26 @@ class PostMessageEvent final : public ns
{
public:
NS_DECL_NSIRUNNABLE
PostMessageEvent(nsGlobalWindow* aSource,
const nsAString& aCallerOrigin,
nsGlobalWindow* aTargetWindow,
nsIPrincipal* aProvidedPrincipal,
+ nsIDocument* aSourceDocument,
bool aTrustedCaller);
private:
~PostMessageEvent();
RefPtr<nsGlobalWindow> mSource;
nsString mCallerOrigin;
RefPtr<nsGlobalWindow> mTargetWindow;
nsCOMPtr<nsIPrincipal> mProvidedPrincipal;
+ nsCOMPtr<nsIDocument> mSourceDocument;
bool mTrustedCaller;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_PostMessageEvent_h
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -7964,16 +7964,19 @@ nsGlobalWindow::PostMessageMozOuter(JSCo
// event creation and dispatch.
RefPtr<PostMessageEvent> event =
new PostMessageEvent(nsContentUtils::IsCallerChrome() || !callerInnerWin
? nullptr
: callerInnerWin->GetOuterWindowInternal(),
origin,
this,
providedPrincipal,
+ !callerInnerWin
+ ? nullptr
+ : callerInnerWin->GetDoc(),
nsContentUtils::IsCallerChrome());
JS::Rooted<JS::Value> message(aCx, aMessage);
JS::Rooted<JS::Value> transfer(aCx, aTransfer);
event->Write(aCx, message, transfer, aError);
if (NS_WARN_IF(aError.Failed())) {
return;
--- a/dom/locales/en-US/chrome/dom/dom.properties
+++ b/dom/locales/en-US/chrome/dom/dom.properties
@@ -185,8 +185,10 @@ BadOpaqueRedirectInterceptionWithURL=Fai
# LOCALIZATION NOTE: Do not translate "ServiceWorker" or "FetchEvent.preventDefault()". %S is a URL.
InterceptionCanceledWithURL=Failed to load '%S'. A ServiceWorker canceled the load by calling FetchEvent.preventDefault().
# LOCALIZATION NOTE: Do not translate "ServiceWorker", "promise", or "FetchEvent.respondWith()". %1$S is a URL. %2$S is an error string.
InterceptionRejectedResponseWithURL=Failed to load '%1$S'. A ServiceWorker passed a promise to FetchEvent.respondWith() that rejected with '%2$S'.
# LOCALIZATION NOTE: Do not translate "ServiceWorker", "promise", "FetchEvent.respondWith()", or "Response". %1$S is a URL. %2$S is an error string.
InterceptedNonResponseWithURL=Failed to load '%1$S'. A ServiceWorker passed a promise to FetchEvent.respondWith() that resolved with non-Response value '%2$S'.
ExecCommandCutCopyDeniedNotInputDriven=document.execCommand('cut'/'copy') was denied because it was not called from inside a short running user-generated event handler.
PatternAttributeCompileFailure=Unable to check <input pattern='%S'> because the pattern is not a valid regexp: %S
+# LOCALIZATION NOTE: Do not translate "postMessage" or DOMWindow. %S values are origins, like https://domain.com:port
+TargetPrincipalDoesNotMatch=Failed to execute 'postMessage' on 'DOMWindow': The target origin provided ('%S') does not match the recipient window's origin ('%S').