Bug 466256 - 'Workers: Prevent cross-site workers and properly resolve relative URIs.' r+sr=jst, a=blocking1.9.1+.

This commit is contained in:
Ben Turner 2008-11-27 01:16:41 -05:00
parent d1a3c39453
commit eb37b43641
13 changed files with 210 additions and 25 deletions

View File

@ -51,6 +51,7 @@
#include "nsDOMClassInfoID.h"
#include "nsGlobalWindow.h"
#include "nsJSUtils.h"
#include "nsProxyRelease.h"
#include "nsThreadUtils.h"
#include "nsDOMThreadService.h"
@ -252,7 +253,7 @@ nsDOMWorkerFunctions::LoadScripts(JSContext* aCx,
return JS_FALSE;
}
rv = loader->LoadScripts(aCx, urls);
rv = loader->LoadScripts(aCx, urls, PR_FALSE);
if (NS_FAILED(rv)) {
if (!JS_IsExceptionPending(aCx)) {
JS_ReportError(aCx, "Failed to load scripts");
@ -780,6 +781,21 @@ nsDOMWorker::~nsDOMWorker()
}
NS_ASSERTION(!mFeatures.Length(), "Live features!");
nsCOMPtr<nsIThread> mainThread;
NS_GetMainThread(getter_AddRefs(mainThread));
nsIPrincipal* principal;
mPrincipal.forget(&principal);
if (principal) {
NS_ProxyRelease(mainThread, principal, PR_FALSE);
}
nsIURI* uri;
mURI.forget(&uri);
if (uri) {
NS_ProxyRelease(mainThread, uri, PR_FALSE);
}
}
/* static */ nsresult
@ -1111,7 +1127,7 @@ nsDOMWorker::CompileGlobalObject(JSContext* aCx)
return PR_FALSE;
}
rv = loader->LoadScript(aCx, mScriptURL);
rv = loader->LoadScript(aCx, mScriptURL, PR_TRUE);
JS_ReportPendingException(aCx);
@ -1121,6 +1137,8 @@ nsDOMWorker::CompileGlobalObject(JSContext* aCx)
return PR_FALSE;
}
NS_ASSERTION(mPrincipal && mURI, "Script loader didn't set our principal!");
return PR_TRUE;
}

View File

@ -42,6 +42,8 @@
#include "nsIDOMEventTarget.h"
#include "nsIDOMWorkers.h"
#include "nsIJSNativeInitializer.h"
#include "nsIPrincipal.h"
#include "nsIURI.h"
#include "nsIXPCScriptable.h"
#include "jsapi.h"
@ -162,6 +164,22 @@ private:
void ResumeFeatures();
void CancelFeatures();
nsIPrincipal* GetPrincipal() {
return mPrincipal;
}
void SetPrincipal(nsIPrincipal* aPrincipal) {
mPrincipal = aPrincipal;
}
nsIURI* GetURI() {
return mURI;
}
void SetURI(nsIURI* aURI) {
mURI = aURI;
}
private:
// mParent will live as long as mParentWN but only mParentWN will keep the JS
@ -190,6 +208,9 @@ private:
nsIXPConnectWrappedNative* mWrappedNative;
nsCOMPtr<nsIPrincipal> mPrincipal;
nsCOMPtr<nsIURI> mURI;
PRPackedBool mCanceled;
PRPackedBool mSuspended;
PRPackedBool mCompileAttempted;

View File

