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.
5 #include "chrome/browser/ssl/security_state_model.h"
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"
35 const base::FilePath::CharType kDocRoot
[] =
36 FILE_PATH_LITERAL("chrome/test/data");
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
);
46 SecurityStateModel
* model
= SecurityStateModel::FromWebContents(contents
);
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);
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
));
65 class SecurityStateModelTest
: public CertVerifierBrowserTest
{
67 SecurityStateModelTest()
68 : https_server_(net::SpawnedTestServer::TYPE_HTTPS
,
69 SSLOptions(SSLOptions::CERT_OK
),
70 base::FilePath(kDocRoot
)) {}
72 void SetUpCommandLine(base::CommandLine
* command_line
) override
{
73 // Browser will both run and display insecure content.
74 command_line
->AppendSwitch(switches::kAllowRunningInsecureContent
);
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();
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
);
101 void SetUpMockCertVerifierForHttpsServer(net::CertStatus cert_status
,
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
;
109 mock_cert_verifier()->AddResultForCert(cert
.get(), verify_result
,
113 net::SpawnedTestServer https_server_
;
116 typedef net::SpawnedTestServer::SSLOptions SSLOptions
;
118 DISALLOW_COPY_AND_ASSIGN(SecurityStateModelTest
);
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
);
129 SecurityStateModel
* model
= SecurityStateModel::FromWebContents(contents
);
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
);
146 IN_PROC_BROWSER_TEST_F(SecurityStateModelTest
, HttpsPage
) {
147 ASSERT_TRUE(https_server_
.Start());
148 SetUpMockCertVerifierForHttpsServer(0, net::OK
);
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 */);
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
164 SetUpMockCertVerifierForHttpsServer(net::CERT_STATUS_SHA1_SIGNATURE_PRESENT
,
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 */);
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
);
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 */);
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();",
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 */);
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 */);
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
237 SetUpMockCertVerifierForHttpsServer(net::CERT_STATUS_SHA1_SIGNATURE_PRESENT
,
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 */);
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();",
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 */);
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 */);
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
);
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 */);
303 ProceedThroughInterstitial(
304 browser()->tab_strip_model()->GetActiveWebContents());
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 */);
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 */);
326 // TODO(estark): test the following cases:
327 // - warning SHA1 (2016 expiration)
328 // - active mixed content + warning SHA1
329 // - broken HTTPS + warning SHA1