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

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: Addressing review comments from PS#13 and #15 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..45ce044be0a1b24ff6c698742cda7a965d15e2a3
--- /dev/null
+++ b/third_party/WebKit/Source/platform/feature_policy/FeaturePolicy.cpp
@@ -0,0 +1,254 @@
+// 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,
+ FeatureList& features) {
+ for (const FeaturePolicyFeature* feature : features) {
+ if (featureName == feature->featureName)
+ return feature;
+ }
+ return nullptr;
+}
+
+// Converts a list of JSON feature policy items into a mapping of features to
+// whitelists. For future compatibility, unrecognized features are simply
+// ignored, as are unparseable origins. Any errors in the input will cause an
+// error message appended to |messages|.
+HashMap<const FeaturePolicyFeature*, std::unique_ptr<FeaturePolicy::Whitelist>>
+parseFeaturePolicyString(std::unique_ptr<JSONArray> policyItems,
+ RefPtr<SecurityOrigin> origin,
+ FeatureList& features,
+ Vector<String>& messages) {
+ HashMap<const FeaturePolicyFeature*,
+ std::unique_ptr<FeaturePolicy::Whitelist>>
+ whitelists;
+
+ for (size_t i = 0; i < policyItems->size(); ++i) {
+ JSONObject* item = JSONObject::cast(policyItems->at(i));
+ if (!item) {
+ messages.append("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);
+ String featureName = entry.first;
+ JSONArray* targets = JSONArray::cast(entry.second);
+ if (!targets) {
+ messages.append("Whitelist is not an array of strings");
+ continue; // Whitelist is not an array; skip
raymes 2016/10/23 23:31:05 nit: I think this comment is redundant now
iclelland 2016/10/24 05:14:32 Yes, it is :)
+ }
+
+ const FeaturePolicyFeature* feature =
+ featureForName(featureName, features);
+ if (!feature)
+ continue; // Feature is not recognized; skip
+
+ std::unique_ptr<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));
+ }
+ }
+ } else {
+ messages.append("Whitelist is not an array of strings");
+ }
+ }
+ whitelists.set(feature, std::move(whitelist));
+ }
+ }
+ return whitelists;
+}
+
+} // namespace
+
+// Definitions of all features controlled by Feature Policy should appear here.
+const FeaturePolicyFeature kDocumentCookie{
+ "cookie", FeaturePolicyFeatureDefault::EnableForAll};
+const FeaturePolicyFeature kDocumentDomain{
+ "domain", FeaturePolicyFeatureDefault::EnableForAll};
+const FeaturePolicyFeature kDocumentWrite{
+ "docwrite", FeaturePolicyFeatureDefault::EnableForAll};
+const FeaturePolicyFeature kGeolocationFeature{
+ "geolocation", FeaturePolicyFeatureDefault::EnableForSelf};
+const FeaturePolicyFeature kMidiFeature{
+ "midi", FeaturePolicyFeatureDefault::EnableForAll};
+const FeaturePolicyFeature kNotificationsFeature{
+ "notifications", FeaturePolicyFeatureDefault::EnableForAll};
+const FeaturePolicyFeature kPaymentFeature{
+ "payment", FeaturePolicyFeatureDefault::EnableForSelf};
+const FeaturePolicyFeature kPushFeature{
+ "push", FeaturePolicyFeatureDefault::EnableForAll};
+const FeaturePolicyFeature kSyncScript{
+ "sync-script", FeaturePolicyFeatureDefault::EnableForAll};
+const FeaturePolicyFeature kSyncXHR{"sync-xhr",
+ FeaturePolicyFeatureDefault::EnableForAll};
+const FeaturePolicyFeature kUsermedia{
+ "usermedia", FeaturePolicyFeatureDefault::EnableForAll};
+const FeaturePolicyFeature kVibrateFeature{
+ "vibrate", FeaturePolicyFeatureDefault::EnableForSelf};
+const FeaturePolicyFeature kWebRTC{"webrtc",
+ FeaturePolicyFeatureDefault::EnableForAll};
+
+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
+const FeatureList& FeaturePolicy::getDefaultFeatureList() {
+ DEFINE_STATIC_LOCAL(
+ Vector<const FeaturePolicyFeature*>, defaultFeatureList,
+ ({&kDocumentCookie, &kDocumentDomain, &kDocumentWrite,
+ &kGeolocationFeature, &kMidiFeature, &kNotificationsFeature,
+ &kPaymentFeature, &kPushFeature, &kSyncScript, &kSyncXHR, &kUsermedia,
+ &kVibrateFeature, &kWebRTC}));
+ return defaultFeatureList;
+}
+
+// static
+FeaturePolicy* FeaturePolicy::createFromParentPolicy(
+ const FeaturePolicy* parent,
+ RefPtr<SecurityOrigin> currentOrigin,
+ FeatureList& features) {
+ DCHECK(currentOrigin);
+ FeaturePolicy* newPolicy = new FeaturePolicy(currentOrigin, features);
+ for (const FeaturePolicyFeature* feature : features) {
+ if (!parent || parent->isFeatureEnabledForOrigin(feature, *currentOrigin)) {
+ newPolicy->m_inheritedFeatures.set(feature, true);
+ } else {
+ newPolicy->m_inheritedFeatures.set(feature, false);
+ }
+ }
+ return newPolicy;
+}
+
+// static
+FeaturePolicy* FeaturePolicy::createFromParentPolicy(
+ const FeaturePolicy* parent,
+ RefPtr<SecurityOrigin> currentOrigin) {
+ return createFromParentPolicy(parent, currentOrigin, getDefaultFeatureList());
+}
+
+void FeaturePolicy::setHeaderPolicy(const String& policy,
+ Vector<String>& messages) {
+ DCHECK(m_headerWhitelists.isEmpty());
+ std::unique_ptr<JSONArray> policyJSON = parseJSONHeader(policy);
+ if (!policyJSON) {
+ messages.append("Unable to parse header");
+ return;
+ }
+ m_headerWhitelists = parseFeaturePolicyString(std::move(policyJSON), m_origin,
+ m_features, messages);
+}
+
+bool FeaturePolicy::isFeatureEnabledForOrigin(
+ const FeaturePolicyFeature* feature,
+ const SecurityOrigin& origin) const {
+ DCHECK(m_inheritedFeatures.contains(feature));
+ if (!m_inheritedFeatures.get(feature)) {
+ return false;
+ }
+ if (m_headerWhitelists.contains(feature)) {
+ return m_headerWhitelists.get(feature)->contains(origin);
+ }
+ if (feature->defaultPolicy == FeaturePolicyFeatureDefault::EnableForAll) {
+ return true;
+ }
+ if (feature->defaultPolicy == FeaturePolicyFeatureDefault::EnableForSelf) {
+ 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,
+ FeatureList& features)
+ : m_origin(std::move(currentOrigin)), m_features(features) {}
+
+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();
+}
+
+} // namespace blink

Powered by Google App Engine
This is Rietveld 408576698