@ -69,7 +69,8 @@ nsDOMWorkerScriptLoader::nsDOMWorkerScriptLoader(nsDOMWorker* aWorker)
: nsDOMWorkerFeature(aWorker),
mTarget(nsnull),
mScriptCount(0),
mCanceled(PR_FALSE)
mCanceled(PR_FALSE),
mForWorker(PR_FALSE)
{
// Created on worker thread.
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
@ -82,7 +83,8 @@ NS_IMPL_ISUPPORTS_INHERITED2(nsDOMWorkerScriptLoader, nsDOMWorkerFeature,
nsresult
nsDOMWorkerScriptLoader::LoadScripts(JSContext* aCx,
const nsTArray<nsString>& aURLs)
const nsTArray<nsString>& aURLs,
PRBool aForWorker)
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aCx, "Null context!");
@ -94,6 +96,8 @@ nsDOMWorkerScriptLoader::LoadScripts(JSContext* aCx,
return NS_ERROR_ABORT;
}
mForWorker = aForWorker;
mScriptCount = aURLs.Length();
if (!mScriptCount) {
return NS_ERROR_INVALID_ARG;
@ -147,12 +151,13 @@ nsDOMWorkerScriptLoader::LoadScripts(JSContext* aCx,
nsresult
nsDOMWorkerScriptLoader::LoadScript(JSContext* aCx,
const nsString& aURL)
const nsString& aURL,
PRBool aForWorker)
{
nsAutoTArray<nsString, 1> url;
url.AppendElement(aURL);
return LoadScripts(aCx, url);
return LoadScripts(aCx, url, aForWorker);
}
nsresult
@ -414,9 +419,36 @@ nsDOMWorkerScriptLoader::RunInternal()
return NS_ERROR_ABORT;
}
nsIPrincipal* principal;
nsIURI* baseURI;
if (mForWorker) {
NS_ASSERTION(mScriptCount == 1, "Bad state!");
nsRefPtr<nsDOMWorker> parentWorker = mWorker->GetParent();
if (parentWorker) {
principal = parentWorker->GetPrincipal();
NS_ENSURE_STATE(principal);
baseURI = parentWorker->GetURI();
NS_ENSURE_STATE(baseURI);
}
else {
principal = parentDoc->NodePrincipal();
NS_ENSURE_STATE(principal);
baseURI = parentDoc->GetBaseURI();
}
}
else {
principal = mWorker->GetPrincipal();
baseURI = mWorker->GetURI();
NS_ASSERTION(principal && baseURI, "Should have been set already!");
}
// All of these can potentially be null, but that should be ok. We'll either
// succeed without them or fail below.
nsIURI* parentBaseURI = parentDoc->GetBaseURI();
nsCOMPtr<nsILoadGroup> loadGroup(parentDoc->GetDocumentLoadGroup());
nsCOMPtr<nsIIOService> ios(do_GetIOService());
@ -427,7 +459,7 @@ nsDOMWorkerScriptLoader::RunInternal()
nsCOMPtr<nsIURI>& uri = loadInfo.finalURI;
rv = nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri),
loadInfo.url, parentDoc,
parentBaseURI);
baseURI);
if (NS_FAILED(rv)) {
return rv;
}
@ -435,21 +467,12 @@ nsDOMWorkerScriptLoader::RunInternal()
nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE);
rv = secMan->CheckLoadURIWithPrincipal(parentDoc->NodePrincipal(), uri, 0);
if (NS_FAILED(rv)) {
return rv;
}
PRInt16 shouldLoad = nsIContentPolicy::ACCEPT;
rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_SCRIPT,
uri,
parentDoc->NodePrincipal(),
parentDoc,
rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_SCRIPT, uri,
principal, parentDoc,
NS_LITERAL_CSTRING("text/javascript"),
nsnull,
&shouldLoad,
nsContentUtils::GetContentPolicy(),
secMan);
nsnull, &shouldLoad,
nsContentUtils::GetContentPolicy(), secMan);
if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
if (NS_FAILED(rv) || shouldLoad != nsIContentPolicy::REJECT_TYPE) {
return NS_ERROR_CONTENT_BLOCKED;
@ -457,6 +480,22 @@ nsDOMWorkerScriptLoader::RunInternal()
return NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
}
// If this script loader is being used to make a new worker then we need to
// do a same-origin check. Otherwise we need to clear the load with the
// security manager.
if (mForWorker) {
rv = principal->CheckMayLoad(uri, PR_FALSE);
NS_ENSURE_SUCCESS(rv, rv);
// Set the principal and URI on the new worker.
mWorker->SetPrincipal(principal);
mWorker->SetURI(uri);
}
else {
rv = secMan->CheckLoadURIWithPrincipal(principal, uri, 0);
NS_ENSURE_SUCCESS(rv, rv);
}
// We need to know which index we're on in OnStreamComplete so we know where
// to put the result.
nsCOMPtr<nsISupportsPRUint32> indexSupports =

View File

