author | Andrea Marchesini <amarchesini@mozilla.com> |
Wed, 19 Aug 2015 13:23:58 -0700 | |
changeset 259329 | 7c5cc8cfca19afaeb4af08c669a78219edea72c4 |
parent 259328 | c47ccde36664172dff25f8b444ffd7a6b150cee3 |
child 259330 | e1d8c2010f8346935cfcfc5b6c3586b4a3efe4c8 |
push id | 29277 |
push user | ryanvm@gmail.com |
push date | Wed, 26 Aug 2015 18:32:23 +0000 |
treeherder | mozilla-central@fea87cbeaa6b [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | bkelly |
bugs | 1185640 |
milestone | 43.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h @@ -1691,26 +1691,22 @@ public: * Case insensitive comparison between two strings. However it only ignores * case for ASCII characters a-z. */ static bool EqualsIgnoreASCIICase(const nsAString& aStr1, const nsAString& aStr2); /** * Convert ASCII A-Z to a-z. - * @return NS_OK on success, or NS_ERROR_OUT_OF_MEMORY if making the string - * writable needs to allocate memory and that allocation fails. */ static void ASCIIToLower(nsAString& aStr); static void ASCIIToLower(const nsAString& aSource, nsAString& aDest); /** * Convert ASCII a-z to A-Z. - * @return NS_OK on success, or NS_ERROR_OUT_OF_MEMORY if making the string - * writable needs to allocate memory and that allocation fails. */ static void ASCIIToUpper(nsAString& aStr); static void ASCIIToUpper(const nsAString& aSource, nsAString& aDest); /** * Return whether aStr contains an ASCII uppercase character. */ static bool StringContainsASCIIUpper(const nsAString& aStr);
--- a/dom/workers/ServiceWorkerContainer.cpp +++ b/dom/workers/ServiceWorkerContainer.cpp @@ -3,16 +3,17 @@ /* 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 "ServiceWorkerContainer.h" #include "nsIDocument.h" #include "nsIServiceWorkerManager.h" +#include "nsIURL.h" #include "nsNetUtil.h" #include "nsPIDOMWindow.h" #include "mozilla/Preferences.h" #include "mozilla/Services.h" #include "nsCycleCollectionParticipant.h" #include "nsServiceManagerUtils.h" @@ -95,16 +96,41 @@ ServiceWorkerContainer::RemoveReadyPromi } JSObject* ServiceWorkerContainer::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) { return ServiceWorkerContainerBinding::Wrap(aCx, this, aGivenProto); } +static nsresult +CheckForSlashEscapedCharsInPath(nsIURI* aURI) +{ + MOZ_ASSERT(aURI); + + nsCOMPtr<nsIURL> url(do_QueryInterface(aURI)); + if (NS_WARN_IF(!url)) { + return NS_ERROR_FAILURE; + } + + nsAutoCString path; + nsresult rv = url->GetFilePath(path); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + ToLowerCase(path); + if (path.Find("%2f") != kNotFound || + path.Find("%5c") != kNotFound) { + return NS_ERROR_DOM_TYPE_ERR; + } + + return NS_OK; +} + already_AddRefed<Promise> ServiceWorkerContainer::Register(const nsAString& aScriptURL, const RegistrationOptions& aOptions, ErrorResult& aRv) { nsCOMPtr<nsISupports> promise; nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager(); @@ -139,16 +165,21 @@ ServiceWorkerContainer::Register(const n nsresult rv; nsCOMPtr<nsIURI> scriptURI; rv = NS_NewURI(getter_AddRefs(scriptURI), aScriptURL, nullptr, baseURI); if (NS_WARN_IF(NS_FAILED(rv))) { aRv.ThrowTypeError(MSG_INVALID_URL, &aScriptURL); return nullptr; } + aRv = CheckForSlashEscapedCharsInPath(scriptURI); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + // In ServiceWorkerContainer.register() the scope argument is parsed against // different base URLs depending on whether it was passed or not. nsCOMPtr<nsIURI> scopeURI; // Step 4. If none passed, parse against script's URL if (!aOptions.mScope.WasPassed()) { NS_NAMED_LITERAL_STRING(defaultScope, "./"); rv = NS_NewURI(getter_AddRefs(scopeURI), defaultScope, @@ -166,16 +197,21 @@ ServiceWorkerContainer::Register(const n nullptr, baseURI); if (NS_WARN_IF(NS_FAILED(rv))) { nsAutoCString spec; baseURI->GetSpec(spec); NS_ConvertUTF8toUTF16 wSpec(spec); aRv.ThrowTypeError(MSG_INVALID_SCOPE, &aOptions.mScope.Value(), &wSpec); return nullptr; } + + aRv = CheckForSlashEscapedCharsInPath(scopeURI); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } } // The spec says that the "client" passed to Register() must be the global // where the ServiceWorkerContainer was retrieved from. nsCOMPtr<nsPIDOMWindow> window = GetOwner(); MOZ_ASSERT(window); aRv = swm->Register(window, scopeURI, scriptURI, getter_AddRefs(promise)); if (aRv.Failed()) {
--- a/dom/workers/test/serviceworkers/mochitest.ini +++ b/dom/workers/test/serviceworkers/mochitest.ini @@ -242,10 +242,11 @@ skip-if = toolkit == "android" || toolki [test_strict_mode_warning.html] [test_third_party_iframes.html] [test_unregister.html] [test_workerUnregister.html] [test_workerUpdate.html] [test_workerupdatefoundevent.html] [test_opaque_intercept.html] [test_fetch_event_client_postmessage.html] +[test_escapedSlashes.html] [test_eventsource_intercept.html] [test_not_intercept_plugin.html]
new file mode 100644 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_escapedSlashes.html @@ -0,0 +1,102 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Test for escaped slashes in navigator.serviceWorker.register</title> + <script type="text/javascript" src="http://mochi.test:8888/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="http://mochi.test:8888/tests/SimpleTest/test.css" /> + <base href="https://mozilla.org/"> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"> +</div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + +var tests = [ + { status: true, + scriptURL: "a.js?foo%2fbar", + scopeURL: null }, + { status: false, + scriptURL: "foo%2fbar", + scopeURL: null }, + { status: true, + scriptURL: "a.js?foo%2Fbar", + scopeURL: null }, + { status: false, + scriptURL: "foo%2Fbar", + scopeURL: null }, + { status: true, + scriptURL: "a.js?foo%5cbar", + scopeURL: null }, + { status: false, + scriptURL: "foo%5cbar", + scopeURL: null }, + { status: true, + scriptURL: "a.js?foo%2Cbar", + scopeURL: null }, + { status: false, + scriptURL: "foo%5Cbar", + scopeURL: null }, + { status: true, + scriptURL: "ok.js", + scopeURL: "/scope?foo%2fbar"}, + { status: false, + scriptURL: "ok.js", + scopeURL: "/foo%2fbar"}, + { status: true, + scriptURL: "ok.js", + scopeURL: "/scope?foo%2Fbar"}, + { status: false, + scriptURL: "ok.js", + scopeURL: "foo%2Fbar"}, + { status: true, + scriptURL: "ok.js", + scopeURL: "/scope?foo%5cbar"}, + { status: false, + scriptURL: "ok.js", + scopeURL: "foo%5cbar"}, + { status: true, + scriptURL: "ok.js", + scopeURL: "/scope?foo%5Cbar"}, + { status: false, + scriptURL: "ok.js", + scopeURL: "foo%5Cbar"}, +]; + +function runTest() { + if (!tests.length) { + SimpleTest.finish(); + return; + } + + var test = tests.shift(); + navigator.serviceWorker.register(test.scriptURL, test.scopeURL) + .then(reg => { + ok(false, "Register should fail"); + }, err => { + if (!test.status) { + is(err.name, "TypeError", "Registration should fail with TypeError"); + } else { + ok(test.status, "Register should fail"); + } + }) + .then(runTest); +} + +SimpleTest.waitForExplicitFinish(); +onload = function() { + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.testing.enabled", true], + ["dom.serviceWorkers.enabled", true], + ]}, runTest); +}; + +</script> +</body> +</html>