blob: c02da35efbdab5f7db7b55279d68c9f9ad9582e8 [file] [log] [blame]
// Copyright 2017 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 "third_party/blink/renderer/core/feature_policy/dom_feature_policy.h"
#include "third_party/blink/renderer/core/feature_policy/iframe_policy.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/core/feature_policy/feature_policy_parser.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
namespace blink {
namespace {
constexpr char kSelfOrigin[] = "https://selforigin.com";
constexpr char kOriginA[] = "https://example.com";
constexpr char kOriginB[] = "https://example.net";
} // namespace
using testing::UnorderedElementsAre;
class PolicyTest : public testing::Test {
public:
void SetUp() override {
page_holder_ = std::make_unique<DummyPageHolder>();
auto origin = SecurityOrigin::CreateFromString(kSelfOrigin);
auto feature_policy = FeaturePolicy::CreateFromParentPolicy(
nullptr, ParsedFeaturePolicy(), origin->ToUrlOrigin());
auto header = FeaturePolicyParser::ParseHeader(
"fullscreen *; payment 'self'; midi 'none'; camera 'self' "
"https://example.com https://example.net",
/* permissions_policy_header */ g_empty_string, origin.get(),
dummy_logger_, dummy_logger_);
feature_policy->SetHeaderPolicy(header);
auto& security_context =
page_holder_->GetFrame().DomWindow()->GetSecurityContext();
security_context.SetSecurityOriginForTesting(origin);
security_context.SetFeaturePolicy(std::move(feature_policy));
}
DOMFeaturePolicy* GetPolicy() const { return policy_; }
PolicyParserMessageBuffer dummy_logger_ =
PolicyParserMessageBuffer("", true /* discard_message */);
protected:
std::unique_ptr<DummyPageHolder> page_holder_;
Persistent<DOMFeaturePolicy> policy_;
};
class DOMFeaturePolicyTest : public PolicyTest {
public:
void SetUp() override {
PolicyTest::SetUp();
policy_ = MakeGarbageCollected<DOMFeaturePolicy>(
page_holder_->GetFrame().DomWindow());
}
};
class IFramePolicyTest : public PolicyTest {
public:
void SetUp() override {
PolicyTest::SetUp();
policy_ = MakeGarbageCollected<IFramePolicy>(
page_holder_->GetFrame().DomWindow(), ParsedFeaturePolicy(),
SecurityOrigin::CreateFromString(kSelfOrigin));
}
};
TEST_F(DOMFeaturePolicyTest, TestAllowsFeature) {
EXPECT_FALSE(GetPolicy()->allowsFeature(nullptr, "badfeature"));
EXPECT_FALSE(GetPolicy()->allowsFeature(nullptr, "midi"));
EXPECT_FALSE(GetPolicy()->allowsFeature(nullptr, "midi", kSelfOrigin));
EXPECT_TRUE(GetPolicy()->allowsFeature(nullptr, "fullscreen"));
EXPECT_TRUE(GetPolicy()->allowsFeature(nullptr, "fullscreen", kOriginA));
EXPECT_TRUE(GetPolicy()->allowsFeature(nullptr, "payment"));
EXPECT_FALSE(GetPolicy()->allowsFeature(nullptr, "payment", kOriginA));
EXPECT_FALSE(GetPolicy()->allowsFeature(nullptr, "payment", kOriginB));
EXPECT_TRUE(GetPolicy()->allowsFeature(nullptr, "camera"));
EXPECT_TRUE(GetPolicy()->allowsFeature(nullptr, "camera", kOriginA));
EXPECT_TRUE(GetPolicy()->allowsFeature(nullptr, "camera", kOriginB));
EXPECT_FALSE(
GetPolicy()->allowsFeature(nullptr, "camera", "https://badorigin.com"));
EXPECT_TRUE(GetPolicy()->allowsFeature(nullptr, "geolocation", kSelfOrigin));
EXPECT_TRUE(GetPolicy()->allowsFeature(nullptr, "sync-xhr"));
EXPECT_TRUE(GetPolicy()->allowsFeature(nullptr, "sync-xhr", kOriginA));
}
TEST_F(DOMFeaturePolicyTest, TestGetAllowList) {
EXPECT_THAT(GetPolicy()->getAllowlistForFeature(nullptr, "camera"),
UnorderedElementsAre(kSelfOrigin, kOriginA, kOriginB));
EXPECT_THAT(GetPolicy()->getAllowlistForFeature(nullptr, "payment"),
UnorderedElementsAre(kSelfOrigin));
EXPECT_THAT(GetPolicy()->getAllowlistForFeature(nullptr, "geolocation"),
UnorderedElementsAre(kSelfOrigin));
EXPECT_THAT(GetPolicy()->getAllowlistForFeature(nullptr, "fullscreen"),
UnorderedElementsAre("*"));
EXPECT_TRUE(
GetPolicy()->getAllowlistForFeature(nullptr, "badfeature").IsEmpty());
EXPECT_TRUE(GetPolicy()->getAllowlistForFeature(nullptr, "midi").IsEmpty());
EXPECT_THAT(GetPolicy()->getAllowlistForFeature(nullptr, "sync-xhr"),
UnorderedElementsAre("*"));
}
TEST_F(DOMFeaturePolicyTest, TestAllowedFeatures) {
Vector<String> allowed_features = GetPolicy()->allowedFeatures(nullptr);
EXPECT_TRUE(allowed_features.Contains("fullscreen"));
EXPECT_TRUE(allowed_features.Contains("payment"));
EXPECT_TRUE(allowed_features.Contains("camera"));
// "geolocation" has default policy as allowed on self origin.
EXPECT_TRUE(allowed_features.Contains("geolocation"));
EXPECT_FALSE(allowed_features.Contains("badfeature"));
EXPECT_FALSE(allowed_features.Contains("midi"));
// "sync-xhr" is allowed on all origins
EXPECT_TRUE(allowed_features.Contains("sync-xhr"));
}
TEST_F(IFramePolicyTest, TestAllowsFeature) {
EXPECT_FALSE(GetPolicy()->allowsFeature(nullptr, "badfeature"));
EXPECT_FALSE(GetPolicy()->allowsFeature(nullptr, "midi"));
EXPECT_FALSE(GetPolicy()->allowsFeature(nullptr, "midi", kSelfOrigin));
EXPECT_TRUE(GetPolicy()->allowsFeature(nullptr, "fullscreen"));
EXPECT_FALSE(GetPolicy()->allowsFeature(nullptr, "fullscreen", kOriginA));
EXPECT_TRUE(GetPolicy()->allowsFeature(nullptr, "fullscreen", kSelfOrigin));
EXPECT_TRUE(GetPolicy()->allowsFeature(nullptr, "payment"));
EXPECT_FALSE(GetPolicy()->allowsFeature(nullptr, "payment", kOriginA));
EXPECT_FALSE(GetPolicy()->allowsFeature(nullptr, "payment", kOriginB));
EXPECT_TRUE(GetPolicy()->allowsFeature(nullptr, "camera"));
EXPECT_FALSE(GetPolicy()->allowsFeature(nullptr, "camera", kOriginA));
EXPECT_FALSE(GetPolicy()->allowsFeature(nullptr, "camera", kOriginB));
EXPECT_FALSE(
GetPolicy()->allowsFeature(nullptr, "camera", "https://badorigin.com"));
EXPECT_TRUE(GetPolicy()->allowsFeature(nullptr, "geolocation", kSelfOrigin));
EXPECT_TRUE(GetPolicy()->allowsFeature(nullptr, "sync-xhr"));
EXPECT_TRUE(GetPolicy()->allowsFeature(nullptr, "sync-xhr", kOriginA));
}
TEST_F(IFramePolicyTest, TestGetAllowList) {
EXPECT_THAT(GetPolicy()->getAllowlistForFeature(nullptr, "camera"),
UnorderedElementsAre(kSelfOrigin));
EXPECT_THAT(GetPolicy()->getAllowlistForFeature(nullptr, "payment"),
UnorderedElementsAre(kSelfOrigin));
EXPECT_THAT(GetPolicy()->getAllowlistForFeature(nullptr, "geolocation"),
UnorderedElementsAre(kSelfOrigin));
EXPECT_THAT(GetPolicy()->getAllowlistForFeature(nullptr, "fullscreen"),
UnorderedElementsAre(kSelfOrigin));
EXPECT_TRUE(
GetPolicy()->getAllowlistForFeature(nullptr, "badfeature").IsEmpty());
EXPECT_TRUE(GetPolicy()->getAllowlistForFeature(nullptr, "midi").IsEmpty());
EXPECT_THAT(GetPolicy()->getAllowlistForFeature(nullptr, "sync-xhr"),
UnorderedElementsAre("*"));
}
TEST_F(IFramePolicyTest, TestSameOriginAllowedFeatures) {
Vector<String> allowed_features = GetPolicy()->allowedFeatures(nullptr);
// These features are allowed in a same origin context, and not restricted by
// the parent document's policy.
EXPECT_TRUE(allowed_features.Contains("fullscreen"));
EXPECT_TRUE(allowed_features.Contains("payment"));
EXPECT_TRUE(allowed_features.Contains("camera"));
EXPECT_TRUE(allowed_features.Contains("geolocation"));
// "midi" is restricted by the parent document's policy.
EXPECT_FALSE(allowed_features.Contains("midi"));
// "sync-xhr" is allowed on all origins.
EXPECT_TRUE(allowed_features.Contains("sync-xhr"));
// This feature does not exist, so should not be advertised as allowed.
EXPECT_FALSE(allowed_features.Contains("badfeature"));
}
TEST_F(IFramePolicyTest, TestCrossOriginAllowedFeatures) {
// Update the iframe's policy, given a new origin.
GetPolicy()->UpdateContainerPolicy(
ParsedFeaturePolicy(), SecurityOrigin::CreateFromString(kOriginA));
Vector<String> allowed_features = GetPolicy()->allowedFeatures(nullptr);
// None of these features should be allowed in a cross-origin context.
EXPECT_FALSE(allowed_features.Contains("fullscreen"));
EXPECT_FALSE(allowed_features.Contains("payment"));
EXPECT_FALSE(allowed_features.Contains("camera"));
EXPECT_FALSE(allowed_features.Contains("geolocation"));
EXPECT_FALSE(allowed_features.Contains("midi"));
// "sync-xhr" is allowed on all origins.
EXPECT_TRUE(allowed_features.Contains("sync-xhr"));
// This feature does not exist, so should not be advertised as allowed.
EXPECT_FALSE(allowed_features.Contains("badfeature"));
}
TEST_F(IFramePolicyTest, TestCombinedPolicy) {
ParsedFeaturePolicy container_policy = FeaturePolicyParser::ParseAttribute(
"geolocation 'src'; payment 'none'; midi; camera 'src'",
SecurityOrigin::CreateFromString(kSelfOrigin),
SecurityOrigin::CreateFromString(kOriginA), dummy_logger_);
GetPolicy()->UpdateContainerPolicy(
container_policy, SecurityOrigin::CreateFromString(kOriginA));
Vector<String> allowed_features = GetPolicy()->allowedFeatures(nullptr);
// These features are not explicitly allowed.
EXPECT_FALSE(allowed_features.Contains("fullscreen"));
EXPECT_FALSE(allowed_features.Contains("payment"));
// These features are explicitly allowed.
EXPECT_TRUE(allowed_features.Contains("geolocation"));
EXPECT_TRUE(allowed_features.Contains("camera"));
// "midi" is allowed by the attribute, but still blocked by the parent
// document's policy.
EXPECT_FALSE(allowed_features.Contains("midi"));
// "sync-xhr" is still implicitly allowed on all origins.
EXPECT_TRUE(allowed_features.Contains("sync-xhr"));
// This feature does not exist, so should not be advertised as allowed.
EXPECT_FALSE(allowed_features.Contains("badfeature"));
}
} // namespace blink