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 |