blob: 30faed4513617bdbe49af964b5d364da2e0f6d8d [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/common/service_worker/service_worker_utils.h"
#include <string>
#include "base/logging.h"
#include "base/strings/string_util.h"
#include "content/public/common/origin_util.h"
namespace content {
namespace {
bool PathContainsDisallowedCharacter(const GURL& url) {
std::string path = url.path();
DCHECK(base::IsStringUTF8(path));
// We should avoid these escaped characters in the path component because
// these can be handled differently depending on server implementation.
if (path.find("%2f") != std::string::npos ||
path.find("%2F") != std::string::npos) {
return true;
}
if (path.find("%5c") != std::string::npos ||
path.find("%5C") != std::string::npos) {
return true;
}
return false;
}
} // namespace
// static
bool ServiceWorkerUtils::ScopeMatches(const GURL& scope, const GURL& url) {
DCHECK(!scope.has_ref());
return base::StartsWith(url.spec(), scope.spec(),
base::CompareCase::SENSITIVE);
}
// static
bool ServiceWorkerUtils::IsPathRestrictionSatisfied(
const GURL& scope,
const GURL& script_url,
const std::string* service_worker_allowed_header_value,
std::string* error_message) {
DCHECK(scope.is_valid());
DCHECK(!scope.has_ref());
DCHECK(script_url.is_valid());
DCHECK(!script_url.has_ref());
DCHECK(error_message);
if (ContainsDisallowedCharacter(scope, script_url, error_message))
return false;
std::string max_scope_string;
if (service_worker_allowed_header_value) {
GURL max_scope = script_url.Resolve(*service_worker_allowed_header_value);
if (!max_scope.is_valid()) {
*error_message = "An invalid Service-Worker-Allowed header value ('";
error_message->append(*service_worker_allowed_header_value);
error_message->append("') was received when fetching the script.");
return false;
}
max_scope_string = max_scope.path();
} else {
max_scope_string = script_url.Resolve(".").path();
}
std::string scope_string = scope.path();
if (!base::StartsWith(scope_string, max_scope_string,
base::CompareCase::SENSITIVE)) {
*error_message = "The path of the provided scope ('";
error_message->append(scope_string);
error_message->append("') is not under the max scope allowed (");
if (service_worker_allowed_header_value)
error_message->append("set by Service-Worker-Allowed: ");
error_message->append("'");
error_message->append(max_scope_string);
error_message->append(
"'). Adjust the scope, move the Service Worker script, or use the "
"Service-Worker-Allowed HTTP header to allow the scope.");
return false;
}
return true;
}
// static
bool ServiceWorkerUtils::ContainsDisallowedCharacter(
const GURL& scope,
const GURL& script_url,
std::string* error_message) {
if (PathContainsDisallowedCharacter(scope) ||
PathContainsDisallowedCharacter(script_url)) {
*error_message = "The provided scope ('";
error_message->append(scope.spec());
error_message->append("') or scriptURL ('");
error_message->append(script_url.spec());
error_message->append("') includes a disallowed escape character.");
return true;
}
return false;
}
// static
bool ServiceWorkerUtils::CanRegisterServiceWorker(const GURL& context_url,
const GURL& pattern,
const GURL& script_url) {
DCHECK(context_url.is_valid());
DCHECK(pattern.is_valid());
DCHECK(script_url.is_valid());
return ServiceWorkerUtils::PassOriginEqualitySecurityCheck<GURL>(
context_url, pattern, script_url) &&
OriginCanAccessServiceWorkers(context_url) &&
OriginCanAccessServiceWorkers(pattern) &&
OriginCanAccessServiceWorkers(script_url);
}
bool LongestScopeMatcher::MatchLongest(const GURL& scope) {
if (!ServiceWorkerUtils::ScopeMatches(scope, url_))
return false;
if (match_.is_empty() || match_.spec().size() < scope.spec().size()) {
match_ = scope;
return true;
}
return false;
}
} // namespace content