Commit 47c09afb authored by Charlie Hu's avatar Charlie Hu Committed by Commit Bot

Implement default endpoint in DocumentPolicyParser

This CL implements default endpoint support for Document-Policy/
Document-Policy-Report-Only header according to
https://w3c.github.io/webappsec-feature-policy/document-policy.html#reporting
section 6.1.2 ~ 6.1.3

Bug: 993790
Change-Id: I227c12d51a409b35beb5de248c87ec8b96656252
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2134583Reviewed-by: default avatarRobert Sesek <rsesek@chromium.org>
Reviewed-by: default avatarIan Clelland <iclelland@chromium.org>
Commit-Queue: Charlie Hu <chenleihu@google.com>
Cr-Commit-Position: refs/heads/master@{#758905}
parent de982caa
...@@ -6,7 +6,7 @@ module blink.mojom; ...@@ -6,7 +6,7 @@ module blink.mojom;
// These values map to the features which can be controlled by Document Policy. // These values map to the features which can be controlled by Document Policy.
enum DocumentPolicyFeature { enum DocumentPolicyFeature {
kNotFound = 0, kDefault = 0,
// Controls access to font-display attribute in @font-face CSS rule // Controls access to font-display attribute in @font-face CSS rule
kFontDisplay = 1, kFontDisplay = 1,
// Takes a parameter, |bpp|, i.e. byte-per-pixel ratio, that images // Takes a parameter, |bpp|, i.e. byte-per-pixel ratio, that images
......
...@@ -11,6 +11,10 @@ namespace blink { ...@@ -11,6 +11,10 @@ namespace blink {
namespace { namespace {
constexpr const char* kReportTo = "report-to";
constexpr const char* kNone = "none";
constexpr const char* kWildCard = "*";
base::Optional<PolicyValue> ItemToPolicyValue( base::Optional<PolicyValue> ItemToPolicyValue(
const net::structured_headers::Item& item) { const net::structured_headers::Item& item) {
switch (item.Type()) { switch (item.Type()) {
...@@ -34,8 +38,32 @@ struct ParsedFeature { ...@@ -34,8 +38,32 @@ struct ParsedFeature {
mojom::blink::DocumentPolicyFeature feature; mojom::blink::DocumentPolicyFeature feature;
PolicyValue policy_value; PolicyValue policy_value;
base::Optional<std::string> endpoint_group; base::Optional<std::string> endpoint_group;
// Wildcard feature('*') is used to specify default endpoint for features.
bool is_wildcard = false;
}; };
base::Optional<ParsedFeature> ParseWildcardFeature(
const net::structured_headers::ParameterizedMember& directive) {
// Wildcard feature can only have 1 param, which is 'report-to'.
if (directive.params.size() != 1)
return base::nullopt;
const auto& param = directive.params.front();
// Wildcard feature can only have 1 param, which is 'report-to'.
if (param.first != kReportTo)
return base::nullopt;
base::Optional<std::string> endpoint_group = ItemToString(param.second);
if (!endpoint_group)
return base::nullopt;
return base::make_optional<ParsedFeature>(
{mojom::blink::DocumentPolicyFeature::kDefault, PolicyValue(),
endpoint_group, true});
}
base::Optional<ParsedFeature> ParseFeature( base::Optional<ParsedFeature> ParseFeature(
const net::structured_headers::ParameterizedMember& directive, const net::structured_headers::ParameterizedMember& directive,
const DocumentPolicyNameFeatureMap& name_feature_map, const DocumentPolicyNameFeatureMap& name_feature_map,
...@@ -59,6 +87,10 @@ base::Optional<ParsedFeature> ParseFeature( ...@@ -59,6 +87,10 @@ base::Optional<ParsedFeature> ParseFeature(
return base::nullopt; return base::nullopt;
std::string feature_name = feature_token.GetString(); std::string feature_name = feature_token.GetString();
if (feature_name == kWildCard)
return ParseWildcardFeature(directive);
auto feature_iter = name_feature_map.find(feature_name); auto feature_iter = name_feature_map.find(feature_name);
// Parse feature_name string to DocumentPolicyFeature. // Parse feature_name string to DocumentPolicyFeature.
...@@ -98,7 +130,7 @@ base::Optional<ParsedFeature> ParseFeature( ...@@ -98,7 +130,7 @@ base::Optional<ParsedFeature> ParseFeature(
// Document-Policy header that specifies the endpoint group that the policy // Document-Policy header that specifies the endpoint group that the policy
// should send report to. If left unspecified, no report will be send upon // should send report to. If left unspecified, no report will be send upon
// policy violation. // policy violation.
if (param_name == "report-to") { if (param_name == kReportTo) {
base::Optional<std::string> endpoint_group = ItemToString(param.second); base::Optional<std::string> endpoint_group = ItemToString(param.second);
if (!endpoint_group) if (!endpoint_group)
return base::nullopt; return base::nullopt;
...@@ -132,6 +164,35 @@ base::Optional<ParsedFeature> ParseFeature( ...@@ -132,6 +164,35 @@ base::Optional<ParsedFeature> ParseFeature(
return parsed_feature; return parsed_feature;
} }
// Apply |default_endpoint| to given |parsed_policy|.
void ApplyDefaultEndpoint(DocumentPolicy::ParsedDocumentPolicy& parsed_policy,
const std::string& default_endpoint) {
DocumentPolicy::FeatureEndpointMap& endpoint_map = parsed_policy.endpoint_map;
if (!default_endpoint.empty()) {
// Fill |default_endpoint| to all feature entry whose |endpoint_group|
// is missing.
for (const auto& feature_and_value : parsed_policy.feature_state) {
mojom::blink::DocumentPolicyFeature feature = feature_and_value.first;
if (endpoint_map.find(feature) == endpoint_map.end())
endpoint_map.emplace(feature, default_endpoint);
}
}
// Remove |endpoint_group| for feature entry if its |endpoint_group|
// is "none".
// Note: if |default_endpoint| is "none", all "none" items are filtered out
// here. it would be equivalent to doing nothing.
for (auto iter = endpoint_map.begin(); iter != endpoint_map.end();) {
if (iter->second == kNone) {
iter = endpoint_map.erase(iter);
} else {
++iter;
}
}
}
} // namespace } // namespace
// static // static
...@@ -157,6 +218,7 @@ DocumentPolicyParser::ParseInternal( ...@@ -157,6 +218,7 @@ DocumentPolicyParser::ParseInternal(
return base::nullopt; return base::nullopt;
DocumentPolicy::ParsedDocumentPolicy parse_result; DocumentPolicy::ParsedDocumentPolicy parse_result;
std::string default_endpoint = "";
for (const net::structured_headers::ParameterizedMember& directive : for (const net::structured_headers::ParameterizedMember& directive :
root.value()) { root.value()) {
base::Optional<ParsedFeature> parsed_feature_option = base::Optional<ParsedFeature> parsed_feature_option =
...@@ -167,6 +229,11 @@ DocumentPolicyParser::ParseInternal( ...@@ -167,6 +229,11 @@ DocumentPolicyParser::ParseInternal(
ParsedFeature parsed_feature = *parsed_feature_option; ParsedFeature parsed_feature = *parsed_feature_option;
if (parsed_feature.is_wildcard) {
default_endpoint = *parsed_feature.endpoint_group;
continue;
}
// If feature is not available, i.e. not enabled, ignore the entry. // If feature is not available, i.e. not enabled, ignore the entry.
if (available_features.find(parsed_feature.feature) == if (available_features.find(parsed_feature.feature) ==
available_features.end()) available_features.end())
...@@ -179,6 +246,9 @@ DocumentPolicyParser::ParseInternal( ...@@ -179,6 +246,9 @@ DocumentPolicyParser::ParseInternal(
*parsed_feature.endpoint_group); *parsed_feature.endpoint_group);
} }
} }
ApplyDefaultEndpoint(parse_result, default_endpoint);
return parse_result; return parse_result;
} }
......
...@@ -63,6 +63,10 @@ const char* const kValidPolicies[] = { ...@@ -63,6 +63,10 @@ const char* const kValidPolicies[] = {
"no-f-bool;report-to=default,f-double;value=2.0;report-to=default", "no-f-bool;report-to=default,f-double;value=2.0;report-to=default",
"no-f-bool;report-to=default,f-double;report-to=default;value=2.0", "no-f-bool;report-to=default,f-double;report-to=default;value=2.0",
"no-f-bool;report-to=default,f-double;report-to=endpoint;value=2.0", "no-f-bool;report-to=default,f-double;report-to=endpoint;value=2.0",
"no-f-bool,f-double;value=2.0;report-to=endpoint,*;report-to=default",
"*;report-to=default", // An empty policy.
"no-f-bool;report-to=none, f-double;value=2.0, *;report-to=default",
"no-f-bool;report-to=none, f-double;value=2.0, *;report-to=none",
}; };
const char* const kInvalidPolicies[] = { const char* const kInvalidPolicies[] = {
...@@ -116,7 +120,43 @@ const std::pair<const char*, DocumentPolicy::ParsedDocumentPolicy> ...@@ -116,7 +120,43 @@ const std::pair<const char*, DocumentPolicy::ParsedDocumentPolicy>
{"no-f-bool;report-to=default,f-double;value=1", {"no-f-bool;report-to=default,f-double;value=1",
{{{kBoolFeature, PolicyValue(false)}, {{{kBoolFeature, PolicyValue(false)},
{kDoubleFeature, PolicyValue(1.0)}}, {kDoubleFeature, PolicyValue(1.0)}},
{{kBoolFeature, "default"}}}}}; {{kBoolFeature, "default"}}}},
{"no-f-bool;report-to=none, f-double;value=2.0, *;report-to=default",
{/* feature_state */ {{kBoolFeature, PolicyValue(false)},
{kDoubleFeature, PolicyValue(2.0)}},
/* endpoint_map */ {{kDoubleFeature, "default"}}}},
{"no-f-bool;report-to=not_none, f-double;value=2.0, "
"*;report-to=default",
{/* feature_state */ {{kBoolFeature, PolicyValue(false)},
{kDoubleFeature, PolicyValue(2.0)}},
/* endpoint_map */ {{kBoolFeature, "not_none"},
{kDoubleFeature, "default"}}}},
{"no-f-bool;report-to=not_none, f-double;value=2.0, *;report-to=none",
{/* feature_state */ {{kBoolFeature, PolicyValue(false)},
{kDoubleFeature, PolicyValue(2.0)}},
/* endpoint_map */ {{kBoolFeature, "not_none"}}}},
// Default endpoint can be specified anywhere in the header, not
// necessary at the end.
{"no-f-bool;report-to=not_none, *;report-to=default, "
"f-double;value=2.0",
{/* feature_state */ {{kBoolFeature, PolicyValue(false)},
{kDoubleFeature, PolicyValue(2.0)}},
/* endpoint_map */ {{kBoolFeature, "not_none"},
{kDoubleFeature, "default"}}}},
// Default endpoint can be specified multiple times in the header.
// According to SH rules, last value wins.
{"no-f-bool;report-to=not_none, f-double;value=2.0, "
"*;report-to=default, "
"*;report-to=none",
{/* feature_state */ {{kBoolFeature, PolicyValue(false)},
{kDoubleFeature, PolicyValue(2.0)}},
/* endpoint_map */ {{kBoolFeature, "not_none"}}}},
// Even if default endpoint is not specified, none still should be
// treated as a reserved keyword for endpoint names.
{"no-f-bool;report-to=none",
{/* feature_state */ {{kBoolFeature, PolicyValue(false)}},
/* endpoint_map */ {}}},
};
const DocumentPolicy::FeatureState kParsedPolicies[] = { const DocumentPolicy::FeatureState kParsedPolicies[] = {
{}, // An empty policy {}, // An empty policy
......
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