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/captive_portal_blocking_page.h"
9 #include "base/callback.h"
10 #include "base/logging.h"
11 #include "base/macros.h"
12 #include "base/prefs/pref_service.h"
13 #include "base/run_loop.h"
14 #include "base/strings/stringprintf.h"
15 #include "chrome/browser/interstitials/security_interstitial_page_test_utils.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/browser/ssl/cert_report_helper.h"
18 #include "chrome/browser/ssl/certificate_reporting_test_utils.h"
19 #include "chrome/browser/ssl/ssl_cert_reporter.h"
20 #include "chrome/browser/ui/browser.h"
21 #include "chrome/browser/ui/tabs/tab_strip_model.h"
22 #include "chrome/common/pref_names.h"
23 #include "chrome/test/base/in_process_browser_test.h"
24 #include "components/captive_portal/captive_portal_detector.h"
25 #include "content/public/browser/interstitial_page.h"
26 #include "content/public/browser/web_contents.h"
27 #include "content/public/test/browser_test_utils.h"
28 #include "net/cert/x509_certificate.h"
29 #include "testing/gtest/include/gtest/gtest.h"
32 using chrome_browser_interstitials::IsInterstitialDisplayingText
;
33 using chrome_browser_interstitials::SecurityInterstitialIDNTest
;
36 // Partial text in the captive portal interstitial's main paragraph when the
37 // login domain isn't displayed.
38 const char kGenericLoginURLText
[] = "its login page";
39 const char kBrokenSSL
[] = "https://broken.ssl";
40 const char kWiFiSSID
[] = "WiFiSSID";
59 class CaptivePortalBlockingPageForTesting
: public CaptivePortalBlockingPage
{
61 CaptivePortalBlockingPageForTesting(
62 content::WebContents
* web_contents
,
63 const GURL
& request_url
,
64 const GURL
& login_url
,
65 scoped_ptr
<SSLCertReporter
> ssl_cert_reporter
,
66 const net::SSLInfo
& ssl_info
,
67 const base::Callback
<void(bool)>& callback
,
69 const std::string
& wifi_ssid
)
70 : CaptivePortalBlockingPage(web_contents
,
73 ssl_cert_reporter
.Pass(),
77 wifi_ssid_(wifi_ssid
) {}
80 bool IsWifiConnection() const override
{ return is_wifi_
; }
81 std::string
GetWiFiSSID() const override
{ return wifi_ssid_
; }
83 const std::string wifi_ssid_
;
86 class CaptivePortalBlockingPageTest
87 : public certificate_reporting_test_utils::CertificateReportingTest
{
89 CaptivePortalBlockingPageTest() {}
91 void TestInterstitial(bool is_wifi_connection
,
92 const std::string
& wifi_ssid
,
93 const GURL
& login_url
,
94 ExpectWiFi expect_wifi
,
95 ExpectWiFiSSID expect_wifi_ssid
,
96 ExpectLoginURL expect_login_url
,
97 scoped_ptr
<SSLCertReporter
> ssl_cert_reporter
,
98 const std::string
& expected_login_hostname
);
100 void TestInterstitial(bool is_wifi_connection
,
101 const std::string
& wifi_ssid
,
102 const GURL
& login_url
,
103 ExpectWiFi expect_wifi
,
104 ExpectWiFiSSID expect_wifi_ssid
,
105 ExpectLoginURL expect_login_url
);
107 void TestInterstitial(bool is_wifi_connection
,
108 const std::string
& wifi_ssid
,
109 const GURL
& login_url
,
110 ExpectWiFi expect_wifi
,
111 ExpectWiFiSSID expect_wifi_ssid
,
112 ExpectLoginURL expect_login_url
,
113 scoped_ptr
<SSLCertReporter
> ssl_cert_reporter
);
115 void TestCertReporting(certificate_reporting_test_utils::OptIn opt_in
);
118 DISALLOW_COPY_AND_ASSIGN(CaptivePortalBlockingPageTest
);
121 void CaptivePortalBlockingPageTest::TestInterstitial(
122 bool is_wifi_connection
,
123 const std::string
& wifi_ssid
,
124 const GURL
& login_url
,
125 ExpectWiFi expect_wifi
,
126 ExpectWiFiSSID expect_wifi_ssid
,
127 ExpectLoginURL expect_login_url
,
128 scoped_ptr
<SSLCertReporter
> ssl_cert_reporter
,
129 const std::string
& expected_login_hostname
) {
130 content::WebContents
* contents
=
131 browser()->tab_strip_model()->GetActiveWebContents();
133 net::SSLInfo ssl_info
;
134 ssl_info
.cert
= new net::X509Certificate(
135 login_url
.host(), "CA", base::Time::Max(), base::Time::Max());
136 // Blocking page is owned by the interstitial.
137 CaptivePortalBlockingPage
* blocking_page
=
138 new CaptivePortalBlockingPageForTesting(
139 contents
, GURL(kBrokenSSL
), login_url
, ssl_cert_reporter
.Pass(),
140 ssl_info
, base::Callback
<void(bool)>(), is_wifi_connection
,
142 blocking_page
->Show();
144 WaitForInterstitialAttach(contents
);
146 WaitForRenderFrameReady(contents
->GetInterstitialPage()->GetMainFrame()));
147 EXPECT_EQ(expect_wifi
== EXPECT_WIFI_YES
,
148 IsInterstitialDisplayingText(contents
->GetInterstitialPage(),
150 if (!wifi_ssid
.empty()) {
151 EXPECT_EQ(expect_wifi_ssid
== EXPECT_WIFI_SSID_YES
,
152 IsInterstitialDisplayingText(contents
->GetInterstitialPage(),
155 EXPECT_EQ(expect_login_url
== EXPECT_LOGIN_URL_YES
,
156 IsInterstitialDisplayingText(contents
->GetInterstitialPage(),
157 expected_login_hostname
));
158 EXPECT_EQ(expect_login_url
== EXPECT_LOGIN_URL_NO
,
159 IsInterstitialDisplayingText(contents
->GetInterstitialPage(),
160 kGenericLoginURLText
));
163 void CaptivePortalBlockingPageTest::TestInterstitial(
164 bool is_wifi_connection
,
165 const std::string
& wifi_ssid
,
166 const GURL
& login_url
,
167 ExpectWiFi expect_wifi
,
168 ExpectWiFiSSID expect_wifi_ssid
,
169 ExpectLoginURL expect_login_url
) {
170 TestInterstitial(is_wifi_connection
, wifi_ssid
, login_url
, expect_wifi
,
171 expect_wifi_ssid
, expect_login_url
, nullptr,
175 void CaptivePortalBlockingPageTest::TestInterstitial(
176 bool is_wifi_connection
,
177 const std::string
& wifi_ssid
,
178 const GURL
& login_url
,
179 ExpectWiFi expect_wifi
,
180 ExpectWiFiSSID expect_wifi_ssid
,
181 ExpectLoginURL expect_login_url
,
182 scoped_ptr
<SSLCertReporter
> ssl_cert_reporter
) {
183 TestInterstitial(is_wifi_connection
, wifi_ssid
, login_url
, expect_wifi
,
184 expect_wifi_ssid
, expect_login_url
, ssl_cert_reporter
.Pass(),
188 void CaptivePortalBlockingPageTest::TestCertReporting(
189 certificate_reporting_test_utils::OptIn opt_in
) {
190 ASSERT_NO_FATAL_FAILURE(SetUpMockReporter());
192 certificate_reporting_test_utils::SetCertReportingOptIn(browser(), opt_in
);
193 base::RunLoop run_loop
;
194 scoped_ptr
<SSLCertReporter
> ssl_cert_reporter
=
195 certificate_reporting_test_utils::SetUpMockSSLCertReporter(
197 opt_in
== certificate_reporting_test_utils::EXTENDED_REPORTING_OPT_IN
198 ? certificate_reporting_test_utils::CERT_REPORT_EXPECTED
199 : certificate_reporting_test_utils::CERT_REPORT_NOT_EXPECTED
);
201 const GURL
kLandingUrl(captive_portal::CaptivePortalDetector::kDefaultURL
);
202 TestInterstitial(true, std::string(), kLandingUrl
, EXPECT_WIFI_YES
,
203 EXPECT_WIFI_SSID_NO
, EXPECT_LOGIN_URL_NO
,
204 ssl_cert_reporter
.Pass());
206 EXPECT_EQ(std::string(), GetLatestHostnameReported());
208 content::WebContents
* tab
=
209 browser()->tab_strip_model()->GetActiveWebContents();
210 tab
->GetInterstitialPage()->DontProceed();
212 if (opt_in
== certificate_reporting_test_utils::EXTENDED_REPORTING_OPT_IN
) {
213 // Check that the mock reporter received a request to send a report.
215 EXPECT_EQ(GURL(kBrokenSSL
).host(), GetLatestHostnameReported());
217 EXPECT_EQ(std::string(), GetLatestHostnameReported());
221 // If the connection is not a Wi-Fi connection, the wired network version of the
222 // captive portal interstitial should be displayed.
223 IN_PROC_BROWSER_TEST_F(CaptivePortalBlockingPageTest
,
224 WiredNetwork_LoginURL
) {
225 TestInterstitial(false, std::string(),
226 GURL("http://captive.portal/landing_url"), EXPECT_WIFI_NO
,
227 EXPECT_WIFI_SSID_NO
, EXPECT_LOGIN_URL_YES
);
230 // Same as above, but SSID is available, so the connection should be assumed to
232 IN_PROC_BROWSER_TEST_F(CaptivePortalBlockingPageTest
,
233 WiredNetwork_LoginURL_With_SSID
) {
234 TestInterstitial(false, kWiFiSSID
, GURL("http://captive.portal/landing_url"),
235 EXPECT_WIFI_YES
, EXPECT_WIFI_SSID_YES
, EXPECT_LOGIN_URL_YES
);
238 // Same as above, expect the login URL is the same as the captive portal ping
239 // url (i.e. the portal intercepts requests without using HTTP redirects), in
240 // which case the login URL shouldn't be displayed.
241 IN_PROC_BROWSER_TEST_F(CaptivePortalBlockingPageTest
,
242 WiredNetwork_NoLoginURL
) {
243 const GURL
kLandingUrl(captive_portal::CaptivePortalDetector::kDefaultURL
);
244 TestInterstitial(false, std::string(), kLandingUrl
, EXPECT_WIFI_NO
,
245 EXPECT_WIFI_SSID_NO
, EXPECT_LOGIN_URL_NO
);
248 // Same as above, but SSID is available, so the connection should be assumed to
250 IN_PROC_BROWSER_TEST_F(CaptivePortalBlockingPageTest
,
251 WiredNetwork_NoLoginURL_With_SSID
) {
252 const GURL
kLandingUrl(captive_portal::CaptivePortalDetector::kDefaultURL
);
253 TestInterstitial(false, kWiFiSSID
, kLandingUrl
, EXPECT_WIFI_YES
,
254 EXPECT_WIFI_SSID_YES
, EXPECT_LOGIN_URL_NO
);
257 // If the connection is a Wi-Fi connection, the Wi-Fi version of the captive
258 // portal interstitial should be displayed.
259 IN_PROC_BROWSER_TEST_F(CaptivePortalBlockingPageTest
,
260 WiFi_SSID_LoginURL
) {
261 TestInterstitial(true, kWiFiSSID
, GURL("http://captive.portal/landing_url"),
262 EXPECT_WIFI_YES
, EXPECT_WIFI_SSID_YES
, EXPECT_LOGIN_URL_YES
);
265 // Same as above, with login URL but no SSID.
266 IN_PROC_BROWSER_TEST_F(CaptivePortalBlockingPageTest
,
267 WiFi_NoSSID_LoginURL
) {
268 TestInterstitial(true, std::string(),
269 GURL("http://captive.portal/landing_url"), EXPECT_WIFI_YES
,
270 EXPECT_WIFI_SSID_NO
, EXPECT_LOGIN_URL_YES
);
273 // Same as above, with SSID but no login URL.
274 IN_PROC_BROWSER_TEST_F(CaptivePortalBlockingPageTest
,
275 WiFi_SSID_NoLoginURL
) {
276 const GURL
kLandingUrl(captive_portal::CaptivePortalDetector::kDefaultURL
);
277 TestInterstitial(true, kWiFiSSID
, kLandingUrl
,
278 EXPECT_WIFI_YES
, EXPECT_WIFI_SSID_YES
, EXPECT_LOGIN_URL_NO
);
281 // Same as above, with no SSID and no login URL.
282 IN_PROC_BROWSER_TEST_F(CaptivePortalBlockingPageTest
,
283 WiFi_NoSSID_NoLoginURL
) {
284 const GURL
kLandingUrl(captive_portal::CaptivePortalDetector::kDefaultURL
);
285 TestInterstitial(true, std::string(), kLandingUrl
, EXPECT_WIFI_YES
,
286 EXPECT_WIFI_SSID_NO
, EXPECT_LOGIN_URL_NO
);
289 IN_PROC_BROWSER_TEST_F(CaptivePortalBlockingPageTest
, CertReportingOptIn
) {
290 // This test should only run if the Finch config is such that reports
291 // will be sent when opted in. This tests that a report *is* sent when
292 // the user opts in under such a Finch config, and the below test
293 // tests that a report *is not* sent when the user doesn't opt in
294 // (under any Finch config).
295 if (certificate_reporting_test_utils::GetReportExpectedFromFinch() ==
296 certificate_reporting_test_utils::CERT_REPORT_EXPECTED
) {
298 certificate_reporting_test_utils::EXTENDED_REPORTING_OPT_IN
);
302 IN_PROC_BROWSER_TEST_F(CaptivePortalBlockingPageTest
, CertReportingOptOut
) {
304 certificate_reporting_test_utils::EXTENDED_REPORTING_DO_NOT_OPT_IN
);
307 class CaptivePortalBlockingPageIDNTest
: public SecurityInterstitialIDNTest
{
309 // SecurityInterstitialIDNTest implementation
310 SecurityInterstitialPage
* CreateInterstitial(
311 content::WebContents
* contents
,
312 const GURL
& request_url
) const override
{
313 net::SSLInfo empty_ssl_info
;
314 // Blocking page is owned by the interstitial.
315 CaptivePortalBlockingPage
* blocking_page
=
316 new CaptivePortalBlockingPageForTesting(
317 contents
, GURL(kBrokenSSL
), request_url
, nullptr, empty_ssl_info
,
318 base::Callback
<void(bool)>(), false, "");
319 return blocking_page
;
323 // Test that an IDN login domain is decoded properly.
324 IN_PROC_BROWSER_TEST_F(CaptivePortalBlockingPageIDNTest
,
325 ShowLoginIDNIfPortalRedirectsDetectionURL
) {
326 EXPECT_TRUE(VerifyIDNDecoded());