Commit 9dc79473 authored by Ian Clelland's avatar Ian Clelland Committed by Commit Bot

Refactor Feature Policy algorithms to more closely resemble spec.

This change restructures the Feature Policy (and proposed Permisssions
Policy) construction code to roughly match the algorithms in the
specifications. Comments are added to show the correspondence between
code and spec.

The Feature Policy inherited policy calculation is based on
https://www.w3.org/TR/2019/WD-feature-policy-1-20190416/#define-inherited-policy-in-container

The code which calculates the proposed Permissions Policy inherited
policy is based on
https://w3c.github.io/webappsec-permissions-policy/#algo-define-inherited-policy-in-container

Change-Id: I6436b41958b6c6175410b9935f48029a04cf34ea
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2460232Reviewed-by: default avatarCharlie Hu <chenleihu@google.com>
Commit-Queue: Ian Clelland <iclelland@chromium.org>
Cr-Commit-Position: refs/heads/master@{#815271}
parent 4cab33d4
...@@ -234,94 +234,91 @@ std::unique_ptr<FeaturePolicy> FeaturePolicy::CreateFromParentPolicy( ...@@ -234,94 +234,91 @@ std::unique_ptr<FeaturePolicy> FeaturePolicy::CreateFromParentPolicy(
std::unique_ptr<FeaturePolicy> new_policy = std::unique_ptr<FeaturePolicy> new_policy =
base::WrapUnique(new FeaturePolicy(origin, features)); base::WrapUnique(new FeaturePolicy(origin, features));
// For features which are not keys in a container policy, which is the case
// here *until* we call AddContainerPolicy at the end of this method,
// https://wicg.github.io/feature-policy/#define-inherited-policy-in-container
// returns true if |feature| is enabled in |parent_policy| for |origin|.
for (const auto& feature : features) { for (const auto& feature : features) {
if (!parent_policy) {
// If no parent policy, set inherited policy to true.
new_policy->inherited_policies_[feature.first] = true;
// Temporary code to support metrics (https://crbug.com/937131)
new_policy->proposed_inherited_policies_[feature.first] = true;
// End of temporary metrics code
} else {
new_policy->inherited_policies_[feature.first] = new_policy->inherited_policies_[feature.first] =
parent_policy->GetFeatureValueForOrigin(feature.first, origin); new_policy->GetInheritedValueForFeature(parent_policy, feature,
container_policy);
// Temporary code to support metrics (https://crbug.com/937131)
new_policy->proposed_inherited_policies_[feature.first] =
parent_policy->GetProposedFeatureValueForOrigin(
feature.first, parent_policy->origin_) &&
parent_policy->GetProposedFeatureValueForOrigin(feature.first,
origin);
// For features which currently use 'self' default allowlist, set the
// proposed inherited policy to "allow self" if the container policy does
// not mention this feature at all.
if (feature.second == FeaturePolicyFeatureDefault::EnableForSelf) {
bool found_in_container_policy = std::any_of(
container_policy.begin(), container_policy.end(),
[&](const auto& decl) { return decl.feature == feature.first; });
if (!found_in_container_policy) {
new_policy->proposed_inherited_policies_[feature.first] = new_policy->proposed_inherited_policies_[feature.first] =
new_policy->proposed_inherited_policies_[feature.first] && new_policy->GetProposedInheritedValueForFeature(parent_policy, feature,
origin.IsSameOriginWith(parent_policy->origin_); container_policy);
} }
} return new_policy;
// End of temporary metrics code }
// Implements Feature Policy 9.8: Define an inherited policy for feature in
// document and 9.9: Define an inherited policy for feature in container at
// origin.
bool FeaturePolicy::GetInheritedValueForFeature(
const FeaturePolicy* parent_policy,
std::pair<mojom::FeaturePolicyFeature, FeaturePolicyFeatureDefault> feature,
const ParsedFeaturePolicy& container_policy) const {
// 9.8 3: Otherwise [If context is not a nested browsing context,] return
// "Enabled".
if (!parent_policy)
return true;
for (const auto& decl : container_policy) {
if (decl.feature == feature.first) {
// 9.9 3.1: If the allowlist for feature in container policy does not
// match origin, return "Disabled".
if (!AllowlistFromDeclaration(decl, feature_list_)->Contains(origin_))
return false;
// 9.9 3.2: If feature is enabled in parent for parent’s origin, return
// "Enabled".
if (parent_policy->GetFeatureValueForOrigin(feature.first,
parent_policy->origin_))
return true;
} }
} }
if (!container_policy.empty())
new_policy->AddContainerPolicy(container_policy, parent_policy); // 9.9 4: If feature is enabled in parent for parent’s origin, return
return new_policy; // "Enabled".
// 9.9 5: Otherwise return "Disabled".
return parent_policy->GetFeatureValueForOrigin(feature.first, origin_);
} }
void FeaturePolicy::AddContainerPolicy( // Temporary code to support metrics (https://crbug.com/937131)
const ParsedFeaturePolicy& container_policy, // Implements Permissions Policy 9.7: Define an inherited policy for feature in
const FeaturePolicy* parent_policy) { // browsing context and 9.8: Define an inherited policy for feature in container
DCHECK(parent_policy); // at origin.
// For features which are keys in a container policy, bool FeaturePolicy::GetProposedInheritedValueForFeature(
// https://wicg.github.io/feature-policy/#define-inherited-policy-in-container const FeaturePolicy* parent_policy,
// returns true only if |feature| is enabled in |parent| for either |origin| std::pair<mojom::FeaturePolicyFeature, FeaturePolicyFeatureDefault> feature,
// or |parent|'s origin, and the allowlist for |feature| matches |origin|. const ParsedFeaturePolicy& container_policy) const {
// // 9.7 2: Otherwise [If context is not a nested browsing context,] return
// Roughly, If a feature is enabled in the parent frame, and the parent // "Enabled".
// chooses to delegate it to the child frame, using the iframe attribute, then if (!parent_policy)
// the feature should be enabled in the child frame. return true;
for (const ParsedFeaturePolicyDeclaration& parsed_declaration :
container_policy) {
mojom::FeaturePolicyFeature feature = parsed_declaration.feature;
// Temporary code to support metrics: (https://crbug.com/937131) // 9.8 2: If policy’s inherited policy for feature is "Disabled", return
// Compute the proposed new inherited value, where the parent *must* allow // "Disabled".
// the feature in the child frame, but where the default header value if not if (!parent_policy->GetProposedFeatureValueForOrigin(feature.first,
// specified is '*'. parent_policy->origin_))
auto proposed_inherited_policy = proposed_inherited_policies_.find(feature); return false;
if (proposed_inherited_policy != proposed_inherited_policies_.end()) {
bool& proposed_inherited_value = proposed_inherited_policy->second; // 9.8 3: If feature is present in policy’s declared policy, and the allowlist
proposed_inherited_value = // for feature in policy’s declared policy does not match origin, then return
proposed_inherited_value && // "Disabled".
AllowlistFromDeclaration(parsed_declaration, feature_list_) if (!parent_policy->GetProposedFeatureValueForOrigin(feature.first, origin_))
->Contains(origin_); return false;
for (const auto& decl : container_policy) {
if (decl.feature == feature.first) {
// 9.8 5.1: If the allowlist for feature in container policy matches
// origin, return "Enabled".
// 9.8 5.2: Otherwise return "Disabled".
return AllowlistFromDeclaration(decl, feature_list_)->Contains(origin_);
} }
// End of metrics code
// Do not allow setting a container policy for a feature which is not in the
// feature list.
auto inherited_policy = inherited_policies_.find(feature);
if (inherited_policy == inherited_policies_.end())
continue;
bool& inherited_value = inherited_policy->second;
// If enabled by |parent_policy| for either |origin| or |parent_policy|'s
// origin, then enable in the child iff the declared container policy
// matches |origin|.
auto parent_value = parent_policy->GetFeatureValueForOrigin(
feature, parent_policy->origin_);
inherited_value = inherited_value || parent_value;
inherited_value = inherited_value && AllowlistFromDeclaration(
parsed_declaration, feature_list_)
->Contains(origin_);
} }
// 9.8 6: If feature’s default allowlist is *, return "Enabled".
if (feature.second == FeaturePolicyFeatureDefault::EnableForAll)
return true;
// 9.8 7: If feature’s default allowlist is 'self', and origin is same origin
// with container’s node document’s origin, return "Enabled".
// 9.8 8: Otherwise return "Disabled".
return origin_.IsSameOriginWith(parent_policy->origin_);
} }
const FeaturePolicyFeatureList& FeaturePolicy::GetFeatureList() const { const FeaturePolicyFeatureList& FeaturePolicy::GetFeatureList() const {
......
...@@ -218,10 +218,17 @@ class BLINK_COMMON_EXPORT FeaturePolicy { ...@@ -218,10 +218,17 @@ class BLINK_COMMON_EXPORT FeaturePolicy {
const url::Origin& origin, const url::Origin& origin,
const FeaturePolicyFeatureList& features); const FeaturePolicyFeatureList& features);
// Updates the inherited policy with the declarations from the iframe allow* bool GetInheritedValueForFeature(
// attributes. const FeaturePolicy* parent_policy,
void AddContainerPolicy(const ParsedFeaturePolicy& container_policy, std::pair<mojom::FeaturePolicyFeature, FeaturePolicyFeatureDefault>
const FeaturePolicy* parent_policy); feature,
const ParsedFeaturePolicy& container_policy) const;
bool GetProposedInheritedValueForFeature(
const FeaturePolicy* parent_policy,
std::pair<mojom::FeaturePolicyFeature, FeaturePolicyFeatureDefault>
feature,
const ParsedFeaturePolicy& container_policy) const;
// The origin of the document with which this policy is associated. // The origin of the document with which this policy is associated.
url::Origin origin_; url::Origin origin_;
......
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