blob: a7dc2956e15a2a035852e7cdc9350a2c36a59f27 [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ssl/https_first_mode_settings_tracker.h"
#include "base/json/values_util.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/simple_test_clock.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/safe_browsing/advanced_protection_status_manager.h"
#include "chrome/browser/safe_browsing/advanced_protection_status_manager_factory.h"
#include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
#include "chrome/browser/ssl/chrome_security_blocking_page_factory.h"
#include "chrome/browser/ssl/https_upgrades_util.h"
#include "chrome/browser/ssl/stateful_ssl_host_state_delegate_factory.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/testing_profile.h"
#include "components/prefs/pref_test_utils.h"
#include "components/security_interstitials/content/stateful_ssl_host_state_delegate.h"
#include "components/security_interstitials/core/https_only_mode_metrics.h"
#include "components/site_engagement/content/site_engagement_score.h"
#include "components/site_engagement/content/site_engagement_service.h"
#include "components/site_engagement/core/pref_names.h"
#include "components/user_prefs/user_prefs.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
using security_interstitials::https_only_mode::
kSiteEngagementHeuristicAccumulatedHostCountHistogram;
using security_interstitials::https_only_mode::
kSiteEngagementHeuristicEnforcementDurationHistogram;
using security_interstitials::https_only_mode::
kSiteEngagementHeuristicHostCountHistogram;
using security_interstitials::https_only_mode::
kSiteEngagementHeuristicStateHistogram;
using security_interstitials::https_only_mode::SiteEngagementHeuristicState;
// Tests for HTTPS First Mode settings, such as enabling HFM through Site
// Engagement scores.
//
// Most of these tests don't need to enable the feature flags for HFM heuristics
// because they call the feature-flag gated functions directly (
// MaybeEnableHttpsFirstModeForEngagedSitesAndWait() and
// CheckUserIsTypicallySecureAndMaybeEnableHttpsFirstMode()).
class HttpsFirstModeSettingsTrackerTest : public testing::Test {
protected:
void SetUp() override {
TestingProfile::Builder builder;
builder.AddTestingFactory(
StatefulSSLHostStateDelegateFactory::GetInstance(),
StatefulSSLHostStateDelegateFactory::GetDefaultFactoryForTesting());
builder.AddTestingFactory(
safe_browsing::AdvancedProtectionStatusManagerFactory::GetInstance(),
safe_browsing::AdvancedProtectionStatusManagerFactory::
GetDefaultFactoryForTesting());
builder.AddTestingFactory(
HttpsFirstModeServiceFactory::GetInstance(),
HttpsFirstModeServiceFactory::GetDefaultFactoryForTesting());
profile_ = IdentityTestEnvironmentProfileAdaptor::
CreateProfileForIdentityTestEnvironment(builder);
ChromeSecurityBlockingPageFactory::SetEnterpriseManagedForTesting(false);
}
void TearDown() override {
HttpsFirstModeServiceFactory::SetClockForTesting(nullptr);
}
void SetSiteEngagementScoreForTypicallySecureUserHeuristic() {
// Typically Secure User heuristic requires a minimum total site engagement
// score.
site_engagement::SiteEngagementService* engagement_service =
site_engagement::SiteEngagementService::Get(profile());
ASSERT_TRUE(engagement_service);
engagement_service->ResetBaseScoreForURL(GURL("https://google.com"), 90);
}
TestingProfile* profile() { return profile_.get(); }
base::test::ScopedFeatureList* feature_list() { return &feature_list_; }
content::BrowserTaskEnvironment task_environment_;
private:
std::unique_ptr<TestingProfile> profile_;
base::test::ScopedFeatureList feature_list_;
};
class HttpsFirstModeSettingsTrackerSiteEngagementHeuristicTest
: public HttpsFirstModeSettingsTrackerTest {
public:
HttpsFirstModeSettingsTrackerSiteEngagementHeuristicTest() {
feature_list()->InitWithFeatures(
/*enabled_features=*/{features::kHttpsFirstModeV2ForEngagedSites,
features::kHttpsFirstBalancedMode},
/*disabled_features=*/{});
}
};
void MaybeEnableHttpsFirstModeForEngagedSitesAndWait(
HttpsFirstModeService* hfm_service) {
base::RunLoop run_loop;
hfm_service->MaybeEnableHttpsFirstModeForEngagedSites(run_loop.QuitClosure());
run_loop.Run();
}
// Check that changing the HFM pref clears Site Engagement heuristic's HTTPS
// enforcelist and effectively disables the heuristic.
TEST_F(HttpsFirstModeSettingsTrackerSiteEngagementHeuristicTest,
ShouldNotEnableHeuristicIfPrefIsSet) {
HttpsFirstModeService* service =
HttpsFirstModeServiceFactory::GetForProfile(profile());
ASSERT_TRUE(service);
site_engagement::SiteEngagementService* engagement_service =
site_engagement::SiteEngagementService::Get(profile());
ASSERT_TRUE(engagement_service);
StatefulSSLHostStateDelegate* state =
StatefulSSLHostStateDelegateFactory::GetForProfile(profile());
ASSERT_TRUE(state);
auto clock = std::make_unique<base::SimpleTestClock>();
auto* clock_ptr = clock.get();
state->SetClockForTesting(std::move(clock));
clock_ptr->SetNow(base::Time::NowFromSystemTime());
// Site Engagement heuristic should enforce HTTPS on hosts with high
// engagement score if Balanced Mode is available.
GURL https_url("https://example.com");
engagement_service->ResetBaseScoreForURL(https_url, 90);
MaybeEnableHttpsFirstModeForEngagedSitesAndWait(service);
EXPECT_TRUE(state->IsHttpsEnforcedForUrl(
GURL("http://example.com"), profile()->GetDefaultStoragePartition()));
EXPECT_TRUE(state->IsHttpsEnforcedForUrl(
GURL("https://example.com"), profile()->GetDefaultStoragePartition()));
// Disable HFM. This should clear the enforcelist.
profile()->GetPrefs()->SetBoolean(prefs::kHttpsOnlyModeEnabled, false);
EXPECT_FALSE(state->IsHttpsEnforcedForUrl(
GURL("http://example.com"), profile()->GetDefaultStoragePartition()));
EXPECT_FALSE(state->IsHttpsEnforcedForUrl(
GURL("https://example.com"), profile()->GetDefaultStoragePartition()));
// Check again. Should remain unenforced.
MaybeEnableHttpsFirstModeForEngagedSitesAndWait(service);
EXPECT_FALSE(state->IsHttpsEnforcedForUrl(
GURL("http://example.com"), profile()->GetDefaultStoragePartition()));
EXPECT_FALSE(state->IsHttpsEnforcedForUrl(
GURL("https://example.com"), profile()->GetDefaultStoragePartition()));
}
// Check that high site engagement scores of HTTPS URLs with non-default ports
// do not auto-enable HTTPS-First Balanced Mode.
TEST_F(HttpsFirstModeSettingsTrackerSiteEngagementHeuristicTest,
ShouldIgnoreEngagementScoreOfUrlWithNonDefaultPort) {
HttpsFirstModeService* service =
HttpsFirstModeServiceFactory::GetForProfile(profile());
ASSERT_TRUE(service);
base::HistogramTester histograms;
site_engagement::SiteEngagementService* engagement_service =
site_engagement::SiteEngagementService::Get(profile());
ASSERT_TRUE(engagement_service);
StatefulSSLHostStateDelegate* state =
StatefulSSLHostStateDelegateFactory::GetForProfile(profile());
ASSERT_TRUE(state);
// HFM should initially be disabled on this site by default.
MaybeEnableHttpsFirstModeForEngagedSitesAndWait(service);
EXPECT_FALSE(state->IsHttpsEnforcedForUrl(
GURL("http://example.com"), profile()->GetDefaultStoragePartition()));
histograms.ExpectTotalCount(kSiteEngagementHeuristicStateHistogram, 0);
histograms.ExpectTotalCount(kSiteEngagementHeuristicHostCountHistogram, 0);
histograms.ExpectTotalCount(
kSiteEngagementHeuristicAccumulatedHostCountHistogram, 0);
histograms.ExpectTotalCount(
kSiteEngagementHeuristicEnforcementDurationHistogram, 0);
// Increase the score. Since the URL has a non-default port, HFM should remain
// disabled.
engagement_service->ResetBaseScoreForURL(GURL("https://example.com:8443"),
90);
MaybeEnableHttpsFirstModeForEngagedSitesAndWait(service);
EXPECT_FALSE(state->IsHttpsEnforcedForUrl(
GURL("http://example.com"), profile()->GetDefaultStoragePartition()));
histograms.ExpectTotalCount(kSiteEngagementHeuristicStateHistogram, 0);
histograms.ExpectTotalCount(kSiteEngagementHeuristicHostCountHistogram, 0);
histograms.ExpectTotalCount(
kSiteEngagementHeuristicAccumulatedHostCountHistogram, 0);
histograms.ExpectTotalCount(
kSiteEngagementHeuristicEnforcementDurationHistogram, 0);
}
// Check that Site Engagement heuristic is never enabled on enterprise
// managed devices.
TEST_F(HttpsFirstModeSettingsTrackerSiteEngagementHeuristicTest,
EnterpriseManaged_ShouldNotEnable) {
HttpsFirstModeService* service =
HttpsFirstModeServiceFactory::GetForProfile(profile());
ASSERT_TRUE(service);
base::HistogramTester histograms;
site_engagement::SiteEngagementService* engagement_service =
site_engagement::SiteEngagementService::Get(profile());
ASSERT_TRUE(engagement_service);
StatefulSSLHostStateDelegate* state =
StatefulSSLHostStateDelegateFactory::GetForProfile(profile());
ASSERT_TRUE(state);
ChromeSecurityBlockingPageFactory::SetEnterpriseManagedForTesting(true);
// Set the https score high. HTTPS shouldn't be enforced because the device
// is managed.
engagement_service->ResetBaseScoreForURL(GURL("https://example.com"), 90);
MaybeEnableHttpsFirstModeForEngagedSitesAndWait(service);
EXPECT_FALSE(state->IsHttpsEnforcedForUrl(
GURL("http://example.com"), profile()->GetDefaultStoragePartition()));
histograms.ExpectTotalCount(kSiteEngagementHeuristicStateHistogram, 0);
histograms.ExpectTotalCount(kSiteEngagementHeuristicHostCountHistogram, 0);
histograms.ExpectTotalCount(
kSiteEngagementHeuristicAccumulatedHostCountHistogram, 0);
histograms.ExpectTotalCount(
kSiteEngagementHeuristicEnforcementDurationHistogram, 0);
}
TEST_F(HttpsFirstModeSettingsTrackerSiteEngagementHeuristicTest,
BalancedModeExceptions) {
HttpsFirstModeService* service =
HttpsFirstModeServiceFactory::GetForProfile(profile());
ASSERT_TRUE(service);
base::HistogramTester histograms;
site_engagement::SiteEngagementService* engagement_service =
site_engagement::SiteEngagementService::Get(profile());
ASSERT_TRUE(engagement_service);
StatefulSSLHostStateDelegate* state =
StatefulSSLHostStateDelegateFactory::GetForProfile(profile());
ASSERT_TRUE(state);
auto clock = std::make_unique<base::SimpleTestClock>();
auto* clock_ptr = clock.get();
state->SetClockForTesting(std::move(clock));
clock_ptr->SetNow(base::Time::NowFromSystemTime());
// HTTPS-First Balanced Mode should ignore non-unique hostnames.
GURL https_url("https://site.test");
GURL http_url("http://site.test");
// HFM should initially be disabled on this site by default.
MaybeEnableHttpsFirstModeForEngagedSitesAndWait(service);
EXPECT_FALSE(state->IsHttpsEnforcedForUrl(
http_url, profile()->GetDefaultStoragePartition()));
histograms.ExpectTotalCount(kSiteEngagementHeuristicStateHistogram, 0);
histograms.ExpectTotalCount(kSiteEngagementHeuristicHostCountHistogram, 0);
histograms.ExpectTotalCount(
kSiteEngagementHeuristicAccumulatedHostCountHistogram, 0);
histograms.ExpectTotalCount(
kSiteEngagementHeuristicEnforcementDurationHistogram, 0);
// Increase the score. This should still not enable HFM on site.test.
engagement_service->ResetBaseScoreForURL(https_url, 90);
MaybeEnableHttpsFirstModeForEngagedSitesAndWait(service);
EXPECT_FALSE(state->IsHttpsEnforcedForUrl(
http_url, profile()->GetDefaultStoragePartition()));
histograms.ExpectTotalCount(kSiteEngagementHeuristicStateHistogram, 0);
histograms.ExpectTotalCount(kSiteEngagementHeuristicHostCountHistogram, 0);
histograms.ExpectTotalCount(
kSiteEngagementHeuristicAccumulatedHostCountHistogram, 0);
histograms.ExpectTotalCount(
kSiteEngagementHeuristicEnforcementDurationHistogram, 0);
}
TEST_F(HttpsFirstModeSettingsTrackerSiteEngagementHeuristicTest,
ShouldEnforceHttps) {
HttpsFirstModeService* service =
HttpsFirstModeServiceFactory::GetForProfile(profile());
ASSERT_TRUE(service);
base::HistogramTester histograms;
site_engagement::SiteEngagementService* engagement_service =
site_engagement::SiteEngagementService::Get(profile());
ASSERT_TRUE(engagement_service);
StatefulSSLHostStateDelegate* state =
StatefulSSLHostStateDelegateFactory::GetForProfile(profile());
ASSERT_TRUE(state);
auto clock = std::make_unique<base::SimpleTestClock>();
auto* clock_ptr = clock.get();
state->SetClockForTesting(std::move(clock));
clock_ptr->SetNow(base::Time::NowFromSystemTime());
GURL https_url("https://example.com");
GURL http_url("http://example.com");
// Step 1: HFM should initially be disabled on this site by default.
MaybeEnableHttpsFirstModeForEngagedSitesAndWait(service);
EXPECT_FALSE(state->IsHttpsEnforcedForUrl(
GURL("http://example.com"), profile()->GetDefaultStoragePartition()));
histograms.ExpectTotalCount(kSiteEngagementHeuristicStateHistogram, 0);
histograms.ExpectTotalCount(kSiteEngagementHeuristicHostCountHistogram, 0);
histograms.ExpectTotalCount(
kSiteEngagementHeuristicAccumulatedHostCountHistogram, 0);
histograms.ExpectTotalCount(
kSiteEngagementHeuristicEnforcementDurationHistogram, 0);
// Step 2: Increase the score, should now enable HFM.
engagement_service->ResetBaseScoreForURL(https_url, 90);
MaybeEnableHttpsFirstModeForEngagedSitesAndWait(service);
EXPECT_TRUE(state->IsHttpsEnforcedForUrl(
GURL("http://example.com"), profile()->GetDefaultStoragePartition()));
// Check events.
histograms.ExpectTotalCount(kSiteEngagementHeuristicStateHistogram, 1);
histograms.ExpectBucketCount(kSiteEngagementHeuristicStateHistogram,
SiteEngagementHeuristicState::kEnabled, 1);
histograms.ExpectBucketCount(kSiteEngagementHeuristicStateHistogram,
SiteEngagementHeuristicState::kDisabled, 0);
// Check host counts.
histograms.ExpectTotalCount(kSiteEngagementHeuristicHostCountHistogram, 1);
histograms.ExpectBucketCount(kSiteEngagementHeuristicHostCountHistogram,
/*sample=*/1, /*expected_count=*/1);
// Check accumulated host counts.
histograms.ExpectTotalCount(
kSiteEngagementHeuristicAccumulatedHostCountHistogram, 1);
histograms.ExpectBucketCount(
kSiteEngagementHeuristicAccumulatedHostCountHistogram,
/*sample=*/1, /*expected_count=*/1);
// Check enforcement durations. Enforcement duration is only recorded when a
// host is removed from the list.
histograms.ExpectTotalCount(
kSiteEngagementHeuristicEnforcementDurationHistogram, 0);
// Subdomains shouldn't be affected.
EXPECT_FALSE(
state->IsHttpsEnforcedForUrl(GURL("http://test.example.com"),
profile()->GetDefaultStoragePartition()));
// Step 3: Decrease the score, but only slightly. This shouldn't result in HFM
// being disabled.
engagement_service->ResetBaseScoreForURL(https_url, 85);
MaybeEnableHttpsFirstModeForEngagedSitesAndWait(service);
EXPECT_TRUE(state->IsHttpsEnforcedForUrl(
GURL("http://example.com"), profile()->GetDefaultStoragePartition()));
// Check events.
histograms.ExpectTotalCount(kSiteEngagementHeuristicStateHistogram, 1);
histograms.ExpectBucketCount(kSiteEngagementHeuristicStateHistogram,
SiteEngagementHeuristicState::kEnabled, 1);
histograms.ExpectBucketCount(kSiteEngagementHeuristicStateHistogram,
SiteEngagementHeuristicState::kDisabled, 0);
// Check host counts.
histograms.ExpectTotalCount(kSiteEngagementHeuristicHostCountHistogram, 1);
histograms.ExpectBucketCount(kSiteEngagementHeuristicHostCountHistogram,
/*sample=*/1, /*expected_count=*/1);
// Check accumulated host counts.
histograms.ExpectTotalCount(
kSiteEngagementHeuristicAccumulatedHostCountHistogram, 1);
histograms.ExpectBucketCount(
kSiteEngagementHeuristicAccumulatedHostCountHistogram,
/*sample=*/1, /*expected_count=*/1);
// Check enforcement durations. Enforcement duration is only recorded when a
// host is removed from the list.
histograms.ExpectTotalCount(
kSiteEngagementHeuristicEnforcementDurationHistogram, 0);
// Step 4: Decrease the score further. This should result in HFM being
// disabled. Also move the time forward.
clock_ptr->Advance(base::Hours(1));
engagement_service->ResetBaseScoreForURL(https_url, 25);
MaybeEnableHttpsFirstModeForEngagedSitesAndWait(service);
EXPECT_FALSE(state->IsHttpsEnforcedForUrl(
GURL("http://example.com"), profile()->GetDefaultStoragePartition()));
// Check events.
histograms.ExpectTotalCount(kSiteEngagementHeuristicStateHistogram, 2);
histograms.ExpectBucketCount(kSiteEngagementHeuristicStateHistogram,
SiteEngagementHeuristicState::kEnabled, 1);
histograms.ExpectBucketCount(kSiteEngagementHeuristicStateHistogram,
SiteEngagementHeuristicState::kDisabled, 1);
// Check host counts.
histograms.ExpectTotalCount(kSiteEngagementHeuristicHostCountHistogram, 2);
histograms.ExpectBucketCount(kSiteEngagementHeuristicHostCountHistogram,
/*sample=*/0, /*expected_count=*/1);
histograms.ExpectBucketCount(kSiteEngagementHeuristicHostCountHistogram,
/*sample=*/1, /*expected_count=*/1);
// Check accumulated host counts.
histograms.ExpectTotalCount(
kSiteEngagementHeuristicAccumulatedHostCountHistogram, 2);
histograms.ExpectBucketCount(
kSiteEngagementHeuristicAccumulatedHostCountHistogram,
/*sample=*/0, /*expected_count=*/0);
histograms.ExpectBucketCount(
kSiteEngagementHeuristicAccumulatedHostCountHistogram,
/*sample=*/1, /*expected_count=*/2);
// Check enforcement durations.
histograms.ExpectTotalCount(
kSiteEngagementHeuristicEnforcementDurationHistogram, 1);
histograms.ExpectTimeBucketCount(
kSiteEngagementHeuristicEnforcementDurationHistogram, base::Hours(1), 1);
// Step 5: Increase the score again and re-enable HFM.
clock_ptr->Advance(base::Hours(2));
engagement_service->ResetBaseScoreForURL(https_url, 90);
MaybeEnableHttpsFirstModeForEngagedSitesAndWait(service);
EXPECT_TRUE(state->IsHttpsEnforcedForUrl(
GURL("http://example.com"), profile()->GetDefaultStoragePartition()));
// Check state.
histograms.ExpectTotalCount(kSiteEngagementHeuristicStateHistogram, 3);
histograms.ExpectBucketCount(kSiteEngagementHeuristicStateHistogram,
SiteEngagementHeuristicState::kEnabled, 2);
histograms.ExpectBucketCount(kSiteEngagementHeuristicStateHistogram,
SiteEngagementHeuristicState::kDisabled, 1);
// Check host counts.
histograms.ExpectTotalCount(kSiteEngagementHeuristicHostCountHistogram, 3);
histograms.ExpectBucketCount(kSiteEngagementHeuristicHostCountHistogram,
/*sample=*/0, /*expected_count=*/1);
histograms.ExpectBucketCount(kSiteEngagementHeuristicHostCountHistogram,
/*sample=*/1, /*expected_count=*/2);
// Check accumulated host counts.
histograms.ExpectTotalCount(
kSiteEngagementHeuristicAccumulatedHostCountHistogram, 3);
histograms.ExpectBucketCount(
kSiteEngagementHeuristicAccumulatedHostCountHistogram,
/*sample=*/0, /*expected_count=*/0);
histograms.ExpectBucketCount(
kSiteEngagementHeuristicAccumulatedHostCountHistogram,
/*sample=*/1, /*expected_count=*/3);
// Check enforcement durations.
histograms.ExpectTotalCount(
kSiteEngagementHeuristicEnforcementDurationHistogram, 1);
histograms.ExpectTimeBucketCount(
kSiteEngagementHeuristicEnforcementDurationHistogram, base::Hours(1), 1);
// Step 6: Also increase the HTTP score. This should disable HFM even though
// the HTTPS score is still high.
engagement_service->ResetBaseScoreForURL(http_url, 20);
MaybeEnableHttpsFirstModeForEngagedSitesAndWait(service);
EXPECT_FALSE(state->IsHttpsEnforcedForUrl(
GURL("http://example.com"), profile()->GetDefaultStoragePartition()));
// Check state.
histograms.ExpectTotalCount(kSiteEngagementHeuristicStateHistogram, 4);
histograms.ExpectBucketCount(kSiteEngagementHeuristicStateHistogram,
SiteEngagementHeuristicState::kEnabled, 2);
histograms.ExpectBucketCount(kSiteEngagementHeuristicStateHistogram,
SiteEngagementHeuristicState::kDisabled, 2);
// Check host count.
histograms.ExpectTotalCount(kSiteEngagementHeuristicHostCountHistogram, 4);
histograms.ExpectBucketCount(kSiteEngagementHeuristicHostCountHistogram,
/*sample=*/0, /*expected_count=*/2);
histograms.ExpectBucketCount(kSiteEngagementHeuristicHostCountHistogram,
/*sample=*/1, /*expected_count=*/2);
// Check accumulated host counts.
histograms.ExpectTotalCount(
kSiteEngagementHeuristicAccumulatedHostCountHistogram, 4);
histograms.ExpectBucketCount(
kSiteEngagementHeuristicAccumulatedHostCountHistogram,
/*sample=*/0, /*expected_count=*/0);
histograms.ExpectBucketCount(
kSiteEngagementHeuristicAccumulatedHostCountHistogram,
/*sample=*/1, /*expected_count=*/4);
// Check enforcement durations.
histograms.ExpectTotalCount(
kSiteEngagementHeuristicEnforcementDurationHistogram, 2);
histograms.ExpectTimeBucketCount(
kSiteEngagementHeuristicEnforcementDurationHistogram, base::Hours(1), 1);
histograms.ExpectTimeBucketCount(
kSiteEngagementHeuristicEnforcementDurationHistogram, base::Hours(2), 1);
// Step 7: Set HTTP score to max and set HTTPS score to zero. This simulates
// the user spending their time on the HTTP URL and the HTTPS score decaying
// over time.
engagement_service->ResetBaseScoreForURL(https_url, 0);
engagement_service->ResetBaseScoreForURL(http_url, 100);
MaybeEnableHttpsFirstModeForEngagedSitesAndWait(service);
EXPECT_FALSE(state->IsHttpsEnforcedForUrl(
GURL("http://example.com"), profile()->GetDefaultStoragePartition()));
// Check state.
histograms.ExpectTotalCount(kSiteEngagementHeuristicStateHistogram, 4);
histograms.ExpectBucketCount(kSiteEngagementHeuristicStateHistogram,
SiteEngagementHeuristicState::kEnabled, 2);
histograms.ExpectBucketCount(kSiteEngagementHeuristicStateHistogram,
SiteEngagementHeuristicState::kDisabled, 2);
// Check host counts.
histograms.ExpectTotalCount(kSiteEngagementHeuristicHostCountHistogram, 4);
histograms.ExpectBucketCount(kSiteEngagementHeuristicHostCountHistogram,
/*sample=*/0, /*expected_count=*/2);
histograms.ExpectBucketCount(kSiteEngagementHeuristicHostCountHistogram,
/*sample=*/1, /*expected_count=*/2);
// Check accumulated host counts.
histograms.ExpectTotalCount(
kSiteEngagementHeuristicAccumulatedHostCountHistogram, 4);
histograms.ExpectBucketCount(
kSiteEngagementHeuristicAccumulatedHostCountHistogram,
/*sample=*/0, /*expected_count=*/0);
histograms.ExpectBucketCount(
kSiteEngagementHeuristicAccumulatedHostCountHistogram,
/*sample=*/1, /*expected_count=*/4);
// Check enforcement durations.
histograms.ExpectTotalCount(
kSiteEngagementHeuristicEnforcementDurationHistogram, 2);
histograms.ExpectTimeBucketCount(
kSiteEngagementHeuristicEnforcementDurationHistogram, base::Hours(1), 1);
histograms.ExpectTimeBucketCount(
kSiteEngagementHeuristicEnforcementDurationHistogram, base::Hours(2), 1);
}
// If a site that was previously HTTPS-enforced is no longer in the site
// engagement list, it should no longer be HTTPS-enforced anymore.
TEST_F(HttpsFirstModeSettingsTrackerSiteEngagementHeuristicTest,
NoEngagementScoreShouldUnenforceHttps) {
HttpsFirstModeService* service =
HttpsFirstModeServiceFactory::GetForProfile(profile());
ASSERT_TRUE(service);
base::HistogramTester histograms;
site_engagement::SiteEngagementService* engagement_service =
site_engagement::SiteEngagementService::Get(profile());
ASSERT_TRUE(engagement_service);
StatefulSSLHostStateDelegate* state =
StatefulSSLHostStateDelegateFactory::GetForProfile(profile());
ASSERT_TRUE(state);
GURL https_url("https://example.com");
GURL http_url("http://example.com");
// Increase the HTTPS engagement score. This will enable HFM Balanced Mode on
// this host if available.
engagement_service->ResetBaseScoreForURL(https_url, 90);
MaybeEnableHttpsFirstModeForEngagedSitesAndWait(service);
EXPECT_TRUE(state->IsHttpsEnforcedForUrl(
http_url, profile()->GetDefaultStoragePartition()));
// Clear up the engagement scores. This should remove the enforcement.
HostContentSettingsMap* settings_map =
HostContentSettingsMapFactory::GetForProfile(profile());
settings_map->SetWebsiteSettingDefaultScope(
https_url, GURL(), ContentSettingsType::SITE_ENGAGEMENT, base::Value());
settings_map->SetWebsiteSettingDefaultScope(
http_url, GURL(), ContentSettingsType::SITE_ENGAGEMENT, base::Value());
MaybeEnableHttpsFirstModeForEngagedSitesAndWait(service);
// Sites that fall outside the site engagement list should no longer have
// HTTPS enforced.
EXPECT_FALSE(state->IsHttpsEnforcedForUrl(
http_url, profile()->GetDefaultStoragePartition()));
EXPECT_FALSE(state->IsHttpsEnforcedForUrl(
https_url, profile()->GetDefaultStoragePartition()));
}
// If a non-unique hostname was HTTPS-enforced by a previous version of Chrome,
// it should be removed from the enforcement list when possible.
TEST_F(HttpsFirstModeSettingsTrackerSiteEngagementHeuristicTest,
PreviouslyEnforcedNonUniqueHostnameShouldBeRemoved) {
HttpsFirstModeService* service =
HttpsFirstModeServiceFactory::GetForProfile(profile());
ASSERT_TRUE(service);
base::HistogramTester histograms;
site_engagement::SiteEngagementService* engagement_service =
site_engagement::SiteEngagementService::Get(profile());
ASSERT_TRUE(engagement_service);
StatefulSSLHostStateDelegate* state =
StatefulSSLHostStateDelegateFactory::GetForProfile(profile());
ASSERT_TRUE(state);
GURL http_url("http://site.test");
state->SetHttpsEnforcementForHost(http_url.host(), /*enforce=*/true,
profile()->GetDefaultStoragePartition());
// The heuristic should clean up the enforcement list and remove non-unique
// hostnames.
MaybeEnableHttpsFirstModeForEngagedSitesAndWait(service);
EXPECT_FALSE(state->IsHttpsEnforcedForUrl(
http_url, profile()->GetDefaultStoragePartition()));
}
// Tests the Typically Secure User heuristic to ensure that it respects the
// finch flag. See TypicallySecureUserPref for more details.
// Regression test for crbug.com/1475747.
TEST_F(HttpsFirstModeSettingsTrackerSiteEngagementHeuristicTest,
TypicallySecureUser_DisabledByDefault) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndDisableFeature(
features::kHttpsFirstModeV2ForTypicallySecureUsers);
HttpsFirstModeService* service =
HttpsFirstModeServiceFactory::GetForProfile(profile());
ASSERT_TRUE(service);
SetSiteEngagementScoreForTypicallySecureUserHeuristic();
base::SimpleTestClock clock;
base::Time now = base::Time::NowFromSystemTime();
clock.SetNow(now);
service->SetClockForTesting(&clock);
// Move the clock so that the profile is old enough.
clock.SetNow(now + base::Days(10));
// This situation would normally qualify for enabling HFM, but it should stay
// disabled since the feature is disabled.
service->RecordHttpsUpgradeFallbackEvent();
EXPECT_FALSE(service->IsInterstitialEnabledByTypicallySecureUserHeuristic());
EXPECT_FALSE(profile()->GetPrefs()->GetBoolean(prefs::kHttpsOnlyModeEnabled));
EXPECT_FALSE(
profile()->GetPrefs()->GetBoolean(prefs::kHttpsOnlyModeAutoEnabled));
}
// Creates the HFM service and waits for it to write the initial values into
// the typically secure fallbacks pref.
HttpsFirstModeService*
CreateHttpsFirstModeServiceAndWaitForTypicallySecureUserPrefInitialized(
Profile* profile,
base::SimpleTestClock* clock) {
HttpsFirstModeServiceFactory::SetClockForTesting(clock);
base::Time now = clock->Now();
HttpsFirstModeService* hfm_service =
HttpsFirstModeServiceFactory::GetForProfile(profile);
base::Value::Dict expected_pref =
base::Value::Dict()
.Set("heuristic_start_timestamp", base::TimeToValue(now))
.Set("fallback_events", base::Value::List());
WaitForPrefValue(profile->GetPrefs(), prefs::kHttpsUpgradeFallbacks,
base::Value(std::move(expected_pref)));
return hfm_service;
}
// A new profile shouldn't write any prefs related to Typically Secure User
// heuristic. TypicallySecureUserTest's SetUp() sets an old profile creation
// time and then create HttpsFirstModeService, so this can't be a
// TypicallySecureUserTest.
TEST_F(HttpsFirstModeSettingsTrackerTest, TypicallySecureUser_NewProfile) {
base::SimpleTestClock clock;
base::Time now = base::Time::NowFromSystemTime();
clock.SetNow(now);
HttpsFirstModeService* hfm_service =
HttpsFirstModeServiceFactory::GetForProfile(profile());
profile()->SetCreationTimeForTesting(now);
// A new profile will not initialize the heuristic on startup, so no need to
// wait for kHttpsUpgradeFallbacks pref.
SetSiteEngagementScoreForTypicallySecureUserHeuristic();
hfm_service->SetClockForTesting(&clock);
// kHttpsUpgradeFallbacks is normally written on startup, but not for new
// profiles. Other prefs shouldn't change.
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsUpgradeFallbacks));
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeAutoEnabled));
// A fallback event should be a no-op for a new profile.
hfm_service->RecordHttpsUpgradeFallbackEvent();
hfm_service->CheckUserIsTypicallySecureAndMaybeEnableHttpsFirstBalancedMode();
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsUpgradeFallbacks));
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeAutoEnabled));
}
// Typically Secure User fallback entries pref written by an older version of
// Chrome should be handled properly by new versions.
// This can't be a TypicallySecureUserTest because we need to write the prefs
// before creating the service.
TEST_F(HttpsFirstModeSettingsTrackerTest, TypicallySecureUser_OldVersion) {
// Write an empty pref without the "start_heuristic_timestamp" key.
base::Value::Dict new_base_pref;
profile()->GetPrefs()->SetDict(prefs::kHttpsUpgradeFallbacks,
std::move(new_base_pref));
base::SimpleTestClock clock;
base::Time now = base::Time::NowFromSystemTime();
clock.SetNow(now);
profile()->SetCreationTimeForTesting(now - base::Days(20));
// Enable the feature flags explicitly for this test.
feature_list()->InitWithFeatures(
/*enabled_features=*/{features::kHttpsFirstModeV2ForTypicallySecureUsers,
features::kHttpsFirstBalancedMode},
/*disabled_features=*/{});
HttpsFirstModeService* hfm_service =
CreateHttpsFirstModeServiceAndWaitForTypicallySecureUserPrefInitialized(
profile(), &clock);
EXPECT_TRUE(hfm_service);
EXPECT_TRUE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsUpgradeFallbacks));
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeAutoEnabled));
EXPECT_EQ(0u, hfm_service->GetFallbackEntryCountForTesting());
// A fallback event should be a no-op as the last fallback event is too
// recent.
hfm_service->RecordHttpsUpgradeFallbackEvent();
hfm_service->CheckUserIsTypicallySecureAndMaybeEnableHttpsFirstBalancedMode();
EXPECT_TRUE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsUpgradeFallbacks));
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeAutoEnabled));
EXPECT_EQ(1u, hfm_service->GetFallbackEntryCountForTesting());
}
class HttpsFirstModeSettingsTrackerTypicallySecureUserTest
: public HttpsFirstModeSettingsTrackerTest {
public:
HttpsFirstModeSettingsTrackerTypicallySecureUserTest() {
base::FieldTrialParams feature_params;
feature_params["min-recent-navigations"] = "5";
feature_list()->InitWithFeaturesAndParameters(
/*enabled_features=*/{{features::
kHttpsFirstModeV2ForTypicallySecureUsers,
feature_params},
{features::kHttpsFirstBalancedMode, {}}},
/*disabled_features=*/{});
}
protected:
void SetUp() override {
HttpsFirstModeSettingsTrackerTest::SetUp();
base::Time now = base::Time::NowFromSystemTime();
profile()->SetCreationTimeForTesting(now - base::Days(20));
clock_.SetNow(now);
SetSiteEngagementScoreForTypicallySecureUserHeuristic();
hfm_service_ =
CreateHttpsFirstModeServiceAndWaitForTypicallySecureUserPrefInitialized(
profile(), &clock_);
ASSERT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
clock_.SetNow(now);
}
void RecordFallbackEventAndMaybeEnableHttpsFirstMode() {
hfm_service()->RecordHttpsUpgradeFallbackEvent();
hfm_service()
->CheckUserIsTypicallySecureAndMaybeEnableHttpsFirstBalancedMode();
}
void IncrementRecentNavigationCount(size_t count) {
for (size_t i = 0; i < count; i++) {
hfm_service()->IncrementRecentNavigationCount();
}
}
HttpsFirstModeService* hfm_service() { return hfm_service_; }
base::SimpleTestClock* clock() { return &clock_; }
private:
raw_ptr<HttpsFirstModeService> hfm_service_;
base::SimpleTestClock clock_;
};
// An old profile should initialize the prefs related to Typically Secure User
// heuristic.
TEST_F(HttpsFirstModeSettingsTrackerTypicallySecureUserTest, ProfileOldEnough) {
SetSiteEngagementScoreForTypicallySecureUserHeuristic();
EXPECT_TRUE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsUpgradeFallbacks));
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeAutoEnabled));
EXPECT_EQ(0u, hfm_service()->GetFallbackEntryCountForTesting());
// A fallback event should be a no-op as the last fallback event is too
// recent.
RecordFallbackEventAndMaybeEnableHttpsFirstMode();
EXPECT_TRUE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsUpgradeFallbacks));
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeAutoEnabled));
EXPECT_EQ(1u, hfm_service()->GetFallbackEntryCountForTesting());
}
// Checks that Typically Secure Heuristic must observe navigations for at least
// a week before enabling HFM pref.
TEST_F(HttpsFirstModeSettingsTrackerTypicallySecureUserTest,
EnablePrefWhenObservedForLongEnough) {
base::Time now = clock()->Now();
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeAutoEnabled));
clock()->SetNow(now + base::Days(3));
RecordFallbackEventAndMaybeEnableHttpsFirstMode();
EXPECT_EQ(1u, hfm_service()->GetFallbackEntryCountForTesting());
// We haven't observed the profile for long enough. HFM shouldn't be enabled
// yet.
EXPECT_FALSE(
hfm_service()->IsInterstitialEnabledByTypicallySecureUserHeuristic());
// Prefs shouldn't be modified yet.
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeAutoEnabled));
clock()->SetNow(now + base::Days(8));
RecordFallbackEventAndMaybeEnableHttpsFirstMode();
EXPECT_EQ(2u, hfm_service()->GetFallbackEntryCountForTesting());
// We have observed for long enough, and we don't have too many fallback
// events (2). However, last fallback event was too recent, don't enable just
// yet.
EXPECT_FALSE(
hfm_service()->IsInterstitialEnabledByTypicallySecureUserHeuristic());
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeAutoEnabled));
clock()->SetNow(now + base::Days(9) + base::Hours(1));
hfm_service()
->CheckUserIsTypicallySecureAndMaybeEnableHttpsFirstBalancedMode();
EXPECT_EQ(2u, hfm_service()->GetFallbackEntryCountForTesting());
// Last fallback event is now a day old, but we don't have enough recent
// navigations. Don't enable yet.
EXPECT_FALSE(
hfm_service()->IsInterstitialEnabledByTypicallySecureUserHeuristic());
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeAutoEnabled));
// Do lots of navigations. Should enable HFM now.
IncrementRecentNavigationCount(100u);
hfm_service()
->CheckUserIsTypicallySecureAndMaybeEnableHttpsFirstBalancedMode();
EXPECT_TRUE(
hfm_service()->IsInterstitialEnabledByTypicallySecureUserHeuristic());
EXPECT_TRUE(
profile()->GetPrefs()->GetBoolean(prefs::kHttpsFirstBalancedMode));
EXPECT_TRUE(
profile()->GetPrefs()->GetBoolean(prefs::kHttpsOnlyModeAutoEnabled));
// Shouldn't modify strict mode pref:
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
}
// Checks that Typically Secure Heuristic must observe navigations for at least
// a week before enabling HFM pref.
TEST_F(HttpsFirstModeSettingsTrackerTypicallySecureUserTest,
DontEnablePrefWhenObservedForLongEnoughWithManyWarnings) {
base::Time now = clock()->Now();
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsFirstBalancedMode));
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeAutoEnabled));
clock()->SetNow(now + base::Days(3));
RecordFallbackEventAndMaybeEnableHttpsFirstMode();
// We haven't observed the profile for long enough. HFM shouldn't be enabled
// yet.
EXPECT_FALSE(
hfm_service()->IsInterstitialEnabledByTypicallySecureUserHeuristic());
// Prefs shouldn't be modified yet.
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsFirstBalancedMode));
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeAutoEnabled));
clock()->SetNow(now + base::Days(8));
// We have observed for long enough, but we have too many fallback events (3).
// HFM should not be enabled.
RecordFallbackEventAndMaybeEnableHttpsFirstMode();
RecordFallbackEventAndMaybeEnableHttpsFirstMode();
EXPECT_EQ(3u, hfm_service()->GetFallbackEntryCountForTesting());
EXPECT_FALSE(
hfm_service()->IsInterstitialEnabledByTypicallySecureUserHeuristic());
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsFirstBalancedMode));
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeAutoEnabled));
}
// Tests the usual flow of the Typically Secure User heuristic. Repeatedly calls
// RecordHttpsUpgradeFallbackEvent which is normally called
// from HTTPS-Upgrade fallbacks in production code. It then checks if the
// HTTPS-First Balanced Mode pref is enabled as expected.
TEST_F(HttpsFirstModeSettingsTrackerTypicallySecureUserTest,
BalancedModeEnabled) {
IncrementRecentNavigationCount(5u);
base::Time now = clock()->Now();
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsFirstBalancedMode));
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeAutoEnabled));
// Fallback #1. There are no previous HTTPS upgrade fallbacks (i.e.
// would-be warnings), so this would normally auto-enable HFM, but
// observation hasn't happened for a long time yet.
RecordFallbackEventAndMaybeEnableHttpsFirstMode();
EXPECT_EQ(1u, hfm_service()->GetFallbackEntryCountForTesting());
EXPECT_FALSE(
hfm_service()->IsInterstitialEnabledByTypicallySecureUserHeuristic());
// Prefs shouldn't be modified yet.
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsFirstBalancedMode));
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeAutoEnabled));
clock()->SetNow(now + base::Days(3));
// Fallback #2. The fallback entry list is now [#1, #2]. HFM is still not
// auto-enabled because we haven't observed navigations for long enough yet.
RecordFallbackEventAndMaybeEnableHttpsFirstMode();
EXPECT_EQ(2u, hfm_service()->GetFallbackEntryCountForTesting());
EXPECT_FALSE(
hfm_service()->IsInterstitialEnabledByTypicallySecureUserHeuristic());
// Prefs shouldn't be modified yet.
EXPECT_FALSE(profile()->GetPrefs()->GetBoolean(prefs::kHttpsOnlyModeEnabled));
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsFirstBalancedMode));
EXPECT_FALSE(
profile()->GetPrefs()->GetBoolean(prefs::kHttpsOnlyModeAutoEnabled));
// Move forward, this drops fallback #1 as it's now 9 days old.
clock()->SetNow(now + base::Days(9));
hfm_service()
->CheckUserIsTypicallySecureAndMaybeEnableHttpsFirstBalancedMode();
EXPECT_EQ(1u, hfm_service()->GetFallbackEntryCountForTesting());
// Fallback list is now [#2] and we observed for long enough. HFM will be
// enabled.
EXPECT_TRUE(
hfm_service()->IsInterstitialEnabledByTypicallySecureUserHeuristic());
EXPECT_FALSE(profile()->GetPrefs()->GetBoolean(prefs::kHttpsOnlyModeEnabled));
EXPECT_TRUE(
profile()->GetPrefs()->GetBoolean(prefs::kHttpsFirstBalancedMode));
EXPECT_TRUE(
profile()->GetPrefs()->GetBoolean(prefs::kHttpsOnlyModeAutoEnabled));
// Fallback #3. The fallback list is now [#2, #3]. Balanced Mode is still
// auto-enabled.
RecordFallbackEventAndMaybeEnableHttpsFirstMode();
EXPECT_EQ(2u, hfm_service()->GetFallbackEntryCountForTesting());
EXPECT_TRUE(
hfm_service()->IsInterstitialEnabledByTypicallySecureUserHeuristic());
// Prefs are now set.
EXPECT_FALSE(profile()->GetPrefs()->GetBoolean(prefs::kHttpsOnlyModeEnabled));
EXPECT_TRUE(
profile()->GetPrefs()->GetBoolean(prefs::kHttpsFirstBalancedMode));
EXPECT_TRUE(
profile()->GetPrefs()->GetBoolean(prefs::kHttpsOnlyModeAutoEnabled));
// Fallback #4. The fallback list is now [#2, #3, #4]. This is too many
// fallbacks, but we don't auto-disable the user pref once it's
// auto-enabled. (Note that this isn't realistic scenario, as the
// interstitial is already enabled after fallback #3, and we don't record a
// fallback when the interstitial is enabled).
RecordFallbackEventAndMaybeEnableHttpsFirstMode();
EXPECT_EQ(3u, hfm_service()->GetFallbackEntryCountForTesting());
EXPECT_TRUE(
hfm_service()->IsInterstitialEnabledByTypicallySecureUserHeuristic());
// Prefs are still set.
EXPECT_FALSE(profile()->GetPrefs()->GetBoolean(prefs::kHttpsOnlyModeEnabled));
EXPECT_TRUE(
profile()->GetPrefs()->GetBoolean(prefs::kHttpsFirstBalancedMode));
EXPECT_TRUE(
profile()->GetPrefs()->GetBoolean(prefs::kHttpsOnlyModeAutoEnabled));
}
// Checks that manually changing the HFM pref in the UI clears the HTTP
// allowlist. A variant of this test
// (TypicallySecureUserTest.PrefUpdatedByHeuristic_ShouldNotClearAllowlist)
// checks that a heuristic auto-enabling HFM does NOT clear the allowlist.
TEST_F(HttpsFirstModeSettingsTrackerTest, PrefUpdated_ShouldClearAllowlist) {
feature_list()->InitAndDisableFeature(features::kHttpsFirstBalancedMode);
// Instantiate the service so that it can track pref changes.
HttpsFirstModeService* service =
HttpsFirstModeServiceFactory::GetForProfile(profile());
ASSERT_TRUE(service);
EXPECT_FALSE(profile()->GetPrefs()->GetBoolean(prefs::kHttpsOnlyModeEnabled));
// Allowlist a host for for http.
StatefulSSLHostStateDelegate* state =
StatefulSSLHostStateDelegateFactory::GetForProfile(profile());
ASSERT_TRUE(state);
content::StoragePartition* storage_partition =
profile()->GetDefaultStoragePartition();
state->AllowHttpForHost("http-allowed.com", storage_partition);
EXPECT_TRUE(
state->IsHttpAllowedForHost("http-allowed.com", storage_partition));
// Change the UI setting. This should clear the http allowlist.
profile()->GetPrefs()->SetBoolean(prefs::kHttpsOnlyModeEnabled, true);
EXPECT_FALSE(
state->IsHttpAllowedForHost("http-allowed.com", storage_partition));
}
TEST_F(HttpsFirstModeSettingsTrackerTypicallySecureUserTest,
PrefUpdatedByHeuristic_ShouldNotClearAllowlist) {
StatefulSSLHostStateDelegate* state =
StatefulSSLHostStateDelegateFactory::GetForProfile(profile());
ASSERT_TRUE(state);
content::StoragePartition* storage_partition =
profile()->GetDefaultStoragePartition();
EXPECT_FALSE(profile()->GetPrefs()->GetBoolean(prefs::kHttpsOnlyModeEnabled));
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeAutoEnabled));
state->AllowHttpForHost("http-allowed.com", storage_partition);
EXPECT_TRUE(
state->IsHttpAllowedForHost("http-allowed.com", storage_partition));
// From here on, do a bunch of navigations and advance the clock so that
// Typically Secure heuristic eventually auto-enables HFM.
base::Time now = clock()->Now();
clock()->SetNow(now + base::Days(3));
// Record a fallback event to start the Typically Secure observation.
RecordFallbackEventAndMaybeEnableHttpsFirstMode();
EXPECT_EQ(1u, hfm_service()->GetFallbackEntryCountForTesting());
// Move forward and record another event.
clock()->SetNow(now + base::Days(8));
RecordFallbackEventAndMaybeEnableHttpsFirstMode();
EXPECT_EQ(2u, hfm_service()->GetFallbackEntryCountForTesting());
// We have observed for long enough, and we don't have too many fallback
// events (2). However, last fallback event was too recent. Move forward
// again.
clock()->SetNow(now + base::Days(9) + base::Hours(1));
hfm_service()
->CheckUserIsTypicallySecureAndMaybeEnableHttpsFirstBalancedMode();
EXPECT_EQ(2u, hfm_service()->GetFallbackEntryCountForTesting());
// Last fallback event is now a day old, but we don't have enough recent
// navigations. Don't enable yet.
EXPECT_FALSE(
hfm_service()->IsInterstitialEnabledByTypicallySecureUserHeuristic());
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsFirstBalancedMode));
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeAutoEnabled));
// Finally, do lots of navigations. Should auto-enable HFM now.
IncrementRecentNavigationCount(100u);
hfm_service()
->CheckUserIsTypicallySecureAndMaybeEnableHttpsFirstBalancedMode();
EXPECT_TRUE(
hfm_service()->IsInterstitialEnabledByTypicallySecureUserHeuristic());
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
EXPECT_TRUE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsFirstBalancedMode));
EXPECT_TRUE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeAutoEnabled));
// Typically Secure heuristic auto-enabling HFM should not clear the
// allowlist.
EXPECT_TRUE(
state->IsHttpAllowedForHost("http-allowed.com", storage_partition));
}
// Same as DontEnablePrefWhenObservedForLongEnoughWithManyWarnings, but with
// an enterprise managed device. Should never enable HFM.
TEST_F(HttpsFirstModeSettingsTrackerTypicallySecureUserTest,
EnterpriseManaged_ShouldNotEnable) {
ChromeSecurityBlockingPageFactory::SetEnterpriseManagedForTesting(true);
base::Time now = clock()->Now();
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeAutoEnabled));
clock()->SetNow(now + base::Days(3));
RecordFallbackEventAndMaybeEnableHttpsFirstMode();
// Fallback entries are recorded even on enterprise managed devices.
EXPECT_EQ(1u, hfm_service()->GetFallbackEntryCountForTesting());
// We haven't observed the profile for long enough. HFM shouldn't be enabled
// yet.
EXPECT_FALSE(
hfm_service()->IsInterstitialEnabledByTypicallySecureUserHeuristic());
// Prefs shouldn't be modified yet.
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeAutoEnabled));
clock()->SetNow(now + base::Days(8));
RecordFallbackEventAndMaybeEnableHttpsFirstMode();
// Fallback entries are recorded even on enterprise managed devices.
EXPECT_EQ(2u, hfm_service()->GetFallbackEntryCountForTesting());
// We have observed for long enough, and we don't have too many fallback
// events (2). However, last fallback event was too recent, don't enable just
// yet.
EXPECT_FALSE(
hfm_service()->IsInterstitialEnabledByTypicallySecureUserHeuristic());
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeAutoEnabled));
clock()->SetNow(now + base::Days(9) + base::Hours(1));
hfm_service()
->CheckUserIsTypicallySecureAndMaybeEnableHttpsFirstBalancedMode();
EXPECT_EQ(2u, hfm_service()->GetFallbackEntryCountForTesting());
// Last fallback event is now a day old, but we don't have enough recent
// navigations. Don't enable yet.
EXPECT_FALSE(
hfm_service()->IsInterstitialEnabledByTypicallySecureUserHeuristic());
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeAutoEnabled));
// Do lots of navigations. Should still not enable HFM because the device is
// managed.
IncrementRecentNavigationCount(100u);
hfm_service()
->CheckUserIsTypicallySecureAndMaybeEnableHttpsFirstBalancedMode();
EXPECT_FALSE(
hfm_service()->IsInterstitialEnabledByTypicallySecureUserHeuristic());
EXPECT_FALSE(
profile()->GetPrefs()->GetBoolean(prefs::kHttpsFirstBalancedMode));
EXPECT_FALSE(
profile()->GetPrefs()->GetBoolean(prefs::kHttpsOnlyModeAutoEnabled));
// Shouldn't modify strict mode pref:
EXPECT_FALSE(
profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
}
// Tests that the correct setting at startup is logged, when the Balanced Mode
// feature flag is enabled but not on by default.
TEST_F(HttpsFirstModeSettingsTrackerTest, StartupBalancedModeAvailable) {
feature_list()->InitAndEnableFeature(features::kHttpsFirstBalancedMode);
base::HistogramTester histograms;
HttpsFirstModeService* service =
HttpsFirstModeServiceFactory::GetForProfile(profile());
ASSERT_TRUE(service);
// Creating the HttpsFirstModeService should emit the "setting at startup"
// histogram.
histograms.ExpectUniqueSample(
"Security.HttpsFirstMode.SettingEnabledAtStartup2",
HttpsFirstModeSetting::kDisabled, 1);
}
// Tests that the correct setting at startup is logged, when Balanced Mode
// is auto-enabled.
TEST_F(HttpsFirstModeSettingsTrackerTest, StartupBalancedModeAutoEnabled) {
base::test::ScopedFeatureList feature_list;
feature_list.InitWithFeatures(
/*enabled_features=*/{features::kHttpsFirstBalancedMode,
features::kHttpsFirstBalancedModeAutoEnable},
/*disabled_features=*/{});
base::HistogramTester histograms;
HttpsFirstModeService* service =
HttpsFirstModeServiceFactory::GetForProfile(profile());
ASSERT_TRUE(service);
// Creating the HttpsFirstModeService should emit the "setting at startup"
// histogram.
histograms.ExpectUniqueSample(
"Security.HttpsFirstMode.SettingEnabledAtStartup2",
HttpsFirstModeSetting::kEnabledBalanced, 1);
}