@ -102,10 +102,12 @@ public:
nsDOMWorkerScriptLoader(nsDOMWorker* aWorker);
nsresult LoadScripts(JSContext* aCx,
const nsTArray<nsString>& aURLs);
const nsTArray<nsString>& aURLs,
PRBool aForWorker);
nsresult LoadScript(JSContext* aCx,
const nsString& aURL);
const nsString& aURL,
PRBool aForWorker);
virtual void Cancel();
@ -216,6 +218,7 @@ private:
nsTArray<ScriptLoaderRunnable*> mPendingRunnables;
PRPackedBool mCanceled;
PRPackedBool mForWorker;
};
#endif /* __NSDOMWORKERSCRIPTLOADER_H__ */

View File

@ -41,7 +41,7 @@ topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
relativesrcdir = dom/src/threads/tests
relativesrcdir = dom/src/threads/test
include $(DEPTH)/config/autoconf.mk
include $(topsrcdir)/config/rules.mk
@ -61,6 +61,10 @@ _TEST_FILES = \
recursion_worker.js \
test_regExpStatics.html \
regExpStatics_worker.js \
test_relativeLoad.html \
relativeLoad_worker.js \
relativeLoad_worker2.js \
relativeLoad_import.js \
test_simpleThread.html \
simpleThread_worker.js \
test_terminate.html \
@ -81,5 +85,14 @@ _TEST_FILES = \
fibonacci_worker.js \
$(NULL)
_SUBDIR_TEST_FILES = \
relativeLoad_sub_worker.js \
relativeLoad_sub_worker2.js \
relativeLoad_sub_import.js \
$(NULL)
libs:: $(_TEST_FILES)
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
libs:: $(_SUBDIR_TEST_FILES)
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)/subdir

View File

@ -0,0 +1 @@
const workerURL = "relativeLoad_worker.js";

View File

@ -0,0 +1 @@
const workerSubURL = "subdir/relativeLoad_sub_worker.js";

View File

@ -0,0 +1,15 @@
const importSubURL = "relativeLoad_sub_import.js";
onmessage = function(event) {
importScripts(importSubURL);
var worker = new Worker("relativeLoad_sub_worker2.js");
worker.onerror = function(event) {
throw event.data;
};
worker.onmessage = function(event) {
if (event.data != workerSubURL) {
throw "Bad data!";
}
postMessage(workerSubURL);
};
};

View File

@ -0,0 +1,5 @@
const importSubURL = "relativeLoad_sub_import.js";
importScripts(importSubURL);
postMessage(workerSubURL);

View File

@ -0,0 +1,15 @@
const importURL = "relativeLoad_import.js";
onmessage = function(event) {
importScripts(importURL);
var worker = new Worker("relativeLoad_worker2.js");
worker.onerror = function(event) {
throw event.data;
};
worker.onmessage = function(event) {
if (event.data != workerURL) {
throw "Bad data!";
}
postMessage(workerURL);
}
};

View File

@ -0,0 +1,5 @@
const importURL = "relativeLoad_import.js";
importScripts(importURL);
postMessage(workerURL);

View File

@ -22,7 +22,7 @@ Tests of DOM Worker Threads
worker.onerror = function(event) {
is(event.data,
'[JavaScript Error: "too much recursion" {file: "http://localhost:8888' +
'/tests/dom/src/threads/tests/recursion_worker.js" line: 2}]');
'/tests/dom/src/threads/test/recursion_worker.js" line: 2}]');
SimpleTest.finish();
}

View File

@ -0,0 +1,49 @@
<!DOCTYPE HTML>
<html>
<!--
Tests of DOM Worker Threads relative load
-->
<head>
<title>Test for DOM Worker Threads</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<pre id="test">
<script class="testbody" type="text/javascript">
var index = -1;
var urls = [
"relativeLoad_worker.js",
"subdir/relativeLoad_sub_worker.js"
];
function messageHandler(event) {
if (index >= 0) {
is(event.data, urls[index], "Bad url!");
if (index == urls.length - 1) {
SimpleTest.finish();
return;
}
}
var worker = new Worker(urls[++index]);
worker.onmessage = messageHandler;
worker.onerror = function(event) {
ok(false, "Worker had an error: " + event.data);
SimpleTest.finish();
};
worker.postMessage("start");
}
messageHandler();
SimpleTest.waitForExplicitFinish();
</script>
</pre>
</body>
</html>