-
Notifications
You must be signed in to change notification settings - Fork 6.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Permissions Policy Wildcards] (1) Add function to detect subdomain m…
…atches This CL focuses just on the wildcard subdomain matching function itself as it's a dangerous point of failure and needs deep review. Design doc: w3c/webappsec-permissions-policy#482 This CL is part of a series: (1) Add function to detect subdomain matches (2) Use OriginWithPossibleWildcards in policy (3) Parse wildcard subdomain matches in policy (4) Add WPTs Bug: 1345994 Change-Id: I42b4513038e91db0883d3e9afa77c7ebed57e3fb Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3910955 Auto-Submit: Ari Chivukula <arichiv@chromium.org> Reviewed-by: Dominick Ng <dominickn@chromium.org> Reviewed-by: Yoav Weiss <yoavweiss@chromium.org> Reviewed-by: Ian Clelland <iclelland@chromium.org> Commit-Queue: Ari Chivukula <arichiv@chromium.org> Cr-Commit-Position: refs/heads/main@{#1052043}
- Loading branch information
Showing
10 changed files
with
336 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
90 changes: 90 additions & 0 deletions
90
third_party/blink/common/permissions_policy/origin_with_possible_wildcards.cc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
// Copyright 2022 The Chromium Authors | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
#include "third_party/blink/public/common/permissions_policy/origin_with_possible_wildcards.h" | ||
|
||
#include "base/feature_list.h" | ||
#include "third_party/blink/public/common/features.h" | ||
#include "url/origin.h" | ||
|
||
namespace blink { | ||
|
||
OriginWithPossibleWildcards::OriginWithPossibleWildcards() = default; | ||
|
||
OriginWithPossibleWildcards::OriginWithPossibleWildcards( | ||
const url::Origin& origin, | ||
bool has_subdomain_wildcard) | ||
: origin(origin), has_subdomain_wildcard(has_subdomain_wildcard) { | ||
// Origins that do have wildcards cannot be opaque. | ||
DCHECK(!origin.opaque() || !has_subdomain_wildcard); | ||
} | ||
|
||
OriginWithPossibleWildcards::OriginWithPossibleWildcards( | ||
const OriginWithPossibleWildcards& rhs) = default; | ||
|
||
OriginWithPossibleWildcards& OriginWithPossibleWildcards::operator=( | ||
const OriginWithPossibleWildcards& rhs) = default; | ||
|
||
OriginWithPossibleWildcards::~OriginWithPossibleWildcards() = default; | ||
|
||
bool OriginWithPossibleWildcards::DoesMatchOrigin( | ||
const url::Origin& match_origin) const { | ||
// TODO(crbug.com/1345994): Merge logic with IsSubdomainOfHost where possible. | ||
if (has_subdomain_wildcard) { | ||
// Only try to match at all if wildcard matching is enabled. | ||
if (!base::FeatureList::IsEnabled( | ||
features::kWildcardSubdomainsInPermissionsPolicy)) { | ||
return false; | ||
} | ||
// This function won't match https://*.foo.com with https://foo.com. | ||
if (origin == match_origin) { | ||
return false; | ||
} | ||
const auto& tested_host = match_origin.host(); | ||
const auto& policy_host = origin.host(); | ||
// The tested host must be at least 2 char longer than the policy host | ||
// to be a subdomain of it. | ||
if (tested_host.length() < (policy_host.length() + 2)) { | ||
return false; | ||
} | ||
// The tested host must end with the policy host. | ||
if (!base::EndsWith(tested_host, policy_host)) { | ||
return false; | ||
} | ||
// The tested host without the policy host must end with a ".". | ||
if (tested_host[tested_host.length() - policy_host.length() - 1] != '.') { | ||
return false; | ||
} | ||
// If anything but the host doesn't match they can't match. | ||
if (origin != url::Origin::CreateFromNormalizedTuple(match_origin.scheme(), | ||
policy_host, | ||
match_origin.port())) { | ||
return false; | ||
} | ||
return true; | ||
} else { | ||
// If there is no wildcard test normal match. | ||
return origin == match_origin; | ||
} | ||
} | ||
|
||
bool operator==(const OriginWithPossibleWildcards& lhs, | ||
const OriginWithPossibleWildcards& rhs) { | ||
return std::tie(lhs.origin, lhs.has_subdomain_wildcard) == | ||
std::tie(rhs.origin, rhs.has_subdomain_wildcard); | ||
} | ||
|
||
bool operator!=(const OriginWithPossibleWildcards& lhs, | ||
const OriginWithPossibleWildcards& rhs) { | ||
return std::tie(lhs.origin, lhs.has_subdomain_wildcard) != | ||
std::tie(rhs.origin, rhs.has_subdomain_wildcard); | ||
} | ||
|
||
bool operator<(const OriginWithPossibleWildcards& lhs, | ||
const OriginWithPossibleWildcards& rhs) { | ||
return std::tie(lhs.origin, lhs.has_subdomain_wildcard) < | ||
std::tie(rhs.origin, rhs.has_subdomain_wildcard); | ||
} | ||
|
||
} // namespace blink |
137 changes: 137 additions & 0 deletions
137
third_party/blink/common/permissions_policy/origin_with_possible_wildcards_unittest.cc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
// Copyright 2022 The Chromium Authors | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
#include "third_party/blink/public/common/permissions_policy/origin_with_possible_wildcards.h" | ||
|
||
#include "base/test/gtest_util.h" | ||
#include "base/test/scoped_feature_list.h" | ||
#include "testing/gtest/include/gtest/gtest.h" | ||
#include "third_party/blink/public/common/features.h" | ||
#include "url/gurl.h" | ||
#include "url/origin.h" | ||
|
||
namespace blink { | ||
|
||
class OriginWithPossibleWildcardsTest : public testing::TestWithParam<bool> { | ||
public: | ||
void SetUp() override { | ||
scoped_feature_list_.InitWithFeatureState( | ||
features::kWildcardSubdomainsInPermissionsPolicy, | ||
HasWildcardSubdomainsInPermissionsPolicy()); | ||
} | ||
|
||
bool HasWildcardSubdomainsInPermissionsPolicy() { return GetParam(); } | ||
|
||
private: | ||
base::test::ScopedFeatureList scoped_feature_list_; | ||
}; | ||
|
||
INSTANTIATE_TEST_SUITE_P(All, OriginWithPossibleWildcardsTest, testing::Bool()); | ||
|
||
TEST_P(OriginWithPossibleWildcardsTest, DoesMatchOrigin) { | ||
// Tuple of {origin to test, origin in policy, w/ wildcard, result, | ||
// description}. | ||
const auto& values = { | ||
std::make_tuple(url::Origin::Create(GURL("https://foo.com")), | ||
url::Origin::Create(GURL("https://foo.com")), false, true, | ||
"Same origin, no wildcard"), | ||
std::make_tuple(url::Origin::Create(GURL("https://foo.com")), | ||
url::Origin::Create(GURL("http://foo.com")), false, false, | ||
"Different scheme, no wildcard"), | ||
std::make_tuple(url::Origin::Create(GURL("https://foo.com")), | ||
url::Origin::Create(GURL("https://foo.com:443")), false, | ||
true, "Ignore default port, no wildcard"), | ||
std::make_tuple(url::Origin::Create(GURL("https://bar.foo.com")), | ||
url::Origin::Create(GURL("https://foo.com")), false, | ||
false, "Subdomain matches, no wildcard"), | ||
std::make_tuple(url::Origin::Create(GURL("https://foo.com")), | ||
url::Origin::Create(GURL("https://bar.foo.com")), false, | ||
false, "Different subdomain, no wildcard"), | ||
std::make_tuple(url::Origin::Create(GURL("https://foo.com")), | ||
url::Origin(), false, false, | ||
"Origin to opaque, no wildcard"), | ||
std::make_tuple(url::Origin(), | ||
url::Origin::Create(GURL("https://foo.com")), false, | ||
false, "Opaque to origin, no wildcard"), | ||
std::make_tuple(url::Origin(), url::Origin(), false, false, | ||
"Opaque to opaque, no wildcard"), | ||
std::make_tuple(url::Origin::Create(GURL("file:///test")), | ||
url::Origin::Create(GURL("file:///test")), false, true, | ||
"File, no wildcard"), | ||
std::make_tuple(url::Origin::Create(GURL("http://192.168.1.1")), | ||
url::Origin::Create(GURL("http://192.168.1.1")), false, | ||
true, "Same IPv4, no wildcard"), | ||
std::make_tuple(url::Origin::Create(GURL("http://192.168.1.1")), | ||
url::Origin::Create(GURL("http://192.168.1.2")), false, | ||
false, "Different IPv4, no wildcard"), | ||
std::make_tuple(url::Origin::Create(GURL("http://[2001:db8::1]")), | ||
url::Origin::Create(GURL("http://[2001:db8::1]")), false, | ||
true, "Same IPv6, no wildcard"), | ||
std::make_tuple(url::Origin::Create(GURL("http://[2001:db8::1]")), | ||
url::Origin::Create(GURL("http://[2001:db8::2]")), false, | ||
false, "Different IPv6, no wildcard"), | ||
std::make_tuple(url::Origin::Create(GURL("https://foo.com")), | ||
url::Origin::Create(GURL("https://foo.com")), true, false, | ||
"Same origin, w/ wildcard"), | ||
std::make_tuple(url::Origin::Create(GURL("https://bar.foo.com")), | ||
url::Origin::Create(GURL("https://foo.com")), true, | ||
HasWildcardSubdomainsInPermissionsPolicy(), | ||
"Subdomain matches, w/ wildcard"), | ||
std::make_tuple(url::Origin::Create(GURL("http://bar.foo.com")), | ||
url::Origin::Create(GURL("https://foo.com")), true, false, | ||
"Different scheme, w/ wildcard"), | ||
std::make_tuple(url::Origin::Create(GURL("https://baz.bar.foo.com")), | ||
url::Origin::Create(GURL("https://foo.com")), true, | ||
HasWildcardSubdomainsInPermissionsPolicy(), | ||
"Sub-subdomain matches, w/ wildcard"), | ||
std::make_tuple(url::Origin::Create(GURL("https://foo.com")), | ||
url::Origin::Create(GURL("https://bar.foo.com")), true, | ||
false, "Subdomain doesn't match, w/ wildcard"), | ||
std::make_tuple(url::Origin::Create(GURL("https://bar.foo.com")), | ||
url::Origin::Create(GURL("https://foo.com:443")), true, | ||
HasWildcardSubdomainsInPermissionsPolicy(), | ||
"Ignore default port, w/ wildcard"), | ||
std::make_tuple(url::Origin(), | ||
url::Origin::Create(GURL("https://foo.com")), true, false, | ||
"Opaque to origin, w/ wildcard"), | ||
std::make_tuple(url::Origin::Create(GURL("file:///test")), | ||
url::Origin::Create(GURL("file:///test")), true, false, | ||
"File, w/ wildcard"), | ||
std::make_tuple(url::Origin::Create(GURL("http://192.168.1.1")), | ||
url::Origin::Create(GURL("http://192.168.1.1")), true, | ||
false, "Same IPv4, no wildcard"), | ||
std::make_tuple(url::Origin::Create(GURL("http://192.168.1.1")), | ||
url::Origin::Create(GURL("http://192.168.1.2")), true, | ||
false, "Different IPv4, no wildcard"), | ||
std::make_tuple(url::Origin::Create(GURL("http://[2001:db8::1]")), | ||
url::Origin::Create(GURL("http://[2001:db8::1]")), true, | ||
false, "Same IPv6, no wildcard"), | ||
std::make_tuple(url::Origin::Create(GURL("http://[2001:db8::1]")), | ||
url::Origin::Create(GURL("http://[2001:db8::2]")), true, | ||
false, "Different IPv6, no wildcard")}; | ||
for (const auto& value : values) { | ||
SCOPED_TRACE(std::get<4>(value)); | ||
EXPECT_EQ(std::get<3>(value), OriginWithPossibleWildcards( | ||
std::get<1>(value), std::get<2>(value)) | ||
.DoesMatchOrigin(std::get<0>(value))); | ||
} | ||
} | ||
|
||
TEST_P(OriginWithPossibleWildcardsTest, Constructors) { | ||
OriginWithPossibleWildcards a; | ||
OriginWithPossibleWildcards b(url::Origin(), false); | ||
OriginWithPossibleWildcards c(b); | ||
OriginWithPossibleWildcards d = c; | ||
EXPECT_NE(a, b); | ||
EXPECT_EQ(b, c); | ||
EXPECT_EQ(c, d); | ||
} | ||
|
||
TEST_P(OriginWithPossibleWildcardsTest, Opaque) { | ||
OriginWithPossibleWildcards opaque_without_wildcards(url::Origin(), false); | ||
EXPECT_DCHECK_DEATH( | ||
OriginWithPossibleWildcards opaque_with_wildcards(url::Origin(), true)); | ||
} | ||
|
||
} // namespace blink |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
54 changes: 54 additions & 0 deletions
54
third_party/blink/public/common/permissions_policy/origin_with_possible_wildcards.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
// Copyright 2022 The Chromium Authors | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
#ifndef THIRD_PARTY_BLINK_PUBLIC_COMMON_PERMISSIONS_POLICY_ORIGIN_WITH_POSSIBLE_WILDCARDS_H_ | ||
#define THIRD_PARTY_BLINK_PUBLIC_COMMON_PERMISSIONS_POLICY_ORIGIN_WITH_POSSIBLE_WILDCARDS_H_ | ||
|
||
#include "third_party/blink/public/common/common_export.h" | ||
#include "url/origin.h" | ||
|
||
namespace blink { | ||
|
||
// This struct can represent an origin like https://foo.com/ or like | ||
// https://*.foo.com/. The wildcard can only represent a subdomain. | ||
// Note that https://*.foo.com/ matches domains like https://example.foo.com/ | ||
// or https://test.example.foo.com/ but does not match https://foo.com/. | ||
// Origins that do have wildcards cannot be opaque. | ||
struct BLINK_COMMON_EXPORT OriginWithPossibleWildcards { | ||
OriginWithPossibleWildcards(); | ||
OriginWithPossibleWildcards(const url::Origin& origin, | ||
bool has_subdomain_wildcard); | ||
OriginWithPossibleWildcards(const OriginWithPossibleWildcards& rhs); | ||
OriginWithPossibleWildcards& operator=( | ||
const OriginWithPossibleWildcards& rhs); | ||
~OriginWithPossibleWildcards(); | ||
|
||
// If there is no subdomain wildcard, this function returns true if the | ||
// origins match. | ||
// For example: https://foo.com/ matches <https://foo.com/, false> but | ||
// https://bar.foo.com/ does not match <https://foo.com/, false>. | ||
// | ||
// If there is a subdomain wildcard, this function returns | ||
// true if and only if the first origin is a subdomain of the second. | ||
// For example: https://bar.foo.com/ matches <https://foo.com/, true> but | ||
// https://foo.com/ does not match <https://foo.com/, true>. | ||
// | ||
// For more details on use see: | ||
// https://github.com/w3c/webappsec-permissions-policy/pull/482 | ||
bool DoesMatchOrigin(const url::Origin& match_origin) const; | ||
|
||
url::Origin origin; | ||
bool has_subdomain_wildcard{false}; | ||
}; | ||
|
||
bool BLINK_COMMON_EXPORT operator==(const OriginWithPossibleWildcards& lhs, | ||
const OriginWithPossibleWildcards& rhs); | ||
bool BLINK_COMMON_EXPORT operator!=(const OriginWithPossibleWildcards& lhs, | ||
const OriginWithPossibleWildcards& rhs); | ||
bool BLINK_COMMON_EXPORT operator<(const OriginWithPossibleWildcards& lhs, | ||
const OriginWithPossibleWildcards& rhs); | ||
|
||
} // namespace blink | ||
|
||
#endif // THIRD_PARTY_BLINK_PUBLIC_COMMON_PERMISSIONS_POLICY_ORIGIN_WITH_POSSIBLE_WILDCARDS_H_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.