summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRyan Sleevi <rsleevi@chromium.org>2015-06-04 23:30:09 -0700
committerRyan Sleevi <rsleevi@chromium.org>2015-06-05 06:32:02 +0000
commite648e4b5848efceda338aff4d83819ce9b34c456 (patch)
tree3b20a23c633479810aea8988e8d80e755031ee41
parent84e7dd2b8dd64a2c6d407e281dfdaf59b8fb2598 (diff)
downloadchromium_src-e648e4b5848efceda338aff4d83819ce9b34c456.zip
chromium_src-e648e4b5848efceda338aff4d83819ce9b34c456.tar.gz
chromium_src-e648e4b5848efceda338aff4d83819ce9b34c456.tar.bz2
Normalize hostnames before searching for HSTS/HPKP preloads
The HSTS/HPKP preload set is pre-normalized at construction time. Since the queries come from the GURL, not from the DNS layer, we need to normalize the hostname before scanning for preloads. This used to be handled by CanonicalizeHost(), which used the same mechanism as the resolver, but the storage of the preloads has changed to be more efficient, and thus no longer uses the resolver-normalized form. BUG=461481 R=davidben@chromium.org Review URL: https://codereview.chromium.org/1149753002 Cr-Commit-Position: refs/heads/master@{#330834} (cherry picked from commit c2dd599bdd35753da237172dc37a447e9d65c463) Review URL: https://codereview.chromium.org/1157033008 Cr-Commit-Position: refs/branch-heads/2357@{#466} Cr-Branched-From: 59d4494849b405682265ed5d3f5164573b9a939b-refs/heads/master@{#323860}
-rw-r--r--net/http/transport_security_state.cc457
-rw-r--r--net/http/transport_security_state.h8
-rw-r--r--net/http/transport_security_state_unittest.cc63
3 files changed, 304 insertions, 224 deletions
diff --git a/net/http/transport_security_state.cc b/net/http/transport_security_state.cc
index 7147b48..d92bd47 100644
--- a/net/http/transport_security_state.cc
+++ b/net/http/transport_security_state.cc
@@ -45,6 +45,8 @@ namespace net {
namespace {
+#include "net/http/transport_security_state_static.h"
+
std::string HashesToBase64String(const HashValueVector& hashes) {
std::string str;
for (size_t i = 0; i != hashes.size(); ++i) {
@@ -82,223 +84,13 @@ bool AddHash(const char* sha1_hash,
return true;
}
-} // namespace
-
-TransportSecurityState::TransportSecurityState()
- : delegate_(NULL), enable_static_pins_(true) {
-// Static pinning is only enabled for official builds to make sure that
-// others don't end up with pins that cannot be easily updated.
-#if !defined(OFFICIAL_BUILD) || defined(OS_ANDROID) || defined(OS_IOS)
- enable_static_pins_ = false;
-#endif
- DCHECK(CalledOnValidThread());
-}
-
-TransportSecurityState::Iterator::Iterator(const TransportSecurityState& state)
- : iterator_(state.enabled_hosts_.begin()),
- end_(state.enabled_hosts_.end()) {
-}
-
-TransportSecurityState::Iterator::~Iterator() {}
-
-bool TransportSecurityState::ShouldSSLErrorsBeFatal(const std::string& host) {
- DomainState state;
- if (GetStaticDomainState(host, &state))
- return true;
- return GetDynamicDomainState(host, &state);
-}
-
-bool TransportSecurityState::ShouldUpgradeToSSL(const std::string& host) {
- DomainState dynamic_state;
- if (GetDynamicDomainState(host, &dynamic_state))
- return dynamic_state.ShouldUpgradeToSSL();
-
- DomainState static_state;
- if (GetStaticDomainState(host, &static_state) &&
- static_state.ShouldUpgradeToSSL()) {
- return true;
- }
-
- return false;
-}
-
-bool TransportSecurityState::CheckPublicKeyPins(
- const std::string& host,
- bool is_issued_by_known_root,
- const HashValueVector& public_key_hashes,
- std::string* pinning_failure_log) {
- // Perform pin validation if, and only if, all these conditions obtain:
- //
- // * the server's certificate chain chains up to a known root (i.e. not a
- // user-installed trust anchor); and
- // * the server actually has public key pins.
- if (!is_issued_by_known_root || !HasPublicKeyPins(host)) {
- return true;
- }
-
- bool pins_are_valid = CheckPublicKeyPinsImpl(
- host, public_key_hashes, pinning_failure_log);
- if (!pins_are_valid) {
- LOG(ERROR) << *pinning_failure_log;
- ReportUMAOnPinFailure(host);
- }
-
- UMA_HISTOGRAM_BOOLEAN("Net.PublicKeyPinSuccess", pins_are_valid);
- return pins_are_valid;
-}
-
-bool TransportSecurityState::HasPublicKeyPins(const std::string& host) {
- DomainState dynamic_state;
- if (GetDynamicDomainState(host, &dynamic_state))
- return dynamic_state.HasPublicKeyPins();
-
- DomainState static_state;
- if (GetStaticDomainState(host, &static_state)) {
- if (static_state.HasPublicKeyPins())
- return true;
- }
-
- return false;
-}
-
-void TransportSecurityState::SetDelegate(
- TransportSecurityState::Delegate* delegate) {
- DCHECK(CalledOnValidThread());
- delegate_ = delegate;
-}
-
-void TransportSecurityState::AddHSTSInternal(
- const std::string& host,
- TransportSecurityState::DomainState::UpgradeMode upgrade_mode,
- const base::Time& expiry,
- bool include_subdomains) {
- DCHECK(CalledOnValidThread());
-
- // Copy-and-modify the existing DomainState for this host (if any).
- DomainState domain_state;
- const std::string canonicalized_host = CanonicalizeHost(host);
- const std::string hashed_host = HashHost(canonicalized_host);
- DomainStateMap::const_iterator i = enabled_hosts_.find(hashed_host);
- if (i != enabled_hosts_.end())
- domain_state = i->second;
-
- domain_state.sts.last_observed = base::Time::Now();
- domain_state.sts.include_subdomains = include_subdomains;
- domain_state.sts.expiry = expiry;
- domain_state.sts.upgrade_mode = upgrade_mode;
- EnableHost(host, domain_state);
-}
-
-void TransportSecurityState::AddHPKPInternal(const std::string& host,
- const base::Time& last_observed,
- const base::Time& expiry,
- bool include_subdomains,
- const HashValueVector& hashes) {
- DCHECK(CalledOnValidThread());
-
- // Copy-and-modify the existing DomainState for this host (if any).
- DomainState domain_state;
- const std::string canonicalized_host = CanonicalizeHost(host);
- const std::string hashed_host = HashHost(canonicalized_host);
- DomainStateMap::const_iterator i = enabled_hosts_.find(hashed_host);
- if (i != enabled_hosts_.end())
- domain_state = i->second;
-
- domain_state.pkp.last_observed = last_observed;
- domain_state.pkp.expiry = expiry;
- domain_state.pkp.include_subdomains = include_subdomains;
- domain_state.pkp.spki_hashes = hashes;
- EnableHost(host, domain_state);
-}
-
-void TransportSecurityState::EnableHost(const std::string& host,
- const DomainState& state) {
- DCHECK(CalledOnValidThread());
-
- const std::string canonicalized_host = CanonicalizeHost(host);
- if (canonicalized_host.empty())
- return;
-
- DomainState state_copy(state);
- // No need to store this value since it is redundant. (|canonicalized_host|
- // is the map key.)
- state_copy.sts.domain.clear();
- state_copy.pkp.domain.clear();
-
- enabled_hosts_[HashHost(canonicalized_host)] = state_copy;
- DirtyNotify();
-}
-
-bool TransportSecurityState::DeleteDynamicDataForHost(const std::string& host) {
- DCHECK(CalledOnValidThread());
-
- const std::string canonicalized_host = CanonicalizeHost(host);
- if (canonicalized_host.empty())
- return false;
-
- DomainStateMap::iterator i = enabled_hosts_.find(
- HashHost(canonicalized_host));
- if (i != enabled_hosts_.end()) {
- enabled_hosts_.erase(i);
- DirtyNotify();
- return true;
- }
- return false;
-}
-
-void TransportSecurityState::ClearDynamicData() {
- DCHECK(CalledOnValidThread());
- enabled_hosts_.clear();
-}
-
-void TransportSecurityState::DeleteAllDynamicDataSince(const base::Time& time) {
- DCHECK(CalledOnValidThread());
-
- bool dirtied = false;
- DomainStateMap::iterator i = enabled_hosts_.begin();
- while (i != enabled_hosts_.end()) {
- // Clear STS and PKP state independently.
- if (i->second.sts.last_observed >= time) {
- dirtied = true;
- i->second.sts.upgrade_mode = DomainState::MODE_DEFAULT;
- }
- if (i->second.pkp.last_observed >= time) {
- dirtied = true;
- i->second.pkp.spki_hashes.clear();
- i->second.pkp.expiry = base::Time();
- }
-
- // If both are now invalid, drop the entry altogether.
- if (!i->second.ShouldUpgradeToSSL() && !i->second.HasPublicKeyPins()) {
- dirtied = true;
- enabled_hosts_.erase(i++);
- continue;
- }
-
- ++i;
- }
-
- if (dirtied)
- DirtyNotify();
-}
-
-TransportSecurityState::~TransportSecurityState() {
- DCHECK(CalledOnValidThread());
-}
-
-void TransportSecurityState::DirtyNotify() {
- DCHECK(CalledOnValidThread());
-
- if (delegate_)
- delegate_->StateIsDirty(this);
-}
-
-// static
-std::string TransportSecurityState::CanonicalizeHost(const std::string& host) {
+// Converts |hostname| from dotted form ("www.google.com") to the form
+// used in DNS: "\x03www\x06google\x03com", lowercases that, and returns
+// the result.
+std::string CanonicalizeHost(const std::string& host) {
// We cannot perform the operations as detailed in the spec here as |host|
// has already undergone IDN processing before it reached us. Thus, we check
// that there are no invalid characters in the host and lowercase the result.
-
std::string new_host;
if (!DNSDomainFromDot(host, &new_host)) {
// DNSDomainFromDot can fail if any label is > 63 bytes or if the whole
@@ -454,8 +246,6 @@ class HuffmanDecoder {
const size_t tree_bytes_;
};
-#include "net/http/transport_security_state_static.h"
-
// PreloadResult is the result of resolving a specific name in the preloaded
// data.
struct PreloadResult {
@@ -495,7 +285,7 @@ struct PreloadResult {
//
// Dispatch tables are always given in order, but the "end of string" (zero)
// value always comes before an entry for '.'.
-bool DecodeHSTSPreloadRaw(const std::string& hostname,
+bool DecodeHSTSPreloadRaw(const std::string& search_hostname,
bool* out_found,
PreloadResult* out) {
HuffmanDecoder huffman(kHSTSHuffmanTree, sizeof(kHSTSHuffmanTree));
@@ -506,9 +296,30 @@ bool DecodeHSTSPreloadRaw(const std::string& hostname,
*out_found = false;
+ // Ensure that |search_hostname| is a valid hostname before
+ // processing.
+ if (CanonicalizeHost(search_hostname).empty()) {
+ return true;
+ }
+
+ // Normalize any trailing '.' used for DNS suffix searches.
+ std::string hostname = search_hostname;
+ size_t found = hostname.find_last_not_of('.');
+ if (found != std::string::npos) {
+ hostname.erase(found + 1);
+ } else {
+ hostname.clear();
+ }
+
+ // |hostname| has already undergone IDN conversion, so should be
+ // entirely A-Labels. The preload data is entirely normalized to
+ // lower case.
+ base::StringToLowerASCII(&hostname);
+
if (hostname.empty()) {
return true;
}
+
// hostname_offset contains one more than the index of the current character
// in the hostname that is being considered. It's one greater so that we can
// represent the position just before the beginning (with zero).
@@ -663,6 +474,218 @@ bool DecodeHSTSPreload(const std::string& hostname,
return found;
}
+} // namespace
+
+TransportSecurityState::TransportSecurityState()
+ : delegate_(NULL), enable_static_pins_(true) {
+// Static pinning is only enabled for official builds to make sure that
+// others don't end up with pins that cannot be easily updated.
+#if !defined(OFFICIAL_BUILD) || defined(OS_ANDROID) || defined(OS_IOS)
+ enable_static_pins_ = false;
+#endif
+ DCHECK(CalledOnValidThread());
+}
+
+TransportSecurityState::Iterator::Iterator(const TransportSecurityState& state)
+ : iterator_(state.enabled_hosts_.begin()),
+ end_(state.enabled_hosts_.end()) {
+}
+
+TransportSecurityState::Iterator::~Iterator() {
+}
+
+bool TransportSecurityState::ShouldSSLErrorsBeFatal(const std::string& host) {
+ DomainState state;
+ if (GetStaticDomainState(host, &state))
+ return true;
+ return GetDynamicDomainState(host, &state);
+}
+
+bool TransportSecurityState::ShouldUpgradeToSSL(const std::string& host) {
+ DomainState dynamic_state;
+ if (GetDynamicDomainState(host, &dynamic_state))
+ return dynamic_state.ShouldUpgradeToSSL();
+
+ DomainState static_state;
+ if (GetStaticDomainState(host, &static_state) &&
+ static_state.ShouldUpgradeToSSL()) {
+ return true;
+ }
+
+ return false;
+}
+
+bool TransportSecurityState::CheckPublicKeyPins(
+ const std::string& host,
+ bool is_issued_by_known_root,
+ const HashValueVector& public_key_hashes,
+ std::string* pinning_failure_log) {
+ // Perform pin validation if, and only if, all these conditions obtain:
+ //
+ // * the server's certificate chain chains up to a known root (i.e. not a
+ // user-installed trust anchor); and
+ // * the server actually has public key pins.
+ if (!is_issued_by_known_root || !HasPublicKeyPins(host)) {
+ return true;
+ }
+
+ bool pins_are_valid =
+ CheckPublicKeyPinsImpl(host, public_key_hashes, pinning_failure_log);
+ if (!pins_are_valid) {
+ LOG(ERROR) << *pinning_failure_log;
+ ReportUMAOnPinFailure(host);
+ }
+
+ UMA_HISTOGRAM_BOOLEAN("Net.PublicKeyPinSuccess", pins_are_valid);
+ return pins_are_valid;
+}
+
+bool TransportSecurityState::HasPublicKeyPins(const std::string& host) {
+ DomainState dynamic_state;
+ if (GetDynamicDomainState(host, &dynamic_state))
+ return dynamic_state.HasPublicKeyPins();
+
+ DomainState static_state;
+ if (GetStaticDomainState(host, &static_state)) {
+ if (static_state.HasPublicKeyPins())
+ return true;
+ }
+
+ return false;
+}
+
+void TransportSecurityState::SetDelegate(
+ TransportSecurityState::Delegate* delegate) {
+ DCHECK(CalledOnValidThread());
+ delegate_ = delegate;
+}
+
+void TransportSecurityState::AddHSTSInternal(
+ const std::string& host,
+ TransportSecurityState::DomainState::UpgradeMode upgrade_mode,
+ const base::Time& expiry,
+ bool include_subdomains) {
+ DCHECK(CalledOnValidThread());
+
+ // Copy-and-modify the existing DomainState for this host (if any).
+ DomainState domain_state;
+ const std::string canonicalized_host = CanonicalizeHost(host);
+ const std::string hashed_host = HashHost(canonicalized_host);
+ DomainStateMap::const_iterator i = enabled_hosts_.find(hashed_host);
+ if (i != enabled_hosts_.end())
+ domain_state = i->second;
+
+ domain_state.sts.last_observed = base::Time::Now();
+ domain_state.sts.include_subdomains = include_subdomains;
+ domain_state.sts.expiry = expiry;
+ domain_state.sts.upgrade_mode = upgrade_mode;
+ EnableHost(host, domain_state);
+}
+
+void TransportSecurityState::AddHPKPInternal(const std::string& host,
+ const base::Time& last_observed,
+ const base::Time& expiry,
+ bool include_subdomains,
+ const HashValueVector& hashes) {
+ DCHECK(CalledOnValidThread());
+
+ // Copy-and-modify the existing DomainState for this host (if any).
+ DomainState domain_state;
+ const std::string canonicalized_host = CanonicalizeHost(host);
+ const std::string hashed_host = HashHost(canonicalized_host);
+ DomainStateMap::const_iterator i = enabled_hosts_.find(hashed_host);
+ if (i != enabled_hosts_.end())
+ domain_state = i->second;
+
+ domain_state.pkp.last_observed = last_observed;
+ domain_state.pkp.expiry = expiry;
+ domain_state.pkp.include_subdomains = include_subdomains;
+ domain_state.pkp.spki_hashes = hashes;
+ EnableHost(host, domain_state);
+}
+
+void TransportSecurityState::EnableHost(const std::string& host,
+ const DomainState& state) {
+ DCHECK(CalledOnValidThread());
+
+ const std::string canonicalized_host = CanonicalizeHost(host);
+ if (canonicalized_host.empty())
+ return;
+
+ DomainState state_copy(state);
+ // No need to store this value since it is redundant. (|canonicalized_host|
+ // is the map key.)
+ state_copy.sts.domain.clear();
+ state_copy.pkp.domain.clear();
+
+ enabled_hosts_[HashHost(canonicalized_host)] = state_copy;
+ DirtyNotify();
+}
+
+bool TransportSecurityState::DeleteDynamicDataForHost(const std::string& host) {
+ DCHECK(CalledOnValidThread());
+
+ const std::string canonicalized_host = CanonicalizeHost(host);
+ if (canonicalized_host.empty())
+ return false;
+
+ DomainStateMap::iterator i =
+ enabled_hosts_.find(HashHost(canonicalized_host));
+ if (i != enabled_hosts_.end()) {
+ enabled_hosts_.erase(i);
+ DirtyNotify();
+ return true;
+ }
+ return false;
+}
+
+void TransportSecurityState::ClearDynamicData() {
+ DCHECK(CalledOnValidThread());
+ enabled_hosts_.clear();
+}
+
+void TransportSecurityState::DeleteAllDynamicDataSince(const base::Time& time) {
+ DCHECK(CalledOnValidThread());
+
+ bool dirtied = false;
+ DomainStateMap::iterator i = enabled_hosts_.begin();
+ while (i != enabled_hosts_.end()) {
+ // Clear STS and PKP state independently.
+ if (i->second.sts.last_observed >= time) {
+ dirtied = true;
+ i->second.sts.upgrade_mode = DomainState::MODE_DEFAULT;
+ }
+ if (i->second.pkp.last_observed >= time) {
+ dirtied = true;
+ i->second.pkp.spki_hashes.clear();
+ i->second.pkp.expiry = base::Time();
+ }
+
+ // If both are now invalid, drop the entry altogether.
+ if (!i->second.ShouldUpgradeToSSL() && !i->second.HasPublicKeyPins()) {
+ dirtied = true;
+ enabled_hosts_.erase(i++);
+ continue;
+ }
+
+ ++i;
+ }
+
+ if (dirtied)
+ DirtyNotify();
+}
+
+TransportSecurityState::~TransportSecurityState() {
+ DCHECK(CalledOnValidThread());
+}
+
+void TransportSecurityState::DirtyNotify() {
+ DCHECK(CalledOnValidThread());
+
+ if (delegate_)
+ delegate_->StateIsDirty(this);
+}
+
bool TransportSecurityState::AddHSTSHeader(const std::string& host,
const std::string& value) {
DCHECK(CalledOnValidThread());
diff --git a/net/http/transport_security_state.h b/net/http/transport_security_state.h
index 14fe0d7..b504de6 100644
--- a/net/http/transport_security_state.h
+++ b/net/http/transport_security_state.h
@@ -193,8 +193,7 @@ class NET_EXPORT TransportSecurityState
void ClearDynamicData();
// Inserts |state| into |enabled_hosts_| under the key |hashed_host|.
- // |hashed_host| is already in the internal representation
- // HashHost(CanonicalizeHost(host)).
+ // |hashed_host| is already in the internal representation.
// Note: This is only used for serializing/deserializing the
// TransportSecurityState.
void AddOrUpdateEnabledHosts(const std::string& hashed_host,
@@ -319,11 +318,6 @@ class NET_EXPORT TransportSecurityState
// The new state for |host| is persisted using the Delegate (if any).
void EnableHost(const std::string& host, const DomainState& state);
- // Converts |hostname| from dotted form ("www.google.com") to the form
- // used in DNS: "\x03www\x06google\x03com", lowercases that, and returns
- // the result.
- static std::string CanonicalizeHost(const std::string& hostname);
-
// The set of hosts that have enabled TransportSecurity. |sts.domain| and
// |pkp.domain| will always be empty for a DomainState in this map; the domain
// comes from the map key instead.
diff --git a/net/http/transport_security_state_unittest.cc b/net/http/transport_security_state_unittest.cc
index 61d2912..cc51e72 100644
--- a/net/http/transport_security_state_unittest.cc
+++ b/net/http/transport_security_state_unittest.cc
@@ -71,6 +71,64 @@ class TransportSecurityStateTest : public testing::Test {
}
};
+TEST_F(TransportSecurityStateTest, DomainNameOddities) {
+ TransportSecurityState state;
+ const base::Time current_time(base::Time::Now());
+ const base::Time expiry = current_time + base::TimeDelta::FromSeconds(1000);
+
+ // DNS suffix search tests. Some DNS resolvers allow a terminal "." to
+ // indicate not perform DNS suffix searching. Ensure that regardless
+ // of how this is treated at the resolver layer, or at the URL/origin
+ // layer (that is, whether they are treated as equivalent or distinct),
+ // ensure that for policy matching, something lacking a terminal "."
+ // is equivalent to something with a terminal "."
+ EXPECT_FALSE(state.ShouldUpgradeToSSL("example.com"));
+
+ state.AddHSTS("example.com", expiry, true /* include_subdomains */);
+ EXPECT_TRUE(state.ShouldUpgradeToSSL("example.com"));
+ // Trailing '.' should be equivalent; it's just a resolver hint
+ EXPECT_TRUE(state.ShouldUpgradeToSSL("example.com."));
+ // Leading '.' should be invalid
+ EXPECT_FALSE(state.ShouldUpgradeToSSL(".example.com"));
+ // Subdomains should work regardless
+ EXPECT_TRUE(state.ShouldUpgradeToSSL("sub.example.com"));
+ EXPECT_TRUE(state.ShouldUpgradeToSSL("sub.example.com."));
+ // But invalid subdomains should be rejected
+ EXPECT_FALSE(state.ShouldUpgradeToSSL("sub..example.com"));
+ EXPECT_FALSE(state.ShouldUpgradeToSSL("sub..example.com."));
+
+ // Now try the inverse form
+ TransportSecurityState state2;
+ state2.AddHSTS("example.net.", expiry, true /* include_subdomains */);
+ EXPECT_TRUE(state2.ShouldUpgradeToSSL("example.net."));
+ EXPECT_TRUE(state2.ShouldUpgradeToSSL("example.net"));
+ EXPECT_TRUE(state2.ShouldUpgradeToSSL("sub.example.net."));
+ EXPECT_TRUE(state2.ShouldUpgradeToSSL("sub.example.net"));
+
+ // Finally, test weird things
+ TransportSecurityState state3;
+ state3.AddHSTS("", expiry, true /* include_subdomains */);
+ EXPECT_FALSE(state3.ShouldUpgradeToSSL(""));
+ EXPECT_FALSE(state3.ShouldUpgradeToSSL("."));
+ EXPECT_FALSE(state3.ShouldUpgradeToSSL("..."));
+ // Make sure it didn't somehow apply HSTS to the world
+ EXPECT_FALSE(state3.ShouldUpgradeToSSL("example.org"));
+
+ TransportSecurityState state4;
+ state4.AddHSTS(".", expiry, true /* include_subdomains */);
+ EXPECT_FALSE(state4.ShouldUpgradeToSSL(""));
+ EXPECT_FALSE(state4.ShouldUpgradeToSSL("."));
+ EXPECT_FALSE(state4.ShouldUpgradeToSSL("..."));
+ EXPECT_FALSE(state4.ShouldUpgradeToSSL("example.org"));
+
+ // Now do the same for preloaded entries
+ TransportSecurityState state5;
+ EXPECT_TRUE(state5.ShouldUpgradeToSSL("accounts.google.com"));
+ EXPECT_TRUE(state5.ShouldUpgradeToSSL("accounts.google.com."));
+ EXPECT_FALSE(state5.ShouldUpgradeToSSL("accounts..google.com"));
+ EXPECT_FALSE(state5.ShouldUpgradeToSSL("accounts..google.com."));
+}
+
TEST_F(TransportSecurityStateTest, SimpleMatches) {
TransportSecurityState state;
const base::Time current_time(base::Time::Now());
@@ -123,10 +181,15 @@ TEST_F(TransportSecurityStateTest, MatchesCase2) {
const base::Time current_time(base::Time::Now());
const base::Time expiry = current_time + base::TimeDelta::FromSeconds(1000);
+ // Check dynamic entries
EXPECT_FALSE(state.ShouldUpgradeToSSL("YAhoo.coM"));
bool include_subdomains = false;
state.AddHSTS("yahoo.com", expiry, include_subdomains);
EXPECT_TRUE(state.ShouldUpgradeToSSL("YAhoo.coM"));
+
+ // Check static entries
+ EXPECT_TRUE(state.ShouldUpgradeToSSL("AccounTs.GooGle.com"));
+ EXPECT_TRUE(state.ShouldUpgradeToSSL("mail.google.COM"));
}
TEST_F(TransportSecurityStateTest, SubdomainMatches) {