Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "platform/feature_policy/FeaturePolicy.h" | 5 #include "platform/feature_policy/FeaturePolicy.h" |
| 6 | 6 |
| 7 #include "platform/json/JSONValues.h" | 7 #include "platform/json/JSONValues.h" |
| 8 #include "platform/network/HTTPParsers.h" | 8 #include "platform/network/HTTPParsers.h" |
| 9 #include "platform/weborigin/KURL.h" | 9 #include "platform/weborigin/KURL.h" |
| 10 #include "platform/weborigin/SecurityOrigin.h" | 10 #include "platform/weborigin/SecurityOrigin.h" |
| 11 #include "wtf/PtrUtil.h" | 11 #include "wtf/PtrUtil.h" |
| 12 #include "wtf/text/StringBuilder.h" | 12 #include "wtf/text/StringBuilder.h" |
| 13 | 13 |
| 14 namespace blink { | 14 namespace blink { |
| 15 | 15 |
| 16 namespace { | 16 namespace { |
| 17 | 17 |
| 18 // Given a string name, return the matching feature struct, or nullptr if it is | 18 // Given a string name, return the matching feature struct, or nullptr if it is |
| 19 // not the name of a policy-controlled feature. | 19 // not the name of a policy-controlled feature. |
| 20 const FeaturePolicy::Feature* featureForName( | 20 const FeaturePolicy::Feature* featureForName( |
| 21 const String& featureName, | 21 const String& featureName, |
| 22 FeaturePolicy::FeatureList& features) { | 22 FeaturePolicy::FeatureList& features) { |
| 23 for (const FeaturePolicy::Feature* feature : features) { | 23 for (const FeaturePolicy::Feature* feature : features) { |
| 24 if (featureName == feature->featureName) | 24 if (featureName == feature->featureName) |
| 25 return feature; | 25 return feature; |
| 26 } | 26 } |
| 27 return nullptr; | 27 return nullptr; |
| 28 } | 28 } |
| 29 | 29 |
| 30 // Converts a list of JSON feature policy items into a mapping of features to | |
| 31 // whitelists. For future compatibility, unrecognized features are simply | |
| 32 // ignored, as are unparseable origins. If |messages| is not null, then any | |
| 33 // errors in the input will cause an error message to be appended to it. | |
| 34 HashMap<const FeaturePolicy::Feature*, | |
| 35 std::unique_ptr<FeaturePolicy::Whitelist>> | |
| 36 parseFeaturePolicyFromJson(std::unique_ptr<JSONArray> policyItems, | |
| 37 RefPtr<SecurityOrigin> origin, | |
| 38 FeaturePolicy::FeatureList& features, | |
| 39 Vector<String>* messages) { | |
| 40 HashMap<const FeaturePolicy::Feature*, | |
| 41 std::unique_ptr<FeaturePolicy::Whitelist>> | |
| 42 whitelists; | |
| 43 | |
| 44 for (size_t i = 0; i < policyItems->size(); ++i) { | |
| 45 JSONObject* item = JSONObject::cast(policyItems->at(i)); | |
| 46 if (!item) { | |
| 47 if (messages) | |
| 48 messages->append("Policy is not an object"); | |
| 49 continue; // Array element is not an object; skip | |
| 50 } | |
| 51 | |
| 52 for (size_t j = 0; j < item->size(); ++j) { | |
| 53 JSONObject::Entry entry = item->at(j); | |
| 54 String featureName = entry.first; | |
| 55 JSONArray* targets = JSONArray::cast(entry.second); | |
| 56 if (!targets) { | |
| 57 if (messages) | |
| 58 messages->append("Whitelist is not an array of strings."); | |
| 59 continue; | |
| 60 } | |
| 61 | |
| 62 const FeaturePolicy::Feature* feature = | |
| 63 featureForName(featureName, features); | |
| 64 if (!feature) | |
| 65 continue; // Feature is not recognized; skip | |
| 66 | |
| 67 std::unique_ptr<FeaturePolicy::Whitelist> whitelist( | |
| 68 new FeaturePolicy::Whitelist); | |
| 69 String targetString; | |
| 70 for (size_t j = 0; j < targets->size(); ++j) { | |
| 71 if (targets->at(j)->asString(&targetString)) { | |
| 72 if (equalIgnoringCase(targetString, "self")) { | |
| 73 whitelist->add(origin); | |
| 74 } else if (targetString == "*") { | |
| 75 whitelist->addAll(); | |
| 76 } else { | |
| 77 KURL originUrl = KURL(KURL(), targetString); | |
| 78 if (originUrl.isValid()) { | |
| 79 whitelist->add(SecurityOrigin::create(originUrl)); | |
| 80 } | |
| 81 } | |
| 82 } else { | |
| 83 if (messages) | |
| 84 messages->append("Whitelist is not an array of strings."); | |
| 85 } | |
| 86 } | |
| 87 whitelists.set(feature, std::move(whitelist)); | |
| 88 } | |
| 89 } | |
| 90 return whitelists; | |
| 91 } | |
| 92 | |
| 93 } // namespace | 30 } // namespace |
| 94 | 31 |
| 95 // Definitions of all features controlled by Feature Policy should appear here. | 32 // Definitions of all features controlled by Feature Policy should appear here. |
| 96 const FeaturePolicy::Feature kDocumentCookie{ | 33 const FeaturePolicy::Feature kDocumentCookie{ |
| 97 "cookie", FeaturePolicy::FeatureDefault::EnableForAll}; | 34 "cookie", FeaturePolicy::FeatureDefault::EnableForAll}; |
| 98 const FeaturePolicy::Feature kDocumentDomain{ | 35 const FeaturePolicy::Feature kDocumentDomain{ |
| 99 "domain", FeaturePolicy::FeatureDefault::EnableForAll}; | 36 "domain", FeaturePolicy::FeatureDefault::EnableForAll}; |
| 100 const FeaturePolicy::Feature kDocumentWrite{ | 37 const FeaturePolicy::Feature kDocumentWrite{ |
| 101 "docwrite", FeaturePolicy::FeatureDefault::EnableForAll}; | 38 "docwrite", FeaturePolicy::FeatureDefault::EnableForAll}; |
| 102 const FeaturePolicy::Feature kGeolocationFeature{ | 39 const FeaturePolicy::Feature kGeolocationFeature{ |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 113 "sync-script", FeaturePolicy::FeatureDefault::EnableForAll}; | 50 "sync-script", FeaturePolicy::FeatureDefault::EnableForAll}; |
| 114 const FeaturePolicy::Feature kSyncXHR{ | 51 const FeaturePolicy::Feature kSyncXHR{ |
| 115 "sync-xhr", FeaturePolicy::FeatureDefault::EnableForAll}; | 52 "sync-xhr", FeaturePolicy::FeatureDefault::EnableForAll}; |
| 116 const FeaturePolicy::Feature kUsermedia{ | 53 const FeaturePolicy::Feature kUsermedia{ |
| 117 "usermedia", FeaturePolicy::FeatureDefault::EnableForAll}; | 54 "usermedia", FeaturePolicy::FeatureDefault::EnableForAll}; |
| 118 const FeaturePolicy::Feature kVibrateFeature{ | 55 const FeaturePolicy::Feature kVibrateFeature{ |
| 119 "vibrate", FeaturePolicy::FeatureDefault::EnableForSelf}; | 56 "vibrate", FeaturePolicy::FeatureDefault::EnableForSelf}; |
| 120 const FeaturePolicy::Feature kWebRTC{ | 57 const FeaturePolicy::Feature kWebRTC{ |
| 121 "webrtc", FeaturePolicy::FeatureDefault::EnableForAll}; | 58 "webrtc", FeaturePolicy::FeatureDefault::EnableForAll}; |
| 122 | 59 |
| 60 // static | |
| 61 std::unique_ptr<FeaturePolicy::Whitelist> FeaturePolicy::Whitelist::from( | |
| 62 const WebFeaturePolicy::ParsedWhitelist& parsedWhitelist) { | |
| 63 std::unique_ptr<Whitelist> whitelist(new FeaturePolicy::Whitelist); | |
| 64 if (parsedWhitelist.matchesAllOrigins) { | |
| 65 whitelist->addAll(); | |
| 66 } else { | |
| 67 for (const WebSecurityOrigin& origin : parsedWhitelist.origins) | |
| 68 whitelist->add(static_cast<WTF::PassRefPtr<SecurityOrigin>>(origin)); | |
| 69 } | |
| 70 return whitelist; | |
| 71 } | |
| 72 | |
| 123 FeaturePolicy::Whitelist::Whitelist() : m_matchesAllOrigins(false) {} | 73 FeaturePolicy::Whitelist::Whitelist() : m_matchesAllOrigins(false) {} |
| 124 | 74 |
| 125 void FeaturePolicy::Whitelist::addAll() { | 75 void FeaturePolicy::Whitelist::addAll() { |
| 126 m_matchesAllOrigins = true; | 76 m_matchesAllOrigins = true; |
| 127 } | 77 } |
| 128 | 78 |
| 129 void FeaturePolicy::Whitelist::add(RefPtr<SecurityOrigin> origin) { | 79 void FeaturePolicy::Whitelist::add(RefPtr<SecurityOrigin> origin) { |
| 130 m_origins.append(std::move(origin)); | 80 m_origins.append(std::move(origin)); |
| 131 } | 81 } |
| 132 | 82 |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 188 } | 138 } |
| 189 | 139 |
| 190 // static | 140 // static |
| 191 std::unique_ptr<FeaturePolicy> FeaturePolicy::createFromParentPolicy( | 141 std::unique_ptr<FeaturePolicy> FeaturePolicy::createFromParentPolicy( |
| 192 const FeaturePolicy* parent, | 142 const FeaturePolicy* parent, |
| 193 RefPtr<SecurityOrigin> currentOrigin) { | 143 RefPtr<SecurityOrigin> currentOrigin) { |
| 194 return createFromParentPolicy(parent, std::move(currentOrigin), | 144 return createFromParentPolicy(parent, std::move(currentOrigin), |
| 195 getDefaultFeatureList()); | 145 getDefaultFeatureList()); |
| 196 } | 146 } |
| 197 | 147 |
| 198 void FeaturePolicy::setHeaderPolicy(const String& policy, | 148 // static |
| 199 Vector<String>* messages) { | 149 WebParsedFeaturePolicy FeaturePolicy::parseFeaturePolicy( |
| 200 DCHECK(m_headerWhitelists.isEmpty()); | 150 const String& policy, |
| 151 RefPtr<SecurityOrigin> origin, | |
| 152 Vector<String>* messages) { | |
| 153 Vector<WebFeaturePolicy::ParsedWhitelist> whitelists; | |
| 154 | |
| 201 // Use a reasonable parse depth limit; the actual maximum depth is only going | 155 // Use a reasonable parse depth limit; the actual maximum depth is only going |
| 202 // to be 4 for a valid policy, but we'll give the featurePolicyParser a chance | 156 // to be 4 for a valid policy, but we'll give the featurePolicyParser a chance |
| 203 // to report more specific errors, unless the string is really invalid. | 157 // to report more specific errors, unless the string is really invalid. |
| 204 std::unique_ptr<JSONArray> policyJSON = parseJSONHeader(policy, 50); | 158 std::unique_ptr<JSONArray> policyItems = parseJSONHeader(policy, 50); |
| 205 if (!policyJSON) { | 159 if (!policyItems) { |
| 206 if (messages) | 160 if (messages) |
| 207 messages->append("Unable to parse header"); | 161 messages->append("Unable to parse header"); |
| 208 return; | 162 return whitelists; |
| 209 } | 163 } |
| 210 m_headerWhitelists = parseFeaturePolicyFromJson( | 164 |
| 211 std::move(policyJSON), m_origin, m_features, messages); | 165 for (size_t i = 0; i < policyItems->size(); ++i) { |
| 166 JSONObject* item = JSONObject::cast(policyItems->at(i)); | |
| 167 if (!item) { | |
| 168 if (messages) | |
| 169 messages->append("Policy is not an object"); | |
| 170 continue; // Array element is not an object; skip | |
| 171 } | |
| 172 | |
| 173 for (size_t j = 0; j < item->size(); ++j) { | |
| 174 JSONObject::Entry entry = item->at(j); | |
| 175 String featureName = entry.first; | |
| 176 JSONArray* targets = JSONArray::cast(entry.second); | |
| 177 if (!targets) { | |
| 178 if (messages) | |
| 179 messages->append("Whitelist is not an array of strings."); | |
| 180 continue; | |
| 181 } | |
| 182 | |
| 183 WebFeaturePolicy::ParsedWhitelist whitelist; | |
| 184 whitelist.featureName = featureName; | |
| 185 Vector<WebSecurityOrigin> origins; | |
| 186 String targetString; | |
| 187 for (size_t j = 0; j < targets->size(); ++j) { | |
| 188 if (targets->at(j)->asString(&targetString)) { | |
| 189 if (equalIgnoringCase(targetString, "self")) { | |
| 190 if (!origin->isUnique()) | |
|
iclelland
2016/11/30 22:13:10
This isn't strictly necessary for the policy objec
Marijn Kruisselbrink
2016/11/30 22:21:00
This is not true. isSameSchemeHostPort() starts wi
raymes
2016/11/30 23:18:23
Good point - I guess it just felt weird to add a u
iclelland
2016/12/01 03:31:25
You're absolutely right -- when I went to check to
| |
| 191 origins.append(origin); | |
| 192 } else if (targetString == "*") { | |
| 193 whitelist.matchesAllOrigins = true; | |
| 194 } else { | |
| 195 WebSecurityOrigin targetOrigin = | |
| 196 WebSecurityOrigin::createFromString(targetString); | |
| 197 if (!targetOrigin.isNull() && !targetOrigin.isUnique()) | |
| 198 origins.append(targetOrigin); | |
| 199 } | |
| 200 } else { | |
| 201 if (messages) | |
| 202 messages->append("Whitelist is not an array of strings."); | |
| 203 } | |
| 204 } | |
| 205 whitelist.origins = origins; | |
| 206 whitelists.append(whitelist); | |
| 207 } | |
| 208 } | |
| 209 return whitelists; | |
| 210 } | |
| 211 | |
| 212 void FeaturePolicy::setHeaderPolicy(const WebParsedFeaturePolicy& policy) { | |
| 213 DCHECK(m_headerWhitelists.isEmpty()); | |
| 214 for (const WebFeaturePolicy::ParsedWhitelist& parsedWhitelist : policy) { | |
| 215 const FeaturePolicy::Feature* feature = | |
| 216 featureForName(parsedWhitelist.featureName, m_features); | |
| 217 if (!feature) | |
| 218 continue; | |
| 219 m_headerWhitelists.set(feature, Whitelist::from(parsedWhitelist)); | |
| 220 } | |
| 212 } | 221 } |
| 213 | 222 |
| 214 bool FeaturePolicy::isFeatureEnabledForOrigin( | 223 bool FeaturePolicy::isFeatureEnabledForOrigin( |
| 215 const FeaturePolicy::Feature& feature, | 224 const FeaturePolicy::Feature& feature, |
| 216 const SecurityOrigin& origin) const { | 225 const SecurityOrigin& origin) const { |
| 217 DCHECK(m_inheritedFeatures.contains(&feature)); | 226 DCHECK(m_inheritedFeatures.contains(&feature)); |
| 218 if (!m_inheritedFeatures.get(&feature)) { | 227 if (!m_inheritedFeatures.get(&feature)) { |
| 219 return false; | 228 return false; |
| 220 } | 229 } |
| 221 if (m_headerWhitelists.contains(&feature)) { | 230 if (m_headerWhitelists.contains(&feature)) { |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 258 sb.append(" "); | 267 sb.append(" "); |
| 259 sb.append(whitelist.key->featureName); | 268 sb.append(whitelist.key->featureName); |
| 260 sb.append(": "); | 269 sb.append(": "); |
| 261 sb.append(whitelist.value->toString()); | 270 sb.append(whitelist.value->toString()); |
| 262 sb.append("\n"); | 271 sb.append("\n"); |
| 263 } | 272 } |
| 264 return sb.toString(); | 273 return sb.toString(); |
| 265 } | 274 } |
| 266 | 275 |
| 267 } // namespace blink | 276 } // namespace blink |
| OLD | NEW |