chromium/chrome/browser/ssl/typed_navigation_upgrade_throttle_browsertest.cc

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <vector>

#include "base/containers/contains.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/history/history_service_factory.h"
#include "chrome/browser/history/history_test_utils.h"
#include "chrome/browser/interstitials/security_interstitial_page_test_utils.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/location_bar/location_bar.h"
#include "chrome/browser/ui/omnibox/omnibox_tab_helper.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/interactive_test_utils.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/error_page/common/net_error_info.h"
#include "components/metrics/content/subprocess_metrics_provider.h"
#include "components/omnibox/browser/omnibox_controller.h"
#include "components/omnibox/browser/omnibox_edit_model.h"
#include "components/omnibox/common/omnibox_features.h"
#include "components/security_interstitials/content/ssl_error_handler.h"
#include "components/security_interstitials/core/omnibox_https_upgrade_metrics.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/content_mock_cert_verifier.h"
#include "content/public/test/test_navigation_observer.h"
#include "content/public/test/url_loader_interceptor.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/cert_test_util.h"
#include "net/test/test_data_directory.h"
#include "services/network/test/test_url_loader_factory.h"
#include "services/network/test/test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "typed_navigation_upgrade_throttle.h"
#include "ui/base/clipboard/clipboard.h"
#include "ui/base/clipboard/scoped_clipboard_writer.h"

Event;
kEventHistogram;

namespace {

// Test URLs that load fine.
const char* const kSiteWithHttp =;
const char* const kSiteWithGoodHttps =;

// Site that loads fine over HTTPS and redirects to kSiteWithGoodHttps.
const char* const kSiteWithGoodHttpsRedirect =;

// Site that returns an SSL error over HTTPS (which would normally show an SSL
// interstitial) but loads fine over HTTP.
const char* const kSiteWithBadHttps =;

// Site that loads slowly over HTTPS, but loads fine over HTTP.
const char* const kSiteWithSlowHttps =;

// Site that returns a connection error over HTTPS but loads fine over HTTP.
const char* const kSiteWithNetError =;

// Site (likely on an intranet) that contains a non-registerable or
// non-assignable domain name (eg: a gTLD that has not been assigned by IANA)
// that therefore is unlikely to support HTTPS.
const char* const kNonUniqueHostname1 =;
const char* const kNonUniqueHostname2 =;

// Hostname of the URL of the search results page when the user types a search
// query in the omnibox.
const char* const kGoogleSearchHost =;

const char kNetErrorHistogram[] =;

enum class NavigationExpectation {};

enum class UpgradeExpectation {};

std::string GetURLWithoutScheme(const GURL& url) {}

GURL MakeHttpsURL(const std::string& url_without_scheme) {}

GURL MakeHttpURL(const std::string& url_without_scheme) {}

GURL MakeURLWithPort(const std::string& url_without_scheme,
                     const std::string& scheme,
                     int port) {}

GURL MakeHttpsURLWithPort(const std::string& url_without_scheme, int port) {}

GURL MakeHttpURLWithPort(const std::string& url_without_scheme, int port) {}

// Stores the given text to clipboard.
void SetClipboardText(const std::u16string& text) {}

}  // namespace

class TypedNavigationUpgradeThrottleBrowserTest
    : public InProcessBrowserTest,
      public testing::WithParamInterface<bool /* IsFeatureEnabled */> {};

INSTANTIATE_TEST_SUITE_P();

// If the user types a full HTTP URL, the navigation should end up on that
// exact URL.
IN_PROC_BROWSER_TEST_P(TypedNavigationUpgradeThrottleBrowserTest,
                       UrlTypedWithHttpScheme_ShouldNotUpgrade) {}

// If the user types a full HTTPS URL, the navigation should end up on that
// exact URL.
IN_PROC_BROWSER_TEST_P(TypedNavigationUpgradeThrottleBrowserTest,
                       UrlTypedWithHttpsScheme_ShouldNotUpgrade) {}

// If the user types a full HTTPS URL, the navigation should end up on that
// exact URL, even if the site has an SSL error.
IN_PROC_BROWSER_TEST_P(TypedNavigationUpgradeThrottleBrowserTest,
                       UrlTypedWithHttpsScheme_BrokenSSL_ShouldNotUpgrade) {}

