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/core/v8/ScriptCallStackFactory.h"
30#include "bindings/core/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/ConsoleMessage.h"
43#include "core/inspector/InspectorInstrumentation.h"
44#include "core/inspector/ScriptCallStack.h"
45#include "core/loader/DocumentLoader.h"
46#include "core/loader/PingLoader.h"
47#include "platform/Crypto.h"
48#include "platform/JSONValues.h"
49#include "platform/NotImplemented.h"
50#include "platform/ParsingUtilities.h"
51#include "platform/RuntimeEnabledFeatures.h"
52#include "platform/network/ContentSecurityPolicyParsers.h"
53#include "platform/network/ContentSecurityPolicyResponseHeaders.h"
54#include "platform/network/FormData.h"
55#include "platform/network/ResourceResponse.h"
56#include "platform/weborigin/KURL.h"
57#include "platform/weborigin/KnownPorts.h"
58#include "platform/weborigin/SchemeRegistry.h"
59#include "platform/weborigin/SecurityOrigin.h"
60#include "public/platform/Platform.h"
61#include "public/platform/WebArrayBuffer.h"
62#include "public/platform/WebCrypto.h"
63#include "public/platform/WebCryptoAlgorithm.h"
64#include "wtf/StringHasher.h"
65#include "wtf/text/StringBuilder.h"
66#include "wtf/text/StringUTF8Adaptor.h"
67
68namespace blink {
69
70// CSP 1.0 Directives
71const char ContentSecurityPolicy::ConnectSrc[] = "connect-src";
72const char ContentSecurityPolicy::DefaultSrc[] = "default-src";
73const char ContentSecurityPolicy::FontSrc[] = "font-src";
74const char ContentSecurityPolicy::FrameSrc[] = "frame-src";
75const char ContentSecurityPolicy::ImgSrc[] = "img-src";
76const char ContentSecurityPolicy::MediaSrc[] = "media-src";
77const char ContentSecurityPolicy::ObjectSrc[] = "object-src";
78const char ContentSecurityPolicy::ReportURI[] = "report-uri";
79const char ContentSecurityPolicy::Sandbox[] = "sandbox";
80const char ContentSecurityPolicy::ScriptSrc[] = "script-src";
81const char ContentSecurityPolicy::StyleSrc[] = "style-src";
82
83// CSP 1.1 Directives
84const char ContentSecurityPolicy::BaseURI[] = "base-uri";
85const char ContentSecurityPolicy::ChildSrc[] = "child-src";
86const char ContentSecurityPolicy::FormAction[] = "form-action";
87const char ContentSecurityPolicy::FrameAncestors[] = "frame-ancestors";
88const char ContentSecurityPolicy::PluginTypes[] = "plugin-types";
89const char ContentSecurityPolicy::ReflectedXSS[] = "reflected-xss";
90const char ContentSecurityPolicy::Referrer[] = "referrer";
91
92bool ContentSecurityPolicy::isDirectiveName(const String& name)
93{
94    return (equalIgnoringCase(name, ConnectSrc)
95        || equalIgnoringCase(name, DefaultSrc)
96        || equalIgnoringCase(name, FontSrc)
97        || equalIgnoringCase(name, FrameSrc)
98        || equalIgnoringCase(name, ImgSrc)
99        || equalIgnoringCase(name, MediaSrc)
100        || equalIgnoringCase(name, ObjectSrc)
101        || equalIgnoringCase(name, ReportURI)
102        || equalIgnoringCase(name, Sandbox)
103        || equalIgnoringCase(name, ScriptSrc)
104        || equalIgnoringCase(name, StyleSrc)
105        || equalIgnoringCase(name, BaseURI)
106        || equalIgnoringCase(name, ChildSrc)
107        || equalIgnoringCase(name, FormAction)
108        || equalIgnoringCase(name, FrameAncestors)
109        || equalIgnoringCase(name, PluginTypes)
110        || equalIgnoringCase(name, ReflectedXSS)
111        || equalIgnoringCase(name, Referrer)
112    );
113}
114
115static UseCounter::Feature getUseCounterType(ContentSecurityPolicyHeaderType type)
116{
117    switch (type) {
118    case ContentSecurityPolicyHeaderTypeEnforce:
119        return UseCounter::ContentSecurityPolicy;
120    case ContentSecurityPolicyHeaderTypeReport:
121        return UseCounter::ContentSecurityPolicyReportOnly;
122    }
123    ASSERT_NOT_REACHED();
124    return UseCounter::NumberOfFeatures;
125}
126
127static ReferrerPolicy mergeReferrerPolicies(ReferrerPolicy a, ReferrerPolicy b)
128{
129    if (a != b)
130        return ReferrerPolicyNever;
131    return a;
132}
133
134ContentSecurityPolicy::ContentSecurityPolicy()
135    : m_executionContext(0)
136    , m_overrideInlineStyleAllowed(false)
137    , m_scriptHashAlgorithmsUsed(ContentSecurityPolicyHashAlgorithmNone)
138    , m_styleHashAlgorithmsUsed(ContentSecurityPolicyHashAlgorithmNone)
139    , m_sandboxMask(0)
140    , m_referrerPolicy(ReferrerPolicyDefault)
141{
142}
143
144void ContentSecurityPolicy::bindToExecutionContext(ExecutionContext* executionContext)
145{
146    m_executionContext = executionContext;
147    applyPolicySideEffectsToExecutionContext();
148}
149
150void ContentSecurityPolicy::applyPolicySideEffectsToExecutionContext()
151{
152    ASSERT(m_executionContext);
153    // Ensure that 'self' processes correctly.
154    m_selfSource = adoptPtr(new CSPSource(this, securityOrigin()->protocol(), securityOrigin()->host(), securityOrigin()->port(), String(), CSPSource::NoWildcard, CSPSource::NoWildcard));
155
156    // If we're in a Document, set the referrer policy and sandbox flags, then dump all the
157    // parsing error messages, then poke at histograms.
158    if (Document* document = this->document()) {
159        document->enforceSandboxFlags(m_sandboxMask);
160        if (didSetReferrerPolicy())
161            document->setReferrerPolicy(m_referrerPolicy);
162
163        for (ConsoleMessageVector::const_iterator iter = m_consoleMessages.begin(); iter != m_consoleMessages.end(); ++iter)
164            m_executionContext->addConsoleMessage(*iter);
165        m_consoleMessages.clear();
166
167        for (CSPDirectiveListVector::const_iterator iter = m_policies.begin(); iter != m_policies.end(); ++iter)
168            UseCounter::count(*document, getUseCounterType((*iter)->headerType()));
169    }
170
171    // We disable 'eval()' even in the case of report-only policies, and rely on the check in the
172    // V8Initializer::codeGenerationCheckCallbackInMainThread callback to determine whether the
173    // call should execute or not.
174    if (!m_disableEvalErrorMessage.isNull())
175        m_executionContext->disableEval(m_disableEvalErrorMessage);
176}
177
178ContentSecurityPolicy::~ContentSecurityPolicy()
179{
180}
181
182Document* ContentSecurityPolicy::document() const
183{
184    return m_executionContext->isDocument() ? toDocument(m_executionContext) : 0;
185}
186
187void ContentSecurityPolicy::copyStateFrom(const ContentSecurityPolicy* other)
188{
189    ASSERT(m_policies.isEmpty());
190    for (CSPDirectiveListVector::const_iterator iter = other->m_policies.begin(); iter != other->m_policies.end(); ++iter)
191        addPolicyFromHeaderValue((*iter)->header(), (*iter)->headerType(), (*iter)->headerSource());
192}
193
194void ContentSecurityPolicy::didReceiveHeaders(const ContentSecurityPolicyResponseHeaders& headers)
195{
196    if (!headers.contentSecurityPolicy().isEmpty())
197        addPolicyFromHeaderValue(headers.contentSecurityPolicy(), ContentSecurityPolicyHeaderTypeEnforce, ContentSecurityPolicyHeaderSourceHTTP);
198    if (!headers.contentSecurityPolicyReportOnly().isEmpty())
199        addPolicyFromHeaderValue(headers.contentSecurityPolicyReportOnly(), ContentSecurityPolicyHeaderTypeReport, ContentSecurityPolicyHeaderSourceHTTP);
200}
201
202void ContentSecurityPolicy::didReceiveHeader(const String& header, ContentSecurityPolicyHeaderType type, ContentSecurityPolicyHeaderSource source)
203{
204    addPolicyFromHeaderValue(header, type, source);
205
206    // This might be called after we've been bound to an execution context. For example, a <meta>
207    // element might be injected after page load.
208    if (m_executionContext)
209        applyPolicySideEffectsToExecutionContext();
210}
211
212void ContentSecurityPolicy::addPolicyFromHeaderValue(const String& header, ContentSecurityPolicyHeaderType type, ContentSecurityPolicyHeaderSource source)
213{
214    // If this is a report-only header inside a <meta> element, bail out.
215    if (source == ContentSecurityPolicyHeaderSourceMeta && type == ContentSecurityPolicyHeaderTypeReport && experimentalFeaturesEnabled()) {
216        reportReportOnlyInMeta(header);
217        return;
218    }
219
220    Vector<UChar> characters;
221    header.appendTo(characters);
222
223    const UChar* begin = characters.data();
224    const UChar* end = begin + characters.size();
225
226    // RFC2616, section 4.2 specifies that headers appearing multiple times can
227    // be combined with a comma. Walk the header string, and parse each comma
228    // separated chunk as a separate header.
229    const UChar* position = begin;
230    while (position < end) {
231        skipUntil<UChar>(position, end, ',');
232
233        // header1,header2 OR header1
234        //        ^                  ^
235        OwnPtr<CSPDirectiveList> policy = CSPDirectiveList::create(this, begin, position, type, source);
236
237        if (type != ContentSecurityPolicyHeaderTypeReport && policy->didSetReferrerPolicy()) {
238            // FIXME: We need a 'ReferrerPolicyUnset' enum to avoid confusing code like this.
239            m_referrerPolicy = didSetReferrerPolicy() ? mergeReferrerPolicies(m_referrerPolicy, policy->referrerPolicy()) : policy->referrerPolicy();
240        }
241
242        if (!policy->allowEval(0, SuppressReport) && m_disableEvalErrorMessage.isNull())
243            m_disableEvalErrorMessage = policy->evalDisabledErrorMessage();
244
245        m_policies.append(policy.release());
246
247        // Skip the comma, and begin the next header from the current position.
248        ASSERT(position == end || *position == ',');
249        skipExactly<UChar>(position, end, ',');
250        begin = position;
251    }
252}
253
254void ContentSecurityPolicy::setOverrideAllowInlineStyle(bool value)
255{
256    m_overrideInlineStyleAllowed = value;
257}
258
259void ContentSecurityPolicy::setOverrideURLForSelf(const KURL& url)
260{
261    // Create a temporary CSPSource so that 'self' expressions can be resolved before we bind to
262    // an execution context (for 'frame-ancestor' resolution, for example). This CSPSource will
263    // be overwritten when we bind this object to an execution context.
264    RefPtr<SecurityOrigin> origin = SecurityOrigin::create(url);
265    m_selfSource = adoptPtr(new CSPSource(this, origin->protocol(), origin->host(), origin->port(), String(), CSPSource::NoWildcard, CSPSource::NoWildcard));
266}
267
268const String& ContentSecurityPolicy::deprecatedHeader() const
269{
270    return m_policies.isEmpty() ? emptyString() : m_policies[0]->header();
271}
272
273ContentSecurityPolicyHeaderType ContentSecurityPolicy::deprecatedHeaderType() const
274{
275    return m_policies.isEmpty() ? ContentSecurityPolicyHeaderTypeEnforce : m_policies[0]->headerType();
276}
277
278template<bool (CSPDirectiveList::*allowed)(ContentSecurityPolicy::ReportingStatus) const>
279bool isAllowedByAll(const CSPDirectiveListVector& policies, ContentSecurityPolicy::ReportingStatus reportingStatus)
280{
281    for (size_t i = 0; i < policies.size(); ++i) {
282        if (!(policies[i].get()->*allowed)(reportingStatus))
283            return false;
284    }
285    return true;
286}
287
288template<bool (CSPDirectiveList::*allowed)(ScriptState* scriptState, ContentSecurityPolicy::ReportingStatus) const>
289bool isAllowedByAllWithState(const CSPDirectiveListVector& policies, ScriptState* scriptState, ContentSecurityPolicy::ReportingStatus reportingStatus)
290{
291    for (size_t i = 0; i < policies.size(); ++i) {
292        if (!(policies[i].get()->*allowed)(scriptState, reportingStatus))
293            return false;
294    }
295    return true;
296}
297
298template<bool (CSPDirectiveList::*allowed)(const String&, const WTF::OrdinalNumber&, ContentSecurityPolicy::ReportingStatus) const>
299bool isAllowedByAllWithContext(const CSPDirectiveListVector& policies, const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus)
300{
301    for (size_t i = 0; i < policies.size(); ++i) {
302        if (!(policies[i].get()->*allowed)(contextURL, contextLine, reportingStatus))
303            return false;
304    }
305    return true;
306}
307
308template<bool (CSPDirectiveList::*allowed)(const String&) const>
309bool isAllowedByAllWithNonce(const CSPDirectiveListVector& policies, const String& nonce)
310{
311    for (size_t i = 0; i < policies.size(); ++i) {
312        if (!(policies[i].get()->*allowed)(nonce))
313            return false;
314    }
315    return true;
316}
317
318template<bool (CSPDirectiveList::*allowed)(const CSPHashValue&) const>
319bool isAllowedByAllWithHash(const CSPDirectiveListVector& policies, const CSPHashValue& hashValue)
320{
321    for (size_t i = 0; i < policies.size(); ++i) {
322        if (!(policies[i].get()->*allowed)(hashValue))
323            return false;
324    }
325    return true;
326}
327
328template<bool (CSPDirectiveList::*allowFromURL)(const KURL&, ContentSecurityPolicy::ReportingStatus) const>
329bool isAllowedByAllWithURL(const CSPDirectiveListVector& policies, const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus)
330{
331    if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol()))
332        return true;
333
334    for (size_t i = 0; i < policies.size(); ++i) {
335        if (!(policies[i].get()->*allowFromURL)(url, reportingStatus))
336            return false;
337    }
338    return true;
339}
340
341template<bool (CSPDirectiveList::*allowed)(LocalFrame*, const KURL&, ContentSecurityPolicy::ReportingStatus) const>
342bool isAllowedByAllWithFrame(const CSPDirectiveListVector& policies, LocalFrame* frame, const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus)
343{
344    for (size_t i = 0; i < policies.size(); ++i) {
345        if (!(policies[i].get()->*allowed)(frame, url, reportingStatus))
346            return false;
347    }
348    return true;
349}
350
351template<bool (CSPDirectiveList::*allowed)(const CSPHashValue&) const>
352bool checkDigest(const String& source, uint8_t hashAlgorithmsUsed, const CSPDirectiveListVector& policies)
353{
354    // Any additions or subtractions from this struct should also modify the
355    // respective entries in the kSupportedPrefixes array in
356    // CSPSourceList::parseHash().
357    static const struct {
358        ContentSecurityPolicyHashAlgorithm cspHashAlgorithm;
359        HashAlgorithm algorithm;
360    } kAlgorithmMap[] = {
361        { ContentSecurityPolicyHashAlgorithmSha1, HashAlgorithmSha1 },
362        { ContentSecurityPolicyHashAlgorithmSha256, HashAlgorithmSha256 },
363        { ContentSecurityPolicyHashAlgorithmSha384, HashAlgorithmSha384 },
364        { ContentSecurityPolicyHashAlgorithmSha512, HashAlgorithmSha512 }
365    };
366
367    // Only bother normalizing the source/computing digests if there are any checks to be done.
368    if (hashAlgorithmsUsed == ContentSecurityPolicyHashAlgorithmNone)
369        return false;
370
371    StringUTF8Adaptor normalizedSource(source, StringUTF8Adaptor::Normalize, WTF::EntitiesForUnencodables);
372
373    // See comment in CSPSourceList::parseHash about why we are using this sizeof
374    // calculation instead of WTF_ARRAY_LENGTH.
375    for (size_t i = 0; i < (sizeof(kAlgorithmMap) / sizeof(kAlgorithmMap[0])); i++) {
376        DigestValue digest;
377        if (kAlgorithmMap[i].cspHashAlgorithm & hashAlgorithmsUsed) {
378            bool digestSuccess = computeDigest(kAlgorithmMap[i].algorithm, normalizedSource.data(), normalizedSource.length(), digest);
379            if (digestSuccess && isAllowedByAllWithHash<allowed>(policies, CSPHashValue(kAlgorithmMap[i].cspHashAlgorithm, digest)))
380                return true;
381        }
382    }
383
384    return false;
385}
386
387bool ContentSecurityPolicy::allowJavaScriptURLs(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const
388{
389    return isAllowedByAllWithContext<&CSPDirectiveList::allowJavaScriptURLs>(m_policies, contextURL, contextLine, reportingStatus);
390}
391
392bool ContentSecurityPolicy::allowInlineEventHandlers(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const
393{
394    return isAllowedByAllWithContext<&CSPDirectiveList::allowInlineEventHandlers>(m_policies, contextURL, contextLine, reportingStatus);
395}
396
397bool ContentSecurityPolicy::allowInlineScript(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const
398{
399    return isAllowedByAllWithContext<&CSPDirectiveList::allowInlineScript>(m_policies, contextURL, contextLine, reportingStatus);
400}
401
402bool ContentSecurityPolicy::allowInlineStyle(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const
403{
404    if (m_overrideInlineStyleAllowed)
405        return true;
406    return isAllowedByAllWithContext<&CSPDirectiveList::allowInlineStyle>(m_policies, contextURL, contextLine, reportingStatus);
407}
408
409bool ContentSecurityPolicy::allowEval(ScriptState* scriptState, ContentSecurityPolicy::ReportingStatus reportingStatus) const
410{
411    return isAllowedByAllWithState<&CSPDirectiveList::allowEval>(m_policies, scriptState, reportingStatus);
412}
413
414String ContentSecurityPolicy::evalDisabledErrorMessage() const
415{
416    for (size_t i = 0; i < m_policies.size(); ++i) {
417        if (!m_policies[i]->allowEval(0, SuppressReport))
418            return m_policies[i]->evalDisabledErrorMessage();
419    }
420    return String();
421}
422
423bool ContentSecurityPolicy::allowPluginType(const String& type, const String& typeAttribute, const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
424{
425    for (size_t i = 0; i < m_policies.size(); ++i) {
426        if (!m_policies[i]->allowPluginType(type, typeAttribute, url, reportingStatus))
427            return false;
428    }
429    return true;
430}
431
432bool ContentSecurityPolicy::allowScriptFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
433{
434    return isAllowedByAllWithURL<&CSPDirectiveList::allowScriptFromSource>(m_policies, url, reportingStatus);
435}
436
437bool ContentSecurityPolicy::allowScriptWithNonce(const String& nonce) const
438{
439    return isAllowedByAllWithNonce<&CSPDirectiveList::allowScriptNonce>(m_policies, nonce);
440}
441
442bool ContentSecurityPolicy::allowStyleWithNonce(const String& nonce) const
443{
444    return isAllowedByAllWithNonce<&CSPDirectiveList::allowStyleNonce>(m_policies, nonce);
445}
446
447bool ContentSecurityPolicy::allowScriptWithHash(const String& source) const
448{
449    return checkDigest<&CSPDirectiveList::allowScriptHash>(source, m_scriptHashAlgorithmsUsed, m_policies);
450}
451
452bool ContentSecurityPolicy::allowStyleWithHash(const String& source) const
453{
454    return checkDigest<&CSPDirectiveList::allowStyleHash>(source, m_styleHashAlgorithmsUsed, m_policies);
455}
456
457void ContentSecurityPolicy::usesScriptHashAlgorithms(uint8_t algorithms)
458{
459    m_scriptHashAlgorithmsUsed |= algorithms;
460}
461
462void ContentSecurityPolicy::usesStyleHashAlgorithms(uint8_t algorithms)
463{
464    m_styleHashAlgorithmsUsed |= algorithms;
465}
466
467bool ContentSecurityPolicy::allowObjectFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
468{
469    return isAllowedByAllWithURL<&CSPDirectiveList::allowObjectFromSource>(m_policies, url, reportingStatus);
470}
471
472bool ContentSecurityPolicy::allowChildFrameFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
473{
474    return isAllowedByAllWithURL<&CSPDirectiveList::allowChildFrameFromSource>(m_policies, url, reportingStatus);
475}
476
477bool ContentSecurityPolicy::allowImageFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
478{
479    return isAllowedByAllWithURL<&CSPDirectiveList::allowImageFromSource>(m_policies, url, reportingStatus);
480}
481
482bool ContentSecurityPolicy::allowStyleFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
483{
484    return isAllowedByAllWithURL<&CSPDirectiveList::allowStyleFromSource>(m_policies, url, reportingStatus);
485}
486
487bool ContentSecurityPolicy::allowFontFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
488{
489    return isAllowedByAllWithURL<&CSPDirectiveList::allowFontFromSource>(m_policies, url, reportingStatus);
490}
491
492bool ContentSecurityPolicy::allowMediaFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
493{
494    return isAllowedByAllWithURL<&CSPDirectiveList::allowMediaFromSource>(m_policies, url, reportingStatus);
495}
496
497bool ContentSecurityPolicy::allowConnectToSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
498{
499    return isAllowedByAllWithURL<&CSPDirectiveList::allowConnectToSource>(m_policies, url, reportingStatus);
500}
501
502bool ContentSecurityPolicy::allowFormAction(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
503{
504    return isAllowedByAllWithURL<&CSPDirectiveList::allowFormAction>(m_policies, url, reportingStatus);
505}
506
507bool ContentSecurityPolicy::allowBaseURI(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
508{
509    return isAllowedByAllWithURL<&CSPDirectiveList::allowBaseURI>(m_policies, url, reportingStatus);
510}
511
512bool ContentSecurityPolicy::allowAncestors(LocalFrame* frame, const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
513{
514    return isAllowedByAllWithFrame<&CSPDirectiveList::allowAncestors>(m_policies, frame, url, reportingStatus);
515}
516
517bool ContentSecurityPolicy::allowChildContextFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
518{
519    return isAllowedByAllWithURL<&CSPDirectiveList::allowChildContextFromSource>(m_policies, url, reportingStatus);
520}
521
522bool ContentSecurityPolicy::allowWorkerContextFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
523{
524    // CSP 1.1 moves workers from 'script-src' to the new 'child-src'. Measure the impact of this backwards-incompatible change.
525    if (Document* document = this->document()) {
526        UseCounter::count(*document, UseCounter::WorkerSubjectToCSP);
527        if (isAllowedByAllWithURL<&CSPDirectiveList::allowChildContextFromSource>(m_policies, url, SuppressReport) && !isAllowedByAllWithURL<&CSPDirectiveList::allowScriptFromSource>(m_policies, url, SuppressReport))
528            UseCounter::count(*document, UseCounter::WorkerAllowedByChildBlockedByScript);
529    }
530
531    return experimentalFeaturesEnabled() ?
532        isAllowedByAllWithURL<&CSPDirectiveList::allowChildContextFromSource>(m_policies, url, reportingStatus) :
533        isAllowedByAllWithURL<&CSPDirectiveList::allowScriptFromSource>(m_policies, url, reportingStatus);
534}
535
536bool ContentSecurityPolicy::isActive() const
537{
538    return !m_policies.isEmpty();
539}
540
541ReflectedXSSDisposition ContentSecurityPolicy::reflectedXSSDisposition() const
542{
543    ReflectedXSSDisposition disposition = ReflectedXSSUnset;
544    for (size_t i = 0; i < m_policies.size(); ++i) {
545        if (m_policies[i]->reflectedXSSDisposition() > disposition)
546            disposition = std::max(disposition, m_policies[i]->reflectedXSSDisposition());
547    }
548    return disposition;
549}
550
551ReferrerPolicy ContentSecurityPolicy::referrerPolicy() const
552{
553    ReferrerPolicy policy = ReferrerPolicyDefault;
554    bool first = true;
555    for (size_t i = 0; i < m_policies.size(); ++i) {
556        if (m_policies[i]->didSetReferrerPolicy()) {
557            if (first)
558                policy = m_policies[i]->referrerPolicy();
559            else
560                policy = mergeReferrerPolicies(policy, m_policies[i]->referrerPolicy());
561        }
562    }
563    return policy;
564}
565
566bool ContentSecurityPolicy::didSetReferrerPolicy() const
567{
568    for (size_t i = 0; i < m_policies.size(); ++i) {
569        if (m_policies[i]->didSetReferrerPolicy())
570            return true;
571    }
572    return false;
573}
574
575SecurityOrigin* ContentSecurityPolicy::securityOrigin() const
576{
577    return m_executionContext->securityContext().securityOrigin();
578}
579
580const KURL ContentSecurityPolicy::url() const
581{
582    return m_executionContext->contextURL();
583}
584
585KURL ContentSecurityPolicy::completeURL(const String& url) const
586{
587    return m_executionContext->contextCompleteURL(url);
588}
589
590void ContentSecurityPolicy::enforceSandboxFlags(SandboxFlags mask)
591{
592    m_sandboxMask |= mask;
593}
594
595static String stripURLForUseInReport(Document* document, const KURL& url)
596{
597    if (!url.isValid())
598        return String();
599    if (!url.isHierarchical() || url.protocolIs("file"))
600        return url.protocol();
601    return document->securityOrigin()->canRequest(url) ? url.strippedForUseAsReferrer() : SecurityOrigin::create(url)->toString();
602}
603
604static void gatherSecurityPolicyViolationEventData(SecurityPolicyViolationEventInit& init, Document* document, const String& directiveText, const String& effectiveDirective, const KURL& blockedURL, const String& header)
605{
606    if (equalIgnoringCase(effectiveDirective, ContentSecurityPolicy::FrameAncestors)) {
607        // If this load was blocked via 'frame-ancestors', then the URL of |document| has not yet
608        // been initialized. In this case, we'll set both 'documentURI' and 'blockedURI' to the
609        // blocked document's URL.
610        init.documentURI = blockedURL.string();
611        init.blockedURI = blockedURL.string();
612    } else {
613        init.documentURI = document->url().string();
614        init.blockedURI = stripURLForUseInReport(document, blockedURL);
615    }
616    init.referrer = document->referrer();
617    init.violatedDirective = directiveText;
618    init.effectiveDirective = effectiveDirective;
619    init.originalPolicy = header;
620    init.sourceFile = String();
621    init.lineNumber = 0;
622    init.columnNumber = 0;
623    init.statusCode = 0;
624
625    if (!SecurityOrigin::isSecure(document->url()) && document->loader())
626        init.statusCode = document->loader()->response().httpStatusCode();
627
628    RefPtrWillBeRawPtr<ScriptCallStack> stack = createScriptCallStack(1, false);
629    if (!stack)
630        return;
631
632    const ScriptCallFrame& callFrame = stack->at(0);
633
634    if (callFrame.lineNumber()) {
635        KURL source = KURL(ParsedURLString, callFrame.sourceURL());
636        init.sourceFile = stripURLForUseInReport(document, source);
637        init.lineNumber = callFrame.lineNumber();
638        init.columnNumber = callFrame.columnNumber();
639    }
640}
641
642void ContentSecurityPolicy::reportViolation(const String& directiveText, const String& effectiveDirective, const String& consoleMessage, const KURL& blockedURL, const Vector<String>& reportEndpoints, const String& header, LocalFrame* contextFrame)
643{
644    ASSERT((m_executionContext && !contextFrame) || (equalIgnoringCase(effectiveDirective, ContentSecurityPolicy::FrameAncestors) && contextFrame));
645
646    // FIXME: Support sending reports from worker.
647    Document* document = contextFrame ? contextFrame->document() : this->document();
648    if (!document)
649        return;
650
651    LocalFrame* frame = document->frame();
652    if (!frame)
653        return;
654
655    SecurityPolicyViolationEventInit violationData;
656    gatherSecurityPolicyViolationEventData(violationData, document, directiveText, effectiveDirective, blockedURL, header);
657
658    if (experimentalFeaturesEnabled())
659        frame->domWindow()->enqueueDocumentEvent(SecurityPolicyViolationEvent::create(EventTypeNames::securitypolicyviolation, violationData));
660
661    if (reportEndpoints.isEmpty())
662        return;
663
664    // We need to be careful here when deciding what information to send to the
665    // report-uri. Currently, we send only the current document's URL and the
666    // directive that was violated. The document's URL is safe to send because
667    // it's the document itself that's requesting that it be sent. You could
668    // make an argument that we shouldn't send HTTPS document URLs to HTTP
669    // report-uris (for the same reasons that we supress the Referer in that
670    // case), but the Referer is sent implicitly whereas this request is only
671    // sent explicitly. As for which directive was violated, that's pretty
672    // harmless information.
673
674    RefPtr<JSONObject> cspReport = JSONObject::create();
675    cspReport->setString("document-uri", violationData.documentURI);
676    cspReport->setString("referrer", violationData.referrer);
677    cspReport->setString("violated-directive", violationData.violatedDirective);
678    if (experimentalFeaturesEnabled())
679        cspReport->setString("effective-directive", violationData.effectiveDirective);
680    cspReport->setString("original-policy", violationData.originalPolicy);
681    cspReport->setString("blocked-uri", violationData.blockedURI);
682    if (!violationData.sourceFile.isEmpty() && violationData.lineNumber) {
683        cspReport->setString("source-file", violationData.sourceFile);
684        cspReport->setNumber("line-number", violationData.lineNumber);
685        cspReport->setNumber("column-number", violationData.columnNumber);
686    }
687    cspReport->setNumber("status-code", violationData.statusCode);
688
689    RefPtr<JSONObject> reportObject = JSONObject::create();
690    reportObject->setObject("csp-report", cspReport.release());
691    String stringifiedReport = reportObject->toJSONString();
692
693    if (!shouldSendViolationReport(stringifiedReport))
694        return;
695
696    RefPtr<FormData> report = FormData::create(stringifiedReport.utf8());
697
698    for (size_t i = 0; i < reportEndpoints.size(); ++i) {
699        // If we have a context frame we're dealing with 'frame-ancestors' and we don't have our
700        // own execution context. Use the frame's document to complete the endpoint URL, overriding
701        // its URL with the blocked document's URL.
702        ASSERT(!contextFrame || !m_executionContext);
703        ASSERT(!contextFrame || equalIgnoringCase(effectiveDirective, FrameAncestors));
704        KURL endpoint = contextFrame ? frame->document()->completeURLWithOverride(reportEndpoints[i], blockedURL) : completeURL(reportEndpoints[i]);
705        PingLoader::sendViolationReport(frame, completeURL(reportEndpoints[i]), report, PingLoader::ContentSecurityPolicyViolationReport);
706    }
707
708    didSendViolationReport(stringifiedReport);
709}
710
711void ContentSecurityPolicy::reportInvalidReferrer(const String& invalidValue)
712{
713    logToConsole("The 'referrer' Content Security Policy directive has the invalid value \"" + invalidValue + "\". Valid values are \"always\", \"default\", \"never\", and \"origin\".");
714}
715
716void ContentSecurityPolicy::reportReportOnlyInMeta(const String& header)
717{
718    logToConsole("The report-only Content Security Policy '" + header + "' was delivered via a <meta> element, which is disallowed. The policy has been ignored.");
719}
720
721void ContentSecurityPolicy::reportMetaOutsideHead(const String& header)
722{
723    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.");
724}
725
726void ContentSecurityPolicy::reportInvalidInReportOnly(const String& name)
727{
728    logToConsole("The Content Security Policy directive '" + name + "' is ignored when delivered in a report-only policy.");
729}
730
731void ContentSecurityPolicy::reportUnsupportedDirective(const String& name)
732{
733    DEFINE_STATIC_LOCAL(String, allow, ("allow"));
734    DEFINE_STATIC_LOCAL(String, options, ("options"));
735    DEFINE_STATIC_LOCAL(String, policyURI, ("policy-uri"));
736    DEFINE_STATIC_LOCAL(String, allowMessage, ("The 'allow' directive has been replaced with 'default-src'. Please use that directive instead, as 'allow' has no effect."));
737    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."));
738    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."));
739
740    String message = "Unrecognized Content-Security-Policy directive '" + name + "'.\n";
741    MessageLevel level = ErrorMessageLevel;
742    if (equalIgnoringCase(name, allow)) {
743        message = allowMessage;
744    } else if (equalIgnoringCase(name, options)) {
745        message = optionsMessage;
746    } else if (equalIgnoringCase(name, policyURI)) {
747        message = policyURIMessage;
748    } else if (isDirectiveName(name)) {
749        message = "The Content-Security-Policy directive '" + name + "' is implemented behind a flag which is currently disabled.\n";
750        level = InfoMessageLevel;
751    }
752
753    logToConsole(message, level);
754}
755
756void ContentSecurityPolicy::reportDirectiveAsSourceExpression(const String& directiveName, const String& sourceExpression)
757{
758    String message = "The Content Security Policy directive '" + directiveName + "' contains '" + sourceExpression + "' as a source expression. Did you mean '" + directiveName + " ...; " + sourceExpression + "...' (note the semicolon)?";
759    logToConsole(message);
760}
761
762void ContentSecurityPolicy::reportDuplicateDirective(const String& name)
763{
764    String message = "Ignoring duplicate Content-Security-Policy directive '" + name + "'.\n";
765    logToConsole(message);
766}
767
768void ContentSecurityPolicy::reportInvalidPluginTypes(const String& pluginType)
769{
770    String message;
771    if (pluginType.isNull())
772        message = "'plugin-types' Content Security Policy directive is empty; all plugins will be blocked.\n";
773    else
774        message = "Invalid plugin type in 'plugin-types' Content Security Policy directive: '" + pluginType + "'.\n";
775    logToConsole(message);
776}
777
778void ContentSecurityPolicy::reportInvalidSandboxFlags(const String& invalidFlags)
779{
780    logToConsole("Error while parsing the 'sandbox' Content Security Policy directive: " + invalidFlags);
781}
782
783void ContentSecurityPolicy::reportInvalidReflectedXSS(const String& invalidValue)
784{
785    logToConsole("The 'reflected-xss' Content Security Policy directive has the invalid value \"" + invalidValue + "\". Valid values are \"allow\", \"filter\", and \"block\".");
786}
787
788void ContentSecurityPolicy::reportInvalidDirectiveValueCharacter(const String& directiveName, const String& value)
789{
790    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.";
791    logToConsole(message);
792}
793
794void ContentSecurityPolicy::reportInvalidPathCharacter(const String& directiveName, const String& value, const char invalidChar)
795{
796    ASSERT(invalidChar == '#' || invalidChar == '?');
797
798    String ignoring = "The fragment identifier, including the '#', will be ignored.";
799    if (invalidChar == '?')
800        ignoring = "The query component, including the '?', will be ignored.";
801    String message = "The source list for Content Security Policy directive '" + directiveName + "' contains a source with an invalid path: '" + value + "'. " + ignoring;
802    logToConsole(message);
803}
804
805void ContentSecurityPolicy::reportInvalidSourceExpression(const String& directiveName, const String& source)
806{
807    String message = "The source list for Content Security Policy directive '" + directiveName + "' contains an invalid source: '" + source + "'. It will be ignored.";
808    if (equalIgnoringCase(source, "'none'"))
809        message = message + " Note that 'none' has no effect unless it is the only expression in the source list.";
810    logToConsole(message);
811}
812
813void ContentSecurityPolicy::reportMissingReportURI(const String& policy)
814{
815    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.");
816}
817
818void ContentSecurityPolicy::logToConsole(const String& message, MessageLevel level)
819{
820    logToConsole(ConsoleMessage::create(SecurityMessageSource, level, message));
821}
822
823void ContentSecurityPolicy::logToConsole(PassRefPtrWillBeRawPtr<ConsoleMessage> consoleMessage, LocalFrame* frame)
824{
825    if (frame)
826        frame->document()->addConsoleMessage(consoleMessage);
827    else if (m_executionContext)
828        m_executionContext->addConsoleMessage(consoleMessage);
829    else
830        m_consoleMessages.append(consoleMessage);
831}
832
833void ContentSecurityPolicy::reportBlockedScriptExecutionToInspector(const String& directiveText) const
834{
835    m_executionContext->reportBlockedScriptExecutionToInspector(directiveText);
836}
837
838bool ContentSecurityPolicy::experimentalFeaturesEnabled() const
839{
840    return RuntimeEnabledFeatures::experimentalContentSecurityPolicyFeaturesEnabled();
841}
842
843bool ContentSecurityPolicy::urlMatchesSelf(const KURL& url) const
844{
845    return m_selfSource->matches(url);
846}
847
848bool ContentSecurityPolicy::protocolMatchesSelf(const KURL& url) const
849{
850    String protectedResourceScheme(securityOrigin()->protocol());
851    if (equalIgnoringCase("http", protectedResourceScheme))
852        return url.protocolIsInHTTPFamily();
853    return equalIgnoringCase(url.protocol(), protectedResourceScheme);
854}
855
856bool ContentSecurityPolicy::shouldBypassMainWorld(ExecutionContext* context)
857{
858    if (context && context->isDocument()) {
859        Document* document = toDocument(context);
860        if (document->frame())
861            return document->frame()->script().shouldBypassMainWorldCSP();
862    }
863    return false;
864}
865
866bool ContentSecurityPolicy::shouldSendViolationReport(const String& report) const
867{
868    // Collisions have no security impact, so we can save space by storing only the string's hash rather than the whole report.
869    return !m_violationReportsSent.contains(report.impl()->hash());
870}
871
872void ContentSecurityPolicy::didSendViolationReport(const String& report)
873{
874    m_violationReportsSent.add(report.impl()->hash());
875}
876
877} // namespace blink
878