Index: third_party/WebKit/Source/core/frame/csp/CSPSource.cpp |
diff --git a/third_party/WebKit/Source/core/frame/csp/CSPSource.cpp b/third_party/WebKit/Source/core/frame/csp/CSPSource.cpp |
index 8f15383227bf8708b37b23f6073c0cd5387bf1cc..573fac8380e14e452b67d8df50bc046345911fbc 100644 |
--- a/third_party/WebKit/Source/core/frame/csp/CSPSource.cpp |
+++ b/third_party/WebKit/Source/core/frame/csp/CSPSource.cpp |
@@ -42,17 +42,28 @@ bool CSPSource::matches(const KURL& url, |
bool CSPSource::schemeMatches(const KURL& url) const { |
if (m_scheme.isEmpty()) |
return m_policy->protocolMatchesSelf(url); |
- if (equalIgnoringCase(m_scheme, "http")) |
- return equalIgnoringCase(url.protocol(), "http") || |
- equalIgnoringCase(url.protocol(), "https"); |
- if (equalIgnoringCase(m_scheme, "ws")) |
- return equalIgnoringCase(url.protocol(), "ws") || |
- equalIgnoringCase(url.protocol(), "wss"); |
- return equalIgnoringCase(url.protocol(), m_scheme); |
+ return schemeMatches(url.protocol()); |
+} |
+ |
+bool CSPSource::schemeMatches(const String& protocol) const { |
+ // TODO(amalika): DCHECK schemes are lower case & remove calls to |
+ // equalIgnoringCase |
+ if (equalIgnoringCase(m_scheme, "http")) { |
+ return equalIgnoringCase(protocol, "http") || |
+ equalIgnoringCase(protocol, "https"); |
+ } |
+ if (equalIgnoringCase(m_scheme, "ws")) { |
+ return equalIgnoringCase(protocol, "ws") || |
+ equalIgnoringCase(protocol, "wss"); |
+ } |
+ return equalIgnoringCase(protocol, m_scheme); |
} |
bool CSPSource::hostMatches(const KURL& url) const { |
- const String& host = url.host(); |
+ return hostMatches(url.host()); |
+} |
+ |
+bool CSPSource::hostMatches(const String& host) const { |
Document* document = m_policy->document(); |
bool match; |
@@ -75,10 +86,14 @@ bool CSPSource::hostMatches(const KURL& url) const { |
} |
bool CSPSource::pathMatches(const KURL& url) const { |
+ return pathMatches(url.path()); |
+} |
+ |
+bool CSPSource::pathMatches(const String& urlPath) const { |
if (m_path.isEmpty()) |
return true; |
- String path = decodeURLEscapeSequences(url.path()); |
+ String path = decodeURLEscapeSequences(urlPath); |
if (m_path.endsWith("/")) |
return path.startsWith(m_path); |
@@ -87,28 +102,208 @@ bool CSPSource::pathMatches(const KURL& url) const { |
} |
bool CSPSource::portMatches(const KURL& url) const { |
+ return portMatches(url.port(), url.protocol()); |
+} |
+ |
+bool CSPSource::portMatches(int port, const String& protocol) const { |
if (m_portWildcard == HasWildcard) |
return true; |
- int port = url.port(); |
- |
if (port == m_port) |
return true; |
if (m_port == 80 && |
- (port == 443 || |
- (port == 0 && defaultPortForProtocol(url.protocol()) == 443))) |
+ (port == 443 || (port == 0 && defaultPortForProtocol(protocol) == 443))) |
return true; |
if (!port) |
- return isDefaultPortForProtocol(m_port, url.protocol()); |
+ return isDefaultPortForProtocol(m_port, protocol); |
if (!m_port) |
- return isDefaultPortForProtocol(port, url.protocol()); |
+ return isDefaultPortForProtocol(port, protocol); |
return false; |
} |
+CSPSource* CSPSource::getPreferredCSPSource(CSPSource* other) { |
+ // This function assumes that schemes, hosts, ports and paths match between |
+ // the two gives sources. CSPSource that has stronger protocol(https/wss) or |
+ // more information (path or port specified) will be returned. |
+ // If nullptr is returned, that means there is no preference between 'this' |
+ // and 'other' CSPSources. If new CSPSource is returned then that means we |
+ // found a match between 'this' and 'other' but neither one is strongly |
+ // stricter than the other so the combination of two has to be created. |
+ bool preferABasedOnScheme = false, preferBBasedOnScheme = false; |
+ // If the schemes match but their lengths are not equal, that means one of the |
+ // schemes is 'https' or 'wss' |
+ if (m_scheme.length() != other->m_scheme.length()) { |
+ String aScheme = m_scheme.lower(); |
+ preferABasedOnScheme = |
+ aScheme.length() > 3 ? (aScheme == "https") : (aScheme == "wss"); |
+ preferBBasedOnScheme = !preferABasedOnScheme; |
+ } |
+ |
+ bool preferABasedOnPort = (m_port && !other->m_port), |
+ preferBBasedOnPort = (!m_port && other->m_port); |
+ bool preferABasedOnPath = (!(m_path.isEmpty() || m_path == "/") && |
+ (other->m_path.isEmpty() || other->m_path == "/")), |
+ preferBBasedOnPath = |
+ ((m_path.isEmpty() || m_path == "/") && |
+ !(other->m_path.isEmpty() || other->m_path == "/")); |
+ |
+ bool preferA = |
+ preferABasedOnPort || preferABasedOnPath || preferABasedOnScheme; |
+ bool preferB = |
+ preferBBasedOnPort || preferBBasedOnPath || preferBBasedOnScheme; |
+ |
+ if (preferA & preferB) { |
+ // If we have conflicting preferences, we have to create a new sources. |
+ String scheme = preferBBasedOnScheme ? other->m_path : m_path; |
+ String host = (m_hostWildcard == HasWildcard) ? other->m_host : m_host; |
+ String path = preferBBasedOnPath ? other->m_path : m_path; |
+ int port = preferBBasedOnPort ? other->m_port : m_port; |
+ WildcardDisposition hostWildcard = (m_hostWildcard == HasWildcard) |
+ ? other->m_hostWildcard |
+ : m_hostWildcard; |
+ WildcardDisposition portWildcard = (m_portWildcard == HasWildcard) |
+ ? other->m_portWildcard |
+ : m_portWildcard; |
+ return new CSPSource(m_policy, scheme, host, port, path, hostWildcard, |
+ portWildcard); |
+ } |
+ |
+ if (preferA || preferB) |
+ return preferA ? this : other; |
+ |
+ // nullptr signifies that there is not preference between 'this' or 'other'. |
+ return nullptr; |
+} |
+ |
+CSPSource* CSPSource::getPreferredCSPSourceBasedOnWildcards( |
+ CSPSource* other, |
+ CSPSource* preferredCSPSource) { |
+ if (preferredCSPSource != other) |
+ return this; |
+ // We mostly want preferredCSPSource except for wildcards. |
+ return new CSPSource(m_policy, preferredCSPSource->m_scheme, |
+ preferredCSPSource->m_host, preferredCSPSource->m_port, |
+ preferredCSPSource->m_path, m_hostWildcard, |
+ m_portWildcard); |
+} |
+ |
+CSPSource* CSPSource::getPreferredCSPSourceBasedOnEmptySchemes( |
+ CSPSource* other, |
+ CSPSource* preferredCSPSource) { |
+ if (preferredCSPSource == other && !isSchemeOnly()) { |
+ return other->getPreferredCSPSourceBasedOnEmptySchemes(this, |
+ preferredCSPSource); |
+ } |
+ if (preferredCSPSource == this && !other->isSchemeOnly()) { |
+ // Case when scheme of 'this' is simply 'https://' and 'other' is |
+ // 'http://example.com' |
+ // So that we need to upgrade scheme of 'other'. |
+ return new CSPSource(other->m_policy, m_scheme, other->m_host, |
+ other->m_port, other->m_path, other->m_hostWildcard, |
+ other->m_portWildcard); |
+ } |
+ if (preferredCSPSource) |
+ return preferredCSPSource; |
+ |
+ return isSchemeOnly() && !other->isSchemeOnly() ? other : this; |
+} |
+ |
+CSPSource* CSPSource::getNormalized(CSPSource* other) { |
+ // Preference always goes to returning 'this' if it is at least as strict as |
+ // 'other'. |
+ // If 'other' is returned, that means 'other' is definitely stricter than |
+ // 'this'. |
+ // If nullptr is returned, that means there is no similarity between 'this' |
+ // and 'other' (unmatching hosts). |
+ // If new CSPSource is returned then that means we found a match between |
+ // 'this' and 'other' but neither one is strongly stricter than the other so |
+ // the combination of two has to be created. |
+ bool schemesMatch = schemeMatches(other->m_scheme) || |
+ (equalIgnoringCase(m_scheme, "https") && |
+ equalIgnoringCase(other->m_scheme, "http")) || |
+ (equalIgnoringCase(m_scheme, "wss") && |
+ equalIgnoringCase(other->m_scheme, "ws")); |
+ bool schemesOnly = isSchemeOnly() || other->isSchemeOnly(); |
+ bool hostsMatch = |
+ schemesOnly || equalIgnoringCase(m_host, other->m_host) || |
+ (m_hostWildcard == HasWildcard && hostMatches(other->m_host)) || |
+ (other->m_hostWildcard == HasWildcard && other->hostMatches(m_host)); |
+ bool portsMatch = schemesOnly || m_portWildcard == HasWildcard || |
+ other->m_portWildcard == HasWildcard || |
+ portMatches(other->m_port, other->m_scheme); |
+ if (!schemesMatch || !hostsMatch || !portsMatch || |
+ !(other->m_path.isEmpty() || other->m_path == "/" || |
+ pathMatches(other->m_path))) { |
+ return nullptr; |
+ } |
+ |
+ CSPSource* preferredCSPSource = getPreferredCSPSource(other); |
+ if (schemesOnly) |
+ return getPreferredCSPSourceBasedOnEmptySchemes(other, preferredCSPSource); |
+ |
+ // If preferredCSPSource is already a new CSPSource, we might as well |
+ // immediately return it since we consider wildcards when we create a new |
+ // CSPSource. |
+ if (preferredCSPSource && preferredCSPSource != this && |
+ preferredCSPSource != other) { |
+ return preferredCSPSource; |
+ } |
+ |
+ int totalNumberOfWildcards = (m_hostWildcard == HasWildcard) + |
+ (m_portWildcard == HasWildcard) + |
+ (other->m_hostWildcard == HasWildcard) + |
+ (other->m_portWildcard == HasWildcard); |
+ |
+ if (totalNumberOfWildcards == 0 || totalNumberOfWildcards == 4) |
+ return preferredCSPSource ? preferredCSPSource : this; |
+ |
+ if (totalNumberOfWildcards == 1) { |
+ if (m_hostWildcard == HasWildcard || m_portWildcard == HasWildcard) { |
+ return other->getPreferredCSPSourceBasedOnWildcards(this, |
+ preferredCSPSource); |
+ } |
+ return getPreferredCSPSourceBasedOnWildcards(other, preferredCSPSource); |
+ } |
+ |
+ if (totalNumberOfWildcards == 2) { |
+ // There is one wildcard in a and in b. |
+ if (m_hostWildcard == other->m_hostWildcard && |
+ m_portWildcard == other->m_portWildcard) { |
+ return preferredCSPSource ? preferredCSPSource : this; |
+ } |
+ if (m_portWildcard == other->m_hostWildcard && |
+ m_hostWildcard == other->m_portWildcard) { |
+ // Since neither one is not stricter than the other, we have to create |
+ // a new CSPSource with no wildcards. |
+ if (!preferredCSPSource) |
+ preferredCSPSource = this; |
+ return new CSPSource(m_policy, preferredCSPSource->m_scheme, |
+ preferredCSPSource->m_host, |
+ preferredCSPSource->m_port, |
+ preferredCSPSource->m_path, NoWildcard, NoWildcard); |
+ } |
+ |
+ // Both wildcards are either in 'this' or both in 'other. |
+ return (m_portWildcard == HasWildcard) |
+ ? other->getPreferredCSPSourceBasedOnWildcards( |
+ this, preferredCSPSource) |
+ : getPreferredCSPSourceBasedOnWildcards(other, |
+ preferredCSPSource); |
+ } |
+ |
+ // There are total of 3 wildcards. |
+ if (m_hostWildcard == HasWildcard && m_portWildcard == HasWildcard) { |
+ return other->getPreferredCSPSourceBasedOnWildcards(this, |
+ preferredCSPSource); |
+ } |
+ |
+ return getPreferredCSPSourceBasedOnWildcards(other, preferredCSPSource); |
+} |
+ |
bool CSPSource::isSchemeOnly() const { |
return m_host.isEmpty(); |
} |