blob: ed5f6eedf82cbfda3cb889b8358704c1791a87df [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 <vector>
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/navigation_simulator.h"
#include "content/public/test/test_renderer_host.h"
#include "content/test/test_render_frame_host.h"
#include "third_party/blink/public/common/feature_policy/feature_policy.h"
#include "third_party/blink/public/common/frame/frame_policy.h"
#include "third_party/blink/public/common/frame/sandbox_flags.h"
#include "url/gurl.h"
#include "url/origin.h"
namespace content {
// Integration tests for feature policy setup and querying through a RFH. These
// tests are not meant to cover every edge case as the FeaturePolicy class
// itself is tested thoroughly in feature_policy_unittest.cc. Instead they are
// meant to ensure that integration with RenderFrameHost works correctly.
class RenderFrameHostFeaturePolicyTest
: public content::RenderViewHostTestHarness {
protected:
static constexpr const char* kOrigin1 = "https://google.com";
static constexpr const char* kOrigin2 = "https://maps.google.com";
static constexpr const char* kOrigin3 = "https://example.com";
static constexpr const char* kOrigin4 = "https://test.com";
static const blink::mojom::FeaturePolicyFeature kDefaultEnabledFeature =
blink::mojom::FeaturePolicyFeature::kDocumentWrite;
static const blink::mojom::FeaturePolicyFeature kDefaultSelfFeature =
blink::mojom::FeaturePolicyFeature::kGeolocation;
static const blink::mojom::FeaturePolicyFeature kParameterizedFeature =
blink::mojom::FeaturePolicyFeature::kOversizedImages;
const blink::PolicyValue sample_double_value =
blink::PolicyValue(2.5, blink::mojom::PolicyValueType::kDecDouble);
const blink::PolicyValue min_double_value =
blink::PolicyValue(2.0, blink::mojom::PolicyValueType::kDecDouble);
const blink::PolicyValue sample_bool_value = blink::PolicyValue(true);
RenderFrameHost* GetMainRFH(const char* origin) {
RenderFrameHost* result = web_contents()->GetMainFrame();
RenderFrameHostTester::For(result)->InitializeRenderFrameIfNeeded();
SimulateNavigation(&result, GURL(origin));
return result;
}
RenderFrameHost* AddChildRFH(RenderFrameHost* parent, const char* origin) {
RenderFrameHost* result =
RenderFrameHostTester::For(parent)->AppendChild("");
RenderFrameHostTester::For(result)->InitializeRenderFrameIfNeeded();
SimulateNavigation(&result, GURL(origin));
return result;
}
// The header policy should only be set once on page load, so we refresh the
// page to simulate that.
void RefreshPageAndSetHeaderPolicy(
RenderFrameHost** rfh,
blink::mojom::FeaturePolicyFeature feature,
const std::map<std::string, blink::PolicyValue>& values) {
RenderFrameHost* current = *rfh;
SimulateNavigation(&current, current->GetLastCommittedURL());
static_cast<TestRenderFrameHost*>(current)->DidSetFramePolicyHeaders(
blink::WebSandboxFlags::kNone, CreateFPHeader(feature, values));
*rfh = current;
}
void SetContainerPolicy(
RenderFrameHost* parent,
RenderFrameHost* child,
blink::mojom::FeaturePolicyFeature feature,
const std::map<std::string, blink::PolicyValue>& values) {
static_cast<TestRenderFrameHost*>(parent)->OnDidChangeFramePolicy(
child->GetRoutingID(),
{blink::WebSandboxFlags::kNone, CreateFPHeader(feature, values)});
}
void SimulateNavigation(RenderFrameHost** rfh, const GURL& url) {
auto navigation_simulator =
NavigationSimulator::CreateRendererInitiated(url, *rfh);
navigation_simulator->Commit();
*rfh = navigation_simulator->GetFinalRenderFrameHost();
}
private:
blink::ParsedFeaturePolicy CreateFPHeader(
blink::mojom::FeaturePolicyFeature feature,
const std::map<std::string, blink::PolicyValue>& values) {
blink::ParsedFeaturePolicy result(1);
result[0].feature = feature;
if (feature == kParameterizedFeature) {
result[0].fallback_value = min_double_value;
result[0].opaque_value = min_double_value;
}
for (auto const& value : values)
result[0].values.insert(std::pair<url::Origin, blink::PolicyValue>(
url::Origin::Create(GURL(value.first)), value.second));
return result;
}
};
TEST_F(RenderFrameHostFeaturePolicyTest, DefaultPolicy) {
RenderFrameHost* parent = GetMainRFH(kOrigin1);
RenderFrameHost* child = AddChildRFH(parent, kOrigin2);
EXPECT_TRUE(parent->IsFeatureEnabled(kDefaultEnabledFeature));
EXPECT_TRUE(parent->IsFeatureEnabled(kDefaultSelfFeature));
EXPECT_TRUE(parent->IsFeatureEnabled(kParameterizedFeature));
EXPECT_TRUE(child->IsFeatureEnabled(kDefaultEnabledFeature));
EXPECT_FALSE(child->IsFeatureEnabled(kDefaultSelfFeature));
EXPECT_TRUE(child->IsFeatureEnabled(kParameterizedFeature));
}
TEST_F(RenderFrameHostFeaturePolicyTest, HeaderPolicy) {
RenderFrameHost* parent = GetMainRFH(kOrigin1);
// Enable the feature for the child in the parent frame.
RefreshPageAndSetHeaderPolicy(&parent, kDefaultSelfFeature,
{{std::string(kOrigin1), sample_bool_value},
{std::string(kOrigin2), sample_bool_value}});
// Create the child.
RenderFrameHost* child = AddChildRFH(parent, kOrigin2);
EXPECT_TRUE(parent->IsFeatureEnabled(kDefaultSelfFeature));
EXPECT_TRUE(child->IsFeatureEnabled(kDefaultSelfFeature));
// Set an empty allowlist in the child to test that the policies combine
// correctly.
RefreshPageAndSetHeaderPolicy(&child, kDefaultSelfFeature,
std::map<std::string, blink::PolicyValue>());
EXPECT_TRUE(parent->IsFeatureEnabled(kDefaultSelfFeature));
EXPECT_FALSE(child->IsFeatureEnabled(kDefaultSelfFeature));
// Re-enable the feature in the child.
RefreshPageAndSetHeaderPolicy(&child, kDefaultSelfFeature,
{{std::string(kOrigin2), sample_bool_value}});
EXPECT_TRUE(child->IsFeatureEnabled(kDefaultSelfFeature));
// Navigate the child. Check that the feature is disabled.
SimulateNavigation(&child, GURL(kOrigin3));
EXPECT_FALSE(child->IsFeatureEnabled(kDefaultSelfFeature));
}
TEST_F(RenderFrameHostFeaturePolicyTest, ContainerPolicy) {
RenderFrameHost* parent = GetMainRFH(kOrigin1);
RenderFrameHost* child = AddChildRFH(parent, kOrigin2);
// Set a container policy on origin 3 to give it the feature. It should not
// be enabled because container policy will only take effect after navigation.
SetContainerPolicy(parent, child, kDefaultSelfFeature,
{{std::string(kOrigin2), sample_bool_value},
{std::string(kOrigin3), sample_bool_value}});
EXPECT_FALSE(child->IsFeatureEnabled(kDefaultSelfFeature));
// Navigate the child so that the container policy takes effect.
SimulateNavigation(&child, GURL(kOrigin3));
EXPECT_TRUE(child->IsFeatureEnabled(kDefaultSelfFeature));
// Navigate the child again, the feature should not be enabled.
SimulateNavigation(&child, GURL(kOrigin4));
EXPECT_FALSE(child->IsFeatureEnabled(kDefaultSelfFeature));
}
TEST_F(RenderFrameHostFeaturePolicyTest, HeaderAndContainerPolicy) {
RenderFrameHost* parent = GetMainRFH(kOrigin1);
// Set a header policy and container policy. Check that they both take effect.
RefreshPageAndSetHeaderPolicy(&parent, kDefaultSelfFeature,
{{std::string(kOrigin1), sample_bool_value},
{std::string(kOrigin2), sample_bool_value}});
RenderFrameHost* child = AddChildRFH(parent, kOrigin2);
SetContainerPolicy(parent, child, kDefaultSelfFeature,
{{std::string(kOrigin3), sample_bool_value}});
// The feature should be enabled in kOrigin2, kOrigin3 but not kOrigin4.
EXPECT_TRUE(child->IsFeatureEnabled(kDefaultSelfFeature));
SimulateNavigation(&child, GURL(kOrigin3));
EXPECT_TRUE(child->IsFeatureEnabled(kDefaultSelfFeature));
SimulateNavigation(&child, GURL(kOrigin4));
EXPECT_FALSE(child->IsFeatureEnabled(kDefaultSelfFeature));
// Change the header policy to turn off the feature. It should be disabled in
// all children.
RefreshPageAndSetHeaderPolicy(&parent, kDefaultSelfFeature,
std::map<std::string, blink::PolicyValue>());
child = AddChildRFH(parent, kOrigin2);
SetContainerPolicy(parent, child, kDefaultSelfFeature,
{{std::string(kOrigin3), sample_bool_value}});
SimulateNavigation(&child, GURL(kOrigin2));
EXPECT_FALSE(child->IsFeatureEnabled(kDefaultSelfFeature));
SimulateNavigation(&child, GURL(kOrigin3));
EXPECT_FALSE(child->IsFeatureEnabled(kDefaultSelfFeature));
}
TEST_F(RenderFrameHostFeaturePolicyTest, ParameterizedHeaderPolicy) {
RenderFrameHost* parent = GetMainRFH(kOrigin1);
blink::PolicyValue large_double_value(
3.5, blink::mojom::PolicyValueType::kDecDouble);
// Enable the feature for the child in the parent frame.
RefreshPageAndSetHeaderPolicy(&parent, kParameterizedFeature,
{{std::string(kOrigin1), sample_double_value},
{std::string(kOrigin2), sample_double_value}});
// Create the child.
RenderFrameHost* child = AddChildRFH(parent, kOrigin2);
EXPECT_TRUE(
parent->IsFeatureEnabled(kParameterizedFeature, sample_double_value));
EXPECT_FALSE(
parent->IsFeatureEnabled(kParameterizedFeature, large_double_value));
EXPECT_TRUE(
child->IsFeatureEnabled(kParameterizedFeature, sample_double_value));
EXPECT_FALSE(
child->IsFeatureEnabled(kParameterizedFeature, large_double_value));
// Set an empty allowlist and in the child to test that the policies combine
// correctly. Child frame should be enabled since the feature default is
// enabled for all.
RefreshPageAndSetHeaderPolicy(&child, kDefaultSelfFeature,
std::map<std::string, blink::PolicyValue>());
EXPECT_TRUE(
child->IsFeatureEnabled(kParameterizedFeature, sample_double_value));
// disable the feature in the child.
RefreshPageAndSetHeaderPolicy(&child, kParameterizedFeature,
{{std::string(kOrigin2), min_double_value}});
EXPECT_TRUE(child->IsFeatureEnabled(kParameterizedFeature, min_double_value));
EXPECT_FALSE(
child->IsFeatureEnabled(kParameterizedFeature, sample_double_value));
// Navigate the child. Check that the feature is disabled.
SimulateNavigation(&child, GURL(kOrigin3));
EXPECT_FALSE(
child->IsFeatureEnabled(kParameterizedFeature, sample_double_value));
}
TEST_F(RenderFrameHostFeaturePolicyTest, ParameterizedContainerPolicy) {
RenderFrameHost* parent = GetMainRFH(kOrigin1);
RenderFrameHost* child = AddChildRFH(parent, kOrigin2);
// Set a container policy on origin 3 to give it the feature. It should not
// be enabled because container policy will only take effect after navigation.
SetContainerPolicy(parent, child, kParameterizedFeature,
{{std::string(kOrigin2), sample_double_value},
{std::string(kOrigin3), sample_double_value}});
blink::PolicyValue large_double_value(
3.0, blink::mojom::PolicyValueType::kDecDouble);
EXPECT_TRUE(
child->IsFeatureEnabled(kParameterizedFeature, large_double_value));
// Navigate the child so that the container policy takes effect.
SimulateNavigation(&child, GURL(kOrigin3));
EXPECT_FALSE(
child->IsFeatureEnabled(kParameterizedFeature, large_double_value));
EXPECT_TRUE(
child->IsFeatureEnabled(kParameterizedFeature, sample_double_value));
// Navigate the child again, the feature should be disabled by container
// policy.
SimulateNavigation(&child, GURL(kOrigin4));
EXPECT_FALSE(
child->IsFeatureEnabled(kParameterizedFeature, sample_double_value));
}
TEST_F(RenderFrameHostFeaturePolicyTest,
ParameterizedHeaderAndContainerPolicy) {
RenderFrameHost* parent = GetMainRFH(kOrigin1);
// Set a header policy and container policy. Check that they both take effect.
RefreshPageAndSetHeaderPolicy(&parent, kParameterizedFeature,
{{std::string(kOrigin1), sample_double_value},
{std::string(kOrigin2), sample_double_value}});
RenderFrameHost* child = AddChildRFH(parent, kOrigin2);
SetContainerPolicy(parent, child, kParameterizedFeature,
{{std::string(kOrigin3), sample_double_value}});
// The feature should be enabled in kOrigin2, kOrigin3 but not kOrigin4.
EXPECT_TRUE(
child->IsFeatureEnabled(kParameterizedFeature, sample_double_value));
SimulateNavigation(&child, GURL(kOrigin3));
EXPECT_TRUE(
child->IsFeatureEnabled(kParameterizedFeature, sample_double_value));
SimulateNavigation(&child, GURL(kOrigin4));
EXPECT_FALSE(
child->IsFeatureEnabled(kParameterizedFeature, sample_double_value));
// Change the header policy to turn off the feature. It should be disabled in
// all children.
RefreshPageAndSetHeaderPolicy(&parent, kParameterizedFeature,
std::map<std::string, blink::PolicyValue>());
child = AddChildRFH(parent, kOrigin2);
SetContainerPolicy(parent, child, kParameterizedFeature,
{{std::string(kOrigin3), sample_double_value}});
SimulateNavigation(&child, GURL(kOrigin2));
EXPECT_FALSE(
child->IsFeatureEnabled(kParameterizedFeature, sample_double_value));
SimulateNavigation(&child, GURL(kOrigin3));
EXPECT_FALSE(
child->IsFeatureEnabled(kParameterizedFeature, sample_double_value));
}
} // namespace content