Use GetNormalizedValue() to retrieve Expect-CT header value
Due to a shameful test coverage oversight on my part :( Expect-CT headers
weren't being enumerated properly. Because the syntax is comma-separated
directives, EnumerateHeader() was returning only the first directive.
This change uses GetNormalizedValue() to retrieve the header value instead.
This returns the full list of comma-separated directives, and it also
combines multiple header values into one as the spec intends, such that
Expect-CT: max-age=100
Expect-CT: enforce
is treated identically to
Expect-CT: max-age=100,enforce.
Bug: 752358
Change-Id: I6b8dd78c706925a1367e9978e3986e3c29ccbd46
Reviewed-on: https://chromium-review.googlesource.com/601441
Reviewed-by: Matt Mueller <[email protected]>
Commit-Queue: Emily Stark <[email protected]>
Cr-Commit-Position: refs/heads/master@{#492484}
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index 78a00de..5f9da873 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -44,6 +44,7 @@
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/values.h"
#include "build/buildflag.h"
@@ -6819,8 +6820,8 @@
ct::CertPolicyCompliance default_result_;
};
-// Tests that Expect CT headers are processed correctly.
-TEST_F(URLRequestTestHTTP, ExpectCTHeader) {
+// Tests that Expect CT headers for the preload list are processed correctly.
+TEST_F(URLRequestTestHTTP, PreloadExpectCTHeader) {
#if !BUILDFLAG(INCLUDE_TRANSPORT_SECURITY_STATE_PRELOAD_LIST)
SetTransportSecurityStateSourceForTesting(&test0::kHSTSSource);
#endif
@@ -6868,7 +6869,7 @@
// Now send a request to trigger the violation.
TestDelegate d;
- GURL url = https_test_server.GetURL("/expect-ct-header.html");
+ GURL url = https_test_server.GetURL("/expect-ct-header-preload.html");
GURL::Replacements replace_host;
replace_host.SetHostStr(kExpectCTStaticHostname);
url = url.ReplaceComponents(replace_host);
@@ -6879,6 +6880,130 @@
EXPECT_EQ(1u, reporter.num_failures());
}
+
+// Tests that Expect CT HTTP headers are processed correctly.
+TEST_F(URLRequestTestHTTP, ExpectCTHeader) {
+ base::test::ScopedFeatureList feature_list;
+ feature_list.InitAndEnableFeature(
+ TransportSecurityState::kDynamicExpectCTFeature);
+
+ EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS);
+ https_test_server.SetSSLConfig(
+ net::EmbeddedTestServer::CERT_COMMON_NAME_IS_DOMAIN);
+ https_test_server.ServeFilesFromSourceDirectory(
+ base::FilePath(kTestFilePath));
+ ASSERT_TRUE(https_test_server.Start());
+
+ MockExpectCTReporter reporter;
+ TransportSecurityState transport_security_state;
+ transport_security_state.SetExpectCTReporter(&reporter);
+
+ // Set up a MockCertVerifier to accept the certificate that the server sends.
+ scoped_refptr<X509Certificate> cert = https_test_server.GetCertificate();
+ ASSERT_TRUE(cert);
+ MockCertVerifier cert_verifier;
+ CertVerifyResult verify_result;
+ verify_result.verified_cert = cert;
+ verify_result.is_issued_by_known_root = true;
+ cert_verifier.AddResultForCert(cert.get(), verify_result, OK);
+
+ // Set up a DoNothingCTVerifier and MockCTPolicyEnforcer to simulate CT
+ // compliance.
+ DoNothingCTVerifier ct_verifier;
+ MockCTPolicyEnforcer ct_policy_enforcer;
+ ct_policy_enforcer.set_default_result(
+ ct::CertPolicyCompliance::CERT_POLICY_COMPLIES_VIA_SCTS);
+
+ TestNetworkDelegate network_delegate;
+ // Use a MockHostResolver (which by default maps all hosts to
+ // 127.0.0.1).
+ MockHostResolver host_resolver;
+ TestURLRequestContext context(true);
+ context.set_host_resolver(&host_resolver);
+ context.set_transport_security_state(&transport_security_state);
+ context.set_network_delegate(&network_delegate);
+ context.set_cert_verifier(&cert_verifier);
+ context.set_cert_transparency_verifier(&ct_verifier);
+ context.set_ct_policy_enforcer(&ct_policy_enforcer);
+ context.Init();
+
+ // Now send a request to trigger the header processing.
+ TestDelegate d;
+ GURL url = https_test_server.GetURL("/expect-ct-header.html");
+ std::unique_ptr<URLRequest> request(context.CreateRequest(
+ url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
+ request->Start();
+ base::RunLoop().Run();
+
+ TransportSecurityState::ExpectCTState state;
+ ASSERT_TRUE(
+ transport_security_state.GetDynamicExpectCTState(url.host(), &state));
+ EXPECT_TRUE(state.enforce);
+ EXPECT_EQ(GURL("https://example.test"), state.report_uri);
+}
+
+// Tests that if multiple Expect CT HTTP headers are sent, they are all
+// processed.
+TEST_F(URLRequestTestHTTP, MultipleExpectCTHeaders) {
+ base::test::ScopedFeatureList feature_list;
+ feature_list.InitAndEnableFeature(
+ TransportSecurityState::kDynamicExpectCTFeature);
+
+ EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS);
+ https_test_server.SetSSLConfig(
+ net::EmbeddedTestServer::CERT_COMMON_NAME_IS_DOMAIN);
+ https_test_server.ServeFilesFromSourceDirectory(
+ base::FilePath(kTestFilePath));
+ ASSERT_TRUE(https_test_server.Start());
+
+ MockExpectCTReporter reporter;
+ TransportSecurityState transport_security_state;
+ transport_security_state.SetExpectCTReporter(&reporter);
+
+ // Set up a MockCertVerifier to accept the certificate that the server sends.
+ scoped_refptr<X509Certificate> cert = https_test_server.GetCertificate();
+ ASSERT_TRUE(cert);
+ MockCertVerifier cert_verifier;
+ CertVerifyResult verify_result;
+ verify_result.verified_cert = cert;
+ verify_result.is_issued_by_known_root = true;
+ cert_verifier.AddResultForCert(cert.get(), verify_result, OK);
+
+ // Set up a DoNothingCTVerifier and MockCTPolicyEnforcer to simulate CT
+ // compliance.
+ DoNothingCTVerifier ct_verifier;
+ MockCTPolicyEnforcer ct_policy_enforcer;
+ ct_policy_enforcer.set_default_result(
+ ct::CertPolicyCompliance::CERT_POLICY_COMPLIES_VIA_SCTS);
+
+ TestNetworkDelegate network_delegate;
+ // Use a MockHostResolver (which by default maps all hosts to
+ // 127.0.0.1).
+ MockHostResolver host_resolver;
+ TestURLRequestContext context(true);
+ context.set_host_resolver(&host_resolver);
+ context.set_transport_security_state(&transport_security_state);
+ context.set_network_delegate(&network_delegate);
+ context.set_cert_verifier(&cert_verifier);
+ context.set_cert_transparency_verifier(&ct_verifier);
+ context.set_ct_policy_enforcer(&ct_policy_enforcer);
+ context.Init();
+
+ // Now send a request to trigger the header processing.
+ TestDelegate d;
+ GURL url = https_test_server.GetURL("/expect-ct-header-multiple.html");
+ std::unique_ptr<URLRequest> request(context.CreateRequest(
+ url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
+ request->Start();
+ base::RunLoop().Run();
+
+ TransportSecurityState::ExpectCTState state;
+ ASSERT_TRUE(
+ transport_security_state.GetDynamicExpectCTState(url.host(), &state));
+ EXPECT_TRUE(state.enforce);
+ EXPECT_EQ(GURL("https://example.test"), state.report_uri);
+}
+
#endif // !defined(OS_IOS)
#if BUILDFLAG(ENABLE_REPORTING)