Commit 4a09210e authored by Charlie Hu's avatar Charlie Hu Committed by Commit Bot

[Permissions-Policy] Use different error message prefix for Permissions-Policy header

Previously parse of 'Permissions-Policy' HTTP header still reports
parsing error with 'Error with Feature Policy: '.

This CL lets parse of 'Permissions-Policy' HTTP header use a separate
logger that has prefix 'Error with Permissions Policy: '.

Note:
Previously merge of feature policy and permissions policy was implicit,
i.e. done by concatenating the two together and relying on the parsing
rule that first occurrence of feature wins.
This CL makes the merge explicit in |FeaturePolicyParser::ParseHeader|.

Change-Id: I25f877899aa709705fb96b6a6cfe003dedd217d8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2514961Reviewed-by: default avatarIan Clelland <iclelland@chromium.org>
Reviewed-by: default avatarDmitry Gozman <dgozman@chromium.org>
Commit-Queue: Charlie Hu <chenleihu@google.com>
Cr-Commit-Position: refs/heads/master@{#826127}
parent e19c21a5
...@@ -148,7 +148,12 @@ void SecurityContextInit::ApplyFeaturePolicy( ...@@ -148,7 +148,12 @@ void SecurityContextInit::ApplyFeaturePolicy(
PolicyParserMessageBuffer feature_policy_logger( PolicyParserMessageBuffer feature_policy_logger(
"Error with Feature-Policy header: "); "Error with Feature-Policy header: ");
PolicyParserMessageBuffer report_only_feature_policy_logger( PolicyParserMessageBuffer report_only_feature_policy_logger(
"Error with Report-Only-Feature-Policy header: "); "Error with Feature-Policy-Report-Only header: ");
PolicyParserMessageBuffer permissions_policy_logger(
"Error with Permissions-Policy header: ");
PolicyParserMessageBuffer report_only_permissions_policy_logger(
"Error with Permissions-Policy-Report-Only header: ");
WTF::StringBuilder policy_builder; WTF::StringBuilder policy_builder;
policy_builder.Append(response.HttpHeaderField(http_names::kFeaturePolicy)); policy_builder.Append(response.HttpHeaderField(http_names::kFeaturePolicy));
...@@ -161,26 +166,28 @@ void SecurityContextInit::ApplyFeaturePolicy( ...@@ -161,26 +166,28 @@ void SecurityContextInit::ApplyFeaturePolicy(
feature_policy_header_ = FeaturePolicyParser::ParseHeader( feature_policy_header_ = FeaturePolicyParser::ParseHeader(
feature_policy_header, permissions_policy_header, feature_policy_header, permissions_policy_header,
execution_context_->GetSecurityOrigin(), feature_policy_logger, execution_context_->GetSecurityOrigin(), feature_policy_logger,
execution_context_); permissions_policy_logger, execution_context_);
ParsedFeaturePolicy report_only_feature_policy_header = ParsedFeaturePolicy report_only_feature_policy_header =
FeaturePolicyParser::ParseHeader( FeaturePolicyParser::ParseHeader(
response.HttpHeaderField(http_names::kFeaturePolicyReportOnly), response.HttpHeaderField(http_names::kFeaturePolicyReportOnly),
report_only_permissions_policy_header, report_only_permissions_policy_header,
execution_context_->GetSecurityOrigin(), execution_context_->GetSecurityOrigin(),
report_only_feature_policy_logger, execution_context_); report_only_feature_policy_logger,
report_only_permissions_policy_logger, execution_context_);
if (!report_only_feature_policy_header.empty()) { if (!report_only_feature_policy_header.empty()) {
UseCounter::Count(execution_context_, UseCounter::Count(execution_context_,
WebFeature::kFeaturePolicyReportOnlyHeader); WebFeature::kFeaturePolicyReportOnlyHeader);
} }
for (const auto& message : feature_policy_logger.GetMessages()) { auto messages = Vector<PolicyParserMessageBuffer::Message>();
execution_context_->AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>( messages.AppendVector(feature_policy_logger.GetMessages());
mojom::blink::ConsoleMessageSource::kSecurity, message.level, messages.AppendVector(report_only_feature_policy_logger.GetMessages());
message.content)); messages.AppendVector(permissions_policy_logger.GetMessages());
} messages.AppendVector(report_only_permissions_policy_logger.GetMessages());
for (const auto& message : report_only_feature_policy_logger.GetMessages()) {
for (const auto& message : messages) {
execution_context_->AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>( execution_context_->AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
mojom::blink::ConsoleMessageSource::kSecurity, message.level, mojom::blink::ConsoleMessageSource::kSecurity, message.level,
message.content)); message.content));
......
...@@ -20,7 +20,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { ...@@ -20,7 +20,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
// TODO(csharrison): Be smarter about parsing this origin for performance. // TODO(csharrison): Be smarter about parsing this origin for performance.
scoped_refptr<const blink::SecurityOrigin> origin = scoped_refptr<const blink::SecurityOrigin> origin =
blink::SecurityOrigin::CreateFromString("https://example.com/"); blink::SecurityOrigin::CreateFromString("https://example.com/");
blink::FeaturePolicyParser::ParseHeader(WTF::String(data, size), blink::FeaturePolicyParser::ParseHeader(
g_empty_string, origin.get(), logger); WTF::String(data, size), g_empty_string, origin.get(), logger, logger);
return 0; return 0;
} }
...@@ -25,16 +25,6 @@ ...@@ -25,16 +25,6 @@
namespace blink { namespace blink {
namespace { namespace {
namespace internal {
// Following is the intermediate represetnation(IR) of feature policy.
// Parsing of syntax structures is done in this IR, but semantic checks, e.g.
// whether feature_name is valid, are not yet performed.
struct FeaturePolicyDeclarationNode {
String feature_name;
Vector<String> allowlist;
};
using FeaturePolicyNode = Vector<FeaturePolicyDeclarationNode>;
} // namespace internal
class ParsedFeaturePolicies final class ParsedFeaturePolicies final
: public GarbageCollected<ParsedFeaturePolicies>, : public GarbageCollected<ParsedFeaturePolicies>,
...@@ -74,6 +64,17 @@ class ParsedFeaturePolicies final ...@@ -74,6 +64,17 @@ class ParsedFeaturePolicies final
const char ParsedFeaturePolicies::kSupplementName[] = "ParsedFeaturePolicies"; const char ParsedFeaturePolicies::kSupplementName[] = "ParsedFeaturePolicies";
class FeatureObserver {
public:
// Returns whether the feature has been observed before or not.
bool FeatureObserved(mojom::blink::FeaturePolicyFeature feature);
private:
std::bitset<
static_cast<size_t>(mojom::blink::FeaturePolicyFeature::kMaxValue) + 1>
features_specified_;
};
class ParsingContext { class ParsingContext {
STACK_ALLOCATED(); STACK_ALLOCATED();
...@@ -91,17 +92,29 @@ class ParsingContext { ...@@ -91,17 +92,29 @@ class ParsingContext {
~ParsingContext() = default; ~ParsingContext() = default;
ParsedFeaturePolicy ParseIR(const internal::FeaturePolicyNode& root); ParsedFeaturePolicy ParseFeaturePolicy(const String& policy);
internal::FeaturePolicyNode ParseFeaturePolicyToIR(const String& policy); ParsedFeaturePolicy ParsePermissionsPolicy(const String& policy);
internal::FeaturePolicyNode ParsePermissionsPolicyToIR(const String& policy);
private: private:
// Following is the intermediate represetnation(IR) of feature policy.
// Parsing of syntax structures is done in this IR, but semantic checks, e.g.
// whether feature_name is valid, are not yet performed.
struct FeaturePolicyDeclarationNode {
String feature_name;
Vector<String> allowlist;
};
using FeaturePolicyNode = Vector<FeaturePolicyDeclarationNode>;
ParsedFeaturePolicy ParseIR(const FeaturePolicyNode& root);
FeaturePolicyNode ParseFeaturePolicyToIR(const String& policy);
FeaturePolicyNode ParsePermissionsPolicyToIR(const String& policy);
// normally 1 char = 1 byte // normally 1 char = 1 byte
// max length to parse = 2^16 = 64 kB // max length to parse = 2^16 = 64 kB
static constexpr wtf_size_t MAX_LENGTH_PARSE = 1 << 16; static constexpr wtf_size_t MAX_LENGTH_PARSE = 1 << 16;
base::Optional<ParsedFeaturePolicyDeclaration> ParseFeature( base::Optional<ParsedFeaturePolicyDeclaration> ParseFeature(
const internal::FeaturePolicyDeclarationNode&); const FeaturePolicyDeclarationNode&);
struct ParsedAllowlist { struct ParsedAllowlist {
std::vector<url::Origin> allowed_origins; std::vector<url::Origin> allowed_origins;
...@@ -117,8 +130,6 @@ class ParsingContext { ...@@ -117,8 +130,6 @@ class ParsingContext {
// Parse allowlist for feature. // Parse allowlist for feature.
ParsedAllowlist ParseAllowlist(const Vector<String>& origin_strings); ParsedAllowlist ParseAllowlist(const Vector<String>& origin_strings);
bool FeatureObserved(mojom::blink::FeaturePolicyFeature feature);
void ReportFeatureUsage(mojom::blink::FeaturePolicyFeature feature); void ReportFeatureUsage(mojom::blink::FeaturePolicyFeature feature);
// This function should be called after Allowlist Histograms related flags // This function should be called after Allowlist Histograms related flags
...@@ -146,12 +157,11 @@ class ParsingContext { ...@@ -146,12 +157,11 @@ class ParsingContext {
bool allowlist_includes_origin_ = false; bool allowlist_includes_origin_ = false;
HashSet<FeaturePolicyAllowlistType> allowlist_types_used_; HashSet<FeaturePolicyAllowlistType> allowlist_types_used_;
std::bitset<
static_cast<size_t>(mojom::blink::FeaturePolicyFeature::kMaxValue) + 1> FeatureObserver feature_observer_;
features_specified_;
}; };
bool ParsingContext::FeatureObserved( bool FeatureObserver::FeatureObserved(
mojom::blink::FeaturePolicyFeature feature) { mojom::blink::FeaturePolicyFeature feature) {
if (features_specified_[static_cast<size_t>(feature)]) { if (features_specified_[static_cast<size_t>(feature)]) {
return true; return true;
...@@ -341,7 +351,7 @@ ParsingContext::ParsedAllowlist ParsingContext::ParseAllowlist( ...@@ -341,7 +351,7 @@ ParsingContext::ParsedAllowlist ParsingContext::ParseAllowlist(
} }
base::Optional<ParsedFeaturePolicyDeclaration> ParsingContext::ParseFeature( base::Optional<ParsedFeaturePolicyDeclaration> ParsingContext::ParseFeature(
const internal::FeaturePolicyDeclarationNode& declaration_node) { const FeaturePolicyDeclarationNode& declaration_node) {
base::Optional<mojom::blink::FeaturePolicyFeature> feature = base::Optional<mojom::blink::FeaturePolicyFeature> feature =
ParseFeatureName(declaration_node.feature_name); ParseFeatureName(declaration_node.feature_name);
if (!feature) if (!feature)
...@@ -350,7 +360,7 @@ base::Optional<ParsedFeaturePolicyDeclaration> ParsingContext::ParseFeature( ...@@ -350,7 +360,7 @@ base::Optional<ParsedFeaturePolicyDeclaration> ParsingContext::ParseFeature(
ParsedAllowlist parsed_allowlist = ParseAllowlist(declaration_node.allowlist); ParsedAllowlist parsed_allowlist = ParseAllowlist(declaration_node.allowlist);
// If same feature appeared more than once, only the first one counts. // If same feature appeared more than once, only the first one counts.
if (FeatureObserved(*feature)) if (feature_observer_.FeatureObserved(*feature))
return base::nullopt; return base::nullopt;
ParsedFeaturePolicyDeclaration parsed_feature(*feature); ParsedFeaturePolicyDeclaration parsed_feature(*feature);
...@@ -361,10 +371,20 @@ base::Optional<ParsedFeaturePolicyDeclaration> ParsingContext::ParseFeature( ...@@ -361,10 +371,20 @@ base::Optional<ParsedFeaturePolicyDeclaration> ParsingContext::ParseFeature(
return parsed_feature; return parsed_feature;
} }
ParsedFeaturePolicy ParsingContext::ParseFeaturePolicy(const String& policy) {
return ParseIR(ParseFeaturePolicyToIR(policy));
}
ParsedFeaturePolicy ParsingContext::ParsePermissionsPolicy(
const String& policy) {
return ParseIR(ParsePermissionsPolicyToIR(policy));
}
ParsedFeaturePolicy ParsingContext::ParseIR( ParsedFeaturePolicy ParsingContext::ParseIR(
const internal::FeaturePolicyNode& root) { const ParsingContext::FeaturePolicyNode& root) {
ParsedFeaturePolicy parsed_policy; ParsedFeaturePolicy parsed_policy;
for (const internal::FeaturePolicyDeclarationNode& declaration_node : root) { for (const ParsingContext::FeaturePolicyDeclarationNode& declaration_node :
root) {
base::Optional<ParsedFeaturePolicyDeclaration> parsed_feature = base::Optional<ParsedFeaturePolicyDeclaration> parsed_feature =
ParseFeature(declaration_node); ParseFeature(declaration_node);
if (parsed_feature) { if (parsed_feature) {
...@@ -376,9 +396,9 @@ ParsedFeaturePolicy ParsingContext::ParseIR( ...@@ -376,9 +396,9 @@ ParsedFeaturePolicy ParsingContext::ParseIR(
return parsed_policy; return parsed_policy;
} }
internal::FeaturePolicyNode ParsingContext::ParseFeaturePolicyToIR( ParsingContext::FeaturePolicyNode ParsingContext::ParseFeaturePolicyToIR(
const String& policy) { const String& policy) {
internal::FeaturePolicyNode root; ParsingContext::FeaturePolicyNode root;
if (policy.length() > MAX_LENGTH_PARSE) { if (policy.length() > MAX_LENGTH_PARSE) {
logger_.Error("Feature policy declaration exceeds size limit(" + logger_.Error("Feature policy declaration exceeds size limit(" +
...@@ -418,7 +438,7 @@ internal::FeaturePolicyNode ParsingContext::ParseFeaturePolicyToIR( ...@@ -418,7 +438,7 @@ internal::FeaturePolicyNode ParsingContext::ParseFeaturePolicyToIR(
if (tokens.IsEmpty()) if (tokens.IsEmpty())
continue; continue;
internal::FeaturePolicyDeclarationNode declaration_node; ParsingContext::FeaturePolicyDeclarationNode declaration_node;
// Break tokens into head & tail, where // Break tokens into head & tail, where
// head = feature_name // head = feature_name
// tail = allowlist // tail = allowlist
...@@ -434,7 +454,7 @@ internal::FeaturePolicyNode ParsingContext::ParseFeaturePolicyToIR( ...@@ -434,7 +454,7 @@ internal::FeaturePolicyNode ParsingContext::ParseFeaturePolicyToIR(
return root; return root;
} }
internal::FeaturePolicyNode ParsingContext::ParsePermissionsPolicyToIR( ParsingContext::FeaturePolicyNode ParsingContext::ParsePermissionsPolicyToIR(
const String& policy) { const String& policy) {
if (policy.length() > MAX_LENGTH_PARSE) { if (policy.length() > MAX_LENGTH_PARSE) {
logger_.Error("Permissions policy declaration exceeds size limit(" + logger_.Error("Permissions policy declaration exceeds size limit(" +
...@@ -451,7 +471,7 @@ internal::FeaturePolicyNode ParsingContext::ParsePermissionsPolicyToIR( ...@@ -451,7 +471,7 @@ internal::FeaturePolicyNode ParsingContext::ParsePermissionsPolicyToIR(
return {}; return {};
} }
internal::FeaturePolicyNode ir_root; ParsingContext::FeaturePolicyNode ir_root;
for (const auto& feature_entry : root.value()) { for (const auto& feature_entry : root.value()) {
const auto& key = feature_entry.first; const auto& key = feature_entry.first;
const char* feature_name = key.c_str(); const char* feature_name = key.c_str();
...@@ -503,7 +523,7 @@ internal::FeaturePolicyNode ParsingContext::ParsePermissionsPolicyToIR( ...@@ -503,7 +523,7 @@ internal::FeaturePolicyNode ParsingContext::ParsePermissionsPolicyToIR(
allowlist.push_back("'none'"); allowlist.push_back("'none'");
ir_root.push_back( ir_root.push_back(
internal::FeaturePolicyDeclarationNode{feature_name, allowlist}); ParsingContext::FeaturePolicyDeclarationNode{feature_name, allowlist});
} }
return ir_root; return ir_root;
...@@ -515,14 +535,37 @@ ParsedFeaturePolicy FeaturePolicyParser::ParseHeader( ...@@ -515,14 +535,37 @@ ParsedFeaturePolicy FeaturePolicyParser::ParseHeader(
const String& feature_policy_header, const String& feature_policy_header,
const String& permissions_policy_header, const String& permissions_policy_header,
scoped_refptr<const SecurityOrigin> origin, scoped_refptr<const SecurityOrigin> origin,
PolicyParserMessageBuffer& logger, PolicyParserMessageBuffer& feature_policy_logger,
PolicyParserMessageBuffer& permissions_policy_logger,
ExecutionContext* execution_context) { ExecutionContext* execution_context) {
ParsingContext context(logger, origin, nullptr, GetDefaultFeatureNameMap(), ParsedFeaturePolicy permissions_policy =
execution_context); ParsingContext(permissions_policy_logger, origin, nullptr,
auto policy_ir = GetDefaultFeatureNameMap(), execution_context)
context.ParsePermissionsPolicyToIR(permissions_policy_header); .ParsePermissionsPolicy(permissions_policy_header);
policy_ir.AppendVector(context.ParseFeaturePolicyToIR(feature_policy_header)); ParsedFeaturePolicy feature_policy =
return context.ParseIR(policy_ir); ParsingContext(feature_policy_logger, origin, nullptr,
GetDefaultFeatureNameMap(), execution_context)
.ParseFeaturePolicy(feature_policy_header);
FeatureObserver observer;
for (const auto& policy_declaration : permissions_policy) {
bool feature_observed =
observer.FeatureObserved(policy_declaration.feature);
DCHECK(!feature_observed);
}
for (const auto& policy_declaration : feature_policy) {
if (!observer.FeatureObserved(policy_declaration.feature)) {
permissions_policy.push_back(policy_declaration);
} else {
feature_policy_logger.Warn(String::Format(
"Feature %s has been specified in both Feature-Policy and "
"Permissions-Policy header. Value defined in Permissions-Policy "
"header will be used.",
GetNameForFeature(policy_declaration.feature).Ascii().c_str()));
}
}
return permissions_policy;
} }
ParsedFeaturePolicy FeaturePolicyParser::ParseAttribute( ParsedFeaturePolicy FeaturePolicyParser::ParseAttribute(
...@@ -531,9 +574,9 @@ ParsedFeaturePolicy FeaturePolicyParser::ParseAttribute( ...@@ -531,9 +574,9 @@ ParsedFeaturePolicy FeaturePolicyParser::ParseAttribute(
scoped_refptr<const SecurityOrigin> src_origin, scoped_refptr<const SecurityOrigin> src_origin,
PolicyParserMessageBuffer& logger, PolicyParserMessageBuffer& logger,
ExecutionContext* execution_context) { ExecutionContext* execution_context) {
ParsingContext context(logger, self_origin, src_origin, return ParsingContext(logger, self_origin, src_origin,
GetDefaultFeatureNameMap(), execution_context); GetDefaultFeatureNameMap(), execution_context)
return context.ParseIR(context.ParseFeaturePolicyToIR(policy)); .ParseFeaturePolicy(policy);
} }
ParsedFeaturePolicy FeaturePolicyParser::ParseFeaturePolicyForTest( ParsedFeaturePolicy FeaturePolicyParser::ParseFeaturePolicyForTest(
...@@ -543,9 +586,9 @@ ParsedFeaturePolicy FeaturePolicyParser::ParseFeaturePolicyForTest( ...@@ -543,9 +586,9 @@ ParsedFeaturePolicy FeaturePolicyParser::ParseFeaturePolicyForTest(
PolicyParserMessageBuffer& logger, PolicyParserMessageBuffer& logger,
const FeatureNameMap& feature_names, const FeatureNameMap& feature_names,
ExecutionContext* execution_context) { ExecutionContext* execution_context) {
ParsingContext context(logger, self_origin, src_origin, feature_names, return ParsingContext(logger, self_origin, src_origin, feature_names,
execution_context); execution_context)
return context.ParseIR(context.ParseFeaturePolicyToIR(policy)); .ParseFeaturePolicy(policy);
} }
ParsedFeaturePolicy FeaturePolicyParser::ParsePermissionsPolicyForTest( ParsedFeaturePolicy FeaturePolicyParser::ParsePermissionsPolicyForTest(
...@@ -555,9 +598,9 @@ ParsedFeaturePolicy FeaturePolicyParser::ParsePermissionsPolicyForTest( ...@@ -555,9 +598,9 @@ ParsedFeaturePolicy FeaturePolicyParser::ParsePermissionsPolicyForTest(
PolicyParserMessageBuffer& logger, PolicyParserMessageBuffer& logger,
const FeatureNameMap& feature_names, const FeatureNameMap& feature_names,
ExecutionContext* execution_context) { ExecutionContext* execution_context) {
ParsingContext context(logger, self_origin, src_origin, feature_names, return ParsingContext(logger, self_origin, src_origin, feature_names,
execution_context); execution_context)
return context.ParseIR(context.ParsePermissionsPolicyToIR(policy)); .ParsePermissionsPolicy(policy);
} }
bool IsFeatureDeclared(mojom::blink::FeaturePolicyFeature feature, bool IsFeatureDeclared(mojom::blink::FeaturePolicyFeature feature,
......
...@@ -56,11 +56,13 @@ class CORE_EXPORT FeaturePolicyParser { ...@@ -56,11 +56,13 @@ class CORE_EXPORT FeaturePolicyParser {
// ExecutionContext is used to determine if any origin trials affect the // ExecutionContext is used to determine if any origin trials affect the
// parsing. Example of a feature policy string: // parsing. Example of a feature policy string:
// "vibrate a.com b.com; fullscreen 'none'; payment 'self', payment *". // "vibrate a.com b.com; fullscreen 'none'; payment 'self', payment *".
static ParsedFeaturePolicy ParseHeader(const String& feature_policy_header, static ParsedFeaturePolicy ParseHeader(
const String& permission_policy_header, const String& feature_policy_header,
scoped_refptr<const SecurityOrigin>, const String& permission_policy_header,
PolicyParserMessageBuffer& logger, scoped_refptr<const SecurityOrigin>,
ExecutionContext* = nullptr); PolicyParserMessageBuffer& feature_policy_logger,
PolicyParserMessageBuffer& permissions_policy_logger,
ExecutionContext* = nullptr);
// Converts a container policy string into a vector of allowlists, given self // Converts a container policy string into a vector of allowlists, given self
// and src origins provided, one for each feature specified. Unrecognized // and src origins provided, one for each feature specified. Unrecognized
......
...@@ -106,7 +106,7 @@ class FeaturePolicyParserTest : public ::testing::Test { ...@@ -106,7 +106,7 @@ class FeaturePolicyParserTest : public ::testing::Test {
PolicyParserMessageBuffer& logger, PolicyParserMessageBuffer& logger,
ExecutionContext* context = nullptr) { ExecutionContext* context = nullptr) {
return FeaturePolicyParser::ParseHeader( return FeaturePolicyParser::ParseHeader(
feature_policy_header, g_empty_string, origin, logger, context); feature_policy_header, g_empty_string, origin, logger, logger, context);
} }
}; };
...@@ -193,6 +193,15 @@ class FeaturePolicyParserParsingTest ...@@ -193,6 +193,15 @@ class FeaturePolicyParserParsingTest
} }
} }
void CheckConsoleMessage(
const Vector<PolicyParserMessageBuffer::Message>& actual,
const std::vector<String> expected) {
ASSERT_EQ(actual.size(), expected.size());
for (size_t i = 0; i < actual.size(); ++i) {
EXPECT_EQ(actual[i].content, expected[i]);
}
}
public: public:
static const FeaturePolicyParserTestCase kCases[]; static const FeaturePolicyParserTestCase kCases[];
}; };
...@@ -550,16 +559,64 @@ TEST_P(FeaturePolicyParserParsingTest, PermissionsPolicyParsedCorrectly) { ...@@ -550,16 +559,64 @@ TEST_P(FeaturePolicyParserParsingTest, PermissionsPolicyParsedCorrectly) {
} }
TEST_F(FeaturePolicyParserParsingTest, TEST_F(FeaturePolicyParserParsingTest,
FeaturePolicyHeaderPermissionsPolicyHeaderCoExist) { FeaturePolicyDuplicatedFeatureDeclaration) {
PolicyParserMessageBuffer logger; PolicyParserMessageBuffer logger;
// When there is conflict take the value from permission policy.
// Non-conflicting entries will be merged. // For Feature-Policy header, if there are multiple declaration for same
// feature, the allowlist value from *FIRST* declaration will be taken.
CheckParsedPolicy(FeaturePolicyParser::ParseHeader(
"geolocation 'none', geolocation 'self'", "",
origin_a_.get(), logger, logger, nullptr /* context */),
{
{
// allowlist value 'none' is expected.
mojom::blink::FeaturePolicyFeature::kGeolocation,
/* matches_all_origins */ false,
/* matches_opaque_src */ false,
{},
},
});
EXPECT_TRUE(logger.GetMessages().IsEmpty());
}
TEST_F(FeaturePolicyParserParsingTest,
PermissionsPolicyDuplicatedFeatureDeclaration) {
PolicyParserMessageBuffer logger;
// For Permissions-Policy header, if there are multiple declaration for same
// feature, the allowlist value from *LAST* declaration will be taken.
CheckParsedPolicy(FeaturePolicyParser::ParseHeader(
"", "geolocation=(), geolocation=self", origin_a_.get(),
logger, logger, nullptr /* context */),
{
{
// allowlist value 'self' is expected.
mojom::blink::FeaturePolicyFeature::kGeolocation,
/* matches_all_origins */ false,
/* matches_opaque_src */ false,
{ORIGIN_A},
},
});
EXPECT_TRUE(logger.GetMessages().IsEmpty());
}
TEST_F(FeaturePolicyParserParsingTest,
FeaturePolicyHeaderPermissionsPolicyHeaderCoExistConflictEntry) {
PolicyParserMessageBuffer logger;
// When there is conflict take the value from permission policy,
// non-conflicting entries will be merged.
CheckParsedPolicy(FeaturePolicyParser::ParseHeader( CheckParsedPolicy(FeaturePolicyParser::ParseHeader(
"geolocation 'none', fullscreen 'self'", "geolocation 'none', fullscreen 'self'",
"geolocation=self, payment=*", origin_a_.get(), logger, "geolocation=self, payment=*", origin_a_.get(), logger,
nullptr /* context */), logger, nullptr /* context */),
{ {
{ {
// With geolocation appearing in both headers,
// the value should be taken from permissions policy
// header, which is 'self' here.
mojom::blink::FeaturePolicyFeature::kGeolocation, mojom::blink::FeaturePolicyFeature::kGeolocation,
/* matches_all_origins */ false, /* matches_all_origins */ false,
/* matches_opaque_src */ false, /* matches_opaque_src */ false,
...@@ -580,6 +637,49 @@ TEST_F(FeaturePolicyParserParsingTest, ...@@ -580,6 +637,49 @@ TEST_F(FeaturePolicyParserParsingTest,
}); });
} }
TEST_F(FeaturePolicyParserParsingTest,
FeaturePolicyHeaderPermissionsPolicyHeaderCoExistSeparateLogger) {
PolicyParserMessageBuffer feature_policy_logger("Feature Policy: ");
PolicyParserMessageBuffer permissions_policy_logger("Permissions Policy: ");
// 'geolocation' in permissions policy has a invalid allowlist item, which
// results in an empty allowlist, which is equivalent to 'none' in feature
// policy syntax.
CheckParsedPolicy(
FeaturePolicyParser::ParseHeader(
"worse-feature 'none', geolocation 'self'" /* feature_policy_header */
,
"bad-feature=*, geolocation=\"data://bad-origin\"" /* permissions_policy_header
*/
,
origin_a_.get(), feature_policy_logger, permissions_policy_logger,
nullptr /* context */
),
{
{
mojom::blink::FeaturePolicyFeature::kGeolocation,
/* matches_all_origins */ false,
/* matches_opaque_src */ false,
{},
},
});
CheckConsoleMessage(
feature_policy_logger.GetMessages(),
{
"Feature Policy: Unrecognized feature: 'worse-feature'.",
"Feature Policy: Feature geolocation has been specified in both "
"Feature-Policy and Permissions-Policy header. Value defined in "
"Permissions-Policy header will be used.",
});
CheckConsoleMessage(
permissions_policy_logger.GetMessages(),
{
"Permissions Policy: Unrecognized feature: 'bad-feature'.",
"Permissions Policy: Unrecognized origin: 'data://bad-origin'.",
});
}
TEST_F(FeaturePolicyParserTest, ParseValidPolicy) { TEST_F(FeaturePolicyParserTest, ParseValidPolicy) {
for (const char* policy_string : kValidPolicies) { for (const char* policy_string : kValidPolicies) {
PolicyParserMessageBuffer logger; PolicyParserMessageBuffer logger;
......
...@@ -21,6 +21,6 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { ...@@ -21,6 +21,6 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
scoped_refptr<const blink::SecurityOrigin> origin = scoped_refptr<const blink::SecurityOrigin> origin =
blink::SecurityOrigin::CreateFromString("https://example.com/"); blink::SecurityOrigin::CreateFromString("https://example.com/");
blink::FeaturePolicyParser::ParseHeader( blink::FeaturePolicyParser::ParseHeader(
g_empty_string, WTF::String(data, size), origin.get(), logger); g_empty_string, WTF::String(data, size), origin.get(), logger, logger);
return 0; return 0;
} }
...@@ -35,7 +35,7 @@ class PolicyTest : public testing::Test { ...@@ -35,7 +35,7 @@ class PolicyTest : public testing::Test {
"fullscreen *; payment 'self'; midi 'none'; camera 'self' " "fullscreen *; payment 'self'; midi 'none'; camera 'self' "
"https://example.com https://example.net", "https://example.com https://example.net",
/* permissions_policy_header */ g_empty_string, origin.get(), /* permissions_policy_header */ g_empty_string, origin.get(),
dummy_logger_); dummy_logger_, dummy_logger_);
feature_policy->SetHeaderPolicy(header); feature_policy->SetHeaderPolicy(header);
auto& security_context = auto& security_context =
......
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