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 @@ ...@@ -14,6 +14,7 @@
#include "content/browser/service_worker/service_worker_version.h" #include "content/browser/service_worker/service_worker_version.h"
#include "content/browser/service_worker/service_worker_write_to_cache_job.h" #include "content/browser/service_worker/service_worker_write_to_cache_job.h"
#include "content/browser/url_loader_factory_getter.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 "content/public/common/resource_response.h"
#include "third_party/WebKit/common/mime_util/mime_util.h" #include "third_party/WebKit/common/mime_util/mime_util.h"
...@@ -157,9 +158,6 @@ void ServiceWorkerScriptURLLoader::OnReceiveResponse( ...@@ -157,9 +158,6 @@ void ServiceWorkerScriptURLLoader::OnReceiveResponse(
return; return;
} }
// TODO(nhiroki): Check the path restriction.
// (See ServiceWorkerWriteToCacheJob::CheckPathRestriction())
// TODO(nhiroki): Check the SSL certificate. // TODO(nhiroki): Check the SSL certificate.
if (resource_type_ == RESOURCE_TYPE_SERVICE_WORKER) { if (resource_type_ == RESOURCE_TYPE_SERVICE_WORKER) {
...@@ -170,6 +168,23 @@ void ServiceWorkerScriptURLLoader::OnReceiveResponse( ...@@ -170,6 +168,23 @@ void ServiceWorkerScriptURLLoader::OnReceiveResponse(
ResourceRequestCompletionStatus(net::ERR_INSECURE_RESPONSE)); ResourceRequestCompletionStatus(net::ERR_INSECURE_RESPONSE));
return; 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); version_->SetMainScriptHttpResponseInfo(*response_info);
} }
......
...@@ -138,7 +138,7 @@ class ServiceWorkerScriptURLLoaderTest : public testing::Test { ...@@ -138,7 +138,7 @@ class ServiceWorkerScriptURLLoaderTest : public testing::Test {
mock_server_->AddResponse(GURL(kNormalScriptURL), mock_server_->AddResponse(GURL(kNormalScriptURL),
std::string("HTTP/1.1 200 OK\n" 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")); std::string("this body came from the network"));
// Initialize URLLoaderFactory. // Initialize URLLoaderFactory.
...@@ -159,8 +159,7 @@ class ServiceWorkerScriptURLLoaderTest : public testing::Test { ...@@ -159,8 +159,7 @@ class ServiceWorkerScriptURLLoaderTest : public testing::Test {
// Sets up ServiceWorkerRegistration and ServiceWorkerVersion. This should be // Sets up ServiceWorkerRegistration and ServiceWorkerVersion. This should be
// called before DoRequest(). // called before DoRequest().
void SetUpRegistration(const GURL& script_url) { void SetUpRegistration(const GURL& script_url, const GURL& scope) {
GURL scope = script_url;
registration_ = base::MakeRefCounted<ServiceWorkerRegistration>( registration_ = base::MakeRefCounted<ServiceWorkerRegistration>(
blink::mojom::ServiceWorkerRegistrationOptions(scope), 1L, blink::mojom::ServiceWorkerRegistrationOptions(scope), 1L,
helper_->context()->AsWeakPtr()); helper_->context()->AsWeakPtr());
...@@ -169,6 +168,12 @@ class ServiceWorkerScriptURLLoaderTest : public testing::Test { ...@@ -169,6 +168,12 @@ class ServiceWorkerScriptURLLoaderTest : public testing::Test {
version_->SetStatus(ServiceWorkerVersion::NEW); 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) { void DoRequest(const GURL& request_url) {
DCHECK(registration_); DCHECK(registration_);
DCHECK(version_); DCHECK(version_);
...@@ -271,7 +276,7 @@ TEST_F(ServiceWorkerScriptURLLoaderTest, Success_EmptyBody) { ...@@ -271,7 +276,7 @@ TEST_F(ServiceWorkerScriptURLLoaderTest, Success_EmptyBody) {
const GURL kEmptyScriptURL("https://example.com/empty.js"); const GURL kEmptyScriptURL("https://example.com/empty.js");
mock_server_->AddResponse(kEmptyScriptURL, mock_server_->AddResponse(kEmptyScriptURL,
std::string("HTTP/1.1 200 OK\n" std::string("HTTP/1.1 200 OK\n"
"CONTENT-TYPE: text/javascript\n\n"), "Content-Type: text/javascript\n\n"),
std::string()); std::string());
SetUpRegistration(kEmptyScriptURL); SetUpRegistration(kEmptyScriptURL);
DoRequest(kEmptyScriptURL); DoRequest(kEmptyScriptURL);
...@@ -328,7 +333,7 @@ TEST_F(ServiceWorkerScriptURLLoaderTest, Error_BadMimeType) { ...@@ -328,7 +333,7 @@ TEST_F(ServiceWorkerScriptURLLoaderTest, Error_BadMimeType) {
const GURL kBadMimeTypeScriptURL("https://example.com/bad-mime-type.js"); const GURL kBadMimeTypeScriptURL("https://example.com/bad-mime-type.js");
mock_server_->AddResponse(kBadMimeTypeScriptURL, mock_server_->AddResponse(kBadMimeTypeScriptURL,
std::string("HTTP/1.1 200 OK\n" 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")); std::string("body with bad MIME type"));
SetUpRegistration(kBadMimeTypeScriptURL); SetUpRegistration(kBadMimeTypeScriptURL);
DoRequest(kBadMimeTypeScriptURL); DoRequest(kBadMimeTypeScriptURL);
...@@ -343,6 +348,55 @@ TEST_F(ServiceWorkerScriptURLLoaderTest, Error_BadMimeType) { ...@@ -343,6 +348,55 @@ TEST_F(ServiceWorkerScriptURLLoaderTest, Error_BadMimeType) {
EXPECT_FALSE(VerifyStoredResponse(kBadMimeTypeScriptURL)); 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) { TEST_F(ServiceWorkerScriptURLLoaderTest, Error_RedundantWorker) {
GURL script_url(kNormalScriptURL); GURL script_url(kNormalScriptURL);
SetUpRegistration(script_url); 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