diff options
-rw-r--r-- | net/base/force_tls_state.cc | 99 | ||||
-rw-r--r-- | net/base/force_tls_state.h | 10 | ||||
-rw-r--r-- | net/base/force_tls_state_unittest.cc | 121 | ||||
-rw-r--r-- | net/net.gyp | 1 |
4 files changed, 230 insertions, 1 deletions
diff --git a/net/base/force_tls_state.cc b/net/base/force_tls_state.cc index 4be33f5..ea2e2f8 100644 --- a/net/base/force_tls_state.cc +++ b/net/base/force_tls_state.cc @@ -5,6 +5,8 @@ #include "net/base/force_tls_state.h" #include "base/logging.h" +#include "base/string_tokenizer.h" +#include "base/string_util.h" #include "googleurl/src/gurl.h" #include "net/base/registry_controlled_domain.h" @@ -31,4 +33,101 @@ bool ForceTLSState::IsEnabledForHost(const std::string& host) { return enabled_hosts_.find(host) != enabled_hosts_.end(); } +// "X-Force-TLS" ":" "max-age" "=" delta-seconds *1INCLUDESUBDOMAINS +// INCLUDESUBDOMAINS = [ " includeSubDomains" ] +bool ForceTLSState::ParseHeader(const std::string& value, + int* max_age, + bool* include_subdomains) { + DCHECK(max_age); + DCHECK(include_subdomains); + + int max_age_candidate; + + enum ParserState { + START, + AFTER_MAX_AGE_LABEL, + AFTER_MAX_AGE_EQUALS, + AFTER_MAX_AGE, + AFTER_MAX_AGE_INCLUDE_SUB_DOMAINS_DELIMITER, + AFTER_INCLUDE_SUBDOMAINS, + } state = START; + + StringTokenizer tokenizer(value, " ="); + tokenizer.set_options(StringTokenizer::RETURN_DELIMS); + while (tokenizer.GetNext()) { + DCHECK(!tokenizer.token_is_delim() || tokenizer.token().length() == 1); + DCHECK(tokenizer.token_is_delim() || *tokenizer.token_begin() != ' '); + switch (state) { + case START: + if (*tokenizer.token_begin() == ' ') + continue; + if (!LowerCaseEqualsASCII(tokenizer.token(), "max-age")) + return false; + state = AFTER_MAX_AGE_LABEL; + break; + + case AFTER_MAX_AGE_LABEL: + if (*tokenizer.token_begin() == ' ') + continue; + if (*tokenizer.token_begin() != '=') + return false; + DCHECK(tokenizer.token().length() == 1); + state = AFTER_MAX_AGE_EQUALS; + break; + + case AFTER_MAX_AGE_EQUALS: + if (*tokenizer.token_begin() == ' ') + continue; + if (!StringToInt(tokenizer.token(), &max_age_candidate)) + return false; + if (max_age_candidate < 0) + return false; + state = AFTER_MAX_AGE; + break; + + case AFTER_MAX_AGE: + if (*tokenizer.token_begin() != ' ') + return false; + state = AFTER_MAX_AGE_INCLUDE_SUB_DOMAINS_DELIMITER; + break; + + case AFTER_MAX_AGE_INCLUDE_SUB_DOMAINS_DELIMITER: + if (*tokenizer.token_begin() == ' ') + continue; + if (!LowerCaseEqualsASCII(tokenizer.token(), "includesubdomains")) + return false; + state = AFTER_INCLUDE_SUBDOMAINS; + break; + + case AFTER_INCLUDE_SUBDOMAINS: + if (*tokenizer.token_begin() != ' ') + return false; + break; + + default: + NOTREACHED(); + } + } + + // We've consumed all the input. Let's see what state we ended up in. + switch (state) { + case START: + case AFTER_MAX_AGE_LABEL: + case AFTER_MAX_AGE_EQUALS: + return false; + case AFTER_MAX_AGE: + case AFTER_MAX_AGE_INCLUDE_SUB_DOMAINS_DELIMITER: + *max_age = max_age_candidate; + *include_subdomains = false; + return true; + case AFTER_INCLUDE_SUBDOMAINS: + *max_age = max_age_candidate; + *include_subdomains = true; + return true; + default: + NOTREACHED(); + return false; + } +} + } // namespace diff --git a/net/base/force_tls_state.h b/net/base/force_tls_state.h index 988e9c07..e52adb9 100644 --- a/net/base/force_tls_state.h +++ b/net/base/force_tls_state.h @@ -19,7 +19,7 @@ namespace net { // // Tracks which hosts have enabled ForceTLS. After a host enables ForceTLS, // then we refuse to talk to the host over HTTP, treat all certificate errors as -// fatal, and refuses to load any mixed content. +// fatal, and refuse to load any mixed content. // class ForceTLSState { public: @@ -35,6 +35,14 @@ class ForceTLSState { // Returns whether |host| has had ForceTLS enabled. bool IsEnabledForHost(const std::string& host); + // Returns |true| if |value| parses as a valid X-Force-TLS header value. + // The values of max-age and and includeSubDomains are returned in |max_age| + // and |include_subdomains|, respectively. The out parameters are not + // modified if the function returns |false|. + static bool ParseHeader(const std::string& value, + int* max_age, + bool* include_subdomains); + private: // The set of hosts that have enabled ForceTLS. std::set<std::string> enabled_hosts_; diff --git a/net/base/force_tls_state_unittest.cc b/net/base/force_tls_state_unittest.cc new file mode 100644 index 0000000..c1f12c1 --- /dev/null +++ b/net/base/force_tls_state_unittest.cc @@ -0,0 +1,121 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/base/force_tls_state.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +class ForceTLSStateTest : public testing::Test { +}; + +TEST_F(ForceTLSStateTest, BogusHeaders) { + int max_age = 42; + bool include_subdomains = false; + + EXPECT_FALSE(net::ForceTLSState::ParseHeader( + "", &max_age, &include_subdomains)); + EXPECT_FALSE(net::ForceTLSState::ParseHeader( + " ", &max_age, &include_subdomains)); + EXPECT_FALSE(net::ForceTLSState::ParseHeader( + "abc", &max_age, &include_subdomains)); + EXPECT_FALSE(net::ForceTLSState::ParseHeader( + " abc", &max_age, &include_subdomains)); + EXPECT_FALSE(net::ForceTLSState::ParseHeader( + " abc ", &max_age, &include_subdomains)); + EXPECT_FALSE(net::ForceTLSState::ParseHeader( + "max-age", &max_age, &include_subdomains)); + EXPECT_FALSE(net::ForceTLSState::ParseHeader( + " max-age", &max_age, &include_subdomains)); + EXPECT_FALSE(net::ForceTLSState::ParseHeader( + " max-age ", &max_age, &include_subdomains)); + EXPECT_FALSE(net::ForceTLSState::ParseHeader( + "max-age=", &max_age, &include_subdomains)); + EXPECT_FALSE(net::ForceTLSState::ParseHeader( + " max-age=", &max_age, &include_subdomains)); + EXPECT_FALSE(net::ForceTLSState::ParseHeader( + " max-age =", &max_age, &include_subdomains)); + EXPECT_FALSE(net::ForceTLSState::ParseHeader( + " max-age= ", &max_age, &include_subdomains)); + EXPECT_FALSE(net::ForceTLSState::ParseHeader( + " max-age = ", &max_age, &include_subdomains)); + EXPECT_FALSE(net::ForceTLSState::ParseHeader( + " max-age = xy", &max_age, &include_subdomains)); + EXPECT_FALSE(net::ForceTLSState::ParseHeader( + " max-age = 3488a923", &max_age, &include_subdomains)); + EXPECT_FALSE(net::ForceTLSState::ParseHeader( + "max-age=3488a923 ", &max_age, &include_subdomains)); + EXPECT_FALSE(net::ForceTLSState::ParseHeader( + "max-ag=3488923", &max_age, &include_subdomains)); + EXPECT_FALSE(net::ForceTLSState::ParseHeader( + "max-aged=3488923", &max_age, &include_subdomains)); + EXPECT_FALSE(net::ForceTLSState::ParseHeader( + "max-age==3488923", &max_age, &include_subdomains)); + EXPECT_FALSE(net::ForceTLSState::ParseHeader( + "amax-age=3488923", &max_age, &include_subdomains)); + EXPECT_FALSE(net::ForceTLSState::ParseHeader( + "max-age=-3488923", &max_age, &include_subdomains)); + EXPECT_FALSE(net::ForceTLSState::ParseHeader( + "max-age=3488923;", &max_age, &include_subdomains)); + EXPECT_FALSE(net::ForceTLSState::ParseHeader( + "max-age=3488923 e", &max_age, &include_subdomains)); + EXPECT_FALSE(net::ForceTLSState::ParseHeader( + "max-age=3488923 includesubdomain", &max_age, &include_subdomains)); + EXPECT_FALSE(net::ForceTLSState::ParseHeader( + "max-age=3488923includesubdomains", &max_age, &include_subdomains)); + EXPECT_FALSE(net::ForceTLSState::ParseHeader( + "max-age=3488923=includesubdomains", &max_age, &include_subdomains)); + EXPECT_FALSE(net::ForceTLSState::ParseHeader( + "max-age=3488923 includesubdomainx", &max_age, &include_subdomains)); + EXPECT_FALSE(net::ForceTLSState::ParseHeader( + "max-age=3488923 includesubdomain=", &max_age, &include_subdomains)); + EXPECT_FALSE(net::ForceTLSState::ParseHeader( + "max-age=3488923 includesubdomain=true", &max_age, &include_subdomains)); + EXPECT_FALSE(net::ForceTLSState::ParseHeader( + "max-age=3488923 includesubdomainsx", &max_age, &include_subdomains)); + EXPECT_FALSE(net::ForceTLSState::ParseHeader( + "max-age=3488923 includesubdomains x", &max_age, &include_subdomains)); + EXPECT_FALSE(net::ForceTLSState::ParseHeader( + "max-age=34889.23 includesubdomains", &max_age, &include_subdomains)); + + EXPECT_EQ(max_age, 42); + EXPECT_FALSE(include_subdomains); +} + +TEST_F(ForceTLSStateTest, ValidHeaders) { + int max_age = 42; + bool include_subdomains = true; + + EXPECT_TRUE(net::ForceTLSState::ParseHeader( + "max-age=243", &max_age, &include_subdomains)); + EXPECT_EQ(max_age, 243); + EXPECT_FALSE(include_subdomains); + + EXPECT_TRUE(net::ForceTLSState::ParseHeader( + " Max-agE = 567", &max_age, &include_subdomains)); + EXPECT_EQ(max_age, 567); + EXPECT_FALSE(include_subdomains); + + EXPECT_TRUE(net::ForceTLSState::ParseHeader( + " mAx-aGe = 890 ", &max_age, &include_subdomains)); + EXPECT_EQ(max_age, 890); + EXPECT_FALSE(include_subdomains); + + EXPECT_TRUE(net::ForceTLSState::ParseHeader( + "max-age=123 incLudesUbdOmains", &max_age, &include_subdomains)); + EXPECT_EQ(max_age, 123); + EXPECT_TRUE(include_subdomains); + + EXPECT_TRUE(net::ForceTLSState::ParseHeader( + "max-age=394082038 incLudesUbdOmains", &max_age, &include_subdomains)); + EXPECT_EQ(max_age, 394082038); + EXPECT_TRUE(include_subdomains); + + EXPECT_TRUE(net::ForceTLSState::ParseHeader( + " max-age=0 incLudesUbdOmains ", &max_age, &include_subdomains)); + EXPECT_EQ(max_age, 0); + EXPECT_TRUE(include_subdomains); +} + +} // namespace diff --git a/net/net.gyp b/net/net.gyp index 73eded7..3490d7c 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -404,6 +404,7 @@ 'base/file_stream_unittest.cc', 'base/filter_unittest.cc', 'base/filter_unittest.h', + 'base/force_tls_state_unittest.cc', 'base/gzip_filter_unittest.cc', 'base/host_resolver_unittest.cc', 'base/listen_socket_unittest.cc', |