Commit 13073107 authored by Hiroki Nakagawa's avatar Hiroki Nakagawa Committed by Commit Bot

S13nServiceWorker: Check the Service-Worker-Allowed header in ServiceWorkerScriptURLLoader

This CL makes ServiceWorkerScriptURLLoader check the path restriction defined in
the Service Worker spec:
https://w3c.github.io/ServiceWorker/#service-worker-script-response

Bug: 748415
Change-Id: I651f83050752a869d0fe9cde1e415d02da70a2c1
Reviewed-on: https://chromium-review.googlesource.com/684104
Commit-Queue: Hiroki Nakagawa <nhiroki@chromium.org>
Reviewed-by: default avatarMatt Falkenhagen <falken@chromium.org>
Cr-Commit-Position: refs/heads/master@{#504495}
parent a5e331cc
......@@ -14,6 +14,7 @@
#include "content/browser/service_worker/service_worker_version.h"
#include "content/browser/service_worker/service_worker_write_to_cache_job.h"
#include "content/browser/url_loader_factory_getter.h"
#include "content/common/service_worker/service_worker_utils.h"
#include "content/public/common/resource_response.h"
#include "third_party/WebKit/common/mime_util/mime_util.h"
......@@ -157,9 +158,6 @@ void ServiceWorkerScriptURLLoader::OnReceiveResponse(
return;
}
// TODO(nhiroki): Check the path restriction.
// (See ServiceWorkerWriteToCacheJob::CheckPathRestriction())
// TODO(nhiroki): Check the SSL certificate.
if (resource_type_ == RESOURCE_TYPE_SERVICE_WORKER) {
......@@ -170,6 +168,23 @@ void ServiceWorkerScriptURLLoader::OnReceiveResponse(
ResourceRequestCompletionStatus(net::ERR_INSECURE_RESPONSE));
return;
}
// Check the path restriction defined in the spec:
// https://w3c.github.io/ServiceWorker/#service-worker-script-response
const char kServiceWorkerAllowed[] = "Service-Worker-Allowed";
std::string service_worker_allowed;
bool has_header = response_head.headers->EnumerateHeader(
nullptr, kServiceWorkerAllowed, &service_worker_allowed);
std::string error_message;
if (!ServiceWorkerUtils::IsPathRestrictionSatisfied(
version_->scope(), request_url_,
has_header ? &service_worker_allowed : nullptr, &error_message)) {
// TODO(nhiroki): Report |error_message|.
CommitCompleted(
ResourceRequestCompletionStatus(net::ERR_INSECURE_RESPONSE));
return;
}
version_->SetMainScriptHttpResponseInfo(*response_info);
}
......
......@@ -138,7 +138,7 @@ class ServiceWorkerScriptURLLoaderTest : public testing::Test {
mock_server_->AddResponse(GURL(kNormalScriptURL),
std::string("HTTP/1.1 200 OK\n"
"CONTENT-TYPE: text/javascript\n\n"),
"Content-Type: text/javascript\n\n"),
std::string("this body came from the network"));
// Initialize URLLoaderFactory.
......@@ -159,8 +159,7 @@ class ServiceWorkerScriptURLLoaderTest : public testing::Test {
// Sets up ServiceWorkerRegistration and ServiceWorkerVersion. This should be
// called before DoRequest().
void SetUpRegistration(const GURL& script_url) {
GURL scope = script_url;
void SetUpRegistration(const GURL& script_url, const GURL& scope) {
registration_ = base::MakeRefCounted<ServiceWorkerRegistration>(
blink::mojom::ServiceWorkerRegistrationOptions(scope), 1L,
helper_->context()->AsWeakPtr());
......@@ -169,6 +168,12 @@ class ServiceWorkerScriptURLLoaderTest : public testing::Test {
version_->SetStatus(ServiceWorkerVersion::NEW);
}
// Sets up ServiceWorkerRegistration and ServiceWorkerVersion with the default
// scope.
void SetUpRegistration(const GURL& script_url) {
SetUpRegistration(script_url, script_url.GetWithoutFilename());
}
void DoRequest(const GURL& request_url) {
DCHECK(registration_);
DCHECK(version_);
......@@ -271,7 +276,7 @@ TEST_F(ServiceWorkerScriptURLLoaderTest, Success_EmptyBody) {
const GURL kEmptyScriptURL("https://example.com/empty.js");
mock_server_->AddResponse(kEmptyScriptURL,
std::string("HTTP/1.1 200 OK\n"
"CONTENT-TYPE: text/javascript\n\n"),
"Content-Type: text/javascript\n\n"),
std::string());
SetUpRegistration(kEmptyScriptURL);
DoRequest(kEmptyScriptURL);
......@@ -328,7 +333,7 @@ TEST_F(ServiceWorkerScriptURLLoaderTest, Error_BadMimeType) {
const GURL kBadMimeTypeScriptURL("https://example.com/bad-mime-type.js");
mock_server_->AddResponse(kBadMimeTypeScriptURL,
std::string("HTTP/1.1 200 OK\n"
"CONTENT-TYPE: text/css\n\n"),
"Content-Type: text/css\n\n"),
std::string("body with bad MIME type"));
SetUpRegistration(kBadMimeTypeScriptURL);
DoRequest(kBadMimeTypeScriptURL);
......@@ -343,6 +348,55 @@ TEST_F(ServiceWorkerScriptURLLoaderTest, Error_BadMimeType) {
EXPECT_FALSE(VerifyStoredResponse(kBadMimeTypeScriptURL));
}
TEST_F(ServiceWorkerScriptURLLoaderTest, Success_PathRestriction) {
// |kScope| is not under the default scope ("/out-of-scope/"), but the
// Service-Worker-Allowed header allows it.
const GURL kScriptURL("https://example.com/out-of-scope/normal.js");
const GURL kScope("https://example.com/in-scope/");
mock_server_->AddResponse(
kScriptURL,
std::string("HTTP/1.1 200 OK\n"
"Content-Type: text/javascript\n"
"Service-Worker-Allowed: /in-scope/\n\n"),
std::string());
SetUpRegistration(kScriptURL, kScope);
DoRequest(kScriptURL);
client_.RunUntilComplete();
EXPECT_EQ(net::OK, client_.completion_status().error_code);
// The client should have received the response.
EXPECT_TRUE(client_.has_received_response());
EXPECT_TRUE(client_.response_body().is_valid());
std::string response;
EXPECT_TRUE(mojo::common::BlockingCopyToString(
client_.response_body_release(), &response));
EXPECT_EQ(mock_server_->GetBody(kScriptURL), response);
// The response should also be stored in the storage.
EXPECT_TRUE(VerifyStoredResponse(kScriptURL));
}
TEST_F(ServiceWorkerScriptURLLoaderTest, Error_PathRestriction) {
// |kScope| is not under the default scope ("/out-of-scope/") and the
// Service-Worker-Allowed header is not specified.
const GURL kScriptURL("https://example.com/out-of-scope/normal.js");
const GURL kScope("https://example.com/in-scope/");
mock_server_->AddResponse(kScriptURL,
std::string("HTTP/1.1 200 OK\n"
"Content-Type: text/javascript\n\n"),
std::string());
SetUpRegistration(kScriptURL, kScope);
DoRequest(kScriptURL);
client_.RunUntilComplete();
// The request should be failed because the scope is not allowed.
EXPECT_EQ(net::ERR_INSECURE_RESPONSE, client_.completion_status().error_code);
EXPECT_FALSE(client_.has_received_response());
// The response shouldn't be stored in the storage.
EXPECT_FALSE(VerifyStoredResponse(kScriptURL));
}
TEST_F(ServiceWorkerScriptURLLoaderTest, Error_RedundantWorker) {
GURL script_url(kNormalScriptURL);
SetUpRegistration(script_url);
......
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