diff options
author | Steve Block <steveblock@google.com> | 2011-06-08 08:26:01 -0700 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2011-06-08 08:26:01 -0700 |
commit | 3742ac093d35d923c81693096ab6671e9b147700 (patch) | |
tree | c2add9100f789dad45ef1ec5328bddde02c47a4c /Source/WebCore/page/ContentSecurityPolicy.cpp | |
parent | 901401d90459bc22580842455d4588b9a697514d (diff) | |
parent | e5926f4a0d6adc9ad4a75824129f117181953560 (diff) | |
download | external_webkit-3742ac093d35d923c81693096ab6671e9b147700.zip external_webkit-3742ac093d35d923c81693096ab6671e9b147700.tar.gz external_webkit-3742ac093d35d923c81693096ab6671e9b147700.tar.bz2 |
Merge changes I55c6d71a,Ifb3277d4,Ia1b847a2,I7ba9cf3f,Ida2b2a8a,I1280ec90,I72f818d5,I2e3b588b,I9a4e6289,Ia724c78b,Icd8612c8,Ie31b15d7,Ie125edae,I77941a88,I89dae78b,I3516e5ca,I1a4c17b5,I2c4ecc1a,I9c8e6537,Ifac13115,Ie1f80e09,Ia541ed77,I60ce9d78
* changes:
Merge WebKit at r82507: Update ThirdPartyProject.prop
Merge WebKit at r82507: Cherry-pick change r88166 to add INSPECTOR guards to ScriptProfiler
Merge WebKit at r82507: Work around a V8 bug
Merge WebKit at r82507: JNIType renamed to JavaType
Merge WebKit at r82507: IconDatabaseClient interface expanded
Merge WebKit at r82507: Don't use new loss-free code path in HTMLCanvasElement::toDataURL()
Merge WebKit at r82507: IcondDatabaseBase::iconForPageURL() renamed
Merge WebKit at r82507: IconDatabaseBase::Open() signature changed
Merge WebKit at r82507: Node::isContentEditable() renamed
Merge WebKit at r82507: Use icon database through IconDatabaseBase
Merge WebKit at r82507: toInputElement() is now a member of Node
Merge WebKit at r82507: FrameLoaderClient::objectContentType() signature changed
Merge WebKit at r82507: StringImpl::computeHash() removed
Merge WebKit at r82507: Stub out FontPlatformData::setOrientation()
Merge WebKit at r82507: Path::strokeBoundingRect() is now const
Merge WebKit at r82507: Add missing UnusedParam.h include in ApplicationCacheGroup.cpp
Merge WebKit at r82507: Continue to use Android's version of FontPlatformData.h
Merge WebKit at r82507: Update signature of FontCustomPlatformData::fontPlatformData()
Merge WebKit at r82507: Fix conflicts due to JNI refactoring
Merge WebKit at r82507: Fix conflicts due to new StorageTracker
Merge WebKit at r82507: Fix conflicts
Merge WebKit at r82507: Fix makefiles
Merge WebKit at r82507: Initial merge by git
Diffstat (limited to 'Source/WebCore/page/ContentSecurityPolicy.cpp')
-rw-r--r-- | Source/WebCore/page/ContentSecurityPolicy.cpp | 488 |
1 files changed, 429 insertions, 59 deletions
diff --git a/Source/WebCore/page/ContentSecurityPolicy.cpp b/Source/WebCore/page/ContentSecurityPolicy.cpp index 97cd447..6bcf99c 100644 --- a/Source/WebCore/page/ContentSecurityPolicy.cpp +++ b/Source/WebCore/page/ContentSecurityPolicy.cpp @@ -25,28 +25,390 @@ #include "config.h" #include "ContentSecurityPolicy.h" + #include "Document.h" +#include "NotImplemented.h" +#include "SecurityOrigin.h" namespace WebCore { -class CSPDirective { +// Normally WebKit uses "static" for internal linkage, but using "static" for +// these functions causes a compile error because these functions are used as +// template parameters. +namespace { + +bool isDirectiveNameCharacter(UChar c) +{ + return isASCIIAlphanumeric(c) || c == '-'; +} + +bool isDirectiveValueCharacter(UChar c) +{ + return isASCIISpace(c) || (c >= 0x21 && c <= 0x7e); // Whitespace + VCHAR +} + +bool isSourceCharacter(UChar c) +{ + return !isASCIISpace(c); +} + +bool isHostCharacter(UChar c) +{ + return isASCIIAlphanumeric(c) || c == '-'; +} + +bool isSchemeContinuationCharacter(UChar c) +{ + return isASCIIAlphanumeric(c) || c == '+' || c == '-' || c == '.'; +} + +} // namespace + +static bool skipExactly(const UChar*& position, const UChar* end, UChar delimiter) +{ + if (position < end && *position == delimiter) { + ++position; + return true; + } + return false; +} + +template<bool characterPredicate(UChar)> +static bool skipExactly(const UChar*& position, const UChar* end) +{ + if (position < end && characterPredicate(*position)) { + ++position; + return true; + } + return false; +} + +static void skipUtil(const UChar*& position, const UChar* end, UChar delimiter) +{ + while (position < end && *position != delimiter) + ++position; +} + +template<bool characterPredicate(UChar)> +static void skipWhile(const UChar*& position, const UChar* end) +{ + while (position < end && characterPredicate(*position)) + ++position; +} + +class CSPSource { public: - explicit CSPDirective(const String& value) - : m_value(value) + CSPSource(const String& scheme, const String& host, int port, bool hostHasWildcard, bool portHasWildcard) + : m_scheme(scheme) + , m_host(host) + , m_port(port) + , m_hostHasWildcard(hostHasWildcard) + , m_portHasWildcard(portHasWildcard) + { + } + + bool matches(const KURL& url) const + { + if (!schemeMatches(url)) + return false; + if (isSchemeOnly()) + return true; + return hostMatches(url) && portMatches(url); + } + +private: + bool schemeMatches(const KURL& url) const + { + return equalIgnoringCase(url.protocol(), m_scheme); + } + + bool hostMatches(const KURL& url) const { + if (m_hostHasWildcard) + notImplemented(); + + return equalIgnoringCase(url.host(), m_host); } - bool allows(const KURL&) + bool portMatches(const KURL& url) const { + if (m_portHasWildcard) + return true; + + // FIXME: Handle explicit default ports correctly. + return url.port() == m_port; + } + + bool isSchemeOnly() const { return m_host.isEmpty(); } + + String m_scheme; + String m_host; + int m_port; + + bool m_hostHasWildcard; + bool m_portHasWildcard; +}; + +class CSPSourceList { +public: + explicit CSPSourceList(SecurityOrigin*); + + void parse(const String&); + bool matches(const KURL&); + +private: + void parse(const UChar* begin, const UChar* end); + + bool parseSource(const UChar* begin, const UChar* end, String& scheme, String& host, int& port, bool& hostHasWildcard, bool& portHasWildcard); + bool parseScheme(const UChar* begin, const UChar* end, String& scheme); + bool parseHost(const UChar* begin, const UChar* end, String& host, bool& hostHasWildcard); + bool parsePort(const UChar* begin, const UChar* end, int& port, bool& portHasWildcard); + + void addSourceSelf(); + + SecurityOrigin* m_origin; + Vector<CSPSource> m_list; +}; + +CSPSourceList::CSPSourceList(SecurityOrigin* origin) + : m_origin(origin) +{ +} + +void CSPSourceList::parse(const String& value) +{ + parse(value.characters(), value.characters() + value.length()); +} + +bool CSPSourceList::matches(const KURL& url) +{ + for (size_t i = 0; i < m_list.size(); ++i) { + if (m_list[i].matches(url)) + return true; + } + return false; +} + +// source-list = *WSP [ source *( 1*WSP source ) *WSP ] +// / *WSP "'none'" *WSP +// +void CSPSourceList::parse(const UChar* begin, const UChar* end) +{ + const UChar* position = begin; + + bool isFirstSourceInList = true; + while (position < end) { + skipWhile<isASCIISpace>(position, end); + const UChar* beginSource = position; + skipWhile<isSourceCharacter>(position, end); + + if (isFirstSourceInList && equalIgnoringCase("'none'", beginSource, position - beginSource)) + return; // We represent 'none' as an empty m_list. + isFirstSourceInList = false; + + String scheme, host; + int port = 0; + bool hostHasWildcard = false; + bool portHasWildcard = false; + + if (parseSource(beginSource, position, scheme, host, port, hostHasWildcard, portHasWildcard)) { + if (scheme.isEmpty()) + scheme = m_origin->protocol(); + m_list.append(CSPSource(scheme, host, port, hostHasWildcard, portHasWildcard)); + } + + ASSERT(position == end || isASCIISpace(*position)); + } +} + +// source = scheme ":" +// / ( [ scheme "://" ] host [ port ] ) +// / "'self'" +// +bool CSPSourceList::parseSource(const UChar* begin, const UChar* end, + String& scheme, String& host, int& port, + bool& hostHasWildcard, bool& portHasWildcard) +{ + if (begin == end) + return false; + + if (equalIgnoringCase("'self'", begin, end - begin)) { + addSourceSelf(); return false; } + const UChar* position = begin; + + const UChar* beginHost = begin; + skipUtil(position, end, ':'); + + if (position == end) { + // This must be a host-only source. + if (!parseHost(beginHost, position, host, hostHasWildcard)) + return false; + return true; + } + + if (end - position == 1) { + ASSERT(*position == ':'); + // This must be a scheme-only source. + if (!parseScheme(begin, position, scheme)) + return false; + return true; + } + + ASSERT(end - position >= 2); + if (position[1] == '/') { + if (!parseScheme(begin, position, scheme) + || !skipExactly(position, end, ':') + || !skipExactly(position, end, '/') + || !skipExactly(position, end, '/')) + return false; + beginHost = position; + skipUtil(position, end, ':'); + } + + if (position == beginHost) + return false; + + if (!parseHost(beginHost, position, host, hostHasWildcard)) + return false; + + if (position == end) { + port = 0; + return true; + } + + if (!skipExactly(position, end, ':')) + ASSERT_NOT_REACHED(); + + if (!parsePort(position, end, port, portHasWildcard)) + return false; + + return true; +} + +// ; <scheme> production from RFC 3986 +// scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) +// +bool CSPSourceList::parseScheme(const UChar* begin, const UChar* end, String& scheme) +{ + ASSERT(begin <= end); + ASSERT(scheme.isEmpty()); + + if (begin == end) + return false; + + const UChar* position = begin; + + if (!skipExactly<isASCIIAlpha>(position, end)) + return false; + + skipWhile<isSchemeContinuationCharacter>(position, end); + + if (position != end) + return false; + + scheme = String(begin, end - begin); + return true; +} + +// host = [ "*." ] 1*host-char *( "." 1*host-char ) +// / "*" +// host-char = ALPHA / DIGIT / "-" +// +bool CSPSourceList::parseHost(const UChar* begin, const UChar* end, String& host, bool& hostHasWildcard) +{ + ASSERT(begin <= end); + ASSERT(host.isEmpty()); + ASSERT(!hostHasWildcard); + + if (begin == end) + return false; + + const UChar* position = begin; + + if (skipExactly(position, end, '*')) { + hostHasWildcard = true; + + if (position == end) + return true; + + if (!skipExactly(position, end, '.')) + return false; + } + + const UChar* hostBegin = position; + + while (position < end) { + if (!skipExactly<isHostCharacter>(position, end)) + return false; + + skipWhile<isHostCharacter>(position, end); + + if (position < end && !skipExactly(position, end, '.')) + return false; + } + + ASSERT(position == end); + host = String(hostBegin, end - hostBegin); + return true; +} + +// port = ":" ( 1*DIGIT / "*" ) +// +bool CSPSourceList::parsePort(const UChar* begin, const UChar* end, int& port, bool& portHasWildcard) +{ + ASSERT(begin <= end); + ASSERT(!port); + ASSERT(!portHasWildcard); + + if (begin == end) + return false; + + if (end - begin == 1 && *begin == '*') { + port = 0; + portHasWildcard = true; + return true; + } + + const UChar* position = begin; + skipWhile<isASCIIDigit>(position, end); + + if (position != end) + return false; + + bool ok; + port = charactersToIntStrict(begin, end - begin, &ok); + return ok; +} + +void CSPSourceList::addSourceSelf() +{ + // FIXME: Inherit the scheme, host, and port from the current URL. + notImplemented(); +} + +class CSPDirective { +public: + CSPDirective(const String& value, SecurityOrigin* origin) + : m_sourceList(origin) + { + m_sourceList.parse(value); + } + + bool allows(const KURL& url) + { + return m_sourceList.matches(url); + } + private: - String m_value; + CSPSourceList m_sourceList; }; -ContentSecurityPolicy::ContentSecurityPolicy() +ContentSecurityPolicy::ContentSecurityPolicy(SecurityOrigin* origin) : m_havePolicy(false) + , m_origin(origin) { } @@ -68,11 +430,19 @@ bool ContentSecurityPolicy::allowJavaScriptURLs() const return !m_scriptSrc; } -bool ContentSecurityPolicy::canLoadExternalScriptFromSrc(const String& url) const +bool ContentSecurityPolicy::allowInlineEventHandlers() const +{ + return !m_scriptSrc; +} + +bool ContentSecurityPolicy::allowScriptFromSource(const KURL& url) const { - return !m_scriptSrc || m_scriptSrc->allows(KURL(ParsedURLString, url)); + return !m_scriptSrc || m_scriptSrc->allows(url); } +// policy = directive-list +// directive-list = [ directive *( ";" [ directive ] ) ] +// void ContentSecurityPolicy::parse(const String& policy) { ASSERT(!m_havePolicy); @@ -80,75 +450,75 @@ void ContentSecurityPolicy::parse(const String& policy) if (policy.isEmpty()) return; - const UChar* pos = policy.characters(); - const UChar* end = pos + policy.length(); + const UChar* position = policy.characters(); + const UChar* end = position + policy.length(); - while (pos < end) { - Vector<UChar, 32> name; - Vector<UChar, 64> value; + while (position < end) { + const UChar* directiveBegin = position; + skipUtil(position, end, ';'); - parseDirective(pos, end, name, value); - if (name.isEmpty()) - continue; + String name, value; + if (parseDirective(directiveBegin, position, name, value)) { + ASSERT(!name.isEmpty()); + addDirective(name, value); + } - // We use a copy here instead of String::adopt because we expect - // the name and the value to be relatively short, so the copy will - // be cheaper than the extra malloc. - emitDirective(String(name), String(value)); + ASSERT(position == end || *position == ';'); + skipExactly(position, end, ';'); } } -void ContentSecurityPolicy::parseDirective(const UChar*& pos, const UChar* end, Vector<UChar, 32>& name, Vector<UChar, 64>& value) +// directive = *WSP [ directive-name [ WSP directive-value ] ] +// directive-name = 1*( ALPHA / DIGIT / "-" ) +// directive-value = *( WSP / <VCHAR except ";"> ) +// +bool ContentSecurityPolicy::parseDirective(const UChar* begin, const UChar* end, String& name, String& value) { - ASSERT(pos < end); ASSERT(name.isEmpty()); ASSERT(value.isEmpty()); - enum { - BeforeDirectiveName, - DirectiveName, - AfterDirectiveName, - DirectiveValue, - } state = BeforeDirectiveName; - - while (pos < end) { - UChar currentCharacter = *pos++; - switch (state) { - case BeforeDirectiveName: - if (isASCIISpace(currentCharacter)) - continue; - state = DirectiveName; - // Fall through. - case DirectiveName: - if (!isASCIISpace(currentCharacter)) { - name.append(currentCharacter); - continue; - } - state = AfterDirectiveName; - // Fall through. - case AfterDirectiveName: - if (isASCIISpace(currentCharacter)) - continue; - state = DirectiveValue; - // Fall through. - case DirectiveValue: - if (currentCharacter != ';') { - value.append(currentCharacter); - continue; - } - return; - } - } + const UChar* position = begin; + skipWhile<isASCIISpace>(position, end); + + const UChar* nameBegin = position; + skipWhile<isDirectiveNameCharacter>(position, end); + + // The directive-name must be non-empty. + if (nameBegin == position) + return false; + + name = String(nameBegin, position - nameBegin); + + if (position == end) + return true; + + if (!skipExactly<isASCIISpace>(position, end)) + return false; + + skipWhile<isASCIISpace>(position, end); + + const UChar* valueBegin = position; + skipWhile<isDirectiveValueCharacter>(position, end); + + if (position != end) + return false; + + // The directive-value may be empty. + if (valueBegin == position) + return true; + + value = String(valueBegin, position - valueBegin); + return true; } -void ContentSecurityPolicy::emitDirective(const String& name, const String& value) +void ContentSecurityPolicy::addDirective(const String& name, const String& value) { DEFINE_STATIC_LOCAL(String, scriptSrc, ("script-src")); ASSERT(!name.isEmpty()); if (!m_scriptSrc && equalIgnoringCase(name, scriptSrc)) - m_scriptSrc = adoptPtr(new CSPDirective(value)); + m_scriptSrc = adoptPtr(new CSPDirective(value, m_origin.get())); } } |