// If the feature is disabled, typing a URL in the omnibox without a scheme
// should load the HTTP version.
IN_PROC_BROWSER_TEST_P(TypedNavigationUpgradeThrottleBrowserTest,
                       UrlTypedWithoutScheme_FeatureDisabled_ShouldNotUpgrade) {}

// Test the case when the user types a search keyword. The keyword may or may
// not be a non-unique hostname. The navigation should always result in a
// search and we should never upgrade it to https.
IN_PROC_BROWSER_TEST_P(TypedNavigationUpgradeThrottleBrowserTest,
                       SearchQuery_ShouldNotUpgrade) {}

// Same as SearchQuery_ShouldNotUpgrade but with two words. This is a definite
// search query, and can never be a hostname.
IN_PROC_BROWSER_TEST_P(TypedNavigationUpgradeThrottleBrowserTest,
                       SearchQuery_TwoWords_ShouldNotUpgrade) {}

// Test the case when the user types a non-unique hostname. We shouldn't upgrade
// it to https.
IN_PROC_BROWSER_TEST_P(TypedNavigationUpgradeThrottleBrowserTest,
                       NonUniqueHostnameTypedWithoutScheme_ShouldNotUpgrade) {}

// Test the case when the user types an IP address. We shouldn't upgrade it to
// https.
IN_PROC_BROWSER_TEST_P(TypedNavigationUpgradeThrottleBrowserTest,
                       IPAddressTypedWithoutScheme_ShouldNotUpgrade) {}

// If the feature is enabled, typing a URL in the omnibox without a scheme
// should load the HTTPS version.
IN_PROC_BROWSER_TEST_P(TypedNavigationUpgradeThrottleBrowserTest,
                       UrlTypedWithoutScheme_GoodHttps) {}

// Pressing CTRL+Enter on a query with a scheme shouldn't result
// in an upgrade.
IN_PROC_BROWSER_TEST_P(TypedNavigationUpgradeThrottleBrowserTest,
                       UrlTypedWithScheme_CtrlEnter_HttpUrl_ShouldNotUpgrade) {}

// Same as UrlTypedWithScheme_CtrlEnter_HttpUrl_ShouldNotUpgrade but the scheme
// is https.
IN_PROC_BROWSER_TEST_P(TypedNavigationUpgradeThrottleBrowserTest,
                       UrlTypedWithScheme_CtrlEnter_HttpsUrl_ShouldNotUpgrade) {}

// If the feature is enabled, typing a URL in the omnibox without a scheme
// and hitting CTRL+ENTER should load the HTTPS version.
IN_PROC_BROWSER_TEST_P(TypedNavigationUpgradeThrottleBrowserTest,
                       UrlTypedWithoutScheme_CtrlEnter_GoodHttps) {}

// Regression test for crbug.com/1202967: Paste a hostname in the omnibox and
// press enter. This should default to HTTPS and the upgrade should succeed.
IN_PROC_BROWSER_TEST_P(TypedNavigationUpgradeThrottleBrowserTest,
                       PasteUrlWithoutASchemeAndHitEnter_GoodHttps) {}

// Regression test for crbug.com/1202967: Paste a hostname in the omnibox and
// press enter. This should hit a bad HTTPS URL and fallback to HTTP, never
// showing an interstitial.
IN_PROC_BROWSER_TEST_P(
    TypedNavigationUpgradeThrottleBrowserTest,
    PasteUrlWithoutASchemeAndHitEnter_BadHttps_ShouldFallback) {}

// If the feature is enabled, right clicking and selecting paste & go in the
// omnibox without a scheme should load the HTTPS version.
IN_PROC_BROWSER_TEST_P(TypedNavigationUpgradeThrottleBrowserTest,
                       PasteAndGoUrlWithoutAScheme_GoodHttps) {}

// If the upgraded HTTPS URL is not available because of an SSL error), we
// should load the HTTP URL.
IN_PROC_BROWSER_TEST_P(TypedNavigationUpgradeThrottleBrowserTest,
                       UrlTypedWithoutScheme_BadHttps_ShouldFallback) {}

// Similar to UrlTypedWithoutScheme_BadHttps_ShouldFallback, except this time
// user presses CTRL+Enter to navigate.
IN_PROC_BROWSER_TEST_P(
    TypedNavigationUpgradeThrottleBrowserTest,
    UrlTypedWithoutScheme_CtrlEnter_BadHttps_ShouldFallback) {}

// If the upgraded HTTPS URL is not available because of a net error, we should
// load the HTTP URL.
IN_PROC_BROWSER_TEST_P(TypedNavigationUpgradeThrottleBrowserTest,
                       UrlTypedWithoutScheme_NetError_ShouldFallback) {}

class TypedNavigationUpgradeThrottleFastTimeoutBrowserTest
    : public TypedNavigationUpgradeThrottleBrowserTest {};

INSTANTIATE_TEST_SUITE_P();

// If the upgraded HTTPS URL does not load within the timeout window, we should
// load the HTTP URL.
IN_PROC_BROWSER_TEST_P(TypedNavigationUpgradeThrottleFastTimeoutBrowserTest,
                       UrlTypedWithoutScheme_SlowHttps_ShouldFallback) {}

// Tests redirects. This is a separate class because as there currently doesn't
// seem to be a good way to properly simulate redirects using
// content::URLLoaderInterceptor. Instead, this class uses an EmbeddedTestServer
// with https.
// Previously, AutocompleteInput didn't upgrade URLs with a non-default port to
// HTTPS. This class passes the port of the https server to AutocompleteInput
// which in turn replaces any non-default http port with the https port.
//
// For example, assume that the http EmbeddedTestServer runs on port 5678 and
// the https EmbeddedTestServer runs on port 8765. Then, AutocompleteInput will
// see example.com:5678 and upgrade it to https://example.com:8765.
//
// TODO(crbug.com/40743298): Fold into TypedNavigationUpgradeThrottleBrowserTest
// when URLLoaderInterceptor supports redirects.
class TypedNavigationUpgradeThrottleRedirectBrowserTest
    : public TypedNavigationUpgradeThrottleBrowserTest {};

INSTANTIATE_TEST_SUITE_P();

// If the feature is enabled, typing a URL in the omnibox without a scheme
// should load the HTTPS version. In this test, the HTTPS site redirects to
// a working HTTPS site and a working HTTP site. Both of these cases should
// count as successful upgrades and histogram entries should be recorded.
IN_PROC_BROWSER_TEST_P(
    TypedNavigationUpgradeThrottleRedirectBrowserTest,
    UrlTypedWithoutScheme_GoodHttps_Redirected_ShouldUpgrade) {}

// Similar to UrlTypedWithoutScheme_GoodHttps_Redirected, but this time the
// redirect target is a broken HTTPS page:
// 1. User types a hostname (site-with-good-https-redirect.com).
// 2. Chrome loads the https URL as part of the upgrade.
// 3. The HTTPS URL redirects to a broken HTTPS URL (site-with-bad-https.com).
// 4. Chrome falls back to the http:// URL of the original hostname
//    (site-with-good-https-redirect.com).
// 5. The http:// URL of the original hostname also redirects to the broken
//    HTTPS URL in step 3.
// 6. The navigation ends up showing an interstitial.
IN_PROC_BROWSER_TEST_P(
    TypedNavigationUpgradeThrottleRedirectBrowserTest,
    UrlTypedWithoutScheme_BadHttps_Redirected_ShouldFallback) {}

// Same as UrlTypedWithoutScheme_BadHttps_Redirected_ShouldFallback, but the
// redirect ends up on a net error instead of an SSL error.
IN_PROC_BROWSER_TEST_P(
    TypedNavigationUpgradeThrottleRedirectBrowserTest,
    UrlTypedWithoutScheme_NetError_Redirected_ShouldFallback) {}

// TODO(crbug.com/40154361): Test the following cases:
// - Various types of omnibox entries (URLs typed with a port, URLs in history,
// non-unique URLs such as machine.local, IP addresses etc.
// - Redirects (either in the upgraded HTTPS navigation or in the fallback)
// - Various types of navigation states such as downloads, external protocols
// etc.
// - Non-cert errors such as HTTP 4XX or 5XX.
// - Test cases for crbug.com/1161620.