| // Copyright 2019 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "weblayer/test/weblayer_browser_test.h" |
| |
| #include "base/files/file_path.h" |
| #include "base/macros.h" |
| #include "base/optional.h" |
| #include "build/build_config.h" |
| #include "net/test/embedded_test_server/embedded_test_server.h" |
| #include "weblayer/browser/ssl_error_handler.h" |
| #include "weblayer/shell/browser/shell.h" |
| #include "weblayer/test/interstitial_utils.h" |
| #include "weblayer/test/load_completion_observer.h" |
| #include "weblayer/test/test_navigation_observer.h" |
| #include "weblayer/test/weblayer_browser_test_utils.h" |
| |
| namespace weblayer { |
| |
| class SSLBrowserTest : public WebLayerBrowserTest { |
| public: |
| SSLBrowserTest() = default; |
| ~SSLBrowserTest() override = default; |
| |
| // WebLayerBrowserTest: |
| void SetUpOnMainThread() override { |
| https_server_ = std::make_unique<net::EmbeddedTestServer>( |
| net::EmbeddedTestServer::TYPE_HTTPS); |
| https_server_->AddDefaultHandlers( |
| base::FilePath(FILE_PATH_LITERAL("weblayer/test/data"))); |
| |
| https_server_mismatched_ = std::make_unique<net::EmbeddedTestServer>( |
| net::EmbeddedTestServer::TYPE_HTTPS); |
| https_server_mismatched_->SetSSLConfig( |
| net::EmbeddedTestServer::CERT_MISMATCHED_NAME); |
| https_server_mismatched_->AddDefaultHandlers( |
| base::FilePath(FILE_PATH_LITERAL("weblayer/test/data"))); |
| |
| ASSERT_TRUE(https_server_->Start()); |
| ASSERT_TRUE(https_server_mismatched_->Start()); |
| } |
| |
| void PostRunTestOnMainThread() override { |
| https_server_.reset(); |
| https_server_mismatched_.reset(); |
| WebLayerBrowserTest::PostRunTestOnMainThread(); |
| } |
| |
| void NavigateToOkPage() { |
| ASSERT_EQ("127.0.0.1", ok_url().host()); |
| NavigateAndWaitForCompletion(ok_url(), shell()); |
| EXPECT_FALSE(IsShowingSecurityInterstitial(shell()->tab())); |
| } |
| |
| void NavigateToPageWithSslErrorExpectSSLInterstitial() { |
| // Do a navigation that should result in an SSL error. |
| NavigateAndWaitForFailure(bad_ssl_url(), shell()); |
| // First check that there *is* an interstitial. |
| ASSERT_TRUE(IsShowingSecurityInterstitial(shell()->tab())); |
| |
| // Now verify that the interstitial is in fact an SSL interstitial. |
| EXPECT_TRUE(IsShowingSSLInterstitial(shell()->tab())); |
| |
| // TODO(blundell): Check the security state once security state is available |
| // via the public WebLayer API, following the example of //chrome's |
| // ssl_browsertest.cc's CheckAuthenticationBrokenState() function. |
| } |
| |
| void NavigateToPageWithSslErrorExpectCaptivePortalInterstitial() { |
| // Do a navigation that should result in an SSL error. |
| NavigateAndWaitForFailure(bad_ssl_url(), shell()); |
| // First check that there *is* an interstitial. |
| ASSERT_TRUE(IsShowingSecurityInterstitial(shell()->tab())); |
| |
| // Now verify that the interstitial is in fact a captive portal |
| // interstitial. |
| EXPECT_TRUE(IsShowingCaptivePortalInterstitial(shell()->tab())); |
| |
| // TODO(blundell): Check the security state once security state is available |
| // via the public WebLayer API, following the example of //chrome's |
| // ssl_browsertest.cc's CheckAuthenticationBrokenState() function. |
| } |
| |
| void NavigateToPageWithSslErrorExpectNotBlocked() { |
| NavigateAndWaitForCompletion(bad_ssl_url(), shell()); |
| EXPECT_FALSE(IsShowingSecurityInterstitial(shell()->tab())); |
| |
| // TODO(blundell): Check the security state once security state is available |
| // via the public WebLayer API, following the example of //chrome's |
| // ssl_browsertest.cc's CheckAuthenticationBrokenState() function. |
| } |
| |
| void SendInterstitialNavigationCommandAndWait( |
| bool proceed, |
| base::Optional<GURL> previous_url = base::nullopt) { |
| GURL expected_url = |
| proceed ? bad_ssl_url() : previous_url.value_or(ok_url()); |
| ASSERT_TRUE(IsShowingSSLInterstitial(shell()->tab())); |
| |
| TestNavigationObserver navigation_observer( |
| expected_url, TestNavigationObserver::NavigationEvent::Completion, |
| shell()); |
| ExecuteScript(shell(), |
| "window.certificateErrorPageController." + |
| std::string(proceed ? "proceed" : "dontProceed") + "();", |
| false /*use_separate_isolate*/); |
| navigation_observer.Wait(); |
| EXPECT_FALSE(IsShowingSSLInterstitial(shell()->tab())); |
| } |
| |
| void SendInterstitialReloadCommandAndWait() { |
| ASSERT_TRUE(IsShowingSSLInterstitial(shell()->tab())); |
| |
| LoadCompletionObserver load_observer(shell()); |
| ExecuteScript(shell(), "window.certificateErrorPageController.reload();", |
| false /*use_separate_isolate*/); |
| load_observer.Wait(); |
| |
| // Should still be showing the SSL interstitial after the reload command is |
| // processed. |
| EXPECT_TRUE(IsShowingSSLInterstitial(shell()->tab())); |
| } |
| |
| #if defined(OS_ANDROID) |
| void SendInterstitialOpenLoginCommandAndWait() { |
| ASSERT_TRUE(IsShowingCaptivePortalInterstitial(shell()->tab())); |
| |
| // Note: The embedded test server cannot actually load the captive portal |
| // login URL, so simply detect the start of the navigation to the page. |
| TestNavigationObserver navigation_observer( |
| GetCaptivePortalLoginPageUrlForTesting(), |
| TestNavigationObserver::NavigationEvent::Start, shell()); |
| ExecuteScript(shell(), "window.certificateErrorPageController.openLogin();", |
| false /*use_separate_isolate*/); |
| navigation_observer.Wait(); |
| } |
| #endif |
| |
| void NavigateToOtherOkPage() { |
| NavigateAndWaitForCompletion(https_server_->GetURL("/simple_page2.html"), |
| shell()); |
| EXPECT_FALSE(IsShowingSecurityInterstitial(shell()->tab())); |
| } |
| |
| GURL ok_url() { return https_server_->GetURL("/simple_page.html"); } |
| GURL bad_ssl_url() { |
| return https_server_mismatched_->GetURL("/simple_page.html"); |
| } |
| |
| protected: |
| std::unique_ptr<net::EmbeddedTestServer> https_server_; |
| std::unique_ptr<net::EmbeddedTestServer> https_server_mismatched_; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(SSLBrowserTest); |
| }; |
| |
| // Tests clicking "take me back" on the interstitial page. |
| IN_PROC_BROWSER_TEST_F(SSLBrowserTest, TakeMeBack) { |
| NavigateToOkPage(); |
| NavigateToPageWithSslErrorExpectSSLInterstitial(); |
| |
| // Click "Take me back". |
| SendInterstitialNavigationCommandAndWait(false /*proceed*/); |
| |
| // Check that it's possible to navigate to a new page. |
| NavigateToOtherOkPage(); |
| |
| // Navigate to the bad SSL page again, an interstitial shows again (in |
| // contrast to what would happen had the user chosen to proceed). |
| NavigateToPageWithSslErrorExpectSSLInterstitial(); |
| } |
| |
| // Tests clicking "take me back" on the interstitial page when there's no |
| // navigation history. The user should be taken to a safe page (about:blank). |
| IN_PROC_BROWSER_TEST_F(SSLBrowserTest, TakeMeBackEmptyNavigationHistory) { |
| NavigateToPageWithSslErrorExpectSSLInterstitial(); |
| |
| // Click "Take me back". |
| SendInterstitialNavigationCommandAndWait(false /*proceed*/, |
| GURL("about:blank")); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SSLBrowserTest, Reload) { |
| NavigateToOkPage(); |
| NavigateToPageWithSslErrorExpectSSLInterstitial(); |
| |
| SendInterstitialReloadCommandAndWait(); |
| |
| // TODO(blundell): Ideally we would fix the SSL error, reload, and verify |
| // that the SSL interstitial isn't showing. However, currently this doesn't |
| // work: Calling ResetSSLConfig() on |http_server_mismatched_| passing |
| // CERT_OK does not cause future reloads or navigations to bad_ssl_url() to |
| // succeed; they still fail and pop an interstitial. I verified that the |
| // LoadCompletionObserver is in fact waiting for a new load, i.e., there is |
| // actually a *new* SSL interstitial popped up. From looking at the |
| // ResetSSLConfig() impl there shouldn't be any waiting or anything needed |
| // within the client. |
| } |
| |
| // Tests clicking proceed link on the interstitial page. This is a PRE_ test |
| // because it also acts as setup for the test below which verifies the behavior |
| // across restarts. |
| IN_PROC_BROWSER_TEST_F(SSLBrowserTest, PRE_Proceed) { |
| NavigateToOkPage(); |
| NavigateToPageWithSslErrorExpectSSLInterstitial(); |
| SendInterstitialNavigationCommandAndWait(true /*proceed*/); |
| |
| // Go back to an OK page, then try to navigate again. The "Proceed" decision |
| // should be saved, so no interstitial is shown this time. |
| NavigateToOkPage(); |
| NavigateToPageWithSslErrorExpectNotBlocked(); |
| } |
| |
| // The proceed decision is not perpetuated across WebLayer sessions, i.e. |
| // WebLayer will block again when navigating to the same bad page that was |
| // previously proceeded through. |
| IN_PROC_BROWSER_TEST_F(SSLBrowserTest, Proceed) { |
| NavigateToPageWithSslErrorExpectSSLInterstitial(); |
| } |
| |
| // Tests navigating away from the interstitial page. |
| IN_PROC_BROWSER_TEST_F(SSLBrowserTest, NavigateAway) { |
| NavigateToOkPage(); |
| NavigateToPageWithSslErrorExpectSSLInterstitial(); |
| NavigateToOtherOkPage(); |
| } |
| |
| // Tests the scenario where the OS reports that an SSL error is due to a |
| // captive portal. A captive portal interstitial should be displayed. The test |
| // then switches OS captive portal status to false and reloads the page. This |
| // time, a normal SSL interstitial should be displayed. |
| IN_PROC_BROWSER_TEST_F(SSLBrowserTest, OSReportsCaptivePortal) { |
| SetDiagnoseSSLErrorsAsCaptivePortalForTesting(true); |
| |
| NavigateToPageWithSslErrorExpectCaptivePortalInterstitial(); |
| |
| // Check that clearing the test setting causes behavior to revert to normal. |
| SetDiagnoseSSLErrorsAsCaptivePortalForTesting(false); |
| NavigateToPageWithSslErrorExpectSSLInterstitial(); |
| } |
| |
| #if defined(OS_ANDROID) |
| // Tests that after reaching a captive portal interstitial, clicking on the |
| // connect link will cause a navigation to the login page. |
| IN_PROC_BROWSER_TEST_F(SSLBrowserTest, CaptivePortalConnectToLoginPage) { |
| SetDiagnoseSSLErrorsAsCaptivePortalForTesting(true); |
| |
| NavigateToPageWithSslErrorExpectCaptivePortalInterstitial(); |
| |
| SendInterstitialOpenLoginCommandAndWait(); |
| } |
| #endif |
| |
| } // namespace weblayer |