Commit 7e618744 authored by Daniel Vogelheim's avatar Daniel Vogelheim Committed by Commit Bot

Origin Policy: Add support for Feature Policy.

Bug: 751996
Change-Id: I969cd67059c6105f468dc56efe01c8de8869565b
Reviewed-on: https://chromium-review.googlesource.com/c/1202202
Commit-Queue: Daniel Vogelheim <vogelheim@chromium.org>
Reviewed-by: default avatarMike West <mkwst@chromium.org>
Cr-Commit-Position: refs/heads/master@{#609331}
parent f8b7ae8d
{
"feature-policy": [
"camera 'self' https://example.org",
"geolocation https://example.org/"
]
}
<!DOCTYPE HTML>
<html>
<head>
<script src='/resources/testharness.js'></script>
<script src='/resources/testharnessreport.js'></script>
</head>
<body>
<script>
async_test(t => {
assert_false(document.policy.allowsFeature('geolocation'));
assert_true(document.policy.allowsFeature('camera'));
t.done();
}, "Origin-Policy-based Feature policy");
</script>
</body>
</html>
......@@ -33,7 +33,12 @@ bool OriginPolicyParser::DoParse(base::StringPiece policy_text) {
return false;
base::Value* csp = json->FindKey("content-security-policy");
return !csp || ParseContentSecurityPolicies(*csp);
bool csp_ok = !csp || ParseContentSecurityPolicies(*csp);
base::Value* features = json->FindKey("feature-policy");
bool features_ok = !features || ParseFeaturePolicies(*features);
return csp_ok && features_ok;
}
bool OriginPolicyParser::ParseContentSecurityPolicies(
......@@ -63,4 +68,23 @@ bool OriginPolicyParser::ParseContentSecurityPolicy(const base::Value& csp) {
return true;
}
bool OriginPolicyParser::ParseFeaturePolicies(const base::Value& policies) {
if (!policies.is_list())
return false;
bool ok = true;
for (const auto& feature : policies.GetList()) {
ok &= ParseFeaturePolicy(feature);
}
return ok;
}
bool OriginPolicyParser::ParseFeaturePolicy(const base::Value& policy) {
if (!policy.is_string())
return false;
policy_->features_.push_back(policy.GetString());
return true;
}
} // namespace blink
......@@ -29,6 +29,8 @@ class OriginPolicyParser {
bool DoParse(base::StringPiece);
bool ParseContentSecurityPolicies(const base::Value&);
bool ParseContentSecurityPolicy(const base::Value&);
bool ParseFeaturePolicies(const base::Value&);
bool ParseFeaturePolicy(const base::Value&);
std::unique_ptr<OriginPolicy> policy_;
......
......@@ -137,3 +137,38 @@ TEST(OriginPolicy, CSPDispositionAbsent) {
)");
ASSERT_FALSE(policy->GetContentSecurityPolicies()[0].report_only);
}
TEST(OriginPolicy, FeatureOne) {
auto policy = blink::OriginPolicy::From(R"(
{ "feature-policy": ["geolocation 'self' http://maps.google.com"] } )");
ASSERT_EQ(1U, policy->GetFeaturePolicies().size());
ASSERT_EQ("geolocation 'self' http://maps.google.com",
policy->GetFeaturePolicies()[0]);
}
TEST(OriginPolicy, FeatureTwo) {
auto policy = blink::OriginPolicy::From(R"(
{ "feature-policy": ["geolocation 'self' http://maps.google.com",
"camera https://example.com"]} )");
ASSERT_EQ(2U, policy->GetFeaturePolicies().size());
ASSERT_EQ("geolocation 'self' http://maps.google.com",
policy->GetFeaturePolicies()[0]);
ASSERT_EQ("camera https://example.com", policy->GetFeaturePolicies()[1]);
}
TEST(OriginPolicy, FeatureTwoPolicies) {
auto policy = blink::OriginPolicy::From(R"(
{ "feature-policy": ["geolocation 'self' http://maps.google.com"],
"feature-policy": ["camera https://example.com"] } )");
// TODO(vogelheim): Determine whether this is the correct behaviour.
ASSERT_EQ(1U, policy->GetFeaturePolicies().size());
}
TEST(OriginPolicy, FeatureComma) {
auto policy = blink::OriginPolicy::From(R"(
{ "feature-policy": ["geolocation 'self' http://maps.google.com, camera https://example.com"]} )");
// TODO: Determine what to do with this case !
ASSERT_EQ(1U, policy->GetFeaturePolicies().size());
}
......@@ -28,12 +28,17 @@ class BLINK_COMMON_EXPORT OriginPolicy {
};
const std::vector<CSP>& GetContentSecurityPolicies() const { return csp_; }
const std::vector<std::string>& GetFeaturePolicies() const {
return features_;
}
private:
friend class OriginPolicyParser;
OriginPolicy();
std::vector<CSP> csp_;
std::vector<std::string> features_;
};
} // namespace blink
......
......@@ -1129,6 +1129,29 @@ bool DocumentLoader::ShouldClearWindowName(
previous_security_origin);
}
// Helper function: Merge the feature policy strings from HTTP headers and the
// origin policy (if any).
// Headers go first, which means that the per-page headers override the
// origin policy features.
void MergeFeaturesFromOriginPolicy(WTF::String& feature_policy,
const String& origin_policy_string) {
if (origin_policy_string.IsEmpty())
return;
std::unique_ptr<OriginPolicy> origin_policy = OriginPolicy::From(
StringUTF8Adaptor(origin_policy_string).AsStringPiece());
if (!origin_policy)
return;
for (const std::string& policy : origin_policy->GetFeaturePolicies()) {
if (!feature_policy.IsEmpty()) {
feature_policy.append(',');
}
feature_policy.append(
WTF::String::FromUTF8(policy.data(), policy.length()));
}
}
void DocumentLoader::InstallNewDocument(
const KURL& url,
Document* owner_document,
......@@ -1246,8 +1269,10 @@ void DocumentLoader::InstallNewDocument(
// FeaturePolicy is reset in the browser process on commit, so this needs to
// be initialized and replicated to the browser process after commit messages
// are sent in didCommitNavigation().
document->ApplyFeaturePolicyFromHeader(
WTF::String feature_policy(
response_.HttpHeaderField(http_names::kFeaturePolicy));
MergeFeaturesFromOriginPolicy(feature_policy, request_.GetOriginPolicy());
document->ApplyFeaturePolicyFromHeader(feature_policy);
GetFrameLoader().DispatchDidClearDocumentOfWindowObject();
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment