OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "chrome/browser/ssl/security_state_model.h" |
| 6 |
| 7 #include "base/command_line.h" |
| 8 #include "base/files/file_path.h" |
| 9 #include "base/macros.h" |
| 10 #include "base/prefs/pref_service.h" |
| 11 #include "chrome/browser/ssl/cert_verifier_browser_test.h" |
| 12 #include "chrome/browser/ssl/ssl_blocking_page.h" |
| 13 #include "chrome/browser/ui/browser.h" |
| 14 #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| 15 #include "chrome/common/chrome_paths.h" |
| 16 #include "chrome/common/chrome_switches.h" |
| 17 #include "chrome/common/pref_names.h" |
| 18 #include "chrome/test/base/in_process_browser_test.h" |
| 19 #include "chrome/test/base/ui_test_utils.h" |
| 20 #include "content/public/browser/cert_store.h" |
| 21 #include "content/public/browser/interstitial_page.h" |
| 22 #include "content/public/browser/navigation_controller.h" |
| 23 #include "content/public/browser/notification_service.h" |
| 24 #include "content/public/browser/notification_types.h" |
| 25 #include "content/public/browser/web_contents.h" |
| 26 #include "content/public/test/browser_test_utils.h" |
| 27 #include "net/base/net_errors.h" |
| 28 #include "net/cert/cert_status_flags.h" |
| 29 #include "net/cert/cert_verify_result.h" |
| 30 #include "net/cert/mock_cert_verifier.h" |
| 31 #include "net/cert/x509_certificate.h" |
| 32 |
| 33 namespace { |
| 34 |
| 35 const base::FilePath::CharType kDocRoot[] = |
| 36 FILE_PATH_LITERAL("chrome/test/data"); |
| 37 |
| 38 void CheckHTTPSSecurityInfo( |
| 39 content::WebContents* contents, |
| 40 SecurityStateModel::SecurityLevel security_level, |
| 41 SecurityStateModel::SHA1DeprecationStatus sha1_status, |
| 42 SecurityStateModel::MixedContentStatus mixed_content_status, |
| 43 bool expect_cert_error) { |
| 44 ASSERT_TRUE(contents); |
| 45 |
| 46 SecurityStateModel* model = SecurityStateModel::FromWebContents(contents); |
| 47 ASSERT_TRUE(model); |
| 48 const SecurityStateModel::SecurityInfo& security_info = |
| 49 model->security_info(); |
| 50 EXPECT_EQ(security_level, security_info.security_level); |
| 51 EXPECT_EQ(sha1_status, security_info.sha1_deprecation_status); |
| 52 EXPECT_EQ(mixed_content_status, security_info.mixed_content_status); |
| 53 EXPECT_TRUE(security_info.sct_verify_statuses.empty()); |
| 54 EXPECT_TRUE(security_info.scheme_is_cryptographic); |
| 55 EXPECT_EQ(expect_cert_error, |
| 56 net::IsCertStatusError(security_info.cert_status)); |
| 57 EXPECT_GT(security_info.security_bits, 0); |
| 58 |
| 59 int cert_id = security_info.cert_id; |
| 60 content::CertStore* cert_store = content::CertStore::GetInstance(); |
| 61 scoped_refptr<net::X509Certificate> cert; |
| 62 EXPECT_TRUE(cert_store->RetrieveCert(cert_id, &cert)); |
| 63 } |
| 64 |
| 65 class SecurityStateModelTest : public CertVerifierBrowserTest { |
| 66 public: |
| 67 SecurityStateModelTest() |
| 68 : https_server_(net::SpawnedTestServer::TYPE_HTTPS, |
| 69 SSLOptions(SSLOptions::CERT_OK), |
| 70 base::FilePath(kDocRoot)) {} |
| 71 |
| 72 void SetUpCommandLine(base::CommandLine* command_line) override { |
| 73 // Browser will both run and display insecure content. |
| 74 command_line->AppendSwitch(switches::kAllowRunningInsecureContent); |
| 75 } |
| 76 |
| 77 void ProceedThroughInterstitial(content::WebContents* tab) { |
| 78 content::InterstitialPage* interstitial_page = tab->GetInterstitialPage(); |
| 79 ASSERT_TRUE(interstitial_page); |
| 80 ASSERT_EQ(SSLBlockingPage::kTypeForTesting, |
| 81 interstitial_page->GetDelegateForTesting()->GetTypeForTesting()); |
| 82 content::WindowedNotificationObserver observer( |
| 83 content::NOTIFICATION_LOAD_STOP, |
| 84 content::Source<content::NavigationController>(&tab->GetController())); |
| 85 interstitial_page->Proceed(); |
| 86 observer.Wait(); |
| 87 } |
| 88 |
| 89 static bool GetFilePathWithHostAndPortReplacement( |
| 90 const std::string& original_file_path, |
| 91 const net::HostPortPair& host_port_pair, |
| 92 std::string* replacement_path) { |
| 93 std::vector<net::SpawnedTestServer::StringPair> replacement_text; |
| 94 replacement_text.push_back( |
| 95 make_pair("REPLACE_WITH_HOST_AND_PORT", host_port_pair.ToString())); |
| 96 return net::SpawnedTestServer::GetFilePathWithReplacements( |
| 97 original_file_path, replacement_text, replacement_path); |
| 98 } |
| 99 |
| 100 protected: |
| 101 void SetUpMockCertVerifierForHttpsServer(net::CertStatus cert_status, |
| 102 int net_result) { |
| 103 scoped_refptr<net::X509Certificate> cert(https_server_.GetCertificate()); |
| 104 net::CertVerifyResult verify_result; |
| 105 verify_result.is_issued_by_known_root = true; |
| 106 verify_result.verified_cert = cert; |
| 107 verify_result.cert_status = cert_status; |
| 108 |
| 109 mock_cert_verifier()->AddResultForCert(cert.get(), verify_result, |
| 110 net_result); |
| 111 } |
| 112 |
| 113 net::SpawnedTestServer https_server_; |
| 114 |
| 115 private: |
| 116 typedef net::SpawnedTestServer::SSLOptions SSLOptions; |
| 117 |
| 118 DISALLOW_COPY_AND_ASSIGN(SecurityStateModelTest); |
| 119 }; |
| 120 |
| 121 IN_PROC_BROWSER_TEST_F(SecurityStateModelTest, HttpPage) { |
| 122 ASSERT_TRUE(test_server()->Start()); |
| 123 ui_test_utils::NavigateToURL(browser(), |
| 124 test_server()->GetURL("files/ssl/google.html")); |
| 125 content::WebContents* contents = |
| 126 browser()->tab_strip_model()->GetActiveWebContents(); |
| 127 ASSERT_TRUE(contents); |
| 128 |
| 129 SecurityStateModel* model = SecurityStateModel::FromWebContents(contents); |
| 130 ASSERT_TRUE(model); |
| 131 const SecurityStateModel::SecurityInfo& security_info = |
| 132 model->security_info(); |
| 133 EXPECT_EQ(SecurityStateModel::NONE, security_info.security_level); |
| 134 EXPECT_EQ(SecurityStateModel::NO_DEPRECATED_SHA1, |
| 135 security_info.sha1_deprecation_status); |
| 136 EXPECT_EQ(SecurityStateModel::NO_MIXED_CONTENT, |
| 137 security_info.mixed_content_status); |
| 138 EXPECT_TRUE(security_info.sct_verify_statuses.empty()); |
| 139 EXPECT_FALSE(security_info.scheme_is_cryptographic); |
| 140 EXPECT_FALSE(net::IsCertStatusError(security_info.cert_status)); |
| 141 EXPECT_EQ(0, security_info.cert_id); |
| 142 EXPECT_EQ(-1, security_info.security_bits); |
| 143 EXPECT_EQ(0, security_info.connection_status); |
| 144 } |
| 145 |
| 146 IN_PROC_BROWSER_TEST_F(SecurityStateModelTest, HttpsPage) { |
| 147 ASSERT_TRUE(https_server_.Start()); |
| 148 SetUpMockCertVerifierForHttpsServer(0, net::OK); |
| 149 |
| 150 ui_test_utils::NavigateToURL(browser(), |
| 151 https_server_.GetURL("files/ssl/google.html")); |
| 152 CheckHTTPSSecurityInfo(browser()->tab_strip_model()->GetActiveWebContents(), |
| 153 SecurityStateModel::SECURE, |
| 154 SecurityStateModel::NO_DEPRECATED_SHA1, |
| 155 SecurityStateModel::NO_MIXED_CONTENT, |
| 156 false /* expect cert status error */); |
| 157 } |
| 158 |
| 159 IN_PROC_BROWSER_TEST_F(SecurityStateModelTest, SHA1Broken) { |
| 160 ASSERT_TRUE(https_server_.Start()); |
| 161 // The test server uses a long-lived cert by default, so a SHA1 |
| 162 // signature in it will register as a "broken" condition rather than |
| 163 // "warning". |
| 164 SetUpMockCertVerifierForHttpsServer(net::CERT_STATUS_SHA1_SIGNATURE_PRESENT, |
| 165 net::OK); |
| 166 |
| 167 ui_test_utils::NavigateToURL(browser(), |
| 168 https_server_.GetURL("files/ssl/google.html")); |
| 169 CheckHTTPSSecurityInfo(browser()->tab_strip_model()->GetActiveWebContents(), |
| 170 SecurityStateModel::SECURITY_ERROR, |
| 171 SecurityStateModel::DEPRECATED_SHA1_BROKEN, |
| 172 SecurityStateModel::NO_MIXED_CONTENT, |
| 173 false /* expect cert status error */); |
| 174 } |
| 175 |
| 176 IN_PROC_BROWSER_TEST_F(SecurityStateModelTest, MixedContent) { |
| 177 ASSERT_TRUE(test_server()->Start()); |
| 178 ASSERT_TRUE(https_server_.Start()); |
| 179 SetUpMockCertVerifierForHttpsServer(0, net::OK); |
| 180 |
| 181 // Navigate to an HTTPS page that displays mixed content. |
| 182 std::string replacement_path; |
| 183 ASSERT_TRUE(GetFilePathWithHostAndPortReplacement( |
| 184 "files/ssl/page_displays_insecure_content.html", |
| 185 test_server()->host_port_pair(), &replacement_path)); |
| 186 ui_test_utils::NavigateToURL(browser(), |
| 187 https_server_.GetURL(replacement_path)); |
| 188 CheckHTTPSSecurityInfo(browser()->tab_strip_model()->GetActiveWebContents(), |
| 189 SecurityStateModel::NONE, |
| 190 SecurityStateModel::NO_DEPRECATED_SHA1, |
| 191 SecurityStateModel::DISPLAYED_MIXED_CONTENT, |
| 192 false /* expect cert status error */); |
| 193 |
| 194 // Navigate to an HTTPS page that displays mixed content dynamically. |
| 195 ASSERT_TRUE(GetFilePathWithHostAndPortReplacement( |
| 196 "files/ssl/page_with_dynamic_insecure_content.html", |
| 197 test_server()->host_port_pair(), &replacement_path)); |
| 198 ui_test_utils::NavigateToURL(browser(), |
| 199 https_server_.GetURL(replacement_path)); |
| 200 CheckHTTPSSecurityInfo(browser()->tab_strip_model()->GetActiveWebContents(), |
| 201 SecurityStateModel::SECURE, |
| 202 SecurityStateModel::NO_DEPRECATED_SHA1, |
| 203 SecurityStateModel::NO_MIXED_CONTENT, |
| 204 false /* expect cert status error */); |
| 205 // Load the insecure image. |
| 206 bool js_result = false; |
| 207 EXPECT_TRUE(content::ExecuteScriptAndExtractBool( |
| 208 browser()->tab_strip_model()->GetActiveWebContents(), "loadBadImage();", |
| 209 &js_result)); |
| 210 EXPECT_TRUE(js_result); |
| 211 CheckHTTPSSecurityInfo(browser()->tab_strip_model()->GetActiveWebContents(), |
| 212 SecurityStateModel::NONE, |
| 213 SecurityStateModel::NO_DEPRECATED_SHA1, |
| 214 SecurityStateModel::DISPLAYED_MIXED_CONTENT, |
| 215 false /* expect cert status error */); |
| 216 |
| 217 // Navigate to an HTTPS page that runs mixed content. |
| 218 ASSERT_TRUE(GetFilePathWithHostAndPortReplacement( |
| 219 "files/ssl/page_runs_insecure_content.html", |
| 220 test_server()->host_port_pair(), &replacement_path)); |
| 221 ui_test_utils::NavigateToURL(browser(), |
| 222 https_server_.GetURL(replacement_path)); |
| 223 CheckHTTPSSecurityInfo(browser()->tab_strip_model()->GetActiveWebContents(), |
| 224 SecurityStateModel::SECURITY_ERROR, |
| 225 SecurityStateModel::NO_DEPRECATED_SHA1, |
| 226 SecurityStateModel::RAN_AND_DISPLAYED_MIXED_CONTENT, |
| 227 false /* expect cert status error */); |
| 228 } |
| 229 |
| 230 // Same as the test above but with a long-lived SHA1 cert. |
| 231 IN_PROC_BROWSER_TEST_F(SecurityStateModelTest, MixedContentWithBrokenSHA1) { |
| 232 ASSERT_TRUE(test_server()->Start()); |
| 233 ASSERT_TRUE(https_server_.Start()); |
| 234 // The test server uses a long-lived cert by default, so a SHA1 |
| 235 // signature in it will register as a "broken" condition rather than |
| 236 // "warning". |
| 237 SetUpMockCertVerifierForHttpsServer(net::CERT_STATUS_SHA1_SIGNATURE_PRESENT, |
| 238 net::OK); |
| 239 |
| 240 // Navigate to an HTTPS page that displays mixed content. |
| 241 std::string replacement_path; |
| 242 ASSERT_TRUE(GetFilePathWithHostAndPortReplacement( |
| 243 "files/ssl/page_displays_insecure_content.html", |
| 244 test_server()->host_port_pair(), &replacement_path)); |
| 245 ui_test_utils::NavigateToURL(browser(), |
| 246 https_server_.GetURL(replacement_path)); |
| 247 CheckHTTPSSecurityInfo(browser()->tab_strip_model()->GetActiveWebContents(), |
| 248 SecurityStateModel::SECURITY_ERROR, |
| 249 SecurityStateModel::DEPRECATED_SHA1_BROKEN, |
| 250 SecurityStateModel::DISPLAYED_MIXED_CONTENT, |
| 251 false /* expect cert status error */); |
| 252 |
| 253 // Navigate to an HTTPS page that displays mixed content dynamically. |
| 254 ASSERT_TRUE(GetFilePathWithHostAndPortReplacement( |
| 255 "files/ssl/page_with_dynamic_insecure_content.html", |
| 256 test_server()->host_port_pair(), &replacement_path)); |
| 257 ui_test_utils::NavigateToURL(browser(), |
| 258 https_server_.GetURL(replacement_path)); |
| 259 CheckHTTPSSecurityInfo(browser()->tab_strip_model()->GetActiveWebContents(), |
| 260 SecurityStateModel::SECURITY_ERROR, |
| 261 SecurityStateModel::DEPRECATED_SHA1_BROKEN, |
| 262 SecurityStateModel::NO_MIXED_CONTENT, |
| 263 false /* expect cert status error */); |
| 264 // Load the insecure image. |
| 265 bool js_result = false; |
| 266 EXPECT_TRUE(content::ExecuteScriptAndExtractBool( |
| 267 browser()->tab_strip_model()->GetActiveWebContents(), "loadBadImage();", |
| 268 &js_result)); |
| 269 EXPECT_TRUE(js_result); |
| 270 CheckHTTPSSecurityInfo(browser()->tab_strip_model()->GetActiveWebContents(), |
| 271 SecurityStateModel::SECURITY_ERROR, |
| 272 SecurityStateModel::DEPRECATED_SHA1_BROKEN, |
| 273 SecurityStateModel::DISPLAYED_MIXED_CONTENT, |
| 274 false /* expect cert status error */); |
| 275 |
| 276 // Navigate to an HTTPS page that runs mixed content. |
| 277 ASSERT_TRUE(GetFilePathWithHostAndPortReplacement( |
| 278 "files/ssl/page_runs_insecure_content.html", |
| 279 test_server()->host_port_pair(), &replacement_path)); |
| 280 ui_test_utils::NavigateToURL(browser(), |
| 281 https_server_.GetURL(replacement_path)); |
| 282 CheckHTTPSSecurityInfo(browser()->tab_strip_model()->GetActiveWebContents(), |
| 283 SecurityStateModel::SECURITY_ERROR, |
| 284 SecurityStateModel::DEPRECATED_SHA1_BROKEN, |
| 285 SecurityStateModel::RAN_AND_DISPLAYED_MIXED_CONTENT, |
| 286 false /* expect cert status error */); |
| 287 } |
| 288 |
| 289 IN_PROC_BROWSER_TEST_F(SecurityStateModelTest, BrokenHTTPS) { |
| 290 ASSERT_TRUE(test_server()->Start()); |
| 291 ASSERT_TRUE(https_server_.Start()); |
| 292 SetUpMockCertVerifierForHttpsServer(net::CERT_STATUS_DATE_INVALID, |
| 293 net::ERR_CERT_DATE_INVALID); |
| 294 |
| 295 ui_test_utils::NavigateToURL(browser(), |
| 296 https_server_.GetURL("files/ssl/google.html")); |
| 297 CheckHTTPSSecurityInfo(browser()->tab_strip_model()->GetActiveWebContents(), |
| 298 SecurityStateModel::SECURITY_ERROR, |
| 299 SecurityStateModel::NO_DEPRECATED_SHA1, |
| 300 SecurityStateModel::NO_MIXED_CONTENT, |
| 301 true /* expect cert status error */); |
| 302 |
| 303 ProceedThroughInterstitial( |
| 304 browser()->tab_strip_model()->GetActiveWebContents()); |
| 305 |
| 306 CheckHTTPSSecurityInfo(browser()->tab_strip_model()->GetActiveWebContents(), |
| 307 SecurityStateModel::SECURITY_ERROR, |
| 308 SecurityStateModel::NO_DEPRECATED_SHA1, |
| 309 SecurityStateModel::NO_MIXED_CONTENT, |
| 310 true /* expect cert status error */); |
| 311 |
| 312 // Navigate to a broken HTTPS page that displays mixed content. |
| 313 std::string replacement_path; |
| 314 ASSERT_TRUE(GetFilePathWithHostAndPortReplacement( |
| 315 "files/ssl/page_displays_insecure_content.html", |
| 316 test_server()->host_port_pair(), &replacement_path)); |
| 317 ui_test_utils::NavigateToURL(browser(), |
| 318 https_server_.GetURL(replacement_path)); |
| 319 CheckHTTPSSecurityInfo(browser()->tab_strip_model()->GetActiveWebContents(), |
| 320 SecurityStateModel::SECURITY_ERROR, |
| 321 SecurityStateModel::NO_DEPRECATED_SHA1, |
| 322 SecurityStateModel::DISPLAYED_MIXED_CONTENT, |
| 323 true /* expect cert status error */); |
| 324 } |
| 325 |
| 326 // TODO(estark): test the following cases: |
| 327 // - warning SHA1 (2016 expiration) |
| 328 // - active mixed content + warning SHA1 |
| 329 // - broken HTTPS + warning SHA1 |
| 330 |
| 331 } // namespace |
OLD | NEW |