OLD | NEW |
---|---|
(Empty) | |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 #include "platform/feature_policy/FeaturePolicy.h" | |
6 | |
7 #include "platform/json/JSONValues.h" | |
8 #include "platform/network/HTTPParsers.h" | |
9 #include "platform/weborigin/KURL.h" | |
10 #include "platform/weborigin/SecurityOrigin.h" | |
11 #include "wtf/text/StringBuilder.h" | |
12 | |
13 namespace blink { | |
14 | |
15 namespace { | |
16 | |
17 // Given a string name, return the matching feature struct, or nullptr if it is | |
18 // not the name of a policy-controlled feature. | |
19 const FeaturePolicyFeature* featureForName(const String& featureName) { | |
20 for (const FeaturePolicyFeature* feature : | |
21 FeaturePolicy::getFeatureRegistry()) { | |
22 if (featureName == feature->featureName) | |
23 return feature; | |
24 } | |
25 return nullptr; | |
26 } | |
27 | |
28 // Parses a feature policy string into a mapping of features to whitelists. The | |
29 // parse algorithm is deliberately very lenient, and will return as much as it | |
30 // can recognize. Unrecognized features are simply ignored, as are invalid | |
31 // policy items and invalid origins inside of whitelists. | |
32 HeapHashMap<const FeaturePolicyFeature*, Member<FeaturePolicy::Whitelist>> | |
33 parseFeaturePolicyString(std::unique_ptr<JSONArray> policyJSON, | |
34 RefPtr<SecurityOrigin> origin) { | |
35 HeapHashMap<const FeaturePolicyFeature*, Member<FeaturePolicy::Whitelist>> | |
36 whitelists; | |
37 | |
38 if (!policyJSON) | |
39 return whitelists; // Policy string is not valid JSON | |
40 | |
41 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.
| |
42 if (!items) | |
43 return whitelists; // Policy string is not an array | |
44 | |
45 for (size_t i = 0; i < items->size(); ++i) { | |
46 JSONObject* item = JSONObject::cast(items->at(i)); | |
47 if (!item) | |
48 continue; // Array element is not an object; skip | |
49 | |
50 for (size_t j = 0; j < item->size(); ++j) { | |
51 JSONObject::Entry entry = item->at(j); | |
52 String featureName = entry.first; | |
53 const FeaturePolicyFeature* feature = featureForName(featureName); | |
54 if (!feature) | |
55 continue; // Feature is not recognized; skip | |
56 | |
57 JSONArray* targets = JSONArray::cast(entry.second); | |
58 if (!targets) | |
59 continue; // Whitelist is not an array; skip | |
60 | |
61 FeaturePolicy::Whitelist* whitelist = new FeaturePolicy::Whitelist; | |
62 String targetString; | |
63 for (size_t j = 0; j < targets->size(); ++j) { | |
64 if (targets->at(j)->asString(&targetString)) { | |
65 if (equalIgnoringCase(targetString, "self")) { | |
66 whitelist->add(origin); | |
67 } else if (targetString == "*") { | |
68 whitelist->addAll(); | |
69 } else { | |
70 KURL originUrl = KURL(KURL(), targetString); | |
71 if (originUrl.isValid()) { | |
72 whitelist->add(SecurityOrigin::create(originUrl)); | |
73 } | |
74 } | |
75 } | |
76 } | |
77 whitelists.set(feature, whitelist); | |
78 } | |
79 } | |
80 return whitelists; | |
81 } | |
82 | |
83 } // namespace | |
84 | |
85 // Definitions of all features controlled by Feature Policy should appear here. | |
86 const FeaturePolicyFeature kDocumentCookie{"cookie", | |
87 kEnableFeatureForAllOrigins}; | |
88 const FeaturePolicyFeature kDocumentDomain{"domain", | |
89 kEnableFeatureForAllOrigins}; | |
90 const FeaturePolicyFeature kDocumentWrite{"docwrite", | |
91 kEnableFeatureForAllOrigins}; | |
92 const FeaturePolicyFeature kGeolocationFeature{"geolocation", | |
93 kEnableFeatureForSelf}; | |
94 const FeaturePolicyFeature kMidiFeature{"midi", kEnableFeatureForAllOrigins}; | |
95 const FeaturePolicyFeature kNotificationsFeature{"notifications", | |
96 kEnableFeatureForAllOrigins}; | |
97 const FeaturePolicyFeature kPaymentFeature{"payment", kEnableFeatureForSelf}; | |
98 const FeaturePolicyFeature kPushFeature{"push", kEnableFeatureForAllOrigins}; | |
99 const FeaturePolicyFeature kSyncScript{"sync-script", | |
100 kEnableFeatureForAllOrigins}; | |
101 const FeaturePolicyFeature kSyncXHR{"sync-xhr", kEnableFeatureForAllOrigins}; | |
102 const FeaturePolicyFeature kUsermedia{"usermedia", kEnableFeatureForAllOrigins}; | |
103 const FeaturePolicyFeature kVibrateFeature{"vibrate", kEnableFeatureForSelf}; | |
104 const FeaturePolicyFeature kWebRTC{"webrtc", kEnableFeatureForAllOrigins}; | |
105 | |
106 FeaturePolicy::Whitelist::Whitelist() : m_matchesAllOrigins(false) {} | |
107 | |
108 void FeaturePolicy::Whitelist::addAll() { | |
109 m_matchesAllOrigins = true; | |
110 } | |
111 | |
112 void FeaturePolicy::Whitelist::add(RefPtr<SecurityOrigin> origin) { | |
113 m_origins.append(std::move(origin)); | |
114 } | |
115 | |
116 bool FeaturePolicy::Whitelist::contains(const SecurityOrigin& origin) const { | |
117 if (m_matchesAllOrigins) | |
118 return true; | |
119 for (const auto& targetOrigin : m_origins) { | |
120 if (targetOrigin->isSameSchemeHostPortAndSuborigin(&origin)) | |
121 return true; | |
122 } | |
123 return false; | |
124 } | |
125 | |
126 String FeaturePolicy::Whitelist::toString() { | |
127 StringBuilder sb; | |
128 sb.append("["); | |
129 if (m_matchesAllOrigins) { | |
130 sb.append("*"); | |
131 } else { | |
132 for (size_t i = 0; i < m_origins.size(); ++i) { | |
133 if (i > 0) { | |
134 sb.append(", "); | |
135 } | |
136 sb.append(m_origins[i]->toString()); | |
137 } | |
138 } | |
139 sb.append("]"); | |
140 return sb.toString(); | |
141 } | |
142 | |
143 // static | |
144 Vector<const FeaturePolicyFeature*>& FeaturePolicy::getFeatureRegistry() { | |
145 DEFINE_STATIC_LOCAL( | |
146 Vector<const FeaturePolicyFeature*>, featureRegistry, | |
147 ({&kDocumentCookie, &kDocumentDomain, &kDocumentWrite, | |
148 &kGeolocationFeature, &kMidiFeature, &kNotificationsFeature, | |
149 &kPaymentFeature, &kPushFeature, &kSyncScript, &kSyncXHR, &kUsermedia, | |
150 &kVibrateFeature, &kWebRTC})); | |
151 return featureRegistry; | |
152 } | |
153 | |
154 // static | |
155 FeaturePolicy* FeaturePolicy::createFromParentPolicy( | |
156 const FeaturePolicy* parent, | |
157 RefPtr<SecurityOrigin> currentOrigin) { | |
158 DCHECK(currentOrigin); | |
159 FeaturePolicy* newPolicy = new FeaturePolicy(currentOrigin); | |
160 for (const FeaturePolicyFeature* feature : getFeatureRegistry()) { | |
161 if (!parent || parent->isFeatureEnabledForOrigin(feature, *currentOrigin)) { | |
162 newPolicy->m_inheritedFeatures.set(feature, true); | |
163 } else { | |
164 newPolicy->m_inheritedFeatures.set(feature, false); | |
165 } | |
166 } | |
167 return newPolicy; | |
168 } | |
169 | |
170 void FeaturePolicy::setHeaderPolicy(const String& policy) { | |
171 DCHECK(m_headerWhitelists.isEmpty()); | |
172 std::unique_ptr<JSONArray> policyJSON = parseJSONHeader(policy); | |
173 if (!policyJSON) | |
174 return; | |
175 for (const auto& whitelist : | |
176 parseFeaturePolicyString(std::move(policyJSON), m_origin)) { | |
177 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
| |
178 } | |
179 } | |
180 | |
181 bool FeaturePolicy::isFeatureEnabledForOrigin( | |
182 const FeaturePolicyFeature* feature, | |
183 const SecurityOrigin& origin) const { | |
184 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
| |
185 if (!m_inheritedFeatures.get(feature)) { | |
186 return false; | |
187 } | |
188 } | |
189 if (m_headerWhitelists.contains(feature)) { | |
190 return m_headerWhitelists.get(feature)->contains(origin); | |
191 } | |
192 if (feature->defaultPolicy == kEnableFeatureForAllOrigins) { | |
193 return true; | |
194 } | |
195 if (feature->defaultPolicy == kEnableFeatureForSelf) { | |
196 return m_origin->isSameSchemeHostPortAndSuborigin(&origin); | |
197 } | |
198 return false; | |
199 } | |
200 | |
201 bool FeaturePolicy::isFeatureEnabled( | |
202 const FeaturePolicyFeature* feature) const { | |
203 DCHECK(m_origin); | |
204 return isFeatureEnabledForOrigin(feature, *m_origin); | |
205 } | |
206 | |
207 FeaturePolicy::FeaturePolicy(RefPtr<SecurityOrigin> currentOrigin) | |
208 : m_origin(std::move(currentOrigin)) {} | |
209 | |
210 String FeaturePolicy::toString() { | |
211 StringBuilder sb; | |
212 sb.append("Feature Policy for frame in origin: "); | |
213 sb.append(m_origin->toString()); | |
214 sb.append("\n"); | |
215 sb.append("Inherited features:\n"); | |
216 for (const auto& inheritedFeature : m_inheritedFeatures) { | |
217 sb.append(" "); | |
218 sb.append(inheritedFeature.key->featureName); | |
219 sb.append(": "); | |
220 sb.append(inheritedFeature.value ? "true" : "false"); | |
221 sb.append("\n"); | |
222 } | |
223 sb.append("Header whitelists:\n"); | |
224 for (const auto& whitelist : m_headerWhitelists) { | |
225 sb.append(" "); | |
226 sb.append(whitelist.key->featureName); | |
227 sb.append(": "); | |
228 sb.append(whitelist.value->toString()); | |
229 sb.append("\n"); | |
230 } | |
231 return sb.toString(); | |
232 } | |
233 | |
234 DEFINE_TRACE(FeaturePolicy) { | |
235 visitor->trace(m_headerWhitelists); | |
236 } | |
237 | |
238 } // namespace blink | |
OLD | NEW |