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/RuntimeEnabledFeatures.h" | 7 #include "platform/RuntimeEnabledFeatures.h" |
8 #include "platform/json/JSONValues.h" | 8 #include "platform/json/JSONValues.h" |
9 #include "platform/network/HTTPParsers.h" | 9 #include "platform/network/HTTPParsers.h" |
10 #include "platform/weborigin/KURL.h" | |
11 #include "platform/weborigin/SecurityOrigin.h" | 10 #include "platform/weborigin/SecurityOrigin.h" |
12 #include "wtf/PtrUtil.h" | 11 #include "wtf/PtrUtil.h" |
13 #include "wtf/text/StringBuilder.h" | |
14 | 12 |
15 namespace blink { | 13 namespace blink { |
16 | 14 |
17 namespace { | 15 WebFeaturePolicyFeature getWebFeaturePolicyFeature(const String& feature) { |
18 | |
19 // Given a string name, return the matching feature struct, or nullptr if it is | |
20 // not the name of a policy-controlled feature. | |
21 const FeaturePolicy::Feature* featureForName( | |
22 const String& featureName, | |
23 FeaturePolicy::FeatureList& features) { | |
24 for (const FeaturePolicy::Feature* feature : features) { | |
25 if (featureName == feature->featureName) | |
26 return feature; | |
27 } | |
28 return nullptr; | |
29 } | |
30 | |
31 } // namespace | |
32 | |
33 // Definitions of all features controlled by Feature Policy should appear here. | |
34 const FeaturePolicy::Feature kDocumentCookie{ | |
35 "cookie", FeaturePolicy::FeatureDefault::EnableForAll}; | |
36 const FeaturePolicy::Feature kDocumentDomain{ | |
37 "domain", FeaturePolicy::FeatureDefault::EnableForAll}; | |
38 const FeaturePolicy::Feature kDocumentWrite{ | |
39 "docwrite", FeaturePolicy::FeatureDefault::EnableForAll}; | |
40 const FeaturePolicy::Feature kFullscreenFeature{ | |
41 "fullscreen", FeaturePolicy::FeatureDefault::EnableForSelf}; | |
42 const FeaturePolicy::Feature kGeolocationFeature{ | |
43 "geolocation", FeaturePolicy::FeatureDefault::EnableForSelf}; | |
44 const FeaturePolicy::Feature kMidiFeature{ | |
45 "midi", FeaturePolicy::FeatureDefault::EnableForAll}; | |
46 const FeaturePolicy::Feature kNotificationsFeature{ | |
47 "notifications", FeaturePolicy::FeatureDefault::EnableForAll}; | |
48 const FeaturePolicy::Feature kPaymentFeature{ | |
49 "payment", FeaturePolicy::FeatureDefault::EnableForSelf}; | |
50 const FeaturePolicy::Feature kPushFeature{ | |
51 "push", FeaturePolicy::FeatureDefault::EnableForAll}; | |
52 const FeaturePolicy::Feature kSyncScript{ | |
53 "sync-script", FeaturePolicy::FeatureDefault::EnableForAll}; | |
54 const FeaturePolicy::Feature kSyncXHR{ | |
55 "sync-xhr", FeaturePolicy::FeatureDefault::EnableForAll}; | |
56 const FeaturePolicy::Feature kUsermedia{ | |
57 "usermedia", FeaturePolicy::FeatureDefault::EnableForAll}; | |
58 const FeaturePolicy::Feature kVibrateFeature{ | |
59 "vibrate", FeaturePolicy::FeatureDefault::EnableForSelf}; | |
60 const FeaturePolicy::Feature kWebRTC{ | |
61 "webrtc", FeaturePolicy::FeatureDefault::EnableForAll}; | |
62 | |
63 WebFeaturePolicyFeature FeaturePolicy::getWebFeaturePolicyFeature( | |
64 const String& feature) { | |
65 if (feature == "fullscreen") | 16 if (feature == "fullscreen") |
66 return WebFeaturePolicyFeature::Fullscreen; | 17 return WebFeaturePolicyFeature::Fullscreen; |
67 if (feature == "payment") | 18 if (feature == "payment") |
68 return WebFeaturePolicyFeature::Payment; | 19 return WebFeaturePolicyFeature::Payment; |
69 if (feature == "vibrate") | 20 if (feature == "vibrate") |
70 return WebFeaturePolicyFeature::Vibrate; | 21 return WebFeaturePolicyFeature::Vibrate; |
71 if (feature == "usermedia") | 22 if (feature == "usermedia") |
72 return WebFeaturePolicyFeature::Usermedia; | 23 return WebFeaturePolicyFeature::Usermedia; |
73 if (RuntimeEnabledFeatures::featurePolicyExperimentalFeaturesEnabled()) { | 24 if (RuntimeEnabledFeatures::featurePolicyExperimentalFeaturesEnabled()) { |
74 if (feature == "cookie") | 25 if (feature == "cookie") |
(...skipping 13 matching lines...) Expand all Loading... |
88 if (feature == "sync-script") | 39 if (feature == "sync-script") |
89 return WebFeaturePolicyFeature::SyncScript; | 40 return WebFeaturePolicyFeature::SyncScript; |
90 if (feature == "sync-xhr") | 41 if (feature == "sync-xhr") |
91 return WebFeaturePolicyFeature::SyncXHR; | 42 return WebFeaturePolicyFeature::SyncXHR; |
92 if (feature == "webrtc") | 43 if (feature == "webrtc") |
93 return WebFeaturePolicyFeature::WebRTC; | 44 return WebFeaturePolicyFeature::WebRTC; |
94 } | 45 } |
95 return WebFeaturePolicyFeature::NotFound; | 46 return WebFeaturePolicyFeature::NotFound; |
96 } | 47 } |
97 | 48 |
98 // static | 49 WebParsedFeaturePolicyHeader parseFeaturePolicy(const String& policy, |
99 std::unique_ptr<FeaturePolicy::Whitelist> FeaturePolicy::Whitelist::from( | 50 RefPtr<SecurityOrigin> origin, |
100 const WebParsedFeaturePolicyDeclaration& parsedDeclaration) { | 51 Vector<String>* messages) { |
101 std::unique_ptr<Whitelist> whitelist(new FeaturePolicy::Whitelist); | |
102 if (parsedDeclaration.matchesAllOrigins) { | |
103 whitelist->addAll(); | |
104 } else { | |
105 for (const WebSecurityOrigin& origin : parsedDeclaration.origins) | |
106 whitelist->add(static_cast<WTF::PassRefPtr<SecurityOrigin>>(origin)); | |
107 } | |
108 return whitelist; | |
109 } | |
110 | |
111 FeaturePolicy::Whitelist::Whitelist() : m_matchesAllOrigins(false) {} | |
112 | |
113 void FeaturePolicy::Whitelist::addAll() { | |
114 m_matchesAllOrigins = true; | |
115 } | |
116 | |
117 void FeaturePolicy::Whitelist::add(RefPtr<SecurityOrigin> origin) { | |
118 m_origins.push_back(std::move(origin)); | |
119 } | |
120 | |
121 bool FeaturePolicy::Whitelist::contains(const SecurityOrigin& origin) const { | |
122 if (m_matchesAllOrigins) | |
123 return true; | |
124 for (const auto& targetOrigin : m_origins) { | |
125 if (targetOrigin->isSameSchemeHostPortAndSuborigin(&origin)) | |
126 return true; | |
127 } | |
128 return false; | |
129 } | |
130 | |
131 String FeaturePolicy::Whitelist::toString() { | |
132 StringBuilder sb; | |
133 sb.append("["); | |
134 if (m_matchesAllOrigins) { | |
135 sb.append("*"); | |
136 } else { | |
137 for (size_t i = 0; i < m_origins.size(); ++i) { | |
138 if (i > 0) { | |
139 sb.append(", "); | |
140 } | |
141 sb.append(m_origins[i]->toString()); | |
142 } | |
143 } | |
144 sb.append("]"); | |
145 return sb.toString(); | |
146 } | |
147 | |
148 // static | |
149 const FeaturePolicy::FeatureList& FeaturePolicy::getDefaultFeatureList() { | |
150 DEFINE_STATIC_LOCAL( | |
151 Vector<const FeaturePolicy::Feature*>, defaultFeatureList, | |
152 ({&kDocumentCookie, &kDocumentDomain, &kDocumentWrite, | |
153 &kGeolocationFeature, &kFullscreenFeature, &kMidiFeature, | |
154 &kNotificationsFeature, &kPaymentFeature, &kPushFeature, &kSyncScript, | |
155 &kSyncXHR, &kUsermedia, &kVibrateFeature, &kWebRTC})); | |
156 return defaultFeatureList; | |
157 } | |
158 | |
159 // static | |
160 std::unique_ptr<FeaturePolicy> FeaturePolicy::createFromParentPolicy( | |
161 const FeaturePolicy* parent, | |
162 const WebParsedFeaturePolicyHeader* containerPolicy, | |
163 RefPtr<SecurityOrigin> currentOrigin, | |
164 FeaturePolicy::FeatureList& features) { | |
165 DCHECK(currentOrigin); | |
166 std::unique_ptr<FeaturePolicy> newPolicy = | |
167 WTF::wrapUnique(new FeaturePolicy(currentOrigin, features)); | |
168 for (const FeaturePolicy::Feature* feature : features) { | |
169 if (!parent || | |
170 parent->isFeatureEnabledForOrigin(*feature, *currentOrigin)) { | |
171 newPolicy->m_inheritedFeatures.set(feature, true); | |
172 } else { | |
173 newPolicy->m_inheritedFeatures.set(feature, false); | |
174 } | |
175 } | |
176 if (containerPolicy) | |
177 newPolicy->addContainerPolicy(containerPolicy, parent); | |
178 return newPolicy; | |
179 } | |
180 | |
181 // static | |
182 std::unique_ptr<FeaturePolicy> FeaturePolicy::createFromParentPolicy( | |
183 const FeaturePolicy* parent, | |
184 const WebParsedFeaturePolicyHeader* containerPolicy, | |
185 RefPtr<SecurityOrigin> currentOrigin) { | |
186 return createFromParentPolicy(parent, containerPolicy, | |
187 std::move(currentOrigin), | |
188 getDefaultFeatureList()); | |
189 } | |
190 | |
191 void FeaturePolicy::addContainerPolicy( | |
192 const WebParsedFeaturePolicyHeader* containerPolicy, | |
193 const FeaturePolicy* parent) { | |
194 DCHECK(containerPolicy); | |
195 DCHECK(parent); | |
196 for (const WebParsedFeaturePolicyDeclaration& parsedDeclaration : | |
197 *containerPolicy) { | |
198 // If a feature is enabled in the parent frame, and the parent chooses to | |
199 // delegate it to the child frame, using the iframe attribute, then the | |
200 // feature should be enabled in the child frame. | |
201 const FeaturePolicy::Feature* feature = | |
202 featureForName(parsedDeclaration.featureName, m_features); | |
203 if (!feature) | |
204 continue; | |
205 if (Whitelist::from(parsedDeclaration)->contains(*m_origin) && | |
206 parent->isFeatureEnabled(*feature)) { | |
207 m_inheritedFeatures.set(feature, true); | |
208 } else { | |
209 m_inheritedFeatures.set(feature, false); | |
210 } | |
211 } | |
212 } | |
213 | |
214 // static | |
215 WebParsedFeaturePolicyHeader FeaturePolicy::parseFeaturePolicy( | |
216 const String& policy, | |
217 RefPtr<SecurityOrigin> origin, | |
218 Vector<String>* messages) { | |
219 Vector<WebParsedFeaturePolicyDeclaration> whitelists; | 52 Vector<WebParsedFeaturePolicyDeclaration> whitelists; |
220 | 53 |
221 // Use a reasonable parse depth limit; the actual maximum depth is only going | 54 // Use a reasonable parse depth limit; the actual maximum depth is only going |
222 // to be 4 for a valid policy, but we'll give the featurePolicyParser a chance | 55 // to be 4 for a valid policy, but we'll give the featurePolicyParser a chance |
223 // to report more specific errors, unless the string is really invalid. | 56 // to report more specific errors, unless the string is really invalid. |
224 std::unique_ptr<JSONArray> policyItems = parseJSONHeader(policy, 50); | 57 std::unique_ptr<JSONArray> policyItems = parseJSONHeader(policy, 50); |
225 if (!policyItems) { | 58 if (!policyItems) { |
226 if (messages) | 59 if (messages) |
227 messages->push_back("Unable to parse header"); | 60 messages->push_back("Unable to parse header"); |
228 return whitelists; | 61 return whitelists; |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
268 messages->push_back("Whitelist is not an array of strings."); | 101 messages->push_back("Whitelist is not an array of strings."); |
269 } | 102 } |
270 } | 103 } |
271 whitelist.origins = origins; | 104 whitelist.origins = origins; |
272 whitelists.push_back(whitelist); | 105 whitelists.push_back(whitelist); |
273 } | 106 } |
274 } | 107 } |
275 return whitelists; | 108 return whitelists; |
276 } | 109 } |
277 | 110 |
278 void FeaturePolicy::setHeaderPolicy( | |
279 const WebParsedFeaturePolicyHeader& policy) { | |
280 DCHECK(m_headerWhitelists.isEmpty()); | |
281 for (const WebParsedFeaturePolicyDeclaration& parsedDeclaration : policy) { | |
282 const FeaturePolicy::Feature* feature = | |
283 featureForName(parsedDeclaration.featureName, m_features); | |
284 if (!feature) | |
285 continue; | |
286 m_headerWhitelists.set(feature, Whitelist::from(parsedDeclaration)); | |
287 } | |
288 } | |
289 | |
290 bool FeaturePolicy::isFeatureEnabledForOrigin( | |
291 const FeaturePolicy::Feature& feature, | |
292 const SecurityOrigin& origin) const { | |
293 DCHECK(m_inheritedFeatures.contains(&feature)); | |
294 if (!m_inheritedFeatures.at(&feature)) { | |
295 return false; | |
296 } | |
297 if (m_headerWhitelists.contains(&feature)) { | |
298 return m_headerWhitelists.at(&feature)->contains(origin); | |
299 } | |
300 if (feature.defaultPolicy == FeaturePolicy::FeatureDefault::EnableForAll) { | |
301 return true; | |
302 } | |
303 if (feature.defaultPolicy == FeaturePolicy::FeatureDefault::EnableForSelf) { | |
304 return m_origin->isSameSchemeHostPortAndSuborigin(&origin); | |
305 } | |
306 return false; | |
307 } | |
308 | |
309 bool FeaturePolicy::isFeatureEnabled( | |
310 const FeaturePolicy::Feature& feature) const { | |
311 DCHECK(m_origin); | |
312 return isFeatureEnabledForOrigin(feature, *m_origin); | |
313 } | |
314 | |
315 FeaturePolicy::FeaturePolicy(RefPtr<SecurityOrigin> currentOrigin, | |
316 FeaturePolicy::FeatureList& features) | |
317 : m_origin(std::move(currentOrigin)), m_features(features) {} | |
318 | |
319 String FeaturePolicy::toString() { | |
320 StringBuilder sb; | |
321 sb.append("Feature Policy for frame in origin: "); | |
322 sb.append(m_origin->toString()); | |
323 sb.append("\n"); | |
324 sb.append("Inherited features:\n"); | |
325 for (const auto& inheritedFeature : m_inheritedFeatures) { | |
326 sb.append(" "); | |
327 sb.append(inheritedFeature.key->featureName); | |
328 sb.append(": "); | |
329 sb.append(inheritedFeature.value ? "true" : "false"); | |
330 sb.append("\n"); | |
331 } | |
332 sb.append("Header whitelists:\n"); | |
333 for (const auto& whitelist : m_headerWhitelists) { | |
334 sb.append(" "); | |
335 sb.append(whitelist.key->featureName); | |
336 sb.append(": "); | |
337 sb.append(whitelist.value->toString()); | |
338 sb.append("\n"); | |
339 } | |
340 return sb.toString(); | |
341 } | |
342 | |
343 } // namespace blink | 111 } // namespace blink |
OLD | NEW |