Commit 786b4918 authored by Amos Lim's avatar Amos Lim Committed by Commit Bot

service worker: Let register('data://blah') throw a TypeError

register('data://blah') should throw a TypeError instead of SecurityError.
If scopeURL’s scheme is not one of "http" and "https", reject promise
with a TypeError and abort these steps.[1]

[1]: https://w3c.github.io/ServiceWorker/#start-register-algorithm

Bug: 877138
Change-Id: Ic1e8cae52ec9393152044aa37da389eb499165bb
Reviewed-on: https://chromium-review.googlesource.com/c/1189688
Commit-Queue: Amos Lim <eui-sang.lim@samsung.com>
Reviewed-by: default avatarMatt Falkenhagen <falken@chromium.org>
Cr-Commit-Position: refs/heads/master@{#608635}
parent 53ce5a61
This is a testharness.js-based test.
PASS Script URL including URL-encoded slash
PASS Script URL including uppercase URL-encoded slash
PASS Script URL including URL-encoded backslash
PASS Script URL including uppercase URL-encoded backslash
FAIL Script URL is a data URL assert_throws: Data URLs should not be registered as service workers. function "function() { throw e }" threw object "SecurityError: Failed to register a ServiceWorker: The origin of the provided scriptURL ('null') does not match the current origin ('https://web-platform.test:8444')." ("SecurityError") expected object "TypeError" ("TypeError")
PASS Script URL including self-reference
PASS Script URL including parent-reference
Harness: the test ran to completion.
...@@ -94,9 +94,9 @@ function registration_tests_scope(register_method, check_error_types) { ...@@ -94,9 +94,9 @@ function registration_tests_scope(register_method, check_error_types) {
var script = 'resources/empty-worker.js'; var script = 'resources/empty-worker.js';
var scope = 'filesystem:' + normalizeURL('resources/scope/filesystem-scope-url'); var scope = 'filesystem:' + normalizeURL('resources/scope/filesystem-scope-url');
return promise_rejects(t, return promise_rejects(t,
check_error_types ? 'SecurityError' : null, check_error_types ? new TypeError : null,
register_method(script, {scope: scope}), register_method(script, {scope: scope}),
'Registering with the scope that has same-origin filesystem: URL ' + 'Registering with the scope that has same-origin filesystem: URL ' +
'should fail with SecurityError.'); 'should fail with TypeError.');
}, 'Scope URL is same-origin filesystem: URL'); }, 'Scope URL is same-origin filesystem: URL');
} }
...@@ -70,9 +70,9 @@ function registration_tests_security_error(register_method, check_error_types) { ...@@ -70,9 +70,9 @@ function registration_tests_security_error(register_method, check_error_types) {
var script = 'filesystem:' + normalizeURL('resources/empty-worker.js'); var script = 'filesystem:' + normalizeURL('resources/empty-worker.js');
var scope = 'resources/scope/filesystem-script-url'; var scope = 'resources/scope/filesystem-script-url';
return promise_rejects(t, return promise_rejects(t,
check_error_types ? 'SecurityError' : null, check_error_types ? new TypeError : null,
register_method(script, {scope: scope}), register_method(script, {scope: scope}),
'Registering a script which has same-origin filesystem: URL should ' + 'Registering a script which has same-origin filesystem: URL should ' +
'fail with SecurityError.'); 'fail with TypeError.');
}, 'Script URL is same-origin filesystem: URL'); }, 'Script URL is same-origin filesystem: URL');
} }
...@@ -10,7 +10,7 @@ async_test(function(t) { ...@@ -10,7 +10,7 @@ async_test(function(t) {
assert_unreached('register() should fail'); assert_unreached('register() should fail');
}, function(e) { }, function(e) {
assert_throws( assert_throws(
'SecurityError', function() { throw e; }, new TypeError, function() { throw e; },
'register() on local file should fail'); 'register() on local file should fail');
assert_equals( assert_equals(
e.message, e.message,
......
...@@ -243,7 +243,7 @@ ScriptPromise ServiceWorkerContainer::registerServiceWorker( ...@@ -243,7 +243,7 @@ ScriptPromise ServiceWorkerContainer::registerServiceWorker(
if (!SchemeRegistry::ShouldTreatURLSchemeAsAllowingServiceWorkers( if (!SchemeRegistry::ShouldTreatURLSchemeAsAllowingServiceWorkers(
page_url.Protocol())) { page_url.Protocol())) {
callbacks->OnError(WebServiceWorkerError( callbacks->OnError(WebServiceWorkerError(
mojom::blink::ServiceWorkerErrorType::kSecurity, mojom::blink::ServiceWorkerErrorType::kType,
String("Failed to register a ServiceWorker: The URL protocol of the " String("Failed to register a ServiceWorker: The URL protocol of the "
"current origin ('" + "current origin ('" +
document_origin->ToString() + "') is not supported."))); document_origin->ToString() + "') is not supported.")));
...@@ -253,6 +253,16 @@ ScriptPromise ServiceWorkerContainer::registerServiceWorker( ...@@ -253,6 +253,16 @@ ScriptPromise ServiceWorkerContainer::registerServiceWorker(
KURL script_url = execution_context->CompleteURL(url); KURL script_url = execution_context->CompleteURL(url);
script_url.RemoveFragmentIdentifier(); script_url.RemoveFragmentIdentifier();
if (!SchemeRegistry::ShouldTreatURLSchemeAsAllowingServiceWorkers(
script_url.Protocol())) {
callbacks->OnError(WebServiceWorkerError(
mojom::blink::ServiceWorkerErrorType::kType,
String("Failed to register a ServiceWorker: The URL protocol of the "
"script ('" +
script_url.GetString() + "') is not supported.")));
return promise;
}
if (!document_origin->CanRequest(script_url)) { if (!document_origin->CanRequest(script_url)) {
scoped_refptr<const SecurityOrigin> script_origin = scoped_refptr<const SecurityOrigin> script_origin =
SecurityOrigin::Create(script_url); SecurityOrigin::Create(script_url);
...@@ -265,47 +275,39 @@ ScriptPromise ServiceWorkerContainer::registerServiceWorker( ...@@ -265,47 +275,39 @@ ScriptPromise ServiceWorkerContainer::registerServiceWorker(
document_origin->ToString() + "')."))); document_origin->ToString() + "').")));
return promise; return promise;
} }
KURL scope_url;
if (options->scope().IsNull())
scope_url = KURL(script_url, "./");
else
scope_url = execution_context->CompleteURL(options->scope());
scope_url.RemoveFragmentIdentifier();
if (!SchemeRegistry::ShouldTreatURLSchemeAsAllowingServiceWorkers( if (!SchemeRegistry::ShouldTreatURLSchemeAsAllowingServiceWorkers(
script_url.Protocol())) { scope_url.Protocol())) {
callbacks->OnError(WebServiceWorkerError( callbacks->OnError(WebServiceWorkerError(
mojom::blink::ServiceWorkerErrorType::kSecurity, mojom::blink::ServiceWorkerErrorType::kType,
String("Failed to register a ServiceWorker: The URL protocol of the " String("Failed to register a ServiceWorker: The URL protocol of the "
"script ('" + "scope ('" +
script_url.GetString() + "') is not supported."))); scope_url.GetString() + "') is not supported.")));
return promise; return promise;
} }
KURL pattern_url; if (!document_origin->CanRequest(scope_url)) {
if (options->scope().IsNull()) scoped_refptr<const SecurityOrigin> scope_origin =
pattern_url = KURL(script_url, "./"); SecurityOrigin::Create(scope_url);
else
pattern_url = execution_context->CompleteURL(options->scope());
pattern_url.RemoveFragmentIdentifier();
if (!document_origin->CanRequest(pattern_url)) {
scoped_refptr<const SecurityOrigin> pattern_origin =
SecurityOrigin::Create(pattern_url);
callbacks->OnError( callbacks->OnError(
WebServiceWorkerError(mojom::blink::ServiceWorkerErrorType::kSecurity, WebServiceWorkerError(mojom::blink::ServiceWorkerErrorType::kSecurity,
String("Failed to register a ServiceWorker: The " String("Failed to register a ServiceWorker: The "
"origin of the provided scope ('" + "origin of the provided scope ('" +
pattern_origin->ToString() + scope_origin->ToString() +
"') does not match the current origin ('" + "') does not match the current origin ('" +
document_origin->ToString() + "')."))); document_origin->ToString() + "').")));
return promise; return promise;
} }
if (!SchemeRegistry::ShouldTreatURLSchemeAsAllowingServiceWorkers(
pattern_url.Protocol())) {
callbacks->OnError(WebServiceWorkerError(
mojom::blink::ServiceWorkerErrorType::kSecurity,
String("Failed to register a ServiceWorker: The URL protocol of the "
"scope ('" +
pattern_url.GetString() + "') is not supported.")));
return promise;
}
WebString web_error_message; WebString web_error_message;
if (!provider_->ValidateScopeAndScriptURL(pattern_url, script_url, if (!provider_->ValidateScopeAndScriptURL(scope_url, script_url,
&web_error_message)) { &web_error_message)) {
callbacks->OnError(WebServiceWorkerError( callbacks->OnError(WebServiceWorkerError(
mojom::blink::ServiceWorkerErrorType::kType, mojom::blink::ServiceWorkerErrorType::kType,
...@@ -335,7 +337,7 @@ ScriptPromise ServiceWorkerContainer::registerServiceWorker( ...@@ -335,7 +337,7 @@ ScriptPromise ServiceWorkerContainer::registerServiceWorker(
ParseUpdateViaCache(options->updateViaCache()); ParseUpdateViaCache(options->updateViaCache());
mojom::ScriptType type = ParseScriptType(options->type()); mojom::ScriptType type = ParseScriptType(options->type());
provider_->RegisterServiceWorker(pattern_url, script_url, type, provider_->RegisterServiceWorker(scope_url, script_url, type,
update_via_cache, std::move(callbacks)); update_via_cache, std::move(callbacks));
return promise; return promise;
} }
......
// Copyright 2014 The Chromium Authors. All rights reserved. // Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
...@@ -121,6 +120,33 @@ class ExpectDOMException : public ScriptValueTest { ...@@ -121,6 +120,33 @@ class ExpectDOMException : public ScriptValueTest {
String expected_message_; String expected_message_;
}; };
// Matches a ScriptValue and a TypeError with a message.
class ExpectTypeError : public ScriptValueTest {
public:
ExpectTypeError(const String& expected_message)
: expected_message_(expected_message) {}
~ExpectTypeError() override = default;
void operator()(ScriptValue value) const override {
v8::Isolate* isolate = value.GetIsolate();
v8::Local<v8::Context> context = value.GetContext();
v8::Local<v8::Object> error_object =
value.V8Value()->ToObject(context).ToLocalChecked();
v8::Local<v8::Value> name =
error_object->Get(context, V8String(isolate, "name")).ToLocalChecked();
v8::Local<v8::Value> message =
error_object->Get(context, V8String(isolate, "message"))
.ToLocalChecked();
EXPECT_EQ("TypeError", ToCoreString(name->ToString(isolate)));
EXPECT_EQ(expected_message_, ToCoreString(message->ToString(isolate)));
}
private:
String expected_message_;
};
// Service Worker-specific tests. // Service Worker-specific tests.
class NotReachedWebServiceWorkerProvider : public WebServiceWorkerProvider { class NotReachedWebServiceWorkerProvider : public WebServiceWorkerProvider {
...@@ -128,7 +154,7 @@ class NotReachedWebServiceWorkerProvider : public WebServiceWorkerProvider { ...@@ -128,7 +154,7 @@ class NotReachedWebServiceWorkerProvider : public WebServiceWorkerProvider {
~NotReachedWebServiceWorkerProvider() override = default; ~NotReachedWebServiceWorkerProvider() override = default;
void RegisterServiceWorker( void RegisterServiceWorker(
const WebURL& pattern, const WebURL& scope,
const WebURL& script_url, const WebURL& script_url,
blink::mojom::ScriptType script_type, blink::mojom::ScriptType script_type,
mojom::ServiceWorkerUpdateViaCache update_via_cache, mojom::ServiceWorkerUpdateViaCache update_via_cache,
...@@ -213,14 +239,24 @@ TEST_F(ServiceWorkerContainerTest, Register_CrossOriginScriptIsRejected) { ...@@ -213,14 +239,24 @@ TEST_F(ServiceWorkerContainerTest, Register_CrossOriginScriptIsRejected) {
"current origin ('https://www.example.com').")); "current origin ('https://www.example.com')."));
} }
TEST_F(ServiceWorkerContainerTest, Register_UnsupportedSchemeIsRejected) {
SetPageURL("https://www.example.com");
TestRegisterRejected(
"https://www.example.com",
"wss://www.example.com/", // Only support http and https
ExpectTypeError(
"Failed to register a ServiceWorker: The URL protocol "
"of the scope ('wss://www.example.com/') is not supported."));
}
TEST_F(ServiceWorkerContainerTest, Register_CrossOriginScopeIsRejected) { TEST_F(ServiceWorkerContainerTest, Register_CrossOriginScopeIsRejected) {
SetPageURL("https://www.example.com"); SetPageURL("https://www.example.com");
TestRegisterRejected( TestRegisterRejected(
"https://www.example.com", "https://www.example.com",
"wss://www.example.com/", // Differs by protocol "http://www.example.com/", // Differs by protocol
ExpectDOMException("SecurityError", ExpectDOMException("SecurityError",
"Failed to register a ServiceWorker: The origin of " "Failed to register a ServiceWorker: The origin of "
"the provided scope ('wss://www.example.com') does " "the provided scope ('http://www.example.com') does "
"not match the current origin " "not match the current origin "
"('https://www.example.com').")); "('https://www.example.com')."));
} }
...@@ -271,14 +307,14 @@ class StubWebServiceWorkerProvider { ...@@ -271,14 +307,14 @@ class StubWebServiceWorkerProvider {
~WebServiceWorkerProviderImpl() override = default; ~WebServiceWorkerProviderImpl() override = default;
void RegisterServiceWorker( void RegisterServiceWorker(
const WebURL& pattern, const WebURL& scope,
const WebURL& script_url, const WebURL& script_url,
blink::mojom::ScriptType script_type, blink::mojom::ScriptType script_type,
mojom::ServiceWorkerUpdateViaCache update_via_cache, mojom::ServiceWorkerUpdateViaCache update_via_cache,
std::unique_ptr<WebServiceWorkerRegistrationCallbacks> callbacks) std::unique_ptr<WebServiceWorkerRegistrationCallbacks> callbacks)
override { override {
owner_.register_call_count_++; owner_.register_call_count_++;
owner_.register_scope_ = pattern; owner_.register_scope_ = scope;
owner_.register_script_url_ = script_url; owner_.register_script_url_ = script_url;
owner_.script_type_ = script_type; owner_.script_type_ = script_type;
owner_.update_via_cache_ = update_via_cache; owner_.update_via_cache_ = update_via_cache;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment