blob: 6828fb9195f08410746e7f971e5db86325e40c98 [file] [log] [blame]
// Copyright 2018 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 "services/network/initiator_lock_compatibility.h"
#include <string>
#include "base/feature_list.h"
#include "base/logging.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "services/network/public/cpp/features.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/mojom/network_context.mojom.h"
#include "url/gurl.h"
#include "url/origin.h"
#include "url/url_constants.h"
namespace network {
InitiatorLockCompatibility VerifyRequestInitiatorLock(
const base::Optional<url::Origin>& request_initiator_site_lock,
const base::Optional<url::Origin>& request_initiator) {
if (!request_initiator_site_lock.has_value())
return InitiatorLockCompatibility::kNoLock;
const url::Origin& lock = request_initiator_site_lock.value();
if (!request_initiator.has_value()) {
if (base::FeatureList::IsEnabled(network::features::kNetworkService)) {
// SECURITY CHECK: Renderer processes should always provide a
// |request_initiator|. Similarly, browser-side features acting on
// behalf of a renderer process (like AppCache), should always provide a
// |request_initiator|.
// Callers associated with features (e.g. Sec-Fetch-Site) that may handle
// browser-initiated requests (e.g. navigations and/or SafeBrowsing
// traffic) need to take extra care to avoid calling
// VerifyRequestInitiatorLock (and/or GetTrustworthyInitiator) with no
// |request_initiator|. Such features should return early when handling
// request with no |request_initiator| but only when the request comes
// through a URLLoaderFactory associated with kBrowserProcessId.
return InitiatorLockCompatibility::kNoInitiator;
const url::Origin& initiator = request_initiator.value();
// TODO(lukasza, nasko): Also consider equality of precursor origins (e.g. if
// |initiator| is opaque, then it's precursor origin should match the |lock|
// [or |lock|'s precursor if |lock| is also opaque]).
if (initiator.opaque() || (initiator == lock))
return InitiatorLockCompatibility::kCompatibleLock;
// TODO(lukasza, nasko): Return kIncorrectLock if
// the origins do not match exactly in the previous if statement. This should
// be possible to do once we no longer fall back to site_url and have
// request_initiator_*origin*_lock instead. In practice, the fallback can go
// away after we no longer vend process-wide factory:
if (!initiator.opaque() && !lock.opaque() &&
initiator.scheme() == lock.scheme() &&
initiator.GetURL().SchemeIsHTTPOrHTTPS() &&
!initiator.GetURL().HostIsIPAddress()) {
std::string lock_domain =;
if (!lock_domain.empty() && lock_domain.back() == '.')
lock_domain.erase(lock_domain.length() - 1);
if (initiator.DomainIs(lock_domain))
return InitiatorLockCompatibility::kCompatibleLock;
return InitiatorLockCompatibility::kIncorrectLock;
url::Origin GetTrustworthyInitiator(
const base::Optional<url::Origin>& request_initiator_site_lock,
const base::Optional<url::Origin>& request_initiator) {
// Returning a unique origin as a fallback should be safe - such origin will
// be considered cross-origin from all other origins.
url::Origin unique_origin_fallback;
if (!request_initiator.has_value())
return unique_origin_fallback;
if (!base::FeatureList::IsEnabled(features::kNetworkService) ||
!base::FeatureList::IsEnabled(features::kRequestInitiatorSiteLock)) {
return request_initiator.value();
InitiatorLockCompatibility initiator_compatibility =
if (initiator_compatibility == InitiatorLockCompatibility::kIncorrectLock)
return unique_origin_fallback;
// If all the checks above passed, then |request_initiator| is trustworthy.
return request_initiator.value();
} // namespace network