2 * Copyright (C) 2011 Google, Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "ContentSecurityPolicy.h"
30 #include "DOMStringList.h"
33 #include "FormDataList.h"
35 #include "InspectorInstrumentation.h"
36 #include "InspectorValues.h"
38 #include "PingLoader.h"
39 #include "SchemeRegistry.h"
40 #include "ScriptCallStack.h"
41 #include "SecurityOrigin.h"
42 #include "TextEncoding.h"
43 #include <wtf/text/TextPosition.h>
44 #include <wtf/text/WTFString.h>
48 // Normally WebKit uses "static" for internal linkage, but using "static" for
49 // these functions causes a compile error because these functions are used as
50 // template parameters.
53 bool isDirectiveNameCharacter(UChar c)
55 return isASCIIAlphanumeric(c) || c == '-';
58 bool isDirectiveValueCharacter(UChar c)
60 return isASCIISpace(c) || (c >= 0x21 && c <= 0x7e); // Whitespace + VCHAR
63 bool isSourceCharacter(UChar c)
65 return !isASCIISpace(c);
68 bool isHostCharacter(UChar c)
70 return isASCIIAlphanumeric(c) || c == '-';
73 bool isSchemeContinuationCharacter(UChar c)
75 return isASCIIAlphanumeric(c) || c == '+' || c == '-' || c == '.';
78 bool isNotASCIISpace(UChar c)
80 return !isASCIISpace(c);
83 bool isNotColonOrSlash(UChar c)
85 return c != ':' && c != '/';
90 static bool skipExactly(const UChar*& position, const UChar* end, UChar delimiter)
92 if (position < end && *position == delimiter) {
99 template<bool characterPredicate(UChar)>
100 static bool skipExactly(const UChar*& position, const UChar* end)
102 if (position < end && characterPredicate(*position)) {
109 static void skipUntil(const UChar*& position, const UChar* end, UChar delimiter)
111 while (position < end && *position != delimiter)
115 template<bool characterPredicate(UChar)>
116 static void skipWhile(const UChar*& position, const UChar* end)
118 while (position < end && characterPredicate(*position))
124 CSPSource(const String& scheme, const String& host, int port, bool hostHasWildcard, bool portHasWildcard)
128 , m_hostHasWildcard(hostHasWildcard)
129 , m_portHasWildcard(portHasWildcard)
133 bool matches(const KURL& url) const
135 if (!schemeMatches(url))
139 return hostMatches(url) && portMatches(url);
143 bool schemeMatches(const KURL& url) const
145 return equalIgnoringCase(url.protocol(), m_scheme);
148 bool hostMatches(const KURL& url) const
150 const String& host = url.host();
151 if (equalIgnoringCase(host, m_host))
153 return m_hostHasWildcard && host.endsWith("." + m_host, false);
157 bool portMatches(const KURL& url) const
159 if (m_portHasWildcard)
162 int port = url.port();
168 return isDefaultPortForProtocol(m_port, m_scheme);
171 return isDefaultPortForProtocol(port, m_scheme);
176 bool isSchemeOnly() const { return m_host.isEmpty(); }
182 bool m_hostHasWildcard;
183 bool m_portHasWildcard;
186 class CSPSourceList {
188 CSPSourceList(ContentSecurityPolicy*, const String& directiveName);
190 void parse(const String&);
191 bool matches(const KURL&);
192 bool allowInline() const { return m_allowInline; }
193 bool allowEval() const { return m_allowEval; }
196 void parse(const UChar* begin, const UChar* end);
198 bool parseSource(const UChar* begin, const UChar* end, String& scheme, String& host, int& port, String& path, bool& hostHasWildcard, bool& portHasWildcard);
199 bool parseScheme(const UChar* begin, const UChar* end, String& scheme);
200 bool parseHost(const UChar* begin, const UChar* end, String& host, bool& hostHasWildcard);
201 bool parsePort(const UChar* begin, const UChar* end, int& port, bool& portHasWildcard);
202 bool parsePath(const UChar* begin, const UChar* end, String& path);
204 void addSourceSelf();
205 void addSourceStar();
206 void addSourceUnsafeInline();
207 void addSourceUnsafeEval();
209 ContentSecurityPolicy* m_policy;
210 Vector<CSPSource> m_list;
211 String m_directiveName;
217 CSPSourceList::CSPSourceList(ContentSecurityPolicy* policy, const String& directiveName)
219 , m_directiveName(directiveName)
221 , m_allowInline(false)
226 void CSPSourceList::parse(const String& value)
228 parse(value.characters(), value.characters() + value.length());
231 bool CSPSourceList::matches(const KURL& url)
236 for (size_t i = 0; i < m_list.size(); ++i) {
237 if (m_list[i].matches(url))
244 // source-list = *WSP [ source *( 1*WSP source ) *WSP ]
245 // / *WSP "'none'" *WSP
247 void CSPSourceList::parse(const UChar* begin, const UChar* end)
249 const UChar* position = begin;
251 bool isFirstSourceInList = true;
252 while (position < end) {
253 skipWhile<isASCIISpace>(position, end);
254 const UChar* beginSource = position;
255 skipWhile<isSourceCharacter>(position, end);
257 if (isFirstSourceInList && equalIgnoringCase("'none'", beginSource, position - beginSource))
258 return; // We represent 'none' as an empty m_list.
259 isFirstSourceInList = false;
261 String scheme, host, path;
263 bool hostHasWildcard = false;
264 bool portHasWildcard = false;
266 if (parseSource(beginSource, position, scheme, host, port, path, hostHasWildcard, portHasWildcard)) {
267 if (scheme.isEmpty())
268 scheme = m_policy->securityOrigin()->protocol();
270 m_policy->reportIgnoredPathComponent(m_directiveName, String(beginSource, position - beginSource), path);
271 m_list.append(CSPSource(scheme, host, port, hostHasWildcard, portHasWildcard));
274 ASSERT(position == end || isASCIISpace(*position));
278 // source = scheme ":"
279 // / ( [ scheme "://" ] host [ port ] [ path ] )
282 bool CSPSourceList::parseSource(const UChar* begin, const UChar* end,
283 String& scheme, String& host, int& port, String& path,
284 bool& hostHasWildcard, bool& portHasWildcard)
289 if (end - begin == 1 && *begin == '*') {
294 if (equalIgnoringCase("'self'", begin, end - begin)) {
299 if (equalIgnoringCase("'unsafe-inline'", begin, end - begin)) {
300 addSourceUnsafeInline();
304 if (equalIgnoringCase("'unsafe-eval'", begin, end - begin)) {
305 addSourceUnsafeEval();
309 const UChar* position = begin;
310 const UChar* beginHost = begin;
311 const UChar* beginPath = end;
312 const UChar* beginPort = 0;
314 skipWhile<isNotColonOrSlash>(position, end);
316 if (position == end) {
319 return parseHost(beginHost, position, host, hostHasWildcard);
322 if (position < end && *position == '/') {
323 // host/path || host/ || /
325 if (!parseHost(beginHost, position, host, hostHasWildcard)
326 || !parsePath(position, end, path)
332 if (position < end && *position == ':') {
333 if (end - position == 1) {
336 return parseScheme(begin, position, scheme);
339 if (position[1] == '/') {
340 // scheme://host || scheme://
342 if (!parseScheme(begin, position, scheme)
343 || !skipExactly(position, end, ':')
344 || !skipExactly(position, end, '/')
345 || !skipExactly(position, end, '/'))
349 beginHost = position;
350 skipWhile<isNotColonOrSlash>(position, end);
353 if (position < end && *position == ':') {
354 // host:port || scheme://host:port
356 beginPort = position;
357 skipUntil(position, end, '/');
361 if (position < end && *position == '/') {
362 // scheme://host/path || scheme://host:port/path
364 if (position == beginHost)
367 beginPath = position;
370 if (!parseHost(beginHost, beginPort ? beginPort : beginPath, host, hostHasWildcard))
374 if (!parsePort(beginPort, beginPath, port, portHasWildcard))
380 if (beginPath != end) {
381 if (!parsePath(beginPath, end, path))
388 // ; <scheme> production from RFC 3986
389 // scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
391 bool CSPSourceList::parseScheme(const UChar* begin, const UChar* end, String& scheme)
393 ASSERT(begin <= end);
394 ASSERT(scheme.isEmpty());
399 const UChar* position = begin;
401 if (!skipExactly<isASCIIAlpha>(position, end))
404 skipWhile<isSchemeContinuationCharacter>(position, end);
409 scheme = String(begin, end - begin);
413 // host = [ "*." ] 1*host-char *( "." 1*host-char )
415 // host-char = ALPHA / DIGIT / "-"
417 bool CSPSourceList::parseHost(const UChar* begin, const UChar* end, String& host, bool& hostHasWildcard)
419 ASSERT(begin <= end);
420 ASSERT(host.isEmpty());
421 ASSERT(!hostHasWildcard);
426 const UChar* position = begin;
428 if (skipExactly(position, end, '*')) {
429 hostHasWildcard = true;
434 if (!skipExactly(position, end, '.'))
438 const UChar* hostBegin = position;
440 while (position < end) {
441 if (!skipExactly<isHostCharacter>(position, end))
444 skipWhile<isHostCharacter>(position, end);
446 if (position < end && !skipExactly(position, end, '.'))
450 ASSERT(position == end);
451 host = String(hostBegin, end - hostBegin);
455 // FIXME: Deal with an actual path. This just sucks up everything to the end of the string.
456 bool CSPSourceList::parsePath(const UChar* begin, const UChar* end, String& path)
458 ASSERT(begin <= end);
459 ASSERT(path.isEmpty());
464 path = String(begin, end - begin);
468 // port = ":" ( 1*DIGIT / "*" )
470 bool CSPSourceList::parsePort(const UChar* begin, const UChar* end, int& port, bool& portHasWildcard)
472 ASSERT(begin <= end);
474 ASSERT(!portHasWildcard);
476 if (!skipExactly(begin, end, ':'))
477 ASSERT_NOT_REACHED();
482 if (end - begin == 1 && *begin == '*') {
484 portHasWildcard = true;
488 const UChar* position = begin;
489 skipWhile<isASCIIDigit>(position, end);
495 port = charactersToIntStrict(begin, end - begin, &ok);
499 void CSPSourceList::addSourceSelf()
501 m_list.append(CSPSource(m_policy->securityOrigin()->protocol(), m_policy->securityOrigin()->host(), m_policy->securityOrigin()->port(), false, false));
504 void CSPSourceList::addSourceStar()
509 void CSPSourceList::addSourceUnsafeInline()
511 m_allowInline = true;
514 void CSPSourceList::addSourceUnsafeEval()
521 CSPDirective(const String& name, const String& value, ContentSecurityPolicy* policy)
522 : m_sourceList(policy, name)
523 , m_text(name + ' ' + value)
524 , m_selfURL(policy->url())
526 m_sourceList.parse(value);
529 bool allows(const KURL& url)
531 return m_sourceList.matches(url.isEmpty() ? m_selfURL : url);
534 bool allowInline() const { return m_sourceList.allowInline(); }
535 bool allowEval() const { return m_sourceList.allowEval(); }
537 const String& text() { return m_text; }
540 CSPSourceList m_sourceList;
545 class CSPDirectiveList {
547 static PassOwnPtr<CSPDirectiveList> create(ContentSecurityPolicy*, const String&, ContentSecurityPolicy::HeaderType);
549 const String& header() const { return m_header; }
550 ContentSecurityPolicy::HeaderType headerType() const { return m_reportOnly ? ContentSecurityPolicy::ReportOnly : ContentSecurityPolicy::EnforcePolicy; }
552 bool allowJavaScriptURLs(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus) const;
553 bool allowInlineEventHandlers(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus) const;
554 bool allowInlineScript(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus) const;
555 bool allowInlineStyle(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus) const;
556 bool allowEval(PassRefPtr<ScriptCallStack>, ContentSecurityPolicy::ReportingStatus) const;
557 bool allowScriptNonce(const String& nonce, const String& contextURL, const WTF::OrdinalNumber& contextLine, const KURL&) const;
559 bool allowScriptFromSource(const KURL&, ContentSecurityPolicy::ReportingStatus) const;
560 bool allowObjectFromSource(const KURL&, ContentSecurityPolicy::ReportingStatus) const;
561 bool allowChildFrameFromSource(const KURL&, ContentSecurityPolicy::ReportingStatus) const;
562 bool allowImageFromSource(const KURL&, ContentSecurityPolicy::ReportingStatus) const;
563 bool allowStyleFromSource(const KURL&, ContentSecurityPolicy::ReportingStatus) const;
564 bool allowFontFromSource(const KURL&, ContentSecurityPolicy::ReportingStatus) const;
565 bool allowMediaFromSource(const KURL&, ContentSecurityPolicy::ReportingStatus) const;
566 bool allowConnectToSource(const KURL&, ContentSecurityPolicy::ReportingStatus) const;
568 void gatherReportURIs(DOMStringList&) const;
571 explicit CSPDirectiveList(ContentSecurityPolicy*);
573 void parse(const String&);
575 bool parseDirective(const UChar* begin, const UChar* end, String& name, String& value);
576 void parseReportURI(const String& name, const String& value);
577 void parseScriptNonce(const String& name, const String& value);
578 void addDirective(const String& name, const String& value);
579 void applySandboxPolicy(const String& name, const String& sandboxPolicy);
581 void setCSPDirective(const String& name, const String& value, OwnPtr<CSPDirective>&);
583 CSPDirective* operativeDirective(CSPDirective*) const;
584 void reportViolation(const String& directiveText, const String& consoleMessage, const KURL& blockedURL = KURL(), const String& contextURL = String(), const WTF::OrdinalNumber& contextLine = WTF::OrdinalNumber::beforeFirst(), PassRefPtr<ScriptCallStack> = 0) const;
586 bool checkEval(CSPDirective*) const;
587 bool checkInline(CSPDirective*) const;
588 bool checkNonce(const String&) const;
589 bool checkSource(CSPDirective*, const KURL&) const;
591 bool checkEvalAndReportViolation(CSPDirective*, const String& consoleMessage, const String& contextURL = String(), const WTF::OrdinalNumber& contextLine = WTF::OrdinalNumber::beforeFirst(), PassRefPtr<ScriptCallStack> = 0) const;
592 bool checkInlineAndReportViolation(CSPDirective*, const String& consoleMessage, const String& contextURL, const WTF::OrdinalNumber& contextLine) const;
593 bool checkNonceAndReportViolation(const String& nonce, const String& consoleMessage, const String& contextURL, const WTF::OrdinalNumber& contextLine) const;
594 bool checkSourceAndReportViolation(CSPDirective*, const KURL&, const String& type) const;
596 bool denyIfEnforcingPolicy() const { return m_reportOnly; }
598 ContentSecurityPolicy* m_policy;
602 bool m_haveSandboxPolicy;
604 OwnPtr<CSPDirective> m_defaultSrc;
605 OwnPtr<CSPDirective> m_scriptSrc;
606 OwnPtr<CSPDirective> m_objectSrc;
607 OwnPtr<CSPDirective> m_frameSrc;
608 OwnPtr<CSPDirective> m_imgSrc;
609 OwnPtr<CSPDirective> m_styleSrc;
610 OwnPtr<CSPDirective> m_fontSrc;
611 OwnPtr<CSPDirective> m_mediaSrc;
612 OwnPtr<CSPDirective> m_connectSrc;
614 Vector<KURL> m_reportURIs;
615 String m_scriptNonce;
618 CSPDirectiveList::CSPDirectiveList(ContentSecurityPolicy* policy)
620 , m_reportOnly(false)
621 , m_haveSandboxPolicy(false)
625 PassOwnPtr<CSPDirectiveList> CSPDirectiveList::create(ContentSecurityPolicy* policy, const String& header, ContentSecurityPolicy::HeaderType type)
627 OwnPtr<CSPDirectiveList> directives = adoptPtr(new CSPDirectiveList(policy));
628 directives->parse(header);
629 directives->m_header = header;
632 case ContentSecurityPolicy::ReportOnly:
633 directives->m_reportOnly = true;
634 return directives.release();
635 case ContentSecurityPolicy::EnforcePolicy:
636 ASSERT(!directives->m_reportOnly);
640 return directives.release();
643 void CSPDirectiveList::reportViolation(const String& directiveText, const String& consoleMessage, const KURL& blockedURL, const String& contextURL, const WTF::OrdinalNumber& contextLine, PassRefPtr<ScriptCallStack> callStack) const
645 String message = m_reportOnly ? "[Report Only] " + consoleMessage : consoleMessage;
646 m_policy->reportViolation(directiveText, message, blockedURL, m_reportURIs, m_header, contextURL, contextLine, callStack);
649 bool CSPDirectiveList::checkEval(CSPDirective* directive) const
651 return !directive || directive->allowEval();
654 bool CSPDirectiveList::checkInline(CSPDirective* directive) const
656 return !directive || directive->allowInline();
659 bool CSPDirectiveList::checkNonce(const String& nonce) const
661 return (m_scriptNonce.isNull()
662 || (!m_scriptNonce.isEmpty()
663 && nonce.stripWhiteSpace() == m_scriptNonce));
666 bool CSPDirectiveList::checkSource(CSPDirective* directive, const KURL& url) const
668 return !directive || directive->allows(url);
671 CSPDirective* CSPDirectiveList::operativeDirective(CSPDirective* directive) const
673 return directive ? directive : m_defaultSrc.get();
676 bool CSPDirectiveList::checkEvalAndReportViolation(CSPDirective* directive, const String& consoleMessage, const String& contextURL, const WTF::OrdinalNumber& contextLine, PassRefPtr<ScriptCallStack> callStack) const
678 if (checkEval(directive))
680 reportViolation(directive->text(), consoleMessage + "\"" + directive->text() + "\".\n", KURL(), contextURL, contextLine, callStack);
681 return denyIfEnforcingPolicy();
684 bool CSPDirectiveList::checkNonceAndReportViolation(const String& nonce, const String& consoleMessage, const String& contextURL, const WTF::OrdinalNumber& contextLine) const
686 if (checkNonce(nonce))
688 reportViolation(m_scriptNonce, consoleMessage + "\"script-nonce " + m_scriptNonce + "\".\n", KURL(), contextURL, contextLine);
689 return denyIfEnforcingPolicy();
692 bool CSPDirectiveList::checkInlineAndReportViolation(CSPDirective* directive, const String& consoleMessage, const String& contextURL, const WTF::OrdinalNumber& contextLine) const
694 if (checkInline(directive))
696 reportViolation(directive->text(), consoleMessage + "\"" + directive->text() + "\".\n", KURL(), contextURL, contextLine);
697 return denyIfEnforcingPolicy();
700 bool CSPDirectiveList::checkSourceAndReportViolation(CSPDirective* directive, const KURL& url, const String& type) const
702 if (checkSource(directive, url))
704 String verb = type == "connect" ? "connect to" : "load the";
705 reportViolation(directive->text(), "Refused to " + verb + " " + type + " '" + url.string() + "' because it violates the following Content Security Policy directive: \"" + directive->text() + "\".\n", url);
706 return denyIfEnforcingPolicy();
709 bool CSPDirectiveList::allowJavaScriptURLs(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const
711 DEFINE_STATIC_LOCAL(String, consoleMessage, ("Refused to execute JavaScript URL because it violates the following Content Security Policy directive: "));
712 if (reportingStatus == ContentSecurityPolicy::SendReport) {
713 return (checkInlineAndReportViolation(operativeDirective(m_scriptSrc.get()), consoleMessage, contextURL, contextLine)
714 && checkNonceAndReportViolation(String(), consoleMessage, contextURL, contextLine));
716 return (checkInline(operativeDirective(m_scriptSrc.get()))
717 && checkNonce(String()));
721 bool CSPDirectiveList::allowInlineEventHandlers(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const
723 DEFINE_STATIC_LOCAL(String, consoleMessage, ("Refused to execute inline event handler because it violates the following Content Security Policy directive: "));
724 if (reportingStatus == ContentSecurityPolicy::SendReport) {
725 return (checkInlineAndReportViolation(operativeDirective(m_scriptSrc.get()), consoleMessage, contextURL, contextLine)
726 && checkNonceAndReportViolation(String(), consoleMessage, contextURL, contextLine));
728 return (checkInline(operativeDirective(m_scriptSrc.get()))
729 && checkNonce(String()));
733 bool CSPDirectiveList::allowInlineScript(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const
735 DEFINE_STATIC_LOCAL(String, consoleMessage, ("Refused to execute inline script because it violates the following Content Security Policy directive: "));
736 return reportingStatus == ContentSecurityPolicy::SendReport ?
737 checkInlineAndReportViolation(operativeDirective(m_scriptSrc.get()), consoleMessage, contextURL, contextLine) :
738 checkInline(operativeDirective(m_scriptSrc.get()));
741 bool CSPDirectiveList::allowInlineStyle(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const
743 DEFINE_STATIC_LOCAL(String, consoleMessage, ("Refused to apply inline style because it violates the following Content Security Policy directive: "));
744 return reportingStatus == ContentSecurityPolicy::SendReport ?
745 checkInlineAndReportViolation(operativeDirective(m_styleSrc.get()), consoleMessage, contextURL, contextLine) :
746 checkInline(operativeDirective(m_styleSrc.get()));
749 bool CSPDirectiveList::allowEval(PassRefPtr<ScriptCallStack> callStack, ContentSecurityPolicy::ReportingStatus reportingStatus) const
751 DEFINE_STATIC_LOCAL(String, consoleMessage, ("Refused to evaluate script because it violates the following Content Security Policy directive: "));
752 return reportingStatus == ContentSecurityPolicy::SendReport ?
753 checkEvalAndReportViolation(operativeDirective(m_scriptSrc.get()), consoleMessage, String(), WTF::OrdinalNumber::beforeFirst(), callStack) :
754 checkEval(operativeDirective(m_scriptSrc.get()));
757 bool CSPDirectiveList::allowScriptNonce(const String& nonce, const String& contextURL, const WTF::OrdinalNumber& contextLine, const KURL& url) const
759 DEFINE_STATIC_LOCAL(String, consoleMessage, ("Refused to execute script because it violates the following Content Security Policy directive: "));
761 return checkNonceAndReportViolation(nonce, consoleMessage, contextURL, contextLine);
762 return checkNonceAndReportViolation(nonce, "Refused to load '" + url.string() + "' because it violates the following Content Security Policy directive: ", contextURL, contextLine);
765 bool CSPDirectiveList::allowScriptFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
767 DEFINE_STATIC_LOCAL(String, type, ("script"));
768 return reportingStatus == ContentSecurityPolicy::SendReport ?
769 checkSourceAndReportViolation(operativeDirective(m_scriptSrc.get()), url, type) :
770 checkSource(operativeDirective(m_scriptSrc.get()), url);
773 bool CSPDirectiveList::allowObjectFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
775 DEFINE_STATIC_LOCAL(String, type, ("object"));
776 if (url.isBlankURL())
778 return reportingStatus == ContentSecurityPolicy::SendReport ?
779 checkSourceAndReportViolation(operativeDirective(m_objectSrc.get()), url, type) :
780 checkSource(operativeDirective(m_objectSrc.get()), url);
783 bool CSPDirectiveList::allowChildFrameFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
785 DEFINE_STATIC_LOCAL(String, type, ("frame"));
786 if (url.isBlankURL())
788 return reportingStatus == ContentSecurityPolicy::SendReport ?
789 checkSourceAndReportViolation(operativeDirective(m_frameSrc.get()), url, type) :
790 checkSource(operativeDirective(m_frameSrc.get()), url);
793 bool CSPDirectiveList::allowImageFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
795 DEFINE_STATIC_LOCAL(String, type, ("image"));
796 return reportingStatus == ContentSecurityPolicy::SendReport ?
797 checkSourceAndReportViolation(operativeDirective(m_imgSrc.get()), url, type) :
798 checkSource(operativeDirective(m_imgSrc.get()), url);
801 bool CSPDirectiveList::allowStyleFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
803 DEFINE_STATIC_LOCAL(String, type, ("style"));
804 return reportingStatus == ContentSecurityPolicy::SendReport ?
805 checkSourceAndReportViolation(operativeDirective(m_styleSrc.get()), url, type) :
806 checkSource(operativeDirective(m_styleSrc.get()), url);
809 bool CSPDirectiveList::allowFontFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
811 DEFINE_STATIC_LOCAL(String, type, ("font"));
812 return reportingStatus == ContentSecurityPolicy::SendReport ?
813 checkSourceAndReportViolation(operativeDirective(m_fontSrc.get()), url, type) :
814 checkSource(operativeDirective(m_fontSrc.get()), url);
817 bool CSPDirectiveList::allowMediaFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
819 DEFINE_STATIC_LOCAL(String, type, ("media"));
820 return reportingStatus == ContentSecurityPolicy::SendReport ?
821 checkSourceAndReportViolation(operativeDirective(m_mediaSrc.get()), url, type) :
822 checkSource(operativeDirective(m_mediaSrc.get()), url);
825 bool CSPDirectiveList::allowConnectToSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
827 DEFINE_STATIC_LOCAL(String, type, ("connect"));
828 return reportingStatus == ContentSecurityPolicy::SendReport ?
829 checkSourceAndReportViolation(operativeDirective(m_connectSrc.get()), url, type) :
830 checkSource(operativeDirective(m_connectSrc.get()), url);
833 void CSPDirectiveList::gatherReportURIs(DOMStringList& list) const
835 for (size_t i = 0; i < m_reportURIs.size(); ++i)
836 list.append(m_reportURIs[i].string());
839 // policy = directive-list
840 // directive-list = [ directive *( ";" [ directive ] ) ]
842 void CSPDirectiveList::parse(const String& policy)
844 if (policy.isEmpty())
847 const UChar* position = policy.characters();
848 const UChar* end = position + policy.length();
850 while (position < end) {
851 const UChar* directiveBegin = position;
852 skipUntil(position, end, ';');
855 if (parseDirective(directiveBegin, position, name, value)) {
856 ASSERT(!name.isEmpty());
857 addDirective(name, value);
860 ASSERT(position == end || *position == ';');
861 skipExactly(position, end, ';');
865 // directive = *WSP [ directive-name [ WSP directive-value ] ]
866 // directive-name = 1*( ALPHA / DIGIT / "-" )
867 // directive-value = *( WSP / <VCHAR except ";"> )
869 bool CSPDirectiveList::parseDirective(const UChar* begin, const UChar* end, String& name, String& value)
871 ASSERT(name.isEmpty());
872 ASSERT(value.isEmpty());
874 const UChar* position = begin;
875 skipWhile<isASCIISpace>(position, end);
877 // Empty directive (e.g. ";;;"). Exit early.
881 const UChar* nameBegin = position;
882 skipWhile<isDirectiveNameCharacter>(position, end);
884 // The directive-name must be non-empty.
885 if (nameBegin == position) {
886 skipWhile<isNotASCIISpace>(position, end);
887 m_policy->reportUnrecognizedDirective(String(nameBegin, position - nameBegin));
891 name = String(nameBegin, position - nameBegin);
896 if (!skipExactly<isASCIISpace>(position, end)) {
897 skipWhile<isNotASCIISpace>(position, end);
898 m_policy->reportUnrecognizedDirective(String(nameBegin, position - nameBegin));
902 skipWhile<isASCIISpace>(position, end);
904 const UChar* valueBegin = position;
905 skipWhile<isDirectiveValueCharacter>(position, end);
910 // The directive-value may be empty.
911 if (valueBegin == position)
914 value = String(valueBegin, position - valueBegin);
918 void CSPDirectiveList::parseReportURI(const String& name, const String& value)
920 if (!m_reportURIs.isEmpty()) {
921 m_policy->reportDuplicateDirective(name);
924 const UChar* position = value.characters();
925 const UChar* end = position + value.length();
927 while (position < end) {
928 skipWhile<isASCIISpace>(position, end);
930 const UChar* urlBegin = position;
931 skipWhile<isNotASCIISpace>(position, end);
933 if (urlBegin < position) {
934 String url = String(urlBegin, position - urlBegin);
935 m_reportURIs.append(m_policy->completeURL(url));
940 void CSPDirectiveList::parseScriptNonce(const String& name, const String& value)
942 if (!m_scriptNonce.isNull()) {
943 m_policy->reportDuplicateDirective(name);
948 const UChar* position = value.characters();
949 const UChar* end = position + value.length();
951 skipWhile<isASCIISpace>(position, end);
952 const UChar* nonceBegin = position;
953 if (position == end) {
954 m_policy->reportInvalidNonce(String());
958 skipWhile<isNotASCIISpace>(position, end);
959 if (nonceBegin < position)
960 nonce = String(nonceBegin, position - nonceBegin);
962 // Trim off trailing whitespace: If we're not at the end of the string, log
964 skipWhile<isASCIISpace>(position, end);
965 if (position < end) {
966 m_policy->reportInvalidNonce(value);
969 m_scriptNonce = nonce;
972 void CSPDirectiveList::setCSPDirective(const String& name, const String& value, OwnPtr<CSPDirective>& directive)
975 m_policy->reportDuplicateDirective(name);
978 directive = adoptPtr(new CSPDirective(name, value, m_policy));
981 void CSPDirectiveList::applySandboxPolicy(const String& name, const String& sandboxPolicy)
983 if (m_haveSandboxPolicy) {
984 m_policy->reportDuplicateDirective(name);
987 m_haveSandboxPolicy = true;
988 m_policy->enforceSandboxFlags(SecurityContext::parseSandboxPolicy(sandboxPolicy));
991 void CSPDirectiveList::addDirective(const String& name, const String& value)
993 DEFINE_STATIC_LOCAL(String, defaultSrc, ("default-src"));
994 DEFINE_STATIC_LOCAL(String, scriptSrc, ("script-src"));
996 DEFINE_STATIC_LOCAL(String, scriptNonce, ("script-nonce"));
998 DEFINE_STATIC_LOCAL(String, objectSrc, ("object-src"));
999 DEFINE_STATIC_LOCAL(String, frameSrc, ("frame-src"));
1000 DEFINE_STATIC_LOCAL(String, imgSrc, ("img-src"));
1001 DEFINE_STATIC_LOCAL(String, styleSrc, ("style-src"));
1002 DEFINE_STATIC_LOCAL(String, fontSrc, ("font-src"));
1003 DEFINE_STATIC_LOCAL(String, mediaSrc, ("media-src"));
1004 DEFINE_STATIC_LOCAL(String, connectSrc, ("connect-src"));
1005 DEFINE_STATIC_LOCAL(String, sandbox, ("sandbox"));
1006 DEFINE_STATIC_LOCAL(String, reportURI, ("report-uri"));
1008 ASSERT(!name.isEmpty());
1010 if (equalIgnoringCase(name, defaultSrc))
1011 setCSPDirective(name, value, m_defaultSrc);
1012 else if (equalIgnoringCase(name, scriptSrc))
1013 setCSPDirective(name, value, m_scriptSrc);
1014 else if (equalIgnoringCase(name, objectSrc))
1015 setCSPDirective(name, value, m_objectSrc);
1016 else if (equalIgnoringCase(name, frameSrc))
1017 setCSPDirective(name, value, m_frameSrc);
1018 else if (equalIgnoringCase(name, imgSrc))
1019 setCSPDirective(name, value, m_imgSrc);
1020 else if (equalIgnoringCase(name, styleSrc))
1021 setCSPDirective(name, value, m_styleSrc);
1022 else if (equalIgnoringCase(name, fontSrc))
1023 setCSPDirective(name, value, m_fontSrc);
1024 else if (equalIgnoringCase(name, mediaSrc))
1025 setCSPDirective(name, value, m_mediaSrc);
1026 else if (equalIgnoringCase(name, connectSrc))
1027 setCSPDirective(name, value, m_connectSrc);
1028 else if (equalIgnoringCase(name, sandbox))
1029 applySandboxPolicy(name, value);
1030 else if (equalIgnoringCase(name, reportURI))
1031 parseReportURI(name, value);
1032 #if ENABLE(CSP_NEXT)
1033 else if (equalIgnoringCase(name, scriptNonce))
1034 parseScriptNonce(name, value);
1037 m_policy->reportUnrecognizedDirective(name);
1040 ContentSecurityPolicy::ContentSecurityPolicy(ScriptExecutionContext* scriptExecutionContext)
1041 : m_scriptExecutionContext(scriptExecutionContext)
1042 , m_overrideInlineStyleAllowed(false)
1046 ContentSecurityPolicy::~ContentSecurityPolicy()
1050 void ContentSecurityPolicy::copyStateFrom(const ContentSecurityPolicy* other)
1052 ASSERT(m_policies.isEmpty());
1053 for (CSPDirectiveListVector::const_iterator iter = other->m_policies.begin(); iter != other->m_policies.end(); ++iter)
1054 didReceiveHeader((*iter)->header(), (*iter)->headerType());
1057 void ContentSecurityPolicy::didReceiveHeader(const String& header, HeaderType type)
1059 // RFC2616, section 4.2 specifies that headers appearing multiple times can
1060 // be combined with a comma. Walk the header string, and parse each comma
1061 // separated chunk as a separate header.
1062 const UChar* begin = header.characters();
1063 const UChar* position = begin;
1064 const UChar* end = begin + header.length();
1065 while (position < end) {
1066 skipUntil(position, end, ',');
1068 // header1,header2 OR header1
1070 m_policies.append(CSPDirectiveList::create(this, String(begin, position - begin), type));
1072 // Skip the comma, and begin the next header from the current position.
1073 ASSERT(position == end || *position == ',');
1074 skipExactly(position, end, ',');
1078 if (!allowEval(0, SuppressReport))
1079 m_scriptExecutionContext->disableEval();
1082 void ContentSecurityPolicy::setOverrideAllowInlineStyle(bool value)
1084 m_overrideInlineStyleAllowed = value;
1087 const String& ContentSecurityPolicy::deprecatedHeader() const
1089 return m_policies.isEmpty() ? emptyString() : m_policies[0]->header();
1092 ContentSecurityPolicy::HeaderType ContentSecurityPolicy::deprecatedHeaderType() const
1094 return m_policies.isEmpty() ? EnforcePolicy : m_policies[0]->headerType();
1097 template<bool (CSPDirectiveList::*allowed)(PassRefPtr<ScriptCallStack>, ContentSecurityPolicy::ReportingStatus) const>
1098 bool isAllowedByAllWithCallStack(const CSPDirectiveListVector& policies, PassRefPtr<ScriptCallStack> callStack, ContentSecurityPolicy::ReportingStatus reportingStatus)
1100 for (size_t i = 0; i < policies.size(); ++i) {
1101 if (!(policies[i].get()->*allowed)(callStack, reportingStatus))
1107 template<bool (CSPDirectiveList::*allowed)(const String&, const WTF::OrdinalNumber&, ContentSecurityPolicy::ReportingStatus) const>
1108 bool isAllowedByAllWithContext(const CSPDirectiveListVector& policies, const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus)
1110 for (size_t i = 0; i < policies.size(); ++i) {
1111 if (!(policies[i].get()->*allowed)(contextURL, contextLine, reportingStatus))
1117 template<bool (CSPDirectiveList::*allowed)(const String&, const String&, const WTF::OrdinalNumber&, const KURL&) const>
1118 bool isAllowedByAllWithNonce(const CSPDirectiveListVector& policies, const String& nonce, const String& contextURL, const WTF::OrdinalNumber& contextLine, const KURL& url)
1120 for (size_t i = 0; i < policies.size(); ++i) {
1121 if (!(policies[i].get()->*allowed)(nonce, contextURL, contextLine, url))
1127 template<bool (CSPDirectiveList::*allowFromURL)(const KURL&, ContentSecurityPolicy::ReportingStatus) const>
1128 bool isAllowedByAllWithURL(const CSPDirectiveListVector& policies, const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus)
1130 if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol()))
1133 for (size_t i = 0; i < policies.size(); ++i) {
1134 if (!(policies[i].get()->*allowFromURL)(url, reportingStatus))
1140 bool ContentSecurityPolicy::allowJavaScriptURLs(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1142 return isAllowedByAllWithContext<&CSPDirectiveList::allowJavaScriptURLs>(m_policies, contextURL, contextLine, reportingStatus);
1145 bool ContentSecurityPolicy::allowInlineEventHandlers(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1147 return isAllowedByAllWithContext<&CSPDirectiveList::allowInlineEventHandlers>(m_policies, contextURL, contextLine, reportingStatus);
1150 bool ContentSecurityPolicy::allowInlineScript(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1152 return isAllowedByAllWithContext<&CSPDirectiveList::allowInlineScript>(m_policies, contextURL, contextLine, reportingStatus);
1155 bool ContentSecurityPolicy::allowInlineStyle(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1157 if (m_overrideInlineStyleAllowed)
1159 return isAllowedByAllWithContext<&CSPDirectiveList::allowInlineStyle>(m_policies, contextURL, contextLine, reportingStatus);
1162 bool ContentSecurityPolicy::allowEval(PassRefPtr<ScriptCallStack> callStack, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1164 return isAllowedByAllWithCallStack<&CSPDirectiveList::allowEval>(m_policies, callStack, reportingStatus);
1167 bool ContentSecurityPolicy::allowScriptNonce(const String& nonce, const String& contextURL, const WTF::OrdinalNumber& contextLine, const KURL& url) const
1169 return isAllowedByAllWithNonce<&CSPDirectiveList::allowScriptNonce>(m_policies, nonce, contextURL, contextLine, url);
1172 bool ContentSecurityPolicy::allowScriptFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1174 return isAllowedByAllWithURL<&CSPDirectiveList::allowScriptFromSource>(m_policies, url, reportingStatus);
1177 bool ContentSecurityPolicy::allowObjectFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1179 return isAllowedByAllWithURL<&CSPDirectiveList::allowObjectFromSource>(m_policies, url, reportingStatus);
1182 bool ContentSecurityPolicy::allowChildFrameFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1184 return isAllowedByAllWithURL<&CSPDirectiveList::allowChildFrameFromSource>(m_policies, url, reportingStatus);
1187 bool ContentSecurityPolicy::allowImageFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1189 return isAllowedByAllWithURL<&CSPDirectiveList::allowImageFromSource>(m_policies, url, reportingStatus);
1192 bool ContentSecurityPolicy::allowStyleFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1194 return isAllowedByAllWithURL<&CSPDirectiveList::allowStyleFromSource>(m_policies, url, reportingStatus);
1197 bool ContentSecurityPolicy::allowFontFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1199 return isAllowedByAllWithURL<&CSPDirectiveList::allowFontFromSource>(m_policies, url, reportingStatus);
1202 bool ContentSecurityPolicy::allowMediaFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1204 return isAllowedByAllWithURL<&CSPDirectiveList::allowMediaFromSource>(m_policies, url, reportingStatus);
1207 bool ContentSecurityPolicy::allowConnectToSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1209 return isAllowedByAllWithURL<&CSPDirectiveList::allowConnectToSource>(m_policies, url, reportingStatus);
1212 bool ContentSecurityPolicy::isActive() const
1214 return !m_policies.isEmpty();
1217 void ContentSecurityPolicy::gatherReportURIs(DOMStringList& list) const
1219 for (size_t i = 0; i < m_policies.size(); ++i)
1220 m_policies[i].get()->gatherReportURIs(list);
1223 SecurityOrigin* ContentSecurityPolicy::securityOrigin() const
1225 return m_scriptExecutionContext->securityOrigin();
1228 const KURL& ContentSecurityPolicy::url() const
1230 return m_scriptExecutionContext->url();
1233 KURL ContentSecurityPolicy::completeURL(const String& url) const
1235 return m_scriptExecutionContext->completeURL(url);
1238 void ContentSecurityPolicy::enforceSandboxFlags(SandboxFlags mask) const
1240 m_scriptExecutionContext->enforceSandboxFlags(mask);
1243 void ContentSecurityPolicy::reportViolation(const String& directiveText, const String& consoleMessage, const KURL& blockedURL, const Vector<KURL>& reportURIs, const String& header, const String& contextURL, const WTF::OrdinalNumber& contextLine, PassRefPtr<ScriptCallStack> callStack) const
1245 logToConsole(consoleMessage, contextURL, contextLine, callStack);
1247 if (reportURIs.isEmpty())
1250 // FIXME: Support sending reports from worker.
1251 if (!m_scriptExecutionContext->isDocument())
1254 Document* document = static_cast<Document*>(m_scriptExecutionContext);
1255 Frame* frame = document->frame();
1259 // We need to be careful here when deciding what information to send to the
1260 // report-uri. Currently, we send only the current document's URL and the
1261 // directive that was violated. The document's URL is safe to send because
1262 // it's the document itself that's requesting that it be sent. You could
1263 // make an argument that we shouldn't send HTTPS document URLs to HTTP
1264 // report-uris (for the same reasons that we supress the Referer in that
1265 // case), but the Referer is sent implicitly whereas this request is only
1266 // sent explicitly. As for which directive was violated, that's pretty
1267 // harmless information.
1269 RefPtr<InspectorObject> cspReport = InspectorObject::create();
1270 cspReport->setString("document-uri", document->url().strippedForUseAsReferrer());
1271 String referrer = document->referrer();
1272 if (!referrer.isEmpty())
1273 cspReport->setString("referrer", referrer);
1274 if (!directiveText.isEmpty())
1275 cspReport->setString("violated-directive", directiveText);
1276 cspReport->setString("original-policy", header);
1277 if (blockedURL.isValid())
1278 cspReport->setString("blocked-uri", document->securityOrigin()->canRequest(blockedURL) ? blockedURL.strippedForUseAsReferrer() : SecurityOrigin::create(blockedURL)->toString());
1280 RefPtr<InspectorObject> reportObject = InspectorObject::create();
1281 reportObject->setObject("csp-report", cspReport.release());
1283 RefPtr<FormData> report = FormData::create(reportObject->toJSONString().utf8());
1285 for (size_t i = 0; i < reportURIs.size(); ++i)
1286 PingLoader::reportContentSecurityPolicyViolation(frame, reportURIs[i], report);
1289 void ContentSecurityPolicy::reportUnrecognizedDirective(const String& name) const
1291 String message = makeString("Unrecognized Content-Security-Policy directive '", name, "'.\n");
1292 logToConsole(message);
1295 void ContentSecurityPolicy::reportDuplicateDirective(const String& name) const
1297 String message = makeString("Ignoring duplicate Content-Security-Policy directive '", name, "'.\n");
1298 logToConsole(message);
1301 void ContentSecurityPolicy::reportInvalidNonce(const String& nonce) const
1303 String message = makeString("Ignoring invalid Content Security Policy script nonce: '", nonce, "'.\n");
1304 logToConsole(message);
1307 void ContentSecurityPolicy::reportIgnoredPathComponent(const String& directiveName, const String& completeSource, const String& path) const
1309 String message = makeString("The source list for Content Security Policy directive '", directiveName, "' contains the source '", completeSource, "'. Content Security Policy 1.0 supports only schemes, hosts, and ports. Paths might be supported in the future, but for now, '", path, "' is being ignored. Be careful.");
1310 logToConsole(message);
1313 void ContentSecurityPolicy::logToConsole(const String& message, const String& contextURL, const WTF::OrdinalNumber& contextLine, PassRefPtr<ScriptCallStack> callStack) const
1315 #if ENABLE(TIZEN_CSP)
1316 TIZEN_LOGI("message(%s)", message.utf8().data());
1318 m_scriptExecutionContext->addConsoleMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, message, contextURL, contextLine.oneBasedInt(), callStack);