Skip to content

Commit

Permalink
Split parsing logic and semantic logic in FeaturePolicyParser
Browse files Browse the repository at this point in the history
This CL splits the parsing logic and semantic logic in
|FeaturePolicyParser| so that parsing logic can be easily
substituted.

This is pre-work to implement permission policy parser which
uses structured header format. (w3c/webappsec-permissions-policy#376)

Change-Id: I116e69a562974d21e0d3a53b15cfbb3276b37d72
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2236726
Commit-Queue: Charlie Hu <[email protected]>
Reviewed-by: Ian Clelland <[email protected]>
Cr-Original-Commit-Position: refs/heads/master@{#779106}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: 126ad87f6939e33404df74db93a9012195b6486f
  • Loading branch information
Charlie Hu authored and Commit Bot committed Jun 17, 2020
1 parent a440f57 commit 86a6930
Showing 1 changed file with 55 additions and 34 deletions.
89 changes: 55 additions & 34 deletions blink/renderer/core/feature_policy/feature_policy_parser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,16 @@

namespace blink {
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 ParsingContext {
public:
Expand All @@ -44,14 +54,15 @@ class ParsingContext {
ParsedFeaturePolicy Parse(const String& policy);

private:
ParsedFeaturePolicy ParseIR(const internal::FeaturePolicyNode& root);
internal::FeaturePolicyNode ParseToIR(const String& policy);

// normally 1 char = 1 byte
// max length to parse = 2^16 = 64 kB
static constexpr wtf_size_t MAX_LENGTH_PARSE = 1 << 16;

// Parse a single feature entry. e.g. feature_a ORIGIN_A ORIGIN_B.
// feature_entry = feature_name ' ' allowlist
base::Optional<ParsedFeaturePolicyDeclaration> ParseFeature(
const String& input);
const internal::FeaturePolicyDeclarationNode&);

struct ParsedAllowlist {
std::vector<url::Origin> allowed_origins;
Expand Down Expand Up @@ -299,31 +310,14 @@ ParsingContext::ParsedAllowlist ParsingContext::ParseAllowlist(
}

base::Optional<ParsedFeaturePolicyDeclaration> ParsingContext::ParseFeature(
const String& input) {
// Split removes extra whitespaces by default
Vector<String> tokens;
input.Split(' ', tokens);

// Empty policy. Skip.
if (tokens.IsEmpty())
return base::nullopt;

// Break tokens into head & tail, where
// head = feature_name
// tail = origins
// After feature_name has been parsed, take tail of tokens vector by
// erasing the first element.
const internal::FeaturePolicyDeclarationNode& declaration_node) {
base::Optional<mojom::blink::FeaturePolicyFeature> feature =
ParseFeatureName(/* feature_name */ tokens.front());

tokens.erase(tokens.begin());

ParsedAllowlist parsed_allowlist =
ParseAllowlist(/* origin_strings */ tokens);

ParseFeatureName(declaration_node.feature_name);
if (!feature)
return base::nullopt;

ParsedAllowlist parsed_allowlist = ParseAllowlist(declaration_node.allowlist);

// If same feature appeared more than once, only the first one counts.
if (FeatureObserved(*feature))
return base::nullopt;
Expand All @@ -337,13 +331,32 @@ base::Optional<ParsedFeaturePolicyDeclaration> ParsingContext::ParseFeature(
}

ParsedFeaturePolicy ParsingContext::Parse(const String& policy) {
return ParseIR(ParseToIR(policy));
}

ParsedFeaturePolicy ParsingContext::ParseIR(
const internal::FeaturePolicyNode& root) {
ParsedFeaturePolicy parsed_policy;
for (const internal::FeaturePolicyDeclarationNode& declaration_node : root) {
base::Optional<ParsedFeaturePolicyDeclaration> parsed_feature =
ParseFeature(declaration_node);
if (parsed_feature) {
ReportFeatureUsage(parsed_feature->feature);
parsed_policy.push_back(*parsed_feature);
}
}
ReportAllowlistTypeUsage();
return parsed_policy;
}

internal::FeaturePolicyNode ParsingContext::ParseToIR(const String& policy) {
internal::FeaturePolicyNode root;

if (policy.length() > MAX_LENGTH_PARSE) {
logger_.Error("Feature policy declaration exceeds size limit(" +
String::Number(policy.length()) + ">" +
String::Number(MAX_LENGTH_PARSE) + ")");
return parsed_policy;
return {};
}

// RFC2616, section 4.2 specifies that headers appearing multiple times can be
Expand All @@ -370,18 +383,26 @@ ParsedFeaturePolicy ParsingContext::Parse(const String& policy) {
}

for (const String& feature_entry : feature_entries) {
base::Optional<ParsedFeaturePolicyDeclaration> parsed_feature =
ParseFeature(feature_entry);
if (parsed_feature) {
ReportFeatureUsage(parsed_feature->feature);
parsed_policy.push_back(*parsed_feature);
}
Vector<String> tokens;
feature_entry.Split(' ', tokens);

if (tokens.IsEmpty())
continue;

internal::FeaturePolicyDeclarationNode declaration_node;
// Break tokens into head & tail, where
// head = feature_name
// tail = allowlist
// After feature_name has been set, take tail of tokens vector by
// erasing the first element.
declaration_node.feature_name = std::move(tokens.front());
tokens.erase(tokens.begin());
declaration_node.allowlist = std::move(tokens);
root.push_back(declaration_node);
}
}

ReportAllowlistTypeUsage();

return parsed_policy;
return root;
}

} // namespace
Expand Down

0 comments on commit 86a6930

Please sign in to comment.