Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(70)

Unified Diff: third_party/WebKit/Source/platform/feature_policy/FeaturePolicy.cpp

Issue 2254533002: [FeaturePolicy] Initial implementation of Feature Policy (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@fp-flag
Patch Set: Conform to JFV spec for header format Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/WebKit/Source/platform/feature_policy/FeaturePolicy.cpp
diff --git a/third_party/WebKit/Source/platform/feature_policy/FeaturePolicy.cpp b/third_party/WebKit/Source/platform/feature_policy/FeaturePolicy.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..552a51a7b3d6816eaaf76f8f29e663de91187ccd
--- /dev/null
+++ b/third_party/WebKit/Source/platform/feature_policy/FeaturePolicy.cpp
@@ -0,0 +1,238 @@
+// 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/json/JSONValues.h"
+#include "platform/network/HTTPParsers.h"
+#include "platform/weborigin/KURL.h"
+#include "platform/weborigin/SecurityOrigin.h"
+#include "wtf/text/StringBuilder.h"
+
+namespace blink {
+
+namespace {
+
+// Given a string name, return the matching feature struct, or nullptr if it is
+// not the name of a policy-controlled feature.
+const FeaturePolicyFeature* featureForName(const String& featureName) {
+ for (const FeaturePolicyFeature* feature :
+ FeaturePolicy::getFeatureRegistry()) {
+ if (featureName == feature->featureName)
+ return feature;
+ }
+ return nullptr;
+}
+
+// Parses a feature policy string into a mapping of features to whitelists. The
+// parse algorithm is deliberately very lenient, and will return as much as it
+// can recognize. Unrecognized features are simply ignored, as are invalid
+// policy items and invalid origins inside of whitelists.
+HeapHashMap<const FeaturePolicyFeature*, Member<FeaturePolicy::Whitelist>>
+parseFeaturePolicyString(std::unique_ptr<JSONArray> policyJSON,
+ RefPtr<SecurityOrigin> origin) {
+ HeapHashMap<const FeaturePolicyFeature*, Member<FeaturePolicy::Whitelist>>
+ whitelists;
+
+ if (!policyJSON)
+ return whitelists; // Policy string is not valid JSON
+
+ std::unique_ptr<JSONArray> items = JSONArray::cast(std::move(policyJSON));
dcheng 2016/10/20 17:41:44 The input is already a JSONArray, so this cast is
iclelland 2016/10/21 13:38:25 Done.
+ if (!items)
+ return whitelists; // Policy string is not an array
+
+ for (size_t i = 0; i < items->size(); ++i) {
+ JSONObject* item = JSONObject::cast(items->at(i));
+ if (!item)
+ continue; // Array element is not an object; skip
+
+ for (size_t j = 0; j < item->size(); ++j) {
+ JSONObject::Entry entry = item->at(j);
+ String featureName = entry.first;
+ const FeaturePolicyFeature* feature = featureForName(featureName);
+ if (!feature)
+ continue; // Feature is not recognized; skip
+
+ JSONArray* targets = JSONArray::cast(entry.second);
+ if (!targets)
+ continue; // Whitelist is not an array; skip
+
+ FeaturePolicy::Whitelist* whitelist = new FeaturePolicy::Whitelist;
+ String targetString;
+ for (size_t j = 0; j < targets->size(); ++j) {
+ if (targets->at(j)->asString(&targetString)) {
+ if (equalIgnoringCase(targetString, "self")) {
+ whitelist->add(origin);
+ } else if (targetString == "*") {
+ whitelist->addAll();
+ } else {
+ KURL originUrl = KURL(KURL(), targetString);
+ if (originUrl.isValid()) {
+ whitelist->add(SecurityOrigin::create(originUrl));
+ }
+ }
+ }
+ }
+ whitelists.set(feature, whitelist);
+ }
+ }
+ return whitelists;
+}
+
+} // namespace
+
+// Definitions of all features controlled by Feature Policy should appear here.
+const FeaturePolicyFeature kDocumentCookie{"cookie",
+ kEnableFeatureForAllOrigins};
+const FeaturePolicyFeature kDocumentDomain{"domain",
+ kEnableFeatureForAllOrigins};
+const FeaturePolicyFeature kDocumentWrite{"docwrite",
+ kEnableFeatureForAllOrigins};
+const FeaturePolicyFeature kGeolocationFeature{"geolocation",
+ kEnableFeatureForSelf};
+const FeaturePolicyFeature kMidiFeature{"midi", kEnableFeatureForAllOrigins};
+const FeaturePolicyFeature kNotificationsFeature{"notifications",
+ kEnableFeatureForAllOrigins};
+const FeaturePolicyFeature kPaymentFeature{"payment", kEnableFeatureForSelf};
+const FeaturePolicyFeature kPushFeature{"push", kEnableFeatureForAllOrigins};
+const FeaturePolicyFeature kSyncScript{"sync-script",
+ kEnableFeatureForAllOrigins};
+const FeaturePolicyFeature kSyncXHR{"sync-xhr", kEnableFeatureForAllOrigins};
+const FeaturePolicyFeature kUsermedia{"usermedia", kEnableFeatureForAllOrigins};
+const FeaturePolicyFeature kVibrateFeature{"vibrate", kEnableFeatureForSelf};
+const FeaturePolicyFeature kWebRTC{"webrtc", kEnableFeatureForAllOrigins};
+
+FeaturePolicy::Whitelist::Whitelist() : m_matchesAllOrigins(false) {}
+
+void FeaturePolicy::Whitelist::addAll() {
+ m_matchesAllOrigins = true;
+}
+
+void FeaturePolicy::Whitelist::add(RefPtr<SecurityOrigin> origin) {
+ m_origins.append(std::move(origin));
+}
+
+bool FeaturePolicy::Whitelist::contains(const SecurityOrigin& origin) const {
+ if (m_matchesAllOrigins)
+ return true;
+ for (const auto& targetOrigin : m_origins) {
+ if (targetOrigin->isSameSchemeHostPortAndSuborigin(&origin))
+ return true;
+ }
+ return false;
+}
+
+String FeaturePolicy::Whitelist::toString() {
+ StringBuilder sb;
+ sb.append("[");
+ if (m_matchesAllOrigins) {
+ sb.append("*");
+ } else {
+ for (size_t i = 0; i < m_origins.size(); ++i) {
+ if (i > 0) {
+ sb.append(", ");
+ }
+ sb.append(m_origins[i]->toString());
+ }
+ }
+ sb.append("]");
+ return sb.toString();
+}
+
+// static
+Vector<const FeaturePolicyFeature*>& FeaturePolicy::getFeatureRegistry() {
+ DEFINE_STATIC_LOCAL(
+ Vector<const FeaturePolicyFeature*>, featureRegistry,
+ ({&kDocumentCookie, &kDocumentDomain, &kDocumentWrite,
+ &kGeolocationFeature, &kMidiFeature, &kNotificationsFeature,
+ &kPaymentFeature, &kPushFeature, &kSyncScript, &kSyncXHR, &kUsermedia,
+ &kVibrateFeature, &kWebRTC}));
+ return featureRegistry;
+}
+
+// static
+FeaturePolicy* FeaturePolicy::createFromParentPolicy(
+ const FeaturePolicy* parent,
+ RefPtr<SecurityOrigin> currentOrigin) {
+ DCHECK(currentOrigin);
+ FeaturePolicy* newPolicy = new FeaturePolicy(currentOrigin);
+ for (const FeaturePolicyFeature* feature : getFeatureRegistry()) {
+ if (!parent || parent->isFeatureEnabledForOrigin(feature, *currentOrigin)) {
+ newPolicy->m_inheritedFeatures.set(feature, true);
+ } else {
+ newPolicy->m_inheritedFeatures.set(feature, false);
+ }
+ }
+ return newPolicy;
+}
+
+void FeaturePolicy::setHeaderPolicy(const String& policy) {
+ DCHECK(m_headerWhitelists.isEmpty());
+ std::unique_ptr<JSONArray> policyJSON = parseJSONHeader(policy);
+ if (!policyJSON)
+ return;
+ for (const auto& whitelist :
+ parseFeaturePolicyString(std::move(policyJSON), m_origin)) {
+ m_headerWhitelists.set(whitelist.key, whitelist.value);
raymes 2016/10/19 23:47:36 Could we just set m_headerWhitelists = parseFeatu
iclelland 2016/10/21 13:38:25 I think we can -- this used to be guarded, feature
+ }
+}
+
+bool FeaturePolicy::isFeatureEnabledForOrigin(
+ const FeaturePolicyFeature* feature,
+ const SecurityOrigin& origin) const {
+ if (m_inheritedFeatures.contains(feature)) {
raymes 2016/10/19 23:47:36 Can we remove this now (or make it a DCHECK?)
iclelland 2016/10/21 13:38:25 I'll make it a dcheck -- it'll just guard against
+ if (!m_inheritedFeatures.get(feature)) {
+ return false;
+ }
+ }
+ if (m_headerWhitelists.contains(feature)) {
+ return m_headerWhitelists.get(feature)->contains(origin);
+ }
+ if (feature->defaultPolicy == kEnableFeatureForAllOrigins) {
+ return true;
+ }
+ if (feature->defaultPolicy == kEnableFeatureForSelf) {
+ return m_origin->isSameSchemeHostPortAndSuborigin(&origin);
+ }
+ return false;
+}
+
+bool FeaturePolicy::isFeatureEnabled(
+ const FeaturePolicyFeature* feature) const {
+ DCHECK(m_origin);
+ return isFeatureEnabledForOrigin(feature, *m_origin);
+}
+
+FeaturePolicy::FeaturePolicy(RefPtr<SecurityOrigin> currentOrigin)
+ : m_origin(std::move(currentOrigin)) {}
+
+String FeaturePolicy::toString() {
+ StringBuilder sb;
+ sb.append("Feature Policy for frame in origin: ");
+ sb.append(m_origin->toString());
+ sb.append("\n");
+ sb.append("Inherited features:\n");
+ for (const auto& inheritedFeature : m_inheritedFeatures) {
+ sb.append(" ");
+ sb.append(inheritedFeature.key->featureName);
+ sb.append(": ");
+ sb.append(inheritedFeature.value ? "true" : "false");
+ sb.append("\n");
+ }
+ sb.append("Header whitelists:\n");
+ for (const auto& whitelist : m_headerWhitelists) {
+ sb.append(" ");
+ sb.append(whitelist.key->featureName);
+ sb.append(": ");
+ sb.append(whitelist.value->toString());
+ sb.append("\n");
+ }
+ return sb.toString();
+}
+
+DEFINE_TRACE(FeaturePolicy) {
+ visitor->trace(m_headerWhitelists);
+}
+
+} // namespace blink

Powered by Google App Engine
This is Rietveld 408576698