Index: net/url_request/url_request_unittest.cc |
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc |
index ecff043877c54d88952a34bbccdcfb1eb4b96ee5..214900aeae9ec1f78377b4d03e63bb92f5a5ddc0 100644 |
--- a/net/url_request/url_request_unittest.cc |
+++ b/net/url_request/url_request_unittest.cc |
@@ -20,6 +20,7 @@ |
#include "base/files/file_util.h" |
#include "base/files/scoped_temp_dir.h" |
#include "base/format_macros.h" |
+#include "base/json/json_reader.h" |
#include "base/location.h" |
#include "base/memory/scoped_ptr.h" |
#include "base/memory/weak_ptr.h" |
@@ -35,6 +36,7 @@ |
#include "base/strings/utf_string_conversions.h" |
#include "base/test/histogram_tester.h" |
#include "base/thread_task_runner_handle.h" |
+#include "base/values.h" |
#include "net/base/chunked_upload_data_stream.h" |
#include "net/base/elements_upload_data_stream.h" |
#include "net/base/load_flags.h" |
@@ -618,6 +620,27 @@ class TestURLRequestContextWithProxy : public TestURLRequestContext { |
~TestURLRequestContextWithProxy() override {} |
}; |
+// A mock ReportSender that just remembers the latest report |
+// URI and report to be sent. |
+class MockCertificateReportSender |
+ : public TransportSecurityState::ReportSender { |
+ public: |
+ MockCertificateReportSender() {} |
+ ~MockCertificateReportSender() override {} |
+ |
+ void Send(const GURL& report_uri, const std::string& report) override { |
+ latest_report_uri_ = report_uri; |
+ latest_report_ = report; |
+ } |
+ |
+ const GURL& latest_report_uri() { return latest_report_uri_; } |
+ const std::string& latest_report() { return latest_report_; } |
+ |
+ private: |
+ GURL latest_report_uri_; |
+ std::string latest_report_; |
+}; |
+ |
} // namespace |
// Inherit PlatformTest since we require the autorelease pool on Mac OS X. |
@@ -5361,13 +5384,26 @@ TEST_F(URLRequestTestHTTP, STSNotProcessedOnIP) { |
// PKPState present because header rejected). |
#if defined(OS_ANDROID) |
#define MAYBE_ProcessPKP DISABLED_ProcessPKP |
+#define MAYBE_ProcessPKPAndSendReport DISABLED_ProcessPKPAndSendReport |
+#define MAYBE_ProcessPKPReportOnly DISABLED_ProcessPKPReportOnly |
+#define MAYBE_ProcessPKPReportOnlyWithNoViolation \ |
+ DISABLED_ProcessPKPReportOnlyWithNoViolation |
#else |
#define MAYBE_ProcessPKP ProcessPKP |
+#define MAYBE_ProcessPKPAndSendReport ProcessPKPAndSendReport |
+#define MAYBE_ProcessPKPReportOnly ProcessPKPReportOnly |
+#define MAYBE_ProcessPKPReportOnlyWithNoViolation \ |
+ ProcessPKPReportOnlyWithNoViolation |
#endif |
+namespace { |
+const char kHPKPReportUri[] = "https://hpkp-report.test"; |
+} // namespace |
+ |
// Tests that enabling HPKP on a domain does not affect the HSTS |
// validity/expiration. |
TEST_F(URLRequestTestHTTP, MAYBE_ProcessPKP) { |
+ GURL report_uri(kHPKPReportUri); |
SpawnedTestServer::SSLOptions ssl_options( |
SpawnedTestServer::SSLOptions::CERT_COMMON_NAME_IS_DOMAIN); |
SpawnedTestServer https_test_server(SpawnedTestServer::TYPE_HTTPS, |
@@ -5397,9 +5433,188 @@ TEST_F(URLRequestTestHTTP, MAYBE_ProcessPKP) { |
EXPECT_FALSE(sts_state.include_subdomains); |
EXPECT_FALSE(pkp_state.include_subdomains); |
EXPECT_TRUE(pkp_state.HasPublicKeyPins()); |
+ EXPECT_EQ(report_uri, pkp_state.report_uri); |
EXPECT_NE(sts_state.expiry, pkp_state.expiry); |
} |
+// Tests that reports get sent on HPKP violations when a report-uri is set. |
+TEST_F(URLRequestTestHTTP, MAYBE_ProcessPKPAndSendReport) { |
+ GURL report_uri(kHPKPReportUri); |
+ SpawnedTestServer::SSLOptions ssl_options( |
+ SpawnedTestServer::SSLOptions::CERT_COMMON_NAME_IS_DOMAIN); |
+ SpawnedTestServer https_test_server(SpawnedTestServer::TYPE_HTTPS, |
+ ssl_options, |
+ base::FilePath(kTestFilePath)); |
+ |
+ ASSERT_TRUE(https_test_server.Start()); |
+ |
+ std::string test_server_hostname = https_test_server.GetURL("").host(); |
+ |
+ // Set up a pin for |test_server_hostname|. |
+ TransportSecurityState security_state; |
+ const base::Time current_time(base::Time::Now()); |
+ const base::Time expiry = current_time + base::TimeDelta::FromSeconds(1000); |
+ HashValueVector hashes; |
+ HashValue hash1; |
+ HashValue hash2; |
+ // The values here don't matter, as long as they are different from |
+ // the mocked CertVerifyResult below. |
+ ASSERT_TRUE(hash1.FromString("sha1/111111111111111111111111111=")); |
+ ASSERT_TRUE(hash2.FromString("sha1/222222222222222222222222222=")); |
+ hashes.push_back(hash1); |
+ hashes.push_back(hash2); |
+ security_state.AddHPKP(test_server_hostname, expiry, |
+ false, /* include subdomains */ |
+ hashes, report_uri); |
+ |
+ MockCertificateReportSender mock_report_sender; |
+ security_state.SetReportSender(&mock_report_sender); |
+ |
+ // Set up a MockCertVerifier to trigger a violation of the previously |
+ // set pin. |
+ 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; |
+ HashValue hash3; |
+ ASSERT_TRUE(hash3.FromString("sha1/333333333333333333333333333=")); |
+ verify_result.public_key_hashes.push_back(hash3); |
+ cert_verifier.AddResultForCert(cert.get(), verify_result, OK); |
+ |
+ TestNetworkDelegate network_delegate; |
+ TestURLRequestContext context(true); |
+ context.set_transport_security_state(&security_state); |
+ context.set_network_delegate(&network_delegate); |
+ context.set_cert_verifier(&cert_verifier); |
+ context.Init(); |
+ |
+ // Now send a request to trigger the violation. |
+ TestDelegate d; |
+ scoped_ptr<URLRequest> violating_request(context.CreateRequest( |
+ https_test_server.GetURL("files/simple.html"), DEFAULT_PRIORITY, &d)); |
+ violating_request->Start(); |
+ base::RunLoop().Run(); |
+ |
+ // Check that a report was sent. |
+ EXPECT_EQ(report_uri, mock_report_sender.latest_report_uri()); |
+ ASSERT_FALSE(mock_report_sender.latest_report().empty()); |
+ scoped_ptr<base::Value> value( |
+ base::JSONReader::Read(mock_report_sender.latest_report())); |
+ ASSERT_TRUE(value); |
+ ASSERT_TRUE(value->IsType(base::Value::TYPE_DICTIONARY)); |
+ base::DictionaryValue* report_dict; |
+ ASSERT_TRUE(value->GetAsDictionary(&report_dict)); |
+ std::string report_hostname; |
+ EXPECT_TRUE(report_dict->GetString("hostname", &report_hostname)); |
+ EXPECT_EQ(test_server_hostname, report_hostname); |
+} |
+ |
+// Tests that reports get sent on requests with |
+// Public-Key-Pins-Report-Only headers. |
+TEST_F(URLRequestTestHTTP, MAYBE_ProcessPKPReportOnly) { |
+ GURL report_uri(kHPKPReportUri); |
+ SpawnedTestServer::SSLOptions ssl_options( |
+ SpawnedTestServer::SSLOptions::CERT_COMMON_NAME_IS_DOMAIN); |
+ SpawnedTestServer https_test_server(SpawnedTestServer::TYPE_HTTPS, |
+ ssl_options, |
+ base::FilePath(kTestFilePath)); |
+ |
+ ASSERT_TRUE(https_test_server.Start()); |
+ |
+ std::string test_server_hostname = https_test_server.GetURL("").host(); |
+ |
+ TransportSecurityState security_state; |
+ MockCertificateReportSender mock_report_sender; |
+ security_state.SetReportSender(&mock_report_sender); |
+ |
+ // Set up a MockCertVerifier to violate the pin in the Report-Only |
+ // header. |
+ 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; |
+ HashValue hash; |
+ // This value doesn't matter, as long as it is different from the pins |
+ // for the request to hpkp-headers-report-only.html. |
+ ASSERT_TRUE(hash.FromString("sha1/111111111111111111111111111=")); |
+ verify_result.public_key_hashes.push_back(hash); |
+ cert_verifier.AddResultForCert(cert.get(), verify_result, OK); |
+ |
+ TestNetworkDelegate network_delegate; |
+ TestURLRequestContext context(true); |
+ context.set_transport_security_state(&security_state); |
+ context.set_network_delegate(&network_delegate); |
+ context.set_cert_verifier(&cert_verifier); |
+ context.Init(); |
+ |
+ // Now send a request to trigger the violation. |
+ TestDelegate d; |
+ scoped_ptr<URLRequest> violating_request(context.CreateRequest( |
+ https_test_server.GetURL("files/hpkp-headers-report-only.html"), |
+ DEFAULT_PRIORITY, &d)); |
+ violating_request->Start(); |
+ base::RunLoop().Run(); |
+ |
+ // Check that a report was sent. |
+ EXPECT_EQ(report_uri, mock_report_sender.latest_report_uri()); |
+ ASSERT_FALSE(mock_report_sender.latest_report().empty()); |
+ scoped_ptr<base::Value> value( |
+ base::JSONReader::Read(mock_report_sender.latest_report())); |
+ ASSERT_TRUE(value); |
+ ASSERT_TRUE(value->IsType(base::Value::TYPE_DICTIONARY)); |
+ base::DictionaryValue* report_dict; |
+ ASSERT_TRUE(value->GetAsDictionary(&report_dict)); |
+ std::string report_hostname; |
+ EXPECT_TRUE(report_dict->GetString("hostname", &report_hostname)); |
+ EXPECT_EQ(test_server_hostname, report_hostname); |
+} |
+ |
+// Tests that reports do not get sent on requests with |
+// Public-Key-Pins-Report-Only headers that don't have pin violations. |
+TEST_F(URLRequestTestHTTP, MAYBE_ProcessPKPReportOnlyWithNoViolation) { |
+ GURL report_uri(kHPKPReportUri); |
+ SpawnedTestServer::SSLOptions ssl_options( |
+ SpawnedTestServer::SSLOptions::CERT_COMMON_NAME_IS_DOMAIN); |
+ SpawnedTestServer https_test_server(SpawnedTestServer::TYPE_HTTPS, |
+ ssl_options, |
+ base::FilePath(kTestFilePath)); |
+ |
+ ASSERT_TRUE(https_test_server.Start()); |
+ |
+ std::string test_server_hostname = https_test_server.GetURL("").host(); |
+ |
+ TransportSecurityState security_state; |
+ MockCertificateReportSender mock_report_sender; |
+ security_state.SetReportSender(&mock_report_sender); |
+ |
+ TestNetworkDelegate network_delegate; |
+ MockCertVerifier mock_cert_verifier; |
+ TestURLRequestContext context(true); |
+ context.set_transport_security_state(&security_state); |
+ context.set_network_delegate(&network_delegate); |
+ context.set_cert_verifier(&mock_cert_verifier); |
+ mock_cert_verifier.set_default_result(OK); |
+ context.Init(); |
+ |
+ // Now send a request that does not trigger the violation. |
+ TestDelegate d; |
+ scoped_ptr<URLRequest> request(context.CreateRequest( |
+ https_test_server.GetURL("files/hpkp-headers-report-only.html"), |
+ DEFAULT_PRIORITY, &d)); |
+ request->Start(); |
+ base::RunLoop().Run(); |
+ |
+ // Check that a report was not sent. |
+ EXPECT_EQ(GURL(), mock_report_sender.latest_report_uri()); |
+ EXPECT_EQ(std::string(), mock_report_sender.latest_report()); |
+} |
+ |
TEST_F(URLRequestTestHTTP, PKPNotProcessedOnIP) { |
SpawnedTestServer https_test_server(SpawnedTestServer::TYPE_HTTPS, |
SpawnedTestServer::SSLOptions(), |