• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 Google, Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #include "config.h"
27 #include "core/frame/csp/ContentSecurityPolicy.h"
28 
29 #include "bindings/v8/ScriptCallStackFactory.h"
30 #include "bindings/v8/ScriptController.h"
31 #include "core/dom/DOMStringList.h"
32 #include "core/dom/Document.h"
33 #include "core/events/SecurityPolicyViolationEvent.h"
34 #include "core/frame/LocalDOMWindow.h"
35 #include "core/frame/LocalFrame.h"
36 #include "core/frame/UseCounter.h"
37 #include "core/frame/csp/CSPDirectiveList.h"
38 #include "core/frame/csp/CSPSource.h"
39 #include "core/frame/csp/CSPSourceList.h"
40 #include "core/frame/csp/MediaListDirective.h"
41 #include "core/frame/csp/SourceListDirective.h"
42 #include "core/inspector/InspectorInstrumentation.h"
43 #include "core/inspector/ScriptCallStack.h"
44 #include "core/loader/DocumentLoader.h"
45 #include "core/loader/PingLoader.h"
46 #include "platform/Crypto.h"
47 #include "platform/JSONValues.h"
48 #include "platform/NotImplemented.h"
49 #include "platform/ParsingUtilities.h"
50 #include "platform/RuntimeEnabledFeatures.h"
51 #include "platform/network/ContentSecurityPolicyParsers.h"
52 #include "platform/network/ContentSecurityPolicyResponseHeaders.h"
53 #include "platform/network/FormData.h"
54 #include "platform/network/ResourceResponse.h"
55 #include "platform/weborigin/KURL.h"
56 #include "platform/weborigin/KnownPorts.h"
57 #include "platform/weborigin/SchemeRegistry.h"
58 #include "platform/weborigin/SecurityOrigin.h"
59 #include "public/platform/Platform.h"
60 #include "public/platform/WebArrayBuffer.h"
61 #include "public/platform/WebCrypto.h"
62 #include "public/platform/WebCryptoAlgorithm.h"
63 #include "wtf/StringHasher.h"
64 #include "wtf/text/StringBuilder.h"
65 #include "wtf/text/StringUTF8Adaptor.h"
66 
67 namespace WebCore {
68 
69 // CSP 1.0 Directives
70 const char ContentSecurityPolicy::ConnectSrc[] = "connect-src";
71 const char ContentSecurityPolicy::DefaultSrc[] = "default-src";
72 const char ContentSecurityPolicy::FontSrc[] = "font-src";
73 const char ContentSecurityPolicy::FrameSrc[] = "frame-src";
74 const char ContentSecurityPolicy::ImgSrc[] = "img-src";
75 const char ContentSecurityPolicy::MediaSrc[] = "media-src";
76 const char ContentSecurityPolicy::ObjectSrc[] = "object-src";
77 const char ContentSecurityPolicy::ReportURI[] = "report-uri";
78 const char ContentSecurityPolicy::Sandbox[] = "sandbox";
79 const char ContentSecurityPolicy::ScriptSrc[] = "script-src";
80 const char ContentSecurityPolicy::StyleSrc[] = "style-src";
81 
82 // CSP 1.1 Directives
83 const char ContentSecurityPolicy::BaseURI[] = "base-uri";
84 const char ContentSecurityPolicy::ChildSrc[] = "child-src";
85 const char ContentSecurityPolicy::FormAction[] = "form-action";
86 const char ContentSecurityPolicy::FrameAncestors[] = "frame-ancestors";
87 const char ContentSecurityPolicy::PluginTypes[] = "plugin-types";
88 const char ContentSecurityPolicy::ReflectedXSS[] = "reflected-xss";
89 const char ContentSecurityPolicy::Referrer[] = "referrer";
90 
isDirectiveName(const String & name)91 bool ContentSecurityPolicy::isDirectiveName(const String& name)
92 {
93     return (equalIgnoringCase(name, ConnectSrc)
94         || equalIgnoringCase(name, DefaultSrc)
95         || equalIgnoringCase(name, FontSrc)
96         || equalIgnoringCase(name, FrameSrc)
97         || equalIgnoringCase(name, ImgSrc)
98         || equalIgnoringCase(name, MediaSrc)
99         || equalIgnoringCase(name, ObjectSrc)
100         || equalIgnoringCase(name, ReportURI)
101         || equalIgnoringCase(name, Sandbox)
102         || equalIgnoringCase(name, ScriptSrc)
103         || equalIgnoringCase(name, StyleSrc)
104         || equalIgnoringCase(name, BaseURI)
105         || equalIgnoringCase(name, ChildSrc)
106         || equalIgnoringCase(name, FormAction)
107         || equalIgnoringCase(name, FrameAncestors)
108         || equalIgnoringCase(name, PluginTypes)
109         || equalIgnoringCase(name, ReflectedXSS)
110         || equalIgnoringCase(name, Referrer)
111     );
112 }
113 
getUseCounterType(ContentSecurityPolicyHeaderType type)114 static UseCounter::Feature getUseCounterType(ContentSecurityPolicyHeaderType type)
115 {
116     switch (type) {
117     case ContentSecurityPolicyHeaderTypeEnforce:
118         return UseCounter::ContentSecurityPolicy;
119     case ContentSecurityPolicyHeaderTypeReport:
120         return UseCounter::ContentSecurityPolicyReportOnly;
121     }
122     ASSERT_NOT_REACHED();
123     return UseCounter::NumberOfFeatures;
124 }
125 
mergeReferrerPolicies(ReferrerPolicy a,ReferrerPolicy b)126 static ReferrerPolicy mergeReferrerPolicies(ReferrerPolicy a, ReferrerPolicy b)
127 {
128     if (a != b)
129         return ReferrerPolicyNever;
130     return a;
131 }
132 
ContentSecurityPolicy(ExecutionContext * executionContext)133 ContentSecurityPolicy::ContentSecurityPolicy(ExecutionContext* executionContext)
134     : m_executionContext(executionContext)
135     , m_overrideInlineStyleAllowed(false)
136     , m_scriptHashAlgorithmsUsed(ContentSecurityPolicyHashAlgorithmNone)
137     , m_styleHashAlgorithmsUsed(ContentSecurityPolicyHashAlgorithmNone)
138 {
139 }
140 
~ContentSecurityPolicy()141 ContentSecurityPolicy::~ContentSecurityPolicy()
142 {
143 }
144 
copyStateFrom(const ContentSecurityPolicy * other)145 void ContentSecurityPolicy::copyStateFrom(const ContentSecurityPolicy* other)
146 {
147     ASSERT(m_policies.isEmpty());
148     for (CSPDirectiveListVector::const_iterator iter = other->m_policies.begin(); iter != other->m_policies.end(); ++iter)
149         addPolicyFromHeaderValue((*iter)->header(), (*iter)->headerType(), (*iter)->headerSource());
150 }
151 
didReceiveHeaders(const ContentSecurityPolicyResponseHeaders & headers)152 void ContentSecurityPolicy::didReceiveHeaders(const ContentSecurityPolicyResponseHeaders& headers)
153 {
154     if (!headers.contentSecurityPolicy().isEmpty())
155         didReceiveHeader(headers.contentSecurityPolicy(), ContentSecurityPolicyHeaderTypeEnforce, ContentSecurityPolicyHeaderSourceHTTP);
156     if (!headers.contentSecurityPolicyReportOnly().isEmpty())
157         didReceiveHeader(headers.contentSecurityPolicyReportOnly(), ContentSecurityPolicyHeaderTypeReport, ContentSecurityPolicyHeaderSourceHTTP);
158 }
159 
didReceiveHeader(const String & header,ContentSecurityPolicyHeaderType type,ContentSecurityPolicyHeaderSource source)160 void ContentSecurityPolicy::didReceiveHeader(const String& header, ContentSecurityPolicyHeaderType type, ContentSecurityPolicyHeaderSource source)
161 {
162     addPolicyFromHeaderValue(header, type, source);
163 }
164 
addPolicyFromHeaderValue(const String & header,ContentSecurityPolicyHeaderType type,ContentSecurityPolicyHeaderSource source)165 void ContentSecurityPolicy::addPolicyFromHeaderValue(const String& header, ContentSecurityPolicyHeaderType type, ContentSecurityPolicyHeaderSource source)
166 {
167     Document* document = this->document();
168     if (document) {
169         UseCounter::count(*document, getUseCounterType(type));
170 
171         // CSP 1.1 defines report-only in a <meta> element as invalid. Measure for now, disable in experimental mode.
172         if (source == ContentSecurityPolicyHeaderSourceMeta && type == ContentSecurityPolicyHeaderTypeReport) {
173             UseCounter::count(*document, UseCounter::ContentSecurityPolicyReportOnlyInMeta);
174             if (experimentalFeaturesEnabled()) {
175                 reportReportOnlyInMeta(header);
176                 return;
177             }
178         }
179     }
180 
181 
182     Vector<UChar> characters;
183     header.appendTo(characters);
184 
185     const UChar* begin = characters.data();
186     const UChar* end = begin + characters.size();
187 
188     // RFC2616, section 4.2 specifies that headers appearing multiple times can
189     // be combined with a comma. Walk the header string, and parse each comma
190     // separated chunk as a separate header.
191     const UChar* position = begin;
192     while (position < end) {
193         skipUntil<UChar>(position, end, ',');
194 
195         // header1,header2 OR header1
196         //        ^                  ^
197         OwnPtr<CSPDirectiveList> policy = CSPDirectiveList::create(this, begin, position, type, source);
198 
199         // We disable 'eval()' even in the case of report-only policies, and rely on the check in the V8Initializer::codeGenerationCheckCallbackInMainThread callback to determine whether the call should execute or not.
200         if (!policy->allowEval(0, SuppressReport))
201             m_executionContext->disableEval(policy->evalDisabledErrorMessage());
202 
203         m_policies.append(policy.release());
204 
205         // Skip the comma, and begin the next header from the current position.
206         ASSERT(position == end || *position == ',');
207         skipExactly<UChar>(position, end, ',');
208         begin = position;
209     }
210 
211     if (document && type != ContentSecurityPolicyHeaderTypeReport && didSetReferrerPolicy())
212         document->setReferrerPolicy(referrerPolicy());
213 }
214 
setOverrideAllowInlineStyle(bool value)215 void ContentSecurityPolicy::setOverrideAllowInlineStyle(bool value)
216 {
217     m_overrideInlineStyleAllowed = value;
218 }
219 
deprecatedHeader() const220 const String& ContentSecurityPolicy::deprecatedHeader() const
221 {
222     return m_policies.isEmpty() ? emptyString() : m_policies[0]->header();
223 }
224 
deprecatedHeaderType() const225 ContentSecurityPolicyHeaderType ContentSecurityPolicy::deprecatedHeaderType() const
226 {
227     return m_policies.isEmpty() ? ContentSecurityPolicyHeaderTypeEnforce : m_policies[0]->headerType();
228 }
229 
230 template<bool (CSPDirectiveList::*allowed)(ContentSecurityPolicy::ReportingStatus) const>
isAllowedByAll(const CSPDirectiveListVector & policies,ContentSecurityPolicy::ReportingStatus reportingStatus)231 bool isAllowedByAll(const CSPDirectiveListVector& policies, ContentSecurityPolicy::ReportingStatus reportingStatus)
232 {
233     for (size_t i = 0; i < policies.size(); ++i) {
234         if (!(policies[i].get()->*allowed)(reportingStatus))
235             return false;
236     }
237     return true;
238 }
239 
240 template<bool (CSPDirectiveList::*allowed)(ScriptState* scriptState, ContentSecurityPolicy::ReportingStatus) const>
isAllowedByAllWithState(const CSPDirectiveListVector & policies,ScriptState * scriptState,ContentSecurityPolicy::ReportingStatus reportingStatus)241 bool isAllowedByAllWithState(const CSPDirectiveListVector& policies, ScriptState* scriptState, ContentSecurityPolicy::ReportingStatus reportingStatus)
242 {
243     for (size_t i = 0; i < policies.size(); ++i) {
244         if (!(policies[i].get()->*allowed)(scriptState, reportingStatus))
245             return false;
246     }
247     return true;
248 }
249 
250 template<bool (CSPDirectiveList::*allowed)(const String&, const WTF::OrdinalNumber&, ContentSecurityPolicy::ReportingStatus) const>
isAllowedByAllWithContext(const CSPDirectiveListVector & policies,const String & contextURL,const WTF::OrdinalNumber & contextLine,ContentSecurityPolicy::ReportingStatus reportingStatus)251 bool isAllowedByAllWithContext(const CSPDirectiveListVector& policies, const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus)
252 {
253     for (size_t i = 0; i < policies.size(); ++i) {
254         if (!(policies[i].get()->*allowed)(contextURL, contextLine, reportingStatus))
255             return false;
256     }
257     return true;
258 }
259 
260 template<bool (CSPDirectiveList::*allowed)(const String&) const>
isAllowedByAllWithNonce(const CSPDirectiveListVector & policies,const String & nonce)261 bool isAllowedByAllWithNonce(const CSPDirectiveListVector& policies, const String& nonce)
262 {
263     for (size_t i = 0; i < policies.size(); ++i) {
264         if (!(policies[i].get()->*allowed)(nonce))
265             return false;
266     }
267     return true;
268 }
269 
270 template<bool (CSPDirectiveList::*allowed)(const CSPHashValue&) const>
isAllowedByAllWithHash(const CSPDirectiveListVector & policies,const CSPHashValue & hashValue)271 bool isAllowedByAllWithHash(const CSPDirectiveListVector& policies, const CSPHashValue& hashValue)
272 {
273     for (size_t i = 0; i < policies.size(); ++i) {
274         if (!(policies[i].get()->*allowed)(hashValue))
275             return false;
276     }
277     return true;
278 }
279 
280 template<bool (CSPDirectiveList::*allowFromURL)(const KURL&, ContentSecurityPolicy::ReportingStatus) const>
isAllowedByAllWithURL(const CSPDirectiveListVector & policies,const KURL & url,ContentSecurityPolicy::ReportingStatus reportingStatus)281 bool isAllowedByAllWithURL(const CSPDirectiveListVector& policies, const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus)
282 {
283     if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol()))
284         return true;
285 
286     for (size_t i = 0; i < policies.size(); ++i) {
287         if (!(policies[i].get()->*allowFromURL)(url, reportingStatus))
288             return false;
289     }
290     return true;
291 }
292 
293 template<bool (CSPDirectiveList::*allowed)(LocalFrame*, ContentSecurityPolicy::ReportingStatus) const>
isAllowedByAllWithFrame(const CSPDirectiveListVector & policies,LocalFrame * frame,ContentSecurityPolicy::ReportingStatus reportingStatus)294 bool isAllowedByAllWithFrame(const CSPDirectiveListVector& policies, LocalFrame* frame, ContentSecurityPolicy::ReportingStatus reportingStatus)
295 {
296     for (size_t i = 0; i < policies.size(); ++i) {
297         if (!(policies[i].get()->*allowed)(frame, reportingStatus))
298             return false;
299     }
300     return true;
301 }
302 
303 template<bool (CSPDirectiveList::*allowed)(const CSPHashValue&) const>
checkDigest(const String & source,uint8_t hashAlgorithmsUsed,const CSPDirectiveListVector & policies)304 bool checkDigest(const String& source, uint8_t hashAlgorithmsUsed, const CSPDirectiveListVector& policies)
305 {
306     // Any additions or subtractions from this struct should also modify the
307     // respective entries in the kSupportedPrefixes array in
308     // CSPSourceList::parseHash().
309     static const struct {
310         ContentSecurityPolicyHashAlgorithm cspHashAlgorithm;
311         HashAlgorithm algorithm;
312     } kAlgorithmMap[] = {
313         { ContentSecurityPolicyHashAlgorithmSha1, HashAlgorithmSha1 },
314         { ContentSecurityPolicyHashAlgorithmSha256, HashAlgorithmSha256 },
315         { ContentSecurityPolicyHashAlgorithmSha384, HashAlgorithmSha384 },
316         { ContentSecurityPolicyHashAlgorithmSha512, HashAlgorithmSha512 }
317     };
318 
319     // Only bother normalizing the source/computing digests if there are any checks to be done.
320     if (hashAlgorithmsUsed == ContentSecurityPolicyHashAlgorithmNone)
321         return false;
322 
323     StringUTF8Adaptor normalizedSource(source, StringUTF8Adaptor::Normalize, WTF::EntitiesForUnencodables);
324 
325     // See comment in CSPSourceList::parseHash about why we are using this sizeof
326     // calculation instead of WTF_ARRAY_LENGTH.
327     for (size_t i = 0; i < (sizeof(kAlgorithmMap) / sizeof(kAlgorithmMap[0])); i++) {
328         DigestValue digest;
329         if (kAlgorithmMap[i].cspHashAlgorithm & hashAlgorithmsUsed) {
330             bool digestSuccess = computeDigest(kAlgorithmMap[i].algorithm, normalizedSource.data(), normalizedSource.length(), digest);
331             if (digestSuccess && isAllowedByAllWithHash<allowed>(policies, CSPHashValue(kAlgorithmMap[i].cspHashAlgorithm, digest)))
332                 return true;
333         }
334     }
335 
336     return false;
337 }
338 
allowJavaScriptURLs(const String & contextURL,const WTF::OrdinalNumber & contextLine,ContentSecurityPolicy::ReportingStatus reportingStatus) const339 bool ContentSecurityPolicy::allowJavaScriptURLs(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const
340 {
341     return isAllowedByAllWithContext<&CSPDirectiveList::allowJavaScriptURLs>(m_policies, contextURL, contextLine, reportingStatus);
342 }
343 
allowInlineEventHandlers(const String & contextURL,const WTF::OrdinalNumber & contextLine,ContentSecurityPolicy::ReportingStatus reportingStatus) const344 bool ContentSecurityPolicy::allowInlineEventHandlers(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const
345 {
346     return isAllowedByAllWithContext<&CSPDirectiveList::allowInlineEventHandlers>(m_policies, contextURL, contextLine, reportingStatus);
347 }
348 
allowInlineScript(const String & contextURL,const WTF::OrdinalNumber & contextLine,ContentSecurityPolicy::ReportingStatus reportingStatus) const349 bool ContentSecurityPolicy::allowInlineScript(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const
350 {
351     return isAllowedByAllWithContext<&CSPDirectiveList::allowInlineScript>(m_policies, contextURL, contextLine, reportingStatus);
352 }
353 
allowInlineStyle(const String & contextURL,const WTF::OrdinalNumber & contextLine,ContentSecurityPolicy::ReportingStatus reportingStatus) const354 bool ContentSecurityPolicy::allowInlineStyle(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const
355 {
356     if (m_overrideInlineStyleAllowed)
357         return true;
358     return isAllowedByAllWithContext<&CSPDirectiveList::allowInlineStyle>(m_policies, contextURL, contextLine, reportingStatus);
359 }
360 
allowEval(ScriptState * scriptState,ContentSecurityPolicy::ReportingStatus reportingStatus) const361 bool ContentSecurityPolicy::allowEval(ScriptState* scriptState, ContentSecurityPolicy::ReportingStatus reportingStatus) const
362 {
363     return isAllowedByAllWithState<&CSPDirectiveList::allowEval>(m_policies, scriptState, reportingStatus);
364 }
365 
evalDisabledErrorMessage() const366 String ContentSecurityPolicy::evalDisabledErrorMessage() const
367 {
368     for (size_t i = 0; i < m_policies.size(); ++i) {
369         if (!m_policies[i]->allowEval(0, SuppressReport))
370             return m_policies[i]->evalDisabledErrorMessage();
371     }
372     return String();
373 }
374 
allowPluginType(const String & type,const String & typeAttribute,const KURL & url,ContentSecurityPolicy::ReportingStatus reportingStatus) const375 bool ContentSecurityPolicy::allowPluginType(const String& type, const String& typeAttribute, const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
376 {
377     for (size_t i = 0; i < m_policies.size(); ++i) {
378         if (!m_policies[i]->allowPluginType(type, typeAttribute, url, reportingStatus))
379             return false;
380     }
381     return true;
382 }
383 
allowScriptFromSource(const KURL & url,ContentSecurityPolicy::ReportingStatus reportingStatus) const384 bool ContentSecurityPolicy::allowScriptFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
385 {
386     return isAllowedByAllWithURL<&CSPDirectiveList::allowScriptFromSource>(m_policies, url, reportingStatus);
387 }
388 
allowScriptNonce(const String & nonce) const389 bool ContentSecurityPolicy::allowScriptNonce(const String& nonce) const
390 {
391     return isAllowedByAllWithNonce<&CSPDirectiveList::allowScriptNonce>(m_policies, nonce);
392 }
393 
allowStyleNonce(const String & nonce) const394 bool ContentSecurityPolicy::allowStyleNonce(const String& nonce) const
395 {
396     return isAllowedByAllWithNonce<&CSPDirectiveList::allowStyleNonce>(m_policies, nonce);
397 }
398 
allowScriptHash(const String & source) const399 bool ContentSecurityPolicy::allowScriptHash(const String& source) const
400 {
401     return checkDigest<&CSPDirectiveList::allowScriptHash>(source, m_scriptHashAlgorithmsUsed, m_policies);
402 }
403 
allowStyleHash(const String & source) const404 bool ContentSecurityPolicy::allowStyleHash(const String& source) const
405 {
406     return checkDigest<&CSPDirectiveList::allowStyleHash>(source, m_styleHashAlgorithmsUsed, m_policies);
407 }
408 
usesScriptHashAlgorithms(uint8_t algorithms)409 void ContentSecurityPolicy::usesScriptHashAlgorithms(uint8_t algorithms)
410 {
411     m_scriptHashAlgorithmsUsed |= algorithms;
412 }
413 
usesStyleHashAlgorithms(uint8_t algorithms)414 void ContentSecurityPolicy::usesStyleHashAlgorithms(uint8_t algorithms)
415 {
416     m_styleHashAlgorithmsUsed |= algorithms;
417 }
418 
allowObjectFromSource(const KURL & url,ContentSecurityPolicy::ReportingStatus reportingStatus) const419 bool ContentSecurityPolicy::allowObjectFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
420 {
421     return isAllowedByAllWithURL<&CSPDirectiveList::allowObjectFromSource>(m_policies, url, reportingStatus);
422 }
423 
allowChildFrameFromSource(const KURL & url,ContentSecurityPolicy::ReportingStatus reportingStatus) const424 bool ContentSecurityPolicy::allowChildFrameFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
425 {
426     return isAllowedByAllWithURL<&CSPDirectiveList::allowChildFrameFromSource>(m_policies, url, reportingStatus);
427 }
428 
allowImageFromSource(const KURL & url,ContentSecurityPolicy::ReportingStatus reportingStatus) const429 bool ContentSecurityPolicy::allowImageFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
430 {
431     return isAllowedByAllWithURL<&CSPDirectiveList::allowImageFromSource>(m_policies, url, reportingStatus);
432 }
433 
allowStyleFromSource(const KURL & url,ContentSecurityPolicy::ReportingStatus reportingStatus) const434 bool ContentSecurityPolicy::allowStyleFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
435 {
436     return isAllowedByAllWithURL<&CSPDirectiveList::allowStyleFromSource>(m_policies, url, reportingStatus);
437 }
438 
allowFontFromSource(const KURL & url,ContentSecurityPolicy::ReportingStatus reportingStatus) const439 bool ContentSecurityPolicy::allowFontFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
440 {
441     return isAllowedByAllWithURL<&CSPDirectiveList::allowFontFromSource>(m_policies, url, reportingStatus);
442 }
443 
allowMediaFromSource(const KURL & url,ContentSecurityPolicy::ReportingStatus reportingStatus) const444 bool ContentSecurityPolicy::allowMediaFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
445 {
446     return isAllowedByAllWithURL<&CSPDirectiveList::allowMediaFromSource>(m_policies, url, reportingStatus);
447 }
448 
allowConnectToSource(const KURL & url,ContentSecurityPolicy::ReportingStatus reportingStatus) const449 bool ContentSecurityPolicy::allowConnectToSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
450 {
451     return isAllowedByAllWithURL<&CSPDirectiveList::allowConnectToSource>(m_policies, url, reportingStatus);
452 }
453 
allowFormAction(const KURL & url,ContentSecurityPolicy::ReportingStatus reportingStatus) const454 bool ContentSecurityPolicy::allowFormAction(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
455 {
456     return isAllowedByAllWithURL<&CSPDirectiveList::allowFormAction>(m_policies, url, reportingStatus);
457 }
458 
allowBaseURI(const KURL & url,ContentSecurityPolicy::ReportingStatus reportingStatus) const459 bool ContentSecurityPolicy::allowBaseURI(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
460 {
461     return isAllowedByAllWithURL<&CSPDirectiveList::allowBaseURI>(m_policies, url, reportingStatus);
462 }
463 
allowAncestors(LocalFrame * frame,ContentSecurityPolicy::ReportingStatus reportingStatus) const464 bool ContentSecurityPolicy::allowAncestors(LocalFrame* frame, ContentSecurityPolicy::ReportingStatus reportingStatus) const
465 {
466     return isAllowedByAllWithFrame<&CSPDirectiveList::allowAncestors>(m_policies, frame, reportingStatus);
467 }
468 
allowChildContextFromSource(const KURL & url,ContentSecurityPolicy::ReportingStatus reportingStatus) const469 bool ContentSecurityPolicy::allowChildContextFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
470 {
471     return isAllowedByAllWithURL<&CSPDirectiveList::allowChildContextFromSource>(m_policies, url, reportingStatus);
472 }
473 
allowWorkerContextFromSource(const KURL & url,ContentSecurityPolicy::ReportingStatus reportingStatus) const474 bool ContentSecurityPolicy::allowWorkerContextFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
475 {
476     // CSP 1.1 moves workers from 'script-src' to the new 'child-src'. Measure the impact of this backwards-incompatible change.
477     if (m_executionContext->isDocument()) {
478         Document* document = static_cast<Document*>(m_executionContext);
479         UseCounter::count(*document, UseCounter::WorkerSubjectToCSP);
480         if (isAllowedByAllWithURL<&CSPDirectiveList::allowChildContextFromSource>(m_policies, url, SuppressReport) && !isAllowedByAllWithURL<&CSPDirectiveList::allowScriptFromSource>(m_policies, url, SuppressReport))
481             UseCounter::count(*document, UseCounter::WorkerAllowedByChildBlockedByScript);
482     }
483 
484     return experimentalFeaturesEnabled() ?
485         isAllowedByAllWithURL<&CSPDirectiveList::allowChildContextFromSource>(m_policies, url, reportingStatus) :
486         isAllowedByAllWithURL<&CSPDirectiveList::allowScriptFromSource>(m_policies, url, reportingStatus);
487 }
488 
isActive() const489 bool ContentSecurityPolicy::isActive() const
490 {
491     return !m_policies.isEmpty();
492 }
493 
reflectedXSSDisposition() const494 ReflectedXSSDisposition ContentSecurityPolicy::reflectedXSSDisposition() const
495 {
496     ReflectedXSSDisposition disposition = ReflectedXSSUnset;
497     for (size_t i = 0; i < m_policies.size(); ++i) {
498         if (m_policies[i]->reflectedXSSDisposition() > disposition)
499             disposition = std::max(disposition, m_policies[i]->reflectedXSSDisposition());
500     }
501     return disposition;
502 }
503 
referrerPolicy() const504 ReferrerPolicy ContentSecurityPolicy::referrerPolicy() const
505 {
506     ReferrerPolicy policy = ReferrerPolicyDefault;
507     bool first = true;
508     for (size_t i = 0; i < m_policies.size(); ++i) {
509         if (m_policies[i]->didSetReferrerPolicy()) {
510             if (first)
511                 policy = m_policies[i]->referrerPolicy();
512             else
513                 policy = mergeReferrerPolicies(policy, m_policies[i]->referrerPolicy());
514         }
515     }
516     return policy;
517 }
518 
didSetReferrerPolicy() const519 bool ContentSecurityPolicy::didSetReferrerPolicy() const
520 {
521     for (size_t i = 0; i < m_policies.size(); ++i) {
522         if (m_policies[i]->didSetReferrerPolicy())
523             return true;
524     }
525     return false;
526 }
527 
securityOrigin() const528 SecurityOrigin* ContentSecurityPolicy::securityOrigin() const
529 {
530     return m_executionContext->securityContext().securityOrigin();
531 }
532 
url() const533 const KURL ContentSecurityPolicy::url() const
534 {
535     return m_executionContext->contextURL();
536 }
537 
completeURL(const String & url) const538 KURL ContentSecurityPolicy::completeURL(const String& url) const
539 {
540     return m_executionContext->contextCompleteURL(url);
541 }
542 
enforceSandboxFlags(SandboxFlags mask) const543 void ContentSecurityPolicy::enforceSandboxFlags(SandboxFlags mask) const
544 {
545     if (Document* document = this->document())
546         document->enforceSandboxFlags(mask);
547 }
548 
stripURLForUseInReport(Document * document,const KURL & url)549 static String stripURLForUseInReport(Document* document, const KURL& url)
550 {
551     if (!url.isValid())
552         return String();
553     if (!url.isHierarchical() || url.protocolIs("file"))
554         return url.protocol();
555     return document->securityOrigin()->canRequest(url) ? url.strippedForUseAsReferrer() : SecurityOrigin::create(url)->toString();
556 }
557 
gatherSecurityPolicyViolationEventData(SecurityPolicyViolationEventInit & init,Document * document,const String & directiveText,const String & effectiveDirective,const KURL & blockedURL,const String & header)558 static void gatherSecurityPolicyViolationEventData(SecurityPolicyViolationEventInit& init, Document* document, const String& directiveText, const String& effectiveDirective, const KURL& blockedURL, const String& header)
559 {
560     init.documentURI = document->url().string();
561     init.referrer = document->referrer();
562     init.blockedURI = stripURLForUseInReport(document, blockedURL);
563     init.violatedDirective = directiveText;
564     init.effectiveDirective = effectiveDirective;
565     init.originalPolicy = header;
566     init.sourceFile = String();
567     init.lineNumber = 0;
568     init.columnNumber = 0;
569     init.statusCode = 0;
570 
571     if (!SecurityOrigin::isSecure(document->url()) && document->loader())
572         init.statusCode = document->loader()->response().httpStatusCode();
573 
574     RefPtrWillBeRawPtr<ScriptCallStack> stack = createScriptCallStack(1, false);
575     if (!stack)
576         return;
577 
578     const ScriptCallFrame& callFrame = stack->at(0);
579 
580     if (callFrame.lineNumber()) {
581         KURL source = KURL(ParsedURLString, callFrame.sourceURL());
582         init.sourceFile = stripURLForUseInReport(document, source);
583         init.lineNumber = callFrame.lineNumber();
584         init.columnNumber = callFrame.columnNumber();
585     }
586 }
587 
reportViolation(const String & directiveText,const String & effectiveDirective,const String & consoleMessage,const KURL & blockedURL,const Vector<KURL> & reportURIs,const String & header)588 void ContentSecurityPolicy::reportViolation(const String& directiveText, const String& effectiveDirective, const String& consoleMessage, const KURL& blockedURL, const Vector<KURL>& reportURIs, const String& header)
589 {
590     // FIXME: Support sending reports from worker.
591     if (!m_executionContext->isDocument())
592         return;
593 
594     Document* document = this->document();
595     LocalFrame* frame = document->frame();
596     if (!frame)
597         return;
598 
599     SecurityPolicyViolationEventInit violationData;
600     gatherSecurityPolicyViolationEventData(violationData, document, directiveText, effectiveDirective, blockedURL, header);
601 
602     if (experimentalFeaturesEnabled())
603         frame->domWindow()->enqueueDocumentEvent(SecurityPolicyViolationEvent::create(EventTypeNames::securitypolicyviolation, violationData));
604 
605     if (reportURIs.isEmpty())
606         return;
607 
608     // We need to be careful here when deciding what information to send to the
609     // report-uri. Currently, we send only the current document's URL and the
610     // directive that was violated. The document's URL is safe to send because
611     // it's the document itself that's requesting that it be sent. You could
612     // make an argument that we shouldn't send HTTPS document URLs to HTTP
613     // report-uris (for the same reasons that we supress the Referer in that
614     // case), but the Referer is sent implicitly whereas this request is only
615     // sent explicitly. As for which directive was violated, that's pretty
616     // harmless information.
617 
618     RefPtr<JSONObject> cspReport = JSONObject::create();
619     cspReport->setString("document-uri", violationData.documentURI);
620     cspReport->setString("referrer", violationData.referrer);
621     cspReport->setString("violated-directive", violationData.violatedDirective);
622     if (experimentalFeaturesEnabled())
623         cspReport->setString("effective-directive", violationData.effectiveDirective);
624     cspReport->setString("original-policy", violationData.originalPolicy);
625     cspReport->setString("blocked-uri", violationData.blockedURI);
626     if (!violationData.sourceFile.isEmpty() && violationData.lineNumber) {
627         cspReport->setString("source-file", violationData.sourceFile);
628         cspReport->setNumber("line-number", violationData.lineNumber);
629         cspReport->setNumber("column-number", violationData.columnNumber);
630     }
631     cspReport->setNumber("status-code", violationData.statusCode);
632 
633     RefPtr<JSONObject> reportObject = JSONObject::create();
634     reportObject->setObject("csp-report", cspReport.release());
635     String stringifiedReport = reportObject->toJSONString();
636 
637     if (!shouldSendViolationReport(stringifiedReport))
638         return;
639 
640     RefPtr<FormData> report = FormData::create(stringifiedReport.utf8());
641 
642     for (size_t i = 0; i < reportURIs.size(); ++i)
643         PingLoader::sendViolationReport(frame, reportURIs[i], report, PingLoader::ContentSecurityPolicyViolationReport);
644 
645     didSendViolationReport(stringifiedReport);
646 }
647 
reportInvalidReferrer(const String & invalidValue) const648 void ContentSecurityPolicy::reportInvalidReferrer(const String& invalidValue) const
649 {
650     logToConsole("The 'referrer' Content Security Policy directive has the invalid value \"" + invalidValue + "\". Valid values are \"always\", \"default\", \"never\", and \"origin\".");
651 }
652 
reportReportOnlyInMeta(const String & header) const653 void ContentSecurityPolicy::reportReportOnlyInMeta(const String& header) const
654 {
655     logToConsole("The report-only Content Security Policy '" + header + "' was delivered via a <meta> element, which is disallowed. The policy has been ignored.");
656 }
657 
reportMetaOutsideHead(const String & header) const658 void ContentSecurityPolicy::reportMetaOutsideHead(const String& header) const
659 {
660     logToConsole("The Content Security Policy '" + header + "' was delivered via a <meta> element outside the document's <head>, which is disallowed. The policy has been ignored.");
661 }
662 
reportInvalidInReportOnly(const String & name) const663 void ContentSecurityPolicy::reportInvalidInReportOnly(const String& name) const
664 {
665     logToConsole("The Content Security Policy directive '" + name + "' is ignored when delivered in a report-only policy.");
666 }
667 
reportUnsupportedDirective(const String & name) const668 void ContentSecurityPolicy::reportUnsupportedDirective(const String& name) const
669 {
670     DEFINE_STATIC_LOCAL(String, allow, ("allow"));
671     DEFINE_STATIC_LOCAL(String, options, ("options"));
672     DEFINE_STATIC_LOCAL(String, policyURI, ("policy-uri"));
673     DEFINE_STATIC_LOCAL(String, allowMessage, ("The 'allow' directive has been replaced with 'default-src'. Please use that directive instead, as 'allow' has no effect."));
674     DEFINE_STATIC_LOCAL(String, optionsMessage, ("The 'options' directive has been replaced with 'unsafe-inline' and 'unsafe-eval' source expressions for the 'script-src' and 'style-src' directives. Please use those directives instead, as 'options' has no effect."));
675     DEFINE_STATIC_LOCAL(String, policyURIMessage, ("The 'policy-uri' directive has been removed from the specification. Please specify a complete policy via the Content-Security-Policy header."));
676 
677     String message = "Unrecognized Content-Security-Policy directive '" + name + "'.\n";
678     if (equalIgnoringCase(name, allow))
679         message = allowMessage;
680     else if (equalIgnoringCase(name, options))
681         message = optionsMessage;
682     else if (equalIgnoringCase(name, policyURI))
683         message = policyURIMessage;
684 
685     logToConsole(message);
686 }
687 
reportDirectiveAsSourceExpression(const String & directiveName,const String & sourceExpression) const688 void ContentSecurityPolicy::reportDirectiveAsSourceExpression(const String& directiveName, const String& sourceExpression) const
689 {
690     String message = "The Content Security Policy directive '" + directiveName + "' contains '" + sourceExpression + "' as a source expression. Did you mean '" + directiveName + " ...; " + sourceExpression + "...' (note the semicolon)?";
691     logToConsole(message);
692 }
693 
reportDuplicateDirective(const String & name) const694 void ContentSecurityPolicy::reportDuplicateDirective(const String& name) const
695 {
696     String message = "Ignoring duplicate Content-Security-Policy directive '" + name + "'.\n";
697     logToConsole(message);
698 }
699 
reportInvalidPluginTypes(const String & pluginType) const700 void ContentSecurityPolicy::reportInvalidPluginTypes(const String& pluginType) const
701 {
702     String message;
703     if (pluginType.isNull())
704         message = "'plugin-types' Content Security Policy directive is empty; all plugins will be blocked.\n";
705     else
706         message = "Invalid plugin type in 'plugin-types' Content Security Policy directive: '" + pluginType + "'.\n";
707     logToConsole(message);
708 }
709 
reportInvalidSandboxFlags(const String & invalidFlags) const710 void ContentSecurityPolicy::reportInvalidSandboxFlags(const String& invalidFlags) const
711 {
712     logToConsole("Error while parsing the 'sandbox' Content Security Policy directive: " + invalidFlags);
713 }
714 
reportInvalidReflectedXSS(const String & invalidValue) const715 void ContentSecurityPolicy::reportInvalidReflectedXSS(const String& invalidValue) const
716 {
717     logToConsole("The 'reflected-xss' Content Security Policy directive has the invalid value \"" + invalidValue + "\". Valid values are \"allow\", \"filter\", and \"block\".");
718 }
719 
reportInvalidDirectiveValueCharacter(const String & directiveName,const String & value) const720 void ContentSecurityPolicy::reportInvalidDirectiveValueCharacter(const String& directiveName, const String& value) const
721 {
722     String message = "The value for Content Security Policy directive '" + directiveName + "' contains an invalid character: '" + value + "'. Non-whitespace characters outside ASCII 0x21-0x7E must be percent-encoded, as described in RFC 3986, section 2.1: http://tools.ietf.org/html/rfc3986#section-2.1.";
723     logToConsole(message);
724 }
725 
reportInvalidPathCharacter(const String & directiveName,const String & value,const char invalidChar) const726 void ContentSecurityPolicy::reportInvalidPathCharacter(const String& directiveName, const String& value, const char invalidChar) const
727 {
728     ASSERT(invalidChar == '#' || invalidChar == '?');
729 
730     String ignoring = "The fragment identifier, including the '#', will be ignored.";
731     if (invalidChar == '?')
732         ignoring = "The query component, including the '?', will be ignored.";
733     String message = "The source list for Content Security Policy directive '" + directiveName + "' contains a source with an invalid path: '" + value + "'. " + ignoring;
734     logToConsole(message);
735 }
736 
reportInvalidSourceExpression(const String & directiveName,const String & source) const737 void ContentSecurityPolicy::reportInvalidSourceExpression(const String& directiveName, const String& source) const
738 {
739     String message = "The source list for Content Security Policy directive '" + directiveName + "' contains an invalid source: '" + source + "'. It will be ignored.";
740     if (equalIgnoringCase(source, "'none'"))
741         message = message + " Note that 'none' has no effect unless it is the only expression in the source list.";
742     logToConsole(message);
743 }
744 
reportMissingReportURI(const String & policy) const745 void ContentSecurityPolicy::reportMissingReportURI(const String& policy) const
746 {
747     logToConsole("The Content Security Policy '" + policy + "' was delivered in report-only mode, but does not specify a 'report-uri'; the policy will have no effect. Please either add a 'report-uri' directive, or deliver the policy via the 'Content-Security-Policy' header.");
748 }
749 
logToConsole(const String & message) const750 void ContentSecurityPolicy::logToConsole(const String& message) const
751 {
752     m_executionContext->addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, message);
753 }
754 
reportBlockedScriptExecutionToInspector(const String & directiveText) const755 void ContentSecurityPolicy::reportBlockedScriptExecutionToInspector(const String& directiveText) const
756 {
757     m_executionContext->reportBlockedScriptExecutionToInspector(directiveText);
758 }
759 
experimentalFeaturesEnabled() const760 bool ContentSecurityPolicy::experimentalFeaturesEnabled() const
761 {
762     return RuntimeEnabledFeatures::experimentalContentSecurityPolicyFeaturesEnabled();
763 }
764 
shouldBypassMainWorld(ExecutionContext * context)765 bool ContentSecurityPolicy::shouldBypassMainWorld(ExecutionContext* context)
766 {
767     if (context && context->isDocument()) {
768         Document* document = toDocument(context);
769         if (document->frame())
770             return document->frame()->script().shouldBypassMainWorldContentSecurityPolicy();
771     }
772     return false;
773 }
774 
shouldSendViolationReport(const String & report) const775 bool ContentSecurityPolicy::shouldSendViolationReport(const String& report) const
776 {
777     // Collisions have no security impact, so we can save space by storing only the string's hash rather than the whole report.
778     return !m_violationReportsSent.contains(report.impl()->hash());
779 }
780 
didSendViolationReport(const String & report)781 void ContentSecurityPolicy::didSendViolationReport(const String& report)
782 {
783     m_violationReportsSent.add(report.impl()->hash());
784 }
785 
786 } // namespace WebCore
787