#include "weblayer/test/weblayer_browser_test.h"
#include "base/files/file_path.h"
#include "base/scoped_observation.h"
#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "components/network_time/network_time_tracker.h"
#include "components/security_interstitials/content/insecure_form_blocking_page.h"
#include "components/security_interstitials/content/ssl_error_assistant.h"
#include "components/security_interstitials/content/ssl_error_handler.h"
#include "net/ssl/ssl_info.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "weblayer/browser/browser_process.h"
#include "weblayer/browser/weblayer_security_blocking_page_factory.h"
#include "weblayer/public/browser.h"
#include "weblayer/public/browser_observer.h"
#include "weblayer/public/error_page.h"
#include "weblayer/public/error_page_delegate.h"
#include "weblayer/public/new_tab_delegate.h"
#include "weblayer/public/tab.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 {
namespace {
#if BUILDFLAG(IS_ANDROID)
class NewTabWaiter : public NewTabDelegate {
public:
explicit NewTabWaiter(const GURL& url) : url_(url) {}
void OnNewTab(Tab* new_tab, NewTabType type) override {
navigation_observer_ = std::make_unique<TestNavigationObserver>(
url_, TestNavigationObserver::NavigationEvent::kStart, new_tab);
run_loop_.Quit();
}
void Wait() {
if (!navigation_observer_)
run_loop_.Run();
navigation_observer_->Wait();
}
private:
GURL url_;
std::unique_ptr<TestNavigationObserver> navigation_observer_;
base::RunLoop run_loop_;
};
#endif
class TestErrorPageDelegate : public ErrorPageDelegate {
public:
bool was_get_error_page_content_called() const {
return was_get_error_page_content_called_;
}
bool OnBackToSafety() override { return false; }
std::unique_ptr<ErrorPage> GetErrorPageContent(
Navigation* navigation) override {
was_get_error_page_content_called_ = true;
return std::make_unique<ErrorPage>();
}
private:
bool was_get_error_page_content_called_ = false;
};
}
class SSLBrowserTest : public WebLayerBrowserTest {
public:
SSLBrowserTest() = default;
SSLBrowserTest(const SSLBrowserTest&) = delete;
SSLBrowserTest& operator=(const SSLBrowserTest&) = delete;
~SSLBrowserTest() override = default;
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")));
https_server_expired_ = std::make_unique<net::EmbeddedTestServer>(
net::EmbeddedTestServer::TYPE_HTTPS);
https_server_expired_->SetSSLConfig(net::EmbeddedTestServer::CERT_EXPIRED);
https_server_expired_->AddDefaultHandlers(
base::FilePath(FILE_PATH_LITERAL("weblayer/test/data")));
ASSERT_TRUE(https_server_->Start());
ASSERT_TRUE(https_server_mismatched_->Start());
ASSERT_TRUE(https_server_expired_->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 NavigateToPageWithMismatchedCertExpectSSLInterstitial() {
NavigateAndWaitForFailure(mismatched_cert_url(), shell());
ASSERT_TRUE(IsShowingSecurityInterstitial(shell()->tab()));
EXPECT_TRUE(IsShowingSSLInterstitial(shell()->tab()));
}
void NavigateToPageWithMismatchedCertExpectCaptivePortalInterstitial() {
NavigateAndWaitForFailure(mismatched_cert_url(), shell());
ASSERT_TRUE(IsShowingSecurityInterstitial(shell()->tab()));
EXPECT_TRUE(IsShowingCaptivePortalInterstitial(shell()->tab()));
}
void NavigateToPageWithExpiredCertExpectSSLInterstitial() {
NavigateAndWaitForFailure(expired_cert_url(), shell());
ASSERT_TRUE(IsShowingSecurityInterstitial(shell()->tab()));
EXPECT_TRUE(IsShowingSSLInterstitial(shell()->tab()));
}
void NavigateToPageWithExpiredCertExpectBadClockInterstitial() {
NavigateAndWaitForFailure(expired_cert_url(), shell());
ASSERT_TRUE(IsShowingSecurityInterstitial(shell()->tab()));
EXPECT_TRUE(IsShowingBadClockInterstitial(shell()->tab()));
}
void NavigateToPageWithMismatchedCertExpectNotBlocked() {
NavigateAndWaitForCompletion(mismatched_cert_url(), shell());
EXPECT_FALSE(IsShowingSecurityInterstitial(shell()->tab()));
}
void SendInterstitialNavigationCommandAndWait(
bool proceed,
absl::optional<GURL> previous_url = absl::nullopt) {
GURL expected_url =
proceed ? mismatched_cert_url() : previous_url.value_or(ok_url());
ASSERT_TRUE(IsShowingSSLInterstitial(shell()->tab()));
TestNavigationObserver navigation_observer(
expected_url, TestNavigationObserver::NavigationEvent::kCompletion,
shell());
ExecuteScript(shell(),
"window.certificateErrorPageController." +
std::string(proceed ? "proceed" : "dontProceed") + "();",
false );
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 );
load_observer.Wait();
EXPECT_TRUE(IsShowingSSLInterstitial(shell()->tab()));
}
#if BUILDFLAG(IS_ANDROID)
void SendInterstitialOpenLoginCommandAndWait() {
ASSERT_TRUE(IsShowingCaptivePortalInterstitial(shell()->tab()));
NewTabWaiter waiter(WebLayerSecurityBlockingPageFactory::
GetCaptivePortalLoginPageUrlForTesting());
shell()->tab()->SetNewTabDelegate(&waiter);
ExecuteScript(shell(), "window.certificateErrorPageController.openLogin();",
false );
waiter.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 mismatched_cert_url() {
return https_server_mismatched_->GetURL("/simple_page.html");
}
GURL expired_cert_url() {
return https_server_expired_->GetURL("/simple_page.html");
}
protected:
std::unique_ptr<net::EmbeddedTestServer> https_server_;
std::unique_ptr<net::EmbeddedTestServer> https_server_mismatched_;
std::unique_ptr<net::EmbeddedTestServer> https_server_expired_;
};
IN_PROC_BROWSER_TEST_F(SSLBrowserTest, TakeMeBack) {
NavigateToOkPage();
NavigateToPageWithMismatchedCertExpectSSLInterstitial();
SendInterstitialNavigationCommandAndWait(false );
NavigateToOtherOkPage();
NavigateToPageWithMismatchedCertExpectSSLInterstitial();
}
IN_PROC_BROWSER_TEST_F(SSLBrowserTest, TakeMeBackEmptyNavigationHistory) {
NavigateToPageWithMismatchedCertExpectSSLInterstitial();
SendInterstitialNavigationCommandAndWait(false ,
GURL("about:blank"));
}
IN_PROC_BROWSER_TEST_F(SSLBrowserTest, Reload) {
NavigateToOkPage();
NavigateToPageWithMismatchedCertExpectSSLInterstitial();
SendInterstitialReloadCommandAndWait();
}
#if BUILDFLAG(IS_ANDROID)
#define PRE_Proceed Proceed
#endif
IN_PROC_BROWSER_TEST_F(SSLBrowserTest, PRE_Proceed) {
NavigateToOkPage();
NavigateToPageWithMismatchedCertExpectSSLInterstitial();
SendInterstitialNavigationCommandAndWait(true );
NavigateToOkPage();
NavigateToPageWithMismatchedCertExpectNotBlocked();
}
#if !BUILDFLAG(IS_ANDROID)
IN_PROC_BROWSER_TEST_F(SSLBrowserTest, Proceed) {
NavigateToPageWithMismatchedCertExpectNotBlocked();
}
#endif
IN_PROC_BROWSER_TEST_F(SSLBrowserTest, NavigateAway) {
NavigateToOkPage();
NavigateToPageWithMismatchedCertExpectSSLInterstitial();
NavigateToOtherOkPage();
}
IN_PROC_BROWSER_TEST_F(SSLBrowserTest, OSReportsCaptivePortal) {
SSLErrorHandler::SetOSReportsCaptivePortalForTesting(true);
NavigateToPageWithMismatchedCertExpectCaptivePortalInterstitial();
SSLErrorHandler::SetOSReportsCaptivePortalForTesting(false);
NavigateToPageWithMismatchedCertExpectSSLInterstitial();
}
#if BUILDFLAG(IS_ANDROID)
IN_PROC_BROWSER_TEST_F(SSLBrowserTest, CaptivePortalConnectToLoginPage) {
SSLErrorHandler::SetOSReportsCaptivePortalForTesting(true);
NavigateToPageWithMismatchedCertExpectCaptivePortalInterstitial();
SendInterstitialOpenLoginCommandAndWait();
}
#endif
IN_PROC_BROWSER_TEST_F(SSLBrowserTest, BadClockInterstitial) {
NavigateToPageWithExpiredCertExpectSSLInterstitial();
BrowserProcess::GetInstance()->GetNetworkTimeTracker()->UpdateNetworkTime(
base::Time::Now() - base::Minutes(10),
base::Milliseconds(1),
base::Milliseconds(500),
base::TimeTicks::Now() );
NavigateToPageWithExpiredCertExpectBadClockInterstitial();
}
IN_PROC_BROWSER_TEST_F(SSLBrowserTest,
CertificateInKnownCaptivePortalsListDetected) {
net::SSLInfo ssl_info_with_known_captive_portal_cert;
net::HashValue captive_portal_public_key;
ASSERT_TRUE(captive_portal_public_key.FromString(
"sha256/fjZPHewEHTrMDX3I1ecEIeoy3WFxHyGplOLv28kIbtI="));
net::HashValueVector public_keys;
public_keys.push_back(captive_portal_public_key);
ssl_info_with_known_captive_portal_cert.public_key_hashes = public_keys;
EXPECT_TRUE(SSLErrorAssistant().IsKnownCaptivePortalCertificate(
ssl_info_with_known_captive_portal_cert));
}
IN_PROC_BROWSER_TEST_F(SSLBrowserTest, ErrorPageNotCalledForMismatch) {
TestErrorPageDelegate error_page_delegate;
shell()->tab()->SetErrorPageDelegate(&error_page_delegate);
NavigateToOkPage();
EXPECT_FALSE(error_page_delegate.was_get_error_page_content_called());
NavigateToPageWithMismatchedCertExpectSSLInterstitial();
EXPECT_FALSE(error_page_delegate.was_get_error_page_content_called());
}
IN_PROC_BROWSER_TEST_F(SSLBrowserTest,
TestDisplaysInsecureFormSubmissionWarning) {
GURL insecure_form_url = https_server_->GetURL("/insecure_form.html");
GURL form_target_url = GURL("http://does-not-exist.test/form_target.html?");
NavigateAndWaitForCompletion(insecure_form_url, shell());
TestNavigationObserver navigation_observer(
form_target_url, TestNavigationObserver::NavigationEvent::kFailure,
shell());
ExecuteScript(shell(), "submitForm();", false );
navigation_observer.Wait();
EXPECT_TRUE(IsShowingInsecureFormInterstitial(shell()->tab()));
}
}