blob: 74d15ee8ecb9da9d538015e35676f3326e495a64 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "platform/feature_policy/FeaturePolicy.h"
#include "platform/RuntimeEnabledFeatures.h"
#include "platform/json/JSONValues.h"
#include "platform/network/HTTPParsers.h"
#include "platform/weborigin/SecurityOrigin.h"
#include "platform/wtf/PtrUtil.h"
namespace blink {
namespace {
void AddAllowFeatureToList(
WebFeaturePolicyFeature feature,
Vector<WebParsedFeaturePolicyDeclaration>& whitelists) {
WebParsedFeaturePolicyDeclaration whitelist;
whitelist.feature = feature;
whitelist.matches_all_origins = true;
whitelists.push_back(whitelist);
}
} // namespace
WebParsedFeaturePolicy ParseFeaturePolicy(const String& policy,
RefPtr<SecurityOrigin> origin,
Vector<String>* messages) {
return ParseFeaturePolicy(policy, origin, messages,
GetDefaultFeatureNameMap());
}
WebParsedFeaturePolicy ParseFeaturePolicy(const String& policy,
RefPtr<SecurityOrigin> origin,
Vector<String>* messages,
const FeatureNameMap& feature_names) {
Vector<WebParsedFeaturePolicyDeclaration> whitelists;
// Use a reasonable parse depth limit; the actual maximum depth is only going
// to be 4 for a valid policy, but we'll give the featurePolicyParser a chance
// to report more specific errors, unless the string is really invalid.
std::unique_ptr<JSONArray> policy_items = ParseJSONHeader(policy, 50);
if (!policy_items) {
if (messages)
messages->push_back("Unable to parse header.");
return whitelists;
}
for (size_t i = 0; i < policy_items->size(); ++i) {
JSONObject* item = JSONObject::Cast(policy_items->at(i));
if (!item) {
if (messages)
messages->push_back("Policy is not an object.");
continue; // Array element is not an object; skip
}
for (size_t j = 0; j < item->size(); ++j) {
JSONObject::Entry entry = item->at(j);
if (!feature_names.Contains(entry.first))
continue; // Unrecognized feature; skip
WebFeaturePolicyFeature feature = feature_names.at(entry.first);
JSONArray* targets = JSONArray::Cast(entry.second);
if (!targets) {
if (messages)
messages->push_back("Whitelist is not an array of strings.");
continue;
}
WebParsedFeaturePolicyDeclaration whitelist;
whitelist.feature = feature;
Vector<WebSecurityOrigin> origins;
String target_string;
for (size_t j = 0; j < targets->size(); ++j) {
if (targets->at(j)->AsString(&target_string)) {
if (EqualIgnoringASCIICase(target_string, "self")) {
if (!origin->IsUnique())
origins.push_back(origin);
} else if (target_string == "*") {
whitelist.matches_all_origins = true;
} else {
WebSecurityOrigin target_origin =
WebSecurityOrigin::CreateFromString(target_string);
if (!target_origin.IsNull() && !target_origin.IsUnique())
origins.push_back(target_origin);
}
} else {
if (messages)
messages->push_back("Whitelist is not an array of strings.");
}
}
whitelist.origins = origins;
whitelists.push_back(whitelist);
}
}
return whitelists;
}
WebParsedFeaturePolicy GetContainerPolicyFromAllowedFeatures(
const WebVector<WebFeaturePolicyFeature>& features,
bool allowfullscreen,
bool allowpayment,
RefPtr<SecurityOrigin> origin) {
Vector<WebParsedFeaturePolicyDeclaration> whitelists;
bool override_payment = false;
bool override_fullscreen = false;
for (const WebFeaturePolicyFeature feature : features) {
// Container policy should override "allowfullscreen" and
// "allowpaymentrequest" policies.
if (feature == WebFeaturePolicyFeature::kPayment)
override_payment = true;
if (feature == WebFeaturePolicyFeature::kFullscreen)
override_fullscreen = true;
WebParsedFeaturePolicyDeclaration whitelist;
whitelist.feature = feature;
whitelist.origins = Vector<WebSecurityOrigin>(1UL, {origin});
whitelists.push_back(whitelist);
}
// If allowfullscreen attribute is present and no fullscreen policy is set,
// enable the feature for all origins; similarly for allowpaymentrequest.
if (allowpayment && !override_payment)
AddAllowFeatureToList(WebFeaturePolicyFeature::kPayment, whitelists);
if (allowfullscreen && !override_fullscreen)
AddAllowFeatureToList(WebFeaturePolicyFeature::kFullscreen, whitelists);
return whitelists;
}
bool IsSupportedInFeaturePolicy(WebFeaturePolicyFeature feature) {
switch (feature) {
// TODO(lunalu): Re-enabled fullscreen in feature policy once tests have
// been updated.
// crbug.com/666761
case WebFeaturePolicyFeature::kFullscreen:
return false;
case WebFeaturePolicyFeature::kPayment:
return true;
case WebFeaturePolicyFeature::kVibrate:
return RuntimeEnabledFeatures::featurePolicyExperimentalFeaturesEnabled();
default:
return false;
}
}
const FeatureNameMap& GetDefaultFeatureNameMap() {
DEFINE_STATIC_LOCAL(FeatureNameMap, default_feature_name_map, ());
if (default_feature_name_map.IsEmpty()) {
default_feature_name_map.Set("fullscreen",
WebFeaturePolicyFeature::kFullscreen);
default_feature_name_map.Set("payment", WebFeaturePolicyFeature::kPayment);
if (RuntimeEnabledFeatures::featurePolicyExperimentalFeaturesEnabled()) {
default_feature_name_map.Set("vibrate",
WebFeaturePolicyFeature::kVibrate);
default_feature_name_map.Set("camera", WebFeaturePolicyFeature::kCamera);
default_feature_name_map.Set("encrypted-media",
WebFeaturePolicyFeature::kEme);
default_feature_name_map.Set("microphone",
WebFeaturePolicyFeature::kMicrophone);
default_feature_name_map.Set("speaker",
WebFeaturePolicyFeature::kSpeaker);
default_feature_name_map.Set("cookie",
WebFeaturePolicyFeature::kDocumentCookie);
default_feature_name_map.Set("domain",
WebFeaturePolicyFeature::kDocumentDomain);
default_feature_name_map.Set("docwrite",
WebFeaturePolicyFeature::kDocumentWrite);
default_feature_name_map.Set("geolocation",
WebFeaturePolicyFeature::kGeolocation);
default_feature_name_map.Set("midi",
WebFeaturePolicyFeature::kMidiFeature);
default_feature_name_map.Set("notifications",
WebFeaturePolicyFeature::kNotifications);
default_feature_name_map.Set("push", WebFeaturePolicyFeature::kPush);
default_feature_name_map.Set("sync-script",
WebFeaturePolicyFeature::kSyncScript);
default_feature_name_map.Set("sync-xhr",
WebFeaturePolicyFeature::kSyncXHR);
default_feature_name_map.Set("webrtc", WebFeaturePolicyFeature::kWebRTC);
}
}
return default_feature_name_map;
}
} // namespace blink