| 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();
|
| }
|
|
|