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;
// These values map to the features which can be controlled by Document Policy.
enum DocumentPolicyFeature {
kNotFound = 0,
kDefault = 0,
// Controls access to font-display attribute in @font-face CSS rule
kFontDisplay = 1,
// Takes a parameter, |bpp|, i.e. byte-per-pixel ratio, that images
......
......@@ -11,6 +11,10 @@ namespace blink {
namespace {
constexpr const char* kReportTo = "report-to";
constexpr const char* kNone = "none";
constexpr const char* kWildCard = "*";
base::Optional<PolicyValue> ItemToPolicyValue(
const net::structured_headers::Item& item) {
switch (item.Type()) {
......@@ -34,8 +38,32 @@ struct ParsedFeature {
mojom::blink::DocumentPolicyFeature feature;
PolicyValue policy_value;
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(
const net::structured_headers::ParameterizedMember& directive,
const DocumentPolicyNameFeatureMap& name_feature_map,
......@@ -59,6 +87,10 @@ base::Optional<ParsedFeature> ParseFeature(
return base::nullopt;
std::string feature_name = feature_token.GetString();
if (feature_name == kWildCard)
return ParseWildcardFeature(directive);
auto feature_iter = name_feature_map.find(feature_name);
// Parse feature_name string to DocumentPolicyFeature.
......@@ -98,7 +130,7 @@ base::Optional<ParsedFeature> ParseFeature(
// Document-Policy header that specifies the endpoint group that the policy
// should send report to. If left unspecified, no report will be send upon
// policy violation.
if (param_name == "report-to") {
if (param_name == kReportTo) {
base::Optional<std::string> endpoint_group = ItemToString(param.second);
if (!endpoint_group)
return base::nullopt;
......@@ -132,6 +164,35 @@ base::Optional<ParsedFeature> ParseFeature(
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
// static
......@@ -157,6 +218,7 @@ DocumentPolicyParser::ParseInternal(
return base::nullopt;
DocumentPolicy::ParsedDocumentPolicy parse_result;
std::string default_endpoint = "";
for (const net::structured_headers::ParameterizedMember& directive :
root.value()) {
base::Optional<ParsedFeature> parsed_feature_option =
......@@ -167,6 +229,11 @@ DocumentPolicyParser::ParseInternal(
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 (available_features.find(parsed_feature.feature) ==
available_features.end())
......@@ -179,6 +246,9 @@ DocumentPolicyParser::ParseInternal(
*parsed_feature.endpoint_group);
}
}
ApplyDefaultEndpoint(parse_result, default_endpoint);
return parse_result;
}
......
......@@ -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;report-to=default;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[] = {
......@@ -116,7 +120,43 @@ const std::pair<const char*, DocumentPolicy::ParsedDocumentPolicy>
{"no-f-bool;report-to=default,f-double;value=1",
{{{kBoolFeature, PolicyValue(false)},
{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[] = {
{}, // 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