1 // Use of this source code is governed by a BSD-style license that can be
2 // found in the LICENSE file.
3
4 #include "third_party/blink/renderer/core/feature_policy/feature_policy_parser.h"
5
6 #include <algorithm>
7 #include <utility>
8
9 #include <bitset>
10 #include "base/metrics/histogram_macros.h"
11 #include "net/http/structured_headers.h"
12 #include "third_party/blink/public/mojom/feature_policy/feature_policy.mojom-blink.h"
13 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
14 #include "third_party/blink/renderer/core/frame/web_feature.h"
15 #include "third_party/blink/renderer/core/origin_trials/origin_trial_context.h"
16 #include "third_party/blink/renderer/core/origin_trials/origin_trials.h"
17 #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
18 #include "third_party/blink/renderer/platform/json/json_values.h"
19 #include "third_party/blink/renderer/platform/network/http_parsers.h"
20 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
21 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
22 #include "third_party/blink/renderer/platform/wtf/hash_set.h"
23 #include "third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h"
24 #include "url/origin.h"
25
26 namespace blink {
27 namespace {
28
29 class ParsedFeaturePolicies final
30 : public GarbageCollected<ParsedFeaturePolicies>,
31 public Supplement<ExecutionContext> {
32 public:
33 static const char kSupplementName[];
34
From(ExecutionContext & context)35 static ParsedFeaturePolicies& From(ExecutionContext& context) {
36 ParsedFeaturePolicies* policies =
37 Supplement<ExecutionContext>::From<ParsedFeaturePolicies>(context);
38 if (!policies) {
39 policies = MakeGarbageCollected<ParsedFeaturePolicies>(context);
40 Supplement<ExecutionContext>::ProvideTo(context, policies);
41 }
42 return *policies;
43 }
44
ParsedFeaturePolicies(ExecutionContext & context)45 explicit ParsedFeaturePolicies(ExecutionContext& context)
46 : Supplement<ExecutionContext>(context),
47 policies_(
48 static_cast<size_t>(mojom::blink::FeaturePolicyFeature::kMaxValue) +
49 1) {}
50
Observed(mojom::blink::FeaturePolicyFeature feature)51 bool Observed(mojom::blink::FeaturePolicyFeature feature) {
52 size_t feature_index = static_cast<size_t>(feature);
53 if (policies_[feature_index])
54 return true;
55 policies_[feature_index] = true;
56 return false;
57 }
58
59 private:
60 // Tracks which feature policies have already been parsed, so as not to count
61 // them multiple times.
62 Vector<bool> policies_;
63 };
64
65 const char ParsedFeaturePolicies::kSupplementName[] = "ParsedFeaturePolicies";
66
67 class FeatureObserver {
68 public:
69 // Returns whether the feature has been observed before or not.
70 bool FeatureObserved(mojom::blink::FeaturePolicyFeature feature);
71
72 private:
73 std::bitset<
74 static_cast<size_t>(mojom::blink::FeaturePolicyFeature::kMaxValue) + 1>
75 features_specified_;
76 };
77
78 class ParsingContext {
79 STACK_ALLOCATED();
80
81 public:
ParsingContext(PolicyParserMessageBuffer & logger,scoped_refptr<const SecurityOrigin> self_origin,scoped_refptr<const SecurityOrigin> src_origin,const FeatureNameMap & feature_names,ExecutionContext * execution_context)82 ParsingContext(PolicyParserMessageBuffer& logger,
83 scoped_refptr<const SecurityOrigin> self_origin,
84 scoped_refptr<const SecurityOrigin> src_origin,
85 const FeatureNameMap& feature_names,
86 ExecutionContext* execution_context)
87 : logger_(logger),
88 self_origin_(self_origin),
89 src_origin_(src_origin),
90 feature_names_(feature_names),
91 execution_context_(execution_context) {}
92
93 ~ParsingContext() = default;
94
95 ParsedFeaturePolicy ParseFeaturePolicy(const String& policy);
96 ParsedFeaturePolicy ParsePermissionsPolicy(const String& policy);
97
98 private:
99 // Following is the intermediate represetnation(IR) of feature policy.
100 // Parsing of syntax structures is done in this IR, but semantic checks, e.g.
101 // whether feature_name is valid, are not yet performed.
102 struct FeaturePolicyDeclarationNode {
103 String feature_name;
104 Vector<String> allowlist;
105 };
106 using FeaturePolicyNode = Vector<FeaturePolicyDeclarationNode>;
107
108 ParsedFeaturePolicy ParseIR(const FeaturePolicyNode& root);
109 FeaturePolicyNode ParseFeaturePolicyToIR(const String& policy);
110 FeaturePolicyNode ParsePermissionsPolicyToIR(const String& policy);
111
112 // normally 1 char = 1 byte
113 // max length to parse = 2^16 = 64 kB
114 static constexpr wtf_size_t MAX_LENGTH_PARSE = 1 << 16;
115
116 base::Optional<ParsedFeaturePolicyDeclaration> ParseFeature(
117 const FeaturePolicyDeclarationNode&);
118
119 struct ParsedAllowlist {
120 std::vector<url::Origin> allowed_origins;
121 bool matches_all_origins{false};
122 bool matches_opaque_src{false};
123
ParsedAllowlistblink::__anon3cd072200111::ParsingContext::ParsedAllowlist124 ParsedAllowlist() : allowed_origins({}) {}
125 };
126
127 base::Optional<mojom::blink::FeaturePolicyFeature> ParseFeatureName(
128 const String& feature_name);
129
130 // Parse allowlist for feature.
131 ParsedAllowlist ParseAllowlist(const Vector<String>& origin_strings);
132
133 void ReportFeatureUsage(mojom::blink::FeaturePolicyFeature feature);
134
135 // This function should be called after Allowlist Histograms related flags
136 // have been captured.
137 void RecordAllowlistTypeUsage(size_t origin_count);
138 // The use of various allowlist types should only be recorded once per page.
139 // For simplicity, this recording assumes that the ParseHeader method is
140 // called once when creating a new document, and similarly the ParseAttribute
141 // method is called once for a frame. It is possible for multiple calls, but
142 // the additional complexity to guarantee only one record isn't warranted as
143 // yet.
144 void ReportAllowlistTypeUsage();
145
146 PolicyParserMessageBuffer& logger_;
147 scoped_refptr<const SecurityOrigin> self_origin_;
148 scoped_refptr<const SecurityOrigin> src_origin_;
149 const FeatureNameMap& feature_names_;
150 ExecutionContext* execution_context_;
151
152 // Flags for the types of items which can be used in allowlists.
153 bool allowlist_includes_star_ = false;
154 bool allowlist_includes_self_ = false;
155 bool allowlist_includes_src_ = false;
156 bool allowlist_includes_none_ = false;
157 bool allowlist_includes_origin_ = false;
158
159 HashSet<FeaturePolicyAllowlistType> allowlist_types_used_;
160
161 FeatureObserver feature_observer_;
162 };
163
FeatureObserved(mojom::blink::FeaturePolicyFeature feature)164 bool FeatureObserver::FeatureObserved(
165 mojom::blink::FeaturePolicyFeature feature) {
166 if (features_specified_[static_cast<size_t>(feature)]) {
167 return true;
168 } else {
169 features_specified_.set(static_cast<size_t>(feature));
170 return false;
171 }
172 }
173
ReportFeatureUsage(mojom::blink::FeaturePolicyFeature feature)174 void ParsingContext::ReportFeatureUsage(
175 mojom::blink::FeaturePolicyFeature feature) {
176 if (src_origin_) {
177 if (!execution_context_ ||
178 !ParsedFeaturePolicies::From(*execution_context_).Observed(feature)) {
179 UMA_HISTOGRAM_ENUMERATION("Blink.UseCounter.FeaturePolicy.Allow",
180 feature);
181 }
182 } else {
183 UMA_HISTOGRAM_ENUMERATION("Blink.UseCounter.FeaturePolicy.Header", feature);
184 }
185 }
186
RecordAllowlistTypeUsage(size_t origin_count)187 void ParsingContext::RecordAllowlistTypeUsage(size_t origin_count) {
188 // Record the type of allowlist used.
189 if (origin_count == 0) {
190 allowlist_types_used_.insert(FeaturePolicyAllowlistType::kEmpty);
191 } else if (origin_count == 1) {
192 if (allowlist_includes_star_)
193 allowlist_types_used_.insert(FeaturePolicyAllowlistType::kStar);
194 else if (allowlist_includes_self_)
195 allowlist_types_used_.insert(FeaturePolicyAllowlistType::kSelf);
196 else if (allowlist_includes_src_)
197 allowlist_types_used_.insert(FeaturePolicyAllowlistType::kSrc);
198 else if (allowlist_includes_none_)
199 allowlist_types_used_.insert(FeaturePolicyAllowlistType::kNone);
200 else
201 allowlist_types_used_.insert(FeaturePolicyAllowlistType::kOrigins);
202 } else {
203 if (allowlist_includes_origin_) {
204 if (allowlist_includes_star_ || allowlist_includes_none_ ||
205 allowlist_includes_src_ || allowlist_includes_self_)
206 allowlist_types_used_.insert(FeaturePolicyAllowlistType::kMixed);
207 else
208 allowlist_types_used_.insert(FeaturePolicyAllowlistType::kOrigins);
209 } else {
210 allowlist_types_used_.insert(FeaturePolicyAllowlistType::kKeywordsOnly);
211 }
212 }
213 // Reset all flags.
214 allowlist_includes_star_ = false;
215 allowlist_includes_self_ = false;
216 allowlist_includes_src_ = false;
217 allowlist_includes_none_ = false;
218 allowlist_includes_origin_ = false;
219 }
220
ReportAllowlistTypeUsage()221 void ParsingContext::ReportAllowlistTypeUsage() {
222 for (const FeaturePolicyAllowlistType allowlist_type :
223 allowlist_types_used_) {
224 if (src_origin_) {
225 UMA_HISTOGRAM_ENUMERATION(
226 "Blink.UseCounter.FeaturePolicy.AttributeAllowlistType",
227 allowlist_type);
228 } else {
229 UMA_HISTOGRAM_ENUMERATION(
230 "Blink.UseCounter.FeaturePolicy.HeaderAllowlistType", allowlist_type);
231 }
232 }
233 }
234
235 base::Optional<mojom::blink::FeaturePolicyFeature>
ParseFeatureName(const String & feature_name)236 ParsingContext::ParseFeatureName(const String& feature_name) {
237 DCHECK(!feature_name.IsEmpty());
238 if (!feature_names_.Contains(feature_name)) {
239 logger_.Warn("Unrecognized feature: '" + feature_name + "'.");
240 return base::nullopt;
241 }
242 if (DisabledByOriginTrial(feature_name, execution_context_)) {
243 logger_.Warn("Origin trial controlled feature not enabled: '" +
244 feature_name + "'.");
245 return base::nullopt;
246 }
247 mojom::blink::FeaturePolicyFeature feature = feature_names_.at(feature_name);
248
249 return feature;
250 }
251
ParseAllowlist(const Vector<String> & origin_strings)252 ParsingContext::ParsedAllowlist ParsingContext::ParseAllowlist(
253 const Vector<String>& origin_strings) {
254 ParsedAllowlist allowlist;
255 if (origin_strings.IsEmpty()) {
256 // If a policy entry has no listed origins (e.g. "feature_name1" in
257 // allow="feature_name1; feature_name2 value"), enable the feature for:
258 // a. |self_origin|, if we are parsing a header policy (i.e.,
259 // |src_origin| is null);
260 // b. |src_origin|, if we are parsing an allow attribute (i.e.,
261 // |src_origin| is not null), |src_origin| is not opaque; or
262 // c. the opaque origin of the frame, if |src_origin| is opaque.
263 if (!src_origin_) {
264 allowlist.allowed_origins.push_back(self_origin_->ToUrlOrigin());
265 } else if (!src_origin_->IsOpaque()) {
266 allowlist.allowed_origins.push_back(src_origin_->ToUrlOrigin());
267 } else {
268 allowlist.matches_opaque_src = true;
269 }
270 } else {
271 for (const String& origin_string : origin_strings) {
272 DCHECK(!origin_string.IsEmpty());
273
274 if (!origin_string.ContainsOnlyASCIIOrEmpty()) {
275 logger_.Warn("Non-ASCII characters in origin.");
276 continue;
277 }
278
279 // Determine the target of the declaration. This may be a specific
280 // origin, either explicitly written, or one of the special keywords
281 // 'self' or 'src'. ('src' can only be used in the iframe allow
282 // attribute.)
283 url::Origin target_origin;
284
285 // If the iframe will have an opaque origin (for example, if it is
286 // sandboxed, or has a data: URL), then 'src' needs to refer to the
287 // opaque origin of the frame, which is not known yet. In this case,
288 // the |matches_opaque_src| flag on the declaration is set, rather than
289 // adding an origin to the allowlist.
290 bool target_is_opaque = false;
291 bool target_is_all = false;
292
293 // 'self' origin is used if the origin is exactly 'self'.
294 if (EqualIgnoringASCIICase(origin_string, "'self'")) {
295 allowlist_includes_self_ = true;
296 target_origin = self_origin_->ToUrlOrigin();
297 }
298 // 'src' origin is used if |src_origin| is available and the
299 // origin is a match for 'src'. |src_origin| is only set
300 // when parsing an iframe allow attribute.
301 else if (src_origin_ && EqualIgnoringASCIICase(origin_string, "'src'")) {
302 allowlist_includes_src_ = true;
303 if (!src_origin_->IsOpaque()) {
304 target_origin = src_origin_->ToUrlOrigin();
305 } else {
306 target_is_opaque = true;
307 }
308 } else if (EqualIgnoringASCIICase(origin_string, "'none'")) {
309 allowlist_includes_none_ = true;
310 continue;
311 } else if (origin_string == "*") {
312 allowlist_includes_star_ = true;
313 target_is_all = true;
314 }
315 // Otherwise, parse the origin string and verify that the result is
316 // valid. Invalid strings will produce an opaque origin, which will
317 // result in an error message.
318 else {
319 scoped_refptr<SecurityOrigin> parsed_origin =
320 SecurityOrigin::CreateFromString(origin_string);
321 if (!parsed_origin->IsOpaque()) {
322 target_origin = parsed_origin->ToUrlOrigin();
323 allowlist_includes_origin_ = true;
324 } else {
325 logger_.Warn("Unrecognized origin: '" + origin_string + "'.");
326 continue;
327 }
328 }
329
330 if (target_is_all) {
331 allowlist.matches_all_origins = true;
332 allowlist.matches_opaque_src = true;
333 } else if (target_is_opaque) {
334 allowlist.matches_opaque_src = true;
335 } else {
336 allowlist.allowed_origins.push_back(target_origin);
337 }
338 }
339 }
340
341 // Size reduction: remove all items in the allowlist if target is all.
342 if (allowlist.matches_all_origins)
343 allowlist.allowed_origins.clear();
344
345 // Sort |allowed_origins| in alphabetical order.
346 std::sort(allowlist.allowed_origins.begin(), allowlist.allowed_origins.end());
347
348 RecordAllowlistTypeUsage(origin_strings.size());
349
350 return allowlist;
351 }
352
ParseFeature(const FeaturePolicyDeclarationNode & declaration_node)353 base::Optional<ParsedFeaturePolicyDeclaration> ParsingContext::ParseFeature(
354 const FeaturePolicyDeclarationNode& declaration_node) {
355 base::Optional<mojom::blink::FeaturePolicyFeature> feature =
356 ParseFeatureName(declaration_node.feature_name);
357 if (!feature)
358 return base::nullopt;
359
360 ParsedAllowlist parsed_allowlist = ParseAllowlist(declaration_node.allowlist);
361
362 // If same feature appeared more than once, only the first one counts.
363 if (feature_observer_.FeatureObserved(*feature))
364 return base::nullopt;
365
366 ParsedFeaturePolicyDeclaration parsed_feature(*feature);
367 parsed_feature.allowed_origins = std::move(parsed_allowlist.allowed_origins);
368 parsed_feature.matches_all_origins = parsed_allowlist.matches_all_origins;
369 parsed_feature.matches_opaque_src = parsed_allowlist.matches_opaque_src;
370
371 return parsed_feature;
372 }
373
ParseFeaturePolicy(const String & policy)374 ParsedFeaturePolicy ParsingContext::ParseFeaturePolicy(const String& policy) {
375 return ParseIR(ParseFeaturePolicyToIR(policy));
376 }
377
ParsePermissionsPolicy(const String & policy)378 ParsedFeaturePolicy ParsingContext::ParsePermissionsPolicy(
379 const String& policy) {
380 return ParseIR(ParsePermissionsPolicyToIR(policy));
381 }
382
ParseIR(const ParsingContext::FeaturePolicyNode & root)383 ParsedFeaturePolicy ParsingContext::ParseIR(
384 const ParsingContext::FeaturePolicyNode& root) {
385 ParsedFeaturePolicy parsed_policy;
386 for (const ParsingContext::FeaturePolicyDeclarationNode& declaration_node :
387 root) {
388 base::Optional<ParsedFeaturePolicyDeclaration> parsed_feature =
389 ParseFeature(declaration_node);
390 if (parsed_feature) {
391 ReportFeatureUsage(parsed_feature->feature);
392 parsed_policy.push_back(*parsed_feature);
393 }
394 }
395 ReportAllowlistTypeUsage();
396 return parsed_policy;
397 }
398
ParseFeaturePolicyToIR(const String & policy)399 ParsingContext::FeaturePolicyNode ParsingContext::ParseFeaturePolicyToIR(
400 const String& policy) {
401 ParsingContext::FeaturePolicyNode root;
402
403 if (policy.length() > MAX_LENGTH_PARSE) {
404 logger_.Error("Feature policy declaration exceeds size limit(" +
405 String::Number(policy.length()) + ">" +
406 String::Number(MAX_LENGTH_PARSE) + ")");
407 return {};
408 }
409
410 // RFC2616, section 4.2 specifies that headers appearing multiple times can be
411 // combined with a comma. Walk the header string, and parse each comma
412 // separated chunk as a separate header.
413 Vector<String> policy_items;
414 // policy_items = [ policy *( "," [ policy ] ) ]
415 policy.Split(',', policy_items);
416
417 if (policy_items.size() > 1) {
418 UseCounter::Count(
419 execution_context_,
420 mojom::blink::WebFeature::kFeaturePolicyCommaSeparatedDeclarations);
421 }
422
423 for (const String& item : policy_items) {
424 Vector<String> feature_entries;
425 // feature_entries = [ feature_entry *( ";" [ feature_entry ] ) ]
426 item.Split(';', feature_entries);
427
428 if (feature_entries.size() > 1) {
429 UseCounter::Count(execution_context_,
430 mojom::blink::WebFeature::
431 kFeaturePolicySemicolonSeparatedDeclarations);
432 }
433
434 for (const String& feature_entry : feature_entries) {
435 Vector<String> tokens;
436 feature_entry.Split(' ', tokens);
437
438 if (tokens.IsEmpty())
439 continue;
440
441 ParsingContext::FeaturePolicyDeclarationNode declaration_node;
442 // Break tokens into head & tail, where
443 // head = feature_name
444 // tail = allowlist
445 // After feature_name has been set, take tail of tokens vector by
446 // erasing the first element.
447 declaration_node.feature_name = std::move(tokens.front());
448 tokens.erase(tokens.begin());
449 declaration_node.allowlist = std::move(tokens);
450 root.push_back(declaration_node);
451 }
452 }
453
454 return root;
455 }
456
ParsePermissionsPolicyToIR(const String & policy)457 ParsingContext::FeaturePolicyNode ParsingContext::ParsePermissionsPolicyToIR(
458 const String& policy) {
459 if (policy.length() > MAX_LENGTH_PARSE) {
460 logger_.Error("Permissions policy declaration exceeds size limit(" +
461 String::Number(policy.length()) + ">" +
462 String::Number(MAX_LENGTH_PARSE) + ")");
463 return {};
464 }
465
466 auto root = net::structured_headers::ParseDictionary(policy.Utf8());
467 if (!root) {
468 logger_.Error(
469 "Parse of permission policy failed because of errors reported by "
470 "strctured header parser.");
471 return {};
472 }
473
474 ParsingContext::FeaturePolicyNode ir_root;
475 for (const auto& feature_entry : root.value()) {
476 const auto& key = feature_entry.first;
477 const char* feature_name = key.c_str();
478 const auto& value = feature_entry.second;
479
480 if (!value.params.empty()) {
481 logger_.Warn(
482 String::Format("Feature %s's parameters are ignored.", feature_name));
483 }
484
485 Vector<String> allowlist;
486 for (const auto& parameterized_item : value.member) {
487 if (!parameterized_item.params.empty()) {
488 logger_.Warn(String::Format("Feature %s's parameters are ignored.",
489 feature_name));
490 }
491
492 String allowlist_item;
493 if (parameterized_item.item.is_token()) {
494 // All special keyword appears as token, i.e. self, src and *.
495 const std::string& token_value = parameterized_item.item.GetString();
496 if (token_value != "*" && token_value != "self") {
497 logger_.Warn(String::Format(
498 "Invalid allowlist item(%s) for feature %s. Allowlist item "
499 "must be *, self or quoted url.",
500 token_value.c_str(), feature_name));
501 continue;
502 }
503
504 if (token_value == "*") {
505 allowlist_item = "*";
506 } else {
507 allowlist_item = String::Format("'%s'", token_value.c_str());
508 }
509 } else if (parameterized_item.item.is_string()) {
510 allowlist_item = parameterized_item.item.GetString().c_str();
511 } else {
512 logger_.Warn(
513 String::Format("Invalid allowlist item for feature %s. Allowlist "
514 "item must be *, self, or quoted url.",
515 feature_name));
516 continue;
517 }
518 if (!allowlist_item.IsEmpty())
519 allowlist.push_back(allowlist_item);
520 }
521
522 if (allowlist.IsEmpty())
523 allowlist.push_back("'none'");
524
525 ir_root.push_back(
526 ParsingContext::FeaturePolicyDeclarationNode{feature_name, allowlist});
527 }
528
529 return ir_root;
530 }
531
532 } // namespace
533
ParseHeader(const String & feature_policy_header,const String & permissions_policy_header,scoped_refptr<const SecurityOrigin> origin,PolicyParserMessageBuffer & feature_policy_logger,PolicyParserMessageBuffer & permissions_policy_logger,ExecutionContext * execution_context)534 ParsedFeaturePolicy FeaturePolicyParser::ParseHeader(
535 const String& feature_policy_header,
536 const String& permissions_policy_header,
537 scoped_refptr<const SecurityOrigin> origin,
538 PolicyParserMessageBuffer& feature_policy_logger,
539 PolicyParserMessageBuffer& permissions_policy_logger,
540 ExecutionContext* execution_context) {
541 ParsedFeaturePolicy permissions_policy =
542 ParsingContext(permissions_policy_logger, origin, nullptr,
543 GetDefaultFeatureNameMap(), execution_context)
544 .ParsePermissionsPolicy(permissions_policy_header);
545 ParsedFeaturePolicy feature_policy =
546 ParsingContext(feature_policy_logger, origin, nullptr,
547 GetDefaultFeatureNameMap(), execution_context)
548 .ParseFeaturePolicy(feature_policy_header);
549
550 FeatureObserver observer;
551 for (const auto& policy_declaration : permissions_policy) {
552 bool feature_observed =
553 observer.FeatureObserved(policy_declaration.feature);
554 DCHECK(!feature_observed);
555 }
556 for (const auto& policy_declaration : feature_policy) {
557 if (!observer.FeatureObserved(policy_declaration.feature)) {
558 permissions_policy.push_back(policy_declaration);
559 } else {
560 feature_policy_logger.Warn(String::Format(
561 "Feature %s has been specified in both Feature-Policy and "
562 "Permissions-Policy header. Value defined in Permissions-Policy "
563 "header will be used.",
564 GetNameForFeature(policy_declaration.feature).Ascii().c_str()));
565 }
566 }
567
568 return permissions_policy;
569 }
570
ParseAttribute(const String & policy,scoped_refptr<const SecurityOrigin> self_origin,scoped_refptr<const SecurityOrigin> src_origin,PolicyParserMessageBuffer & logger,ExecutionContext * execution_context)571 ParsedFeaturePolicy FeaturePolicyParser::ParseAttribute(
572 const String& policy,
573 scoped_refptr<const SecurityOrigin> self_origin,
574 scoped_refptr<const SecurityOrigin> src_origin,
575 PolicyParserMessageBuffer& logger,
576 ExecutionContext* execution_context) {
577 return ParsingContext(logger, self_origin, src_origin,
578 GetDefaultFeatureNameMap(), execution_context)
579 .ParseFeaturePolicy(policy);
580 }
581
ParseFeaturePolicyForTest(const String & policy,scoped_refptr<const SecurityOrigin> self_origin,scoped_refptr<const SecurityOrigin> src_origin,PolicyParserMessageBuffer & logger,const FeatureNameMap & feature_names,ExecutionContext * execution_context)582 ParsedFeaturePolicy FeaturePolicyParser::ParseFeaturePolicyForTest(
583 const String& policy,
584 scoped_refptr<const SecurityOrigin> self_origin,
585 scoped_refptr<const SecurityOrigin> src_origin,
586 PolicyParserMessageBuffer& logger,
587 const FeatureNameMap& feature_names,
588 ExecutionContext* execution_context) {
589 return ParsingContext(logger, self_origin, src_origin, feature_names,
590 execution_context)
591 .ParseFeaturePolicy(policy);
592 }
593
ParsePermissionsPolicyForTest(const String & policy,scoped_refptr<const SecurityOrigin> self_origin,scoped_refptr<const SecurityOrigin> src_origin,PolicyParserMessageBuffer & logger,const FeatureNameMap & feature_names,ExecutionContext * execution_context)594 ParsedFeaturePolicy FeaturePolicyParser::ParsePermissionsPolicyForTest(
595 const String& policy,
596 scoped_refptr<const SecurityOrigin> self_origin,
597 scoped_refptr<const SecurityOrigin> src_origin,
598 PolicyParserMessageBuffer& logger,
599 const FeatureNameMap& feature_names,
600 ExecutionContext* execution_context) {
601 return ParsingContext(logger, self_origin, src_origin, feature_names,
602 execution_context)
603 .ParsePermissionsPolicy(policy);
604 }
605
IsFeatureDeclared(mojom::blink::FeaturePolicyFeature feature,const ParsedFeaturePolicy & policy)606 bool IsFeatureDeclared(mojom::blink::FeaturePolicyFeature feature,
607 const ParsedFeaturePolicy& policy) {
608 return std::any_of(policy.begin(), policy.end(),
609 [feature](const auto& declaration) {
610 return declaration.feature == feature;
611 });
612 }
613
RemoveFeatureIfPresent(mojom::blink::FeaturePolicyFeature feature,ParsedFeaturePolicy & policy)614 bool RemoveFeatureIfPresent(mojom::blink::FeaturePolicyFeature feature,
615 ParsedFeaturePolicy& policy) {
616 auto new_end = std::remove_if(policy.begin(), policy.end(),
617 [feature](const auto& declaration) {
618 return declaration.feature == feature;
619 });
620 if (new_end == policy.end())
621 return false;
622 policy.erase(new_end, policy.end());
623 return true;
624 }
625
DisallowFeatureIfNotPresent(mojom::blink::FeaturePolicyFeature feature,ParsedFeaturePolicy & policy)626 bool DisallowFeatureIfNotPresent(mojom::blink::FeaturePolicyFeature feature,
627 ParsedFeaturePolicy& policy) {
628 if (IsFeatureDeclared(feature, policy))
629 return false;
630 ParsedFeaturePolicyDeclaration allowlist(feature);
631 policy.push_back(allowlist);
632 return true;
633 }
634
AllowFeatureEverywhereIfNotPresent(mojom::blink::FeaturePolicyFeature feature,ParsedFeaturePolicy & policy)635 bool AllowFeatureEverywhereIfNotPresent(
636 mojom::blink::FeaturePolicyFeature feature,
637 ParsedFeaturePolicy& policy) {
638 if (IsFeatureDeclared(feature, policy))
639 return false;
640 ParsedFeaturePolicyDeclaration allowlist(feature);
641 allowlist.matches_all_origins = true;
642 allowlist.matches_opaque_src = true;
643 policy.push_back(allowlist);
644 return true;
645 }
646
DisallowFeature(mojom::blink::FeaturePolicyFeature feature,ParsedFeaturePolicy & policy)647 void DisallowFeature(mojom::blink::FeaturePolicyFeature feature,
648 ParsedFeaturePolicy& policy) {
649 RemoveFeatureIfPresent(feature, policy);
650 DisallowFeatureIfNotPresent(feature, policy);
651 }
652
IsFeatureForMeasurementOnly(mojom::blink::FeaturePolicyFeature feature)653 bool IsFeatureForMeasurementOnly(mojom::blink::FeaturePolicyFeature feature) {
654 return feature == mojom::blink::FeaturePolicyFeature::kWebShare;
655 }
656
AllowFeatureEverywhere(mojom::blink::FeaturePolicyFeature feature,ParsedFeaturePolicy & policy)657 void AllowFeatureEverywhere(mojom::blink::FeaturePolicyFeature feature,
658 ParsedFeaturePolicy& policy) {
659 RemoveFeatureIfPresent(feature, policy);
660 AllowFeatureEverywhereIfNotPresent(feature, policy);
661 }
662
GetAvailableFeatures(ExecutionContext * execution_context)663 const Vector<String> GetAvailableFeatures(ExecutionContext* execution_context) {
664 Vector<String> available_features;
665 for (const auto& feature : GetDefaultFeatureNameMap()) {
666 if (!DisabledByOriginTrial(feature.key, execution_context) &&
667 !IsFeatureForMeasurementOnly(feature.value)) {
668 available_features.push_back(feature.key);
669 }
670 }
671 return available_features;
672 }
673
GetNameForFeature(mojom::blink::FeaturePolicyFeature feature)674 const String& GetNameForFeature(mojom::blink::FeaturePolicyFeature feature) {
675 for (const auto& entry : GetDefaultFeatureNameMap()) {
676 if (entry.value == feature)
677 return entry.key;
678 }
679 return g_empty_string;
680 }
681
682 } // namespace blink
683