chromium/components/security_interstitials/content/ssl_error_handler_unittest.cc

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

#include "components/security_interstitials/content/ssl_error_handler.h"

#include <memory>
#include <utility>
#include <vector>

#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/metrics/field_trial.h"
#include "base/run_loop.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/simple_test_clock.h"
#include "base/test/simple_test_tick_clock.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "build/chromecast_buildflags.h"
#include "components/captive_portal/content/captive_portal_service.h"
#include "components/captive_portal/core/buildflags.h"
#include "components/captive_portal/core/captive_portal_testing_utils.h"
#include "components/embedder_support/pref_names.h"
#include "components/network_time/network_time_test_utils.h"
#include "components/network_time/network_time_tracker.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/testing_pref_service.h"
#include "components/security_interstitials/content/common_name_mismatch_handler.h"
#include "components/security_interstitials/content/ssl_error_assistant.h"
#include "components/security_interstitials/content/ssl_error_assistant.pb.h"
#include "components/security_interstitials/core/ssl_error_options_mask.h"
#include "components/security_interstitials/core/ssl_error_ui.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/mock_navigation_handle.h"
#include "content/public/test/test_renderer_host.h"
#include "net/base/net_errors.h"
#include "net/cert/cert_status_flags.h"
#include "net/cert/x509_certificate.h"
#include "net/http/http_response_headers.h"
#include "net/ssl/ssl_info.h"
#include "net/test/cert_test_util.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/http_response.h"
#include "net/test/test_certificate_data.h"
#include "net/test/test_data_directory.h"
#include "services/network/test/test_shared_url_loader_factory.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace {

const net::SHA256HashValue kCertPublicKeyHashValue =;

const char kOkayCertName[] =;

const uint32_t kLargeVersionId =;

// These certificates are self signed certificates with relevant issuer common
// names generated using the following openssl command:
//  openssl req -new -x509 -keyout server.pem -out server.pem -days 365 -nodes

// Common name: "Misconfigured Firewall_4GHPOS5412EF"
// Organization name: "Misconfigured Firewall"
const char kMisconfiguredFirewallCert[] =;

// Common name: None
// Organization name: None
const char kCertWithoutOrganizationOrCommonName[] =;

// Runs |quit_closure| on the UI thread once a URL request has been
// seen. Returns a request that hangs.
std::unique_ptr<net::test_server::HttpResponse> WaitForRequest(
    base::OnceClosure quit_closure,
    const net::test_server::HttpRequest& request) {}

class TestSSLErrorHandler : public SSLErrorHandler {};

class TestSSLErrorHandlerDelegate : public SSLErrorHandler::Delegate {};

}  // namespace

// A class to test name mismatch errors. Creates an error handler with a name
// mismatch error.
class SSLErrorHandlerNameMismatchTest
    : public content::RenderViewHostTestHarness {};

// A class to test name mismatch errors, where the certificate lacks a
// SubjectAltName. Creates an error handler with a name mismatch error.
class SSLErrorHandlerNameMismatchNoSANTest
    : public SSLErrorHandlerNameMismatchTest {};

// A class to test the captive portal certificate list feature. Creates an error
// handler with a name mismatch error by default. The error handler can be
// recreated by calling ResetErrorHandler() with an appropriate cert status.
class SSLErrorAssistantProtoTest : public content::RenderViewHostTestHarness {};

class SSLErrorAssistantProtoCaptivePortalEnabledTest
    : public SSLErrorAssistantProtoTest {};

class SSLErrorAssistantProtoCaptivePortalDisabledTest
    : public SSLErrorAssistantProtoTest {};

class SSLErrorAssistantProtoMITMSoftwareEnabledTest
    : public SSLErrorAssistantProtoTest {};

class SSLErrorAssistantProtoMITMSoftwareDisabledTest
    : public SSLErrorAssistantProtoTest {};

class SSLErrorHandlerDateInvalidTest
    : public content::RenderViewHostTestHarness {};

#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)

TEST_F(SSLErrorHandlerNameMismatchTest,
       ShouldShowSSLInterstitialOnTimerExpired) {}

TEST_F(SSLErrorHandlerNameMismatchTest,
       ShouldShowCustomInterstitialOnCaptivePortalResult) {}

TEST_F(SSLErrorHandlerNameMismatchTest,
       ShouldShowSSLInterstitialOnNoCaptivePortalResult) {}

TEST_F(SSLErrorHandlerNameMismatchTest,
       ShouldNotCheckSuggestedUrlIfNoSuggestedUrl) {}

TEST_F(SSLErrorHandlerNameMismatchTest,
       ShouldNotCheckCaptivePortalIfSuggestedUrlExists) {}

TEST_F(SSLErrorHandlerNameMismatchTest,
       ShouldNotHandleNameMismatchOnNonOverridableError) {}

#else  // #if !BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)

TEST_F(SSLErrorHandlerNameMismatchTest,
       ShouldShowSSLInterstitialOnCaptivePortalDetectionDisabled) {
  base::HistogramTester histograms;
  EXPECT_FALSE(error_handler()->IsTimerRunningForTesting());
  error_handler()->StartHandlingError();
  EXPECT_FALSE(error_handler()->IsTimerRunningForTesting());
  EXPECT_FALSE(delegate()->captive_portal_checked());
  EXPECT_TRUE(delegate()->ssl_interstitial_shown());
  EXPECT_FALSE(delegate()->captive_portal_interstitial_shown());

  histograms.ExpectTotalCount(SSLErrorHandler::GetHistogramNameForTesting(), 2);
  histograms.ExpectBucketCount(SSLErrorHandler::GetHistogramNameForTesting(),
                               SSLErrorHandler::HANDLE_ALL, 1);
  histograms.ExpectBucketCount(
      SSLErrorHandler::GetHistogramNameForTesting(),
      SSLErrorHandler::SHOW_SSL_INTERSTITIAL_OVERRIDABLE, 1);
}

#endif  // BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)

// Test that a captive portal interstitial is shown if the OS reports a portal.
TEST_F(SSLErrorHandlerNameMismatchTest, OSReportsCaptivePortal) {}

class SSLErrorHandlerNameMismatchCaptivePortalInterstitialDisabledTest
    : public SSLErrorHandlerNameMismatchTest {};

// Test that a captive portal interstitial isn't shown if the OS reports a
// portal but CaptivePortalInterstitial feature is disabled.
TEST_F(SSLErrorHandlerNameMismatchCaptivePortalInterstitialDisabledTest,
       OSReportsCaptivePortal_FeatureDisabled) {}

TEST_F(SSLErrorHandlerNameMismatchTest,
       ShouldShowSSLInterstitialOnTimerExpiredWhenSuggestedUrlExists) {}

TEST_F(SSLErrorHandlerNameMismatchTest,
       ShouldRedirectOnSuggestedUrlCheckResult) {}

// No suggestions should be requested if certificate lacks a SubjectAltName.
TEST_F(SSLErrorHandlerNameMismatchNoSANTest,
       SSLCommonNameMismatchHandlingRequiresSubjectAltName) {}

TEST_F(SSLErrorHandlerNameMismatchTest,
       ShouldShowSSLInterstitialOnInvalidUrlCheckResult) {}

// Flakily fails on linux_chromium_tsan_rel_ng. http://crbug.com/989128
#if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)) && defined(THREAD_SANITIZER)
#define MAYBE_TimeQueryStarted
#else
#define MAYBE_TimeQueryStarted
#endif
TEST_F(SSLErrorHandlerDateInvalidTest, MAYBE_TimeQueryStarted) {}

// Tests that an SSL interstitial is shown if the accuracy of the system
// clock can't be determined because network time is unavailable.

// Flakily fails on linux_chromium_tsan_rel_ng. http://crbug.com/989225
#if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)) && defined(THREAD_SANITIZER)
#define MAYBE_NoTimeQueries
#else
#define MAYBE_NoTimeQueries
#endif
TEST_F(SSLErrorHandlerDateInvalidTest, MAYBE_NoTimeQueries) {}

// Tests that an SSL interstitial is shown if determing the accuracy of
// the system clock times out (e.g. because a network time query hangs).

// Flakily fails on linux_chromium_tsan_rel_ng. http://crbug.com/989289
#if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)) && defined(THREAD_SANITIZER)
#define MAYBE_TimeQueryHangs
#else
#define MAYBE_TimeQueryHangs
#endif
TEST_F(SSLErrorHandlerDateInvalidTest, MAYBE_TimeQueryHangs) {}

// Tests that a certificate marked as a known captive portal certificate causes
// the captive portal interstitial to be shown.
TEST_F(SSLErrorAssistantProtoCaptivePortalEnabledTest,
       CaptivePortal_FeatureEnabled) {}

// Tests that a certificate marked as a known captive portal certificate does
// not cause the captive portal interstitial to be shown, if the feature is
// disabled.
TEST_F(SSLErrorAssistantProtoCaptivePortalDisabledTest,
       CaptivePortal_FeatureDisabled) {}

// Tests that an error other than name mismatch does not cause a captive portal
// interstitial to be shown, even if the certificate is marked as a known
// captive portal certificate.
TEST_F(SSLErrorAssistantProtoCaptivePortalEnabledTest,
       CaptivePortal_AuthorityInvalidError_NoInterstitial) {}

// Tests that an authority invalid error in addition to name mismatch error does
// not cause a captive portal interstitial to be shown, even if the certificate
// is marked as a known captive portal certificate. The resulting error is
// authority-invalid.
TEST_F(SSLErrorAssistantProtoCaptivePortalEnabledTest,
       CaptivePortal_TwoErrors_NoInterstitial) {}

// Tests that another error in addition to name mismatch error does not cause a
// captive portal interstitial to be shown, even if the certificate is marked as
// a known captive portal certificate. Similar to
// NameMismatchAndAuthorityInvalid, except the resulting error is name mismatch.
TEST_F(SSLErrorAssistantProtoCaptivePortalEnabledTest,
       CaptivePortal_TwoErrorsIncludingNameMismatch_NoInterstitial) {}

// Tests that if a certificate matches the issuer common name regex of a MITM
// software entry but not the issuer organization name a MITM software
// interstitial will not be displayed.
TEST_F(SSLErrorAssistantProtoMITMSoftwareEnabledTest,
       MITMSoftware_CertificateDoesNotMatchOrganizationName_NoInterstitial) {}

// Tests that if a certificate matches the issuer organization name regex of a
// MITM software entry but not the issuer common name a MITM software
// interstitial will not be displayed.
TEST_F(SSLErrorAssistantProtoMITMSoftwareEnabledTest,
       MITMSoftware_CertificateDoesNotMatchCommonName_NoInterstitial) {}

// Tests that a certificate with no organization name or common name will not
// trigger a MITM software interstitial.
TEST_F(SSLErrorAssistantProtoMITMSoftwareEnabledTest,
       MITMSoftware_CertificateWithNoOrganizationOrCommonName_NoInterstitial) {}

// Tests that when everything else is in order, a matching MITM software
// certificate will trigger the MITM software interstitial.
TEST_F(SSLErrorAssistantProtoMITMSoftwareEnabledTest,
       MITMSoftware_CertificateMatchesCommonNameAndOrganizationName) {}

// Tests that a known MITM software entry in the SSL error assistant proto that
// has a common name regex but not an organization name regex can still trigger
// a MITM software interstitial.
TEST_F(SSLErrorAssistantProtoMITMSoftwareEnabledTest,
       MITMSoftware_CertificateMatchesCommonName) {}

// Tests that a known MITM software entry in the SSL error assistant proto that
// has an organization name regex but not a common name name regex can still
// trigger a MITM software interstitial.
TEST_F(SSLErrorAssistantProtoMITMSoftwareEnabledTest,
       MITMSoftware_CertificateMatchesOrganizationName) {}

// Tests that only a full regex match will trigger the MITM software
// interstitial. For example, a common name regex "Match" should not trigger the
// MITM software interstitial on a certificate that's common name is
// "Full Match".
TEST_F(SSLErrorAssistantProtoMITMSoftwareEnabledTest,
       MITMSoftware_PartialRegexMatch_NoInterstitial) {}

// Tests that a MITM software interstitial is not triggered when neither the
// common name or the organization name match.
TEST_F(SSLErrorAssistantProtoMITMSoftwareEnabledTest,
       MITMSoftware_NonMatchingCertificate_NoInterstitial) {}

// Tests that the MITM software interstitial is not triggered when the feature
// is disabled by Finch.
TEST_F(SSLErrorAssistantProtoMITMSoftwareDisabledTest,
       MITMSoftware_FeatureDisabled) {}

// Tests that the MITM software interstitial is not triggered when an error
// other than net::CERT_STATUS_AUTHORITY_INVALID is thrown.
TEST_F(SSLErrorAssistantProtoMITMSoftwareEnabledTest,
       MITMSoftware_WrongError_NoInterstitial) {}

// Tests that the MITM software interstitial is not triggered when more than one
// error is thrown.
TEST_F(SSLErrorAssistantProtoMITMSoftwareEnabledTest,
       MITMSoftware_TwoErrors_NoInterstitial) {}

// Tests that the MITM software interstitial is not triggered if the error
// thrown is overridable.
TEST_F(SSLErrorAssistantProtoMITMSoftwareEnabledTest,
       MITMSoftware_Overridable_NoInterstitial) {}

TEST_F(SSLErrorAssistantProtoMITMSoftwareEnabledTest,
       MITMSoftware_IgnoreDynamicUpdateWithSmallVersionId) {}

SSLErrorHandlerTest;

// Test that a blocked interception interstitial is shown. It would be nicer to
// set the SSLInfo properly so that the cert is blocked at net level rather than
// because of set_has_blocked_interception(), but that code path is already
// executed in net unit tests and SSL browser tests. This test mainly checks
// histogram accuracy.
TEST_F(SSLErrorHandlerTest, BlockedInterceptionInterstitial) {}

// Tests that non-primary main frame navigations should not affect
// SSLErrorHandler.
TEST_F(SSLErrorHandlerTest, NonPrimaryMainframeShouldNotAffectSSLErrorHandler) {}