[HFM] Ignore URLs with non-default ports from Site Engagement Heuristic
Site Engagement heuristic can auto-enable HTTPS-First Mode if a hostname has a high engagement score on its HTTPS URLs and a low score on its HTTP URLs. Presently, it can auto-enable HFM on an origin with a non-default port. However, the persisted HTTPS-enforcement list only stores hostnames, so a high site engagement score on https://example.com:8443 enables HFM on all of example.com. Reverse is also true: a high score on https://example.com auto-enables HFM on https://example.com:8443. This behavior may be confusing to developers and is in general error prone. This CL does two things: - It ignores site engagement scores of origins with non-default ports. - It stops automatically enforcing HTTPS on origins with non-default ports. Bug: 1442679 Change-Id: I69bc0376b32adf501f2e5f61e71cfd35eeb9dd8e Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5035039 Reviewed-by: Scott Violet <sky@chromium.org> Reviewed-by: Nate Fischer <ntfschr@chromium.org> Commit-Queue: Mustafa Emre Acer <meacer@chromium.org> Reviewed-by: Dmitry Gozman <dgozman@chromium.org> Reviewed-by: Chris Thompson <cthomp@chromium.org> Code-Coverage: findit-for-me@appspot.gserviceaccount.com <findit-for-me@appspot.gserviceaccount.com> Cr-Commit-Position: refs/heads/main@{#1225896}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
74792d0cb7
commit
0ce005b0c6
android_webview/browser
chrome/browser/ssl
https_first_mode_settings_tracker.cchttps_first_mode_settings_tracker_unittest.cchttps_upgrades_browsertest.cchttps_upgrades_interceptor.cchttps_upgrades_navigation_throttle.cc
components
page_info
security_interstitials
content
@ -82,8 +82,8 @@ void AwSSLHostStateDelegate::SetHttpsEnforcementForHost(
|
||||
// Intentional no-op for Android WebView.
|
||||
}
|
||||
|
||||
bool AwSSLHostStateDelegate::IsHttpsEnforcedForHost(
|
||||
const std::string& host,
|
||||
bool AwSSLHostStateDelegate::IsHttpsEnforcedForUrl(
|
||||
const GURL& url,
|
||||
content::StoragePartition* storage_partition) {
|
||||
// Intentional no-op for Android WebView. Return value does not matter as
|
||||
// HTTPS-First Mode is not enabled on WebView.
|
||||
|
@ -86,8 +86,8 @@ class AwSSLHostStateDelegate : public content::SSLHostStateDelegate {
|
||||
const std::string& host,
|
||||
bool enforce,
|
||||
content::StoragePartition* storage_partition) override;
|
||||
bool IsHttpsEnforcedForHost(
|
||||
const std::string& host,
|
||||
bool IsHttpsEnforcedForUrl(
|
||||
const GURL& url,
|
||||
content::StoragePartition* storage_partition) override;
|
||||
|
||||
// Revokes all SSL certificate error allow exceptions made by the user for
|
||||
|
@ -527,7 +527,7 @@ void HttpsFirstModeService::ProcessEngagedSitesList(
|
||||
// TODO(crbug.com/1435222): Sites dropping off from the engaged sites list
|
||||
// should no longer have HTTPS enforced.
|
||||
for (const site_engagement::mojom::SiteEngagementDetails& detail : details) {
|
||||
if (detail.origin.SchemeIsHTTPOrHTTPS()) {
|
||||
if (detail.origin.SchemeIsHTTPOrHTTPS() && detail.origin.port().empty()) {
|
||||
MaybeEnableHttpsFirstModeForUrl(detail.origin, engagement_service, state);
|
||||
}
|
||||
}
|
||||
@ -541,8 +541,8 @@ void HttpsFirstModeService::MaybeEnableHttpsFirstModeForUrl(
|
||||
const GURL& url,
|
||||
site_engagement::SiteEngagementService* engagement_service,
|
||||
StatefulSSLHostStateDelegate* state) {
|
||||
bool enforced = state->IsHttpsEnforcedForHost(
|
||||
url.host(), profile_->GetDefaultStoragePartition());
|
||||
bool enforced =
|
||||
state->IsHttpsEnforcedForUrl(url, profile_->GetDefaultStoragePartition());
|
||||
GURL https_url = url.SchemeIsCryptographic() ? url : GetHttpsUrlFromHttp(url);
|
||||
GURL http_url = !url.SchemeIsCryptographic() ? url : GetHttpUrlFromHttps(url);
|
||||
|
||||
|
@ -113,21 +113,72 @@ TEST_F(HttpsFirstModeSettingsTrackerTest,
|
||||
GURL https_url("https://example.com");
|
||||
engagement_service->ResetBaseScoreForURL(https_url, 90);
|
||||
MaybeEnableHttpsFirstModeForEngagedSitesAndWait(service);
|
||||
EXPECT_TRUE(state->IsHttpsEnforcedForHost(
|
||||
"example.com", profile()->GetDefaultStoragePartition()));
|
||||
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->IsHttpsEnforcedForHost(
|
||||
"example.com", profile()->GetDefaultStoragePartition()));
|
||||
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->IsHttpsEnforcedForHost(
|
||||
"example.com", profile()->GetDefaultStoragePartition()));
|
||||
EXPECT_FALSE(state->IsHttpsEnforcedForUrl(
|
||||
GURL("http://example.com"), profile()->GetDefaultStoragePartition()));
|
||||
EXPECT_FALSE(state->IsHttpsEnforcedForUrl(
|
||||
GURL("https://example.com"), profile()->GetDefaultStoragePartition()));
|
||||
}
|
||||
|
||||
TEST_F(HttpsFirstModeSettingsTrackerTest, SiteEngagementHeuristic) {
|
||||
// Check that high site engagement scores of HTTPS URLs with non-default ports
|
||||
// do not auto-enable HTTPS-First Mode.
|
||||
TEST_F(
|
||||
HttpsFirstModeSettingsTrackerTest,
|
||||
SiteEngagementHeuristic_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);
|
||||
}
|
||||
|
||||
TEST_F(HttpsFirstModeSettingsTrackerTest,
|
||||
SiteEngagementHeuristic_ShouldEnforceHttps) {
|
||||
HttpsFirstModeService* service =
|
||||
HttpsFirstModeServiceFactory::GetForProfile(profile());
|
||||
ASSERT_TRUE(service);
|
||||
@ -155,8 +206,8 @@ TEST_F(HttpsFirstModeSettingsTrackerTest, SiteEngagementHeuristic) {
|
||||
|
||||
// Step 1: HFM should initially be disabled on this site by default.
|
||||
MaybeEnableHttpsFirstModeForEngagedSitesAndWait(service);
|
||||
EXPECT_FALSE(state->IsHttpsEnforcedForHost(
|
||||
"example.com", profile()->GetDefaultStoragePartition()));
|
||||
EXPECT_FALSE(state->IsHttpsEnforcedForUrl(
|
||||
GURL("http://example.com"), profile()->GetDefaultStoragePartition()));
|
||||
histograms.ExpectTotalCount(kSiteEngagementHeuristicStateHistogram, 0);
|
||||
histograms.ExpectTotalCount(kSiteEngagementHeuristicHostCountHistogram, 0);
|
||||
histograms.ExpectTotalCount(
|
||||
@ -167,8 +218,8 @@ TEST_F(HttpsFirstModeSettingsTrackerTest, SiteEngagementHeuristic) {
|
||||
// Step 2: Increase the score, should now enable HFM.
|
||||
engagement_service->ResetBaseScoreForURL(https_url, 90);
|
||||
MaybeEnableHttpsFirstModeForEngagedSitesAndWait(service);
|
||||
EXPECT_TRUE(state->IsHttpsEnforcedForHost(
|
||||
"example.com", profile()->GetDefaultStoragePartition()));
|
||||
EXPECT_TRUE(state->IsHttpsEnforcedForUrl(
|
||||
GURL("http://example.com"), profile()->GetDefaultStoragePartition()));
|
||||
// Check events.
|
||||
histograms.ExpectTotalCount(kSiteEngagementHeuristicStateHistogram, 1);
|
||||
histograms.ExpectBucketCount(kSiteEngagementHeuristicStateHistogram,
|
||||
@ -191,15 +242,16 @@ TEST_F(HttpsFirstModeSettingsTrackerTest, SiteEngagementHeuristic) {
|
||||
kSiteEngagementHeuristicEnforcementDurationHistogram, 0);
|
||||
|
||||
// Subdomains shouldn't be affected.
|
||||
EXPECT_FALSE(state->IsHttpsEnforcedForHost(
|
||||
"test.example.com", profile()->GetDefaultStoragePartition()));
|
||||
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->IsHttpsEnforcedForHost(
|
||||
"example.com", profile()->GetDefaultStoragePartition()));
|
||||
EXPECT_TRUE(state->IsHttpsEnforcedForUrl(
|
||||
GURL("http://example.com"), profile()->GetDefaultStoragePartition()));
|
||||
// Check events.
|
||||
histograms.ExpectTotalCount(kSiteEngagementHeuristicStateHistogram, 1);
|
||||
histograms.ExpectBucketCount(kSiteEngagementHeuristicStateHistogram,
|
||||
@ -226,8 +278,8 @@ TEST_F(HttpsFirstModeSettingsTrackerTest, SiteEngagementHeuristic) {
|
||||
clock_ptr->Advance(base::Hours(1));
|
||||
engagement_service->ResetBaseScoreForURL(https_url, 25);
|
||||
MaybeEnableHttpsFirstModeForEngagedSitesAndWait(service);
|
||||
EXPECT_FALSE(state->IsHttpsEnforcedForHost(
|
||||
"example.com", profile()->GetDefaultStoragePartition()));
|
||||
EXPECT_FALSE(state->IsHttpsEnforcedForUrl(
|
||||
GURL("http://example.com"), profile()->GetDefaultStoragePartition()));
|
||||
// Check events.
|
||||
histograms.ExpectTotalCount(kSiteEngagementHeuristicStateHistogram, 2);
|
||||
histograms.ExpectBucketCount(kSiteEngagementHeuristicStateHistogram,
|
||||
@ -259,8 +311,8 @@ TEST_F(HttpsFirstModeSettingsTrackerTest, SiteEngagementHeuristic) {
|
||||
clock_ptr->Advance(base::Hours(2));
|
||||
engagement_service->ResetBaseScoreForURL(https_url, 90);
|
||||
MaybeEnableHttpsFirstModeForEngagedSitesAndWait(service);
|
||||
EXPECT_TRUE(state->IsHttpsEnforcedForHost(
|
||||
"example.com", profile()->GetDefaultStoragePartition()));
|
||||
EXPECT_TRUE(state->IsHttpsEnforcedForUrl(
|
||||
GURL("http://example.com"), profile()->GetDefaultStoragePartition()));
|
||||
// Check state.
|
||||
histograms.ExpectTotalCount(kSiteEngagementHeuristicStateHistogram, 3);
|
||||
histograms.ExpectBucketCount(kSiteEngagementHeuristicStateHistogram,
|
||||
@ -292,8 +344,8 @@ TEST_F(HttpsFirstModeSettingsTrackerTest, SiteEngagementHeuristic) {
|
||||
// the HTTPS score is still high.
|
||||
engagement_service->ResetBaseScoreForURL(http_url, 20);
|
||||
MaybeEnableHttpsFirstModeForEngagedSitesAndWait(service);
|
||||
EXPECT_FALSE(state->IsHttpsEnforcedForHost(
|
||||
"example.com", profile()->GetDefaultStoragePartition()));
|
||||
EXPECT_FALSE(state->IsHttpsEnforcedForUrl(
|
||||
GURL("http://example.com"), profile()->GetDefaultStoragePartition()));
|
||||
// Check state.
|
||||
histograms.ExpectTotalCount(kSiteEngagementHeuristicStateHistogram, 4);
|
||||
histograms.ExpectBucketCount(kSiteEngagementHeuristicStateHistogram,
|
||||
@ -329,8 +381,8 @@ TEST_F(HttpsFirstModeSettingsTrackerTest, SiteEngagementHeuristic) {
|
||||
engagement_service->ResetBaseScoreForURL(https_url, 0);
|
||||
engagement_service->ResetBaseScoreForURL(http_url, 100);
|
||||
MaybeEnableHttpsFirstModeForEngagedSitesAndWait(service);
|
||||
EXPECT_FALSE(state->IsHttpsEnforcedForHost(
|
||||
"example.com", profile()->GetDefaultStoragePartition()));
|
||||
EXPECT_FALSE(state->IsHttpsEnforcedForUrl(
|
||||
GURL("http://example.com"), profile()->GetDefaultStoragePartition()));
|
||||
// Check state.
|
||||
histograms.ExpectTotalCount(kSiteEngagementHeuristicStateHistogram, 4);
|
||||
histograms.ExpectBucketCount(kSiteEngagementHeuristicStateHistogram,
|
||||
@ -388,8 +440,8 @@ TEST_F(HttpsFirstModeSettingsTrackerTest,
|
||||
// Increase the HTTPS engagement score to enable HFM on this host.
|
||||
engagement_service->ResetBaseScoreForURL(https_url, 90);
|
||||
MaybeEnableHttpsFirstModeForEngagedSitesAndWait(service);
|
||||
EXPECT_TRUE(state->IsHttpsEnforcedForHost(
|
||||
"example.com", profile()->GetDefaultStoragePartition()));
|
||||
EXPECT_TRUE(state->IsHttpsEnforcedForUrl(
|
||||
GURL("http://example.com"), profile()->GetDefaultStoragePartition()));
|
||||
|
||||
// Clear up the engagement scores. This should remove the enforcement.
|
||||
HostContentSettingsMap* settings_map =
|
||||
@ -402,8 +454,8 @@ TEST_F(HttpsFirstModeSettingsTrackerTest,
|
||||
|
||||
// TODO(crbug.com/1435222): Sites that fall outside the site engagement list
|
||||
// should no longer have HTTPS enforced.
|
||||
EXPECT_TRUE(state->IsHttpsEnforcedForHost(
|
||||
"example.com", profile()->GetDefaultStoragePartition()));
|
||||
EXPECT_TRUE(state->IsHttpsEnforcedForUrl(
|
||||
GURL("http://example.com"), profile()->GetDefaultStoragePartition()));
|
||||
|
||||
service->Shutdown();
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "base/memory/raw_ptr.h"
|
||||
@ -727,13 +728,41 @@ void MaybeEnableHttpsFirstModeForEngagedSitesAndWait(
|
||||
run_loop.Run();
|
||||
}
|
||||
|
||||
// Returns a URL loader interceptor that responds to HTTPS URLs with a cert
|
||||
// error and to HTTP URLs with a good response.
|
||||
std::unique_ptr<content::URLLoaderInterceptor>
|
||||
MakeInterceptorForSiteEngagementHeuristic() {
|
||||
return std::make_unique<content::URLLoaderInterceptor>(
|
||||
base::BindLambdaForTesting(
|
||||
[](content::URLLoaderInterceptor::RequestParams* params) {
|
||||
if (params->url_request.url.SchemeIs("https")) {
|
||||
// Fail with an SSL error.
|
||||
network::URLLoaderCompletionStatus status;
|
||||
status.error_code = net::ERR_CERT_COMMON_NAME_INVALID;
|
||||
status.ssl_info = net::SSLInfo();
|
||||
status.ssl_info->cert_status =
|
||||
net::CERT_STATUS_COMMON_NAME_INVALID;
|
||||
// The cert doesn't matter.
|
||||
status.ssl_info->cert = net::ImportCertFromFile(
|
||||
net::GetTestCertsDirectory(), "ok_cert.pem");
|
||||
status.ssl_info->unverified_cert = status.ssl_info->cert;
|
||||
params->client->OnComplete(status);
|
||||
return true;
|
||||
}
|
||||
content::URLLoaderInterceptor::WriteResponse(
|
||||
"HTTP/1.1 200 OK\nContent-type: text/html\n\n",
|
||||
"<html>Done</html>", params->client.get());
|
||||
return true;
|
||||
}));
|
||||
}
|
||||
|
||||
// TODO(https://crbug.com/1435222): Fails on the linux-wayland-rel bot.
|
||||
#if defined(OZONE_PLATFORM_WAYLAND)
|
||||
#define MAYBE_UrlWithHttpScheme_BrokenSSL_ShouldInterstitial_SiteEngagement \
|
||||
DISABLED_UrlWithHttpScheme_BrokenSSL_ShouldInterstitial_SiteEngagement
|
||||
#define MAYBE_UrlWithHttpScheme_BrokenSSL_SiteEngagementHeuristic_ShouldInterstitial \
|
||||
DISABLED_UrlWithHttpScheme_BrokenSSL_SiteEngagementHeuristic_ShouldInterstitial
|
||||
#else
|
||||
#define MAYBE_UrlWithHttpScheme_BrokenSSL_ShouldInterstitial_SiteEngagement \
|
||||
UrlWithHttpScheme_BrokenSSL_ShouldInterstitial_SiteEngagement
|
||||
#define MAYBE_UrlWithHttpScheme_BrokenSSL_SiteEngagementHeuristic_ShouldInterstitial \
|
||||
UrlWithHttpScheme_BrokenSSL_SiteEngagementHeuristic_ShouldInterstitial
|
||||
#endif
|
||||
// Test for Site Engagement Heuristic, a feature that enables HFM on specific
|
||||
// sites based on their site engagement scores.
|
||||
@ -743,11 +772,16 @@ void MaybeEnableHttpsFirstModeForEngagedSitesAndWait(
|
||||
// Heuristic if the interstitial isn't enabled.
|
||||
IN_PROC_BROWSER_TEST_P(
|
||||
HttpsUpgradesBrowserTest,
|
||||
MAYBE_UrlWithHttpScheme_BrokenSSL_ShouldInterstitial_SiteEngagement) {
|
||||
MAYBE_UrlWithHttpScheme_BrokenSSL_SiteEngagementHeuristic_ShouldInterstitial) {
|
||||
// HFM+SE is not enabled in Incognito.
|
||||
if (IsIncognito()) {
|
||||
return;
|
||||
}
|
||||
// Disable the testing port configuration, as this test doesn't use the
|
||||
// EmbeddedTestServer.
|
||||
HttpsUpgradesInterceptor::SetHttpsPortForTesting(0);
|
||||
HttpsUpgradesInterceptor::SetHttpPortForTesting(0);
|
||||
auto url_loader_interceptor = MakeInterceptorForSiteEngagementHeuristic();
|
||||
|
||||
content::WebContents* contents =
|
||||
GetBrowser()->tab_strip_model()->GetActiveWebContents();
|
||||
@ -764,8 +798,8 @@ IN_PROC_BROWSER_TEST_P(
|
||||
// Start the clock at standard system time.
|
||||
clock_ptr->SetNow(base::Time::NowFromSystemTime());
|
||||
|
||||
GURL http_url = http_server()->GetURL("bad-https.com", "/simple.html");
|
||||
GURL https_url = https_server()->GetURL("bad-https.com", "/simple.html");
|
||||
GURL http_url("http://bad-https.com");
|
||||
GURL https_url("https://bad-https.com");
|
||||
SetSiteEngagementScore(http_url, kLowSiteEngagementScore);
|
||||
SetSiteEngagementScore(https_url, kHighSiteEnagementScore);
|
||||
HttpsFirstModeService* hfm_service =
|
||||
@ -959,6 +993,123 @@ IN_PROC_BROWSER_TEST_P(
|
||||
}
|
||||
}
|
||||
|
||||
// Test that Site Engagement Heuristic doesn't enforce HTTPS on URLs with
|
||||
// non-default ports.
|
||||
IN_PROC_BROWSER_TEST_P(
|
||||
HttpsUpgradesBrowserTest,
|
||||
UrlWithHttpScheme_BrokenSSL_SiteEngagementHeuristic_ShouldIgnoreUrlsWithNonDefaultPorts) {
|
||||
// HFM+SE is not enabled in Incognito.
|
||||
if (IsIncognito()) {
|
||||
return;
|
||||
}
|
||||
// Disable the testing port configuration, as this test doesn't use the
|
||||
// EmbeddedTestServer.
|
||||
HttpsUpgradesInterceptor::SetHttpsPortForTesting(0);
|
||||
HttpsUpgradesInterceptor::SetHttpPortForTesting(0);
|
||||
auto url_loader_interceptor = MakeInterceptorForSiteEngagementHeuristic();
|
||||
|
||||
content::WebContents* contents =
|
||||
GetBrowser()->tab_strip_model()->GetActiveWebContents();
|
||||
Profile* profile = GetBrowser()->profile();
|
||||
content::SSLHostStateDelegate* state = profile->GetSSLHostStateDelegate();
|
||||
|
||||
// Set test clock.
|
||||
auto clock = std::make_unique<base::SimpleTestClock>();
|
||||
auto* clock_ptr = clock.get();
|
||||
StatefulSSLHostStateDelegate* chrome_state =
|
||||
static_cast<StatefulSSLHostStateDelegate*>(state);
|
||||
chrome_state->SetClockForTesting(std::move(clock));
|
||||
|
||||
// Start the clock at standard system time.
|
||||
clock_ptr->SetNow(base::Time::NowFromSystemTime());
|
||||
|
||||
GURL http_url("http://bad-https.com");
|
||||
GURL https_url("https://bad-https.com");
|
||||
GURL navigated_url("http://bad-https.com:8080");
|
||||
|
||||
SetSiteEngagementScore(http_url, kLowSiteEngagementScore);
|
||||
SetSiteEngagementScore(https_url, kHighSiteEnagementScore);
|
||||
HttpsFirstModeService* hfm_service =
|
||||
HttpsFirstModeServiceFactory::GetForProfile(profile);
|
||||
MaybeEnableHttpsFirstModeForEngagedSitesAndWait(hfm_service);
|
||||
|
||||
// This URL should be upgraded by HTTPS-Upgrades, but not have HFM
|
||||
// auto-enabled on it because it has a non-default port.
|
||||
NavigateAndWaitForFallback(contents, navigated_url);
|
||||
EXPECT_EQ(navigated_url, contents->GetLastCommittedURL());
|
||||
|
||||
if (IsHttpsFirstModePrefEnabled()) {
|
||||
EXPECT_TRUE(
|
||||
chrome_browser_interstitials::IsShowingHttpsFirstModeInterstitial(
|
||||
contents));
|
||||
EXPECT_TRUE(chrome_browser_interstitials::IsInterstitialDisplayingText(
|
||||
contents->GetPrimaryMainFrame(),
|
||||
"You are seeing this warning because this site does not support "
|
||||
"HTTPS."));
|
||||
} else {
|
||||
EXPECT_FALSE(
|
||||
chrome_browser_interstitials::IsShowingHttpsFirstModeInterstitial(
|
||||
contents));
|
||||
}
|
||||
|
||||
// Verify that navigation event metrics were correctly recorded.
|
||||
if (IsHttpUpgradingEnabled()) {
|
||||
histograms()->ExpectTotalCount(kEventHistogram, 3);
|
||||
histograms()->ExpectBucketCount(kEventHistogram, Event::kUpgradeAttempted,
|
||||
1);
|
||||
histograms()->ExpectBucketCount(kEventHistogram, Event::kUpgradeFailed, 1);
|
||||
histograms()->ExpectBucketCount(kEventHistogram, Event::kUpgradeCertError,
|
||||
1);
|
||||
|
||||
// Engagement heuristic shouldn't handle any navigation events because we
|
||||
// didn't navigate to example.com.
|
||||
histograms()->ExpectTotalCount(kEventHistogramWithEngagementHeuristic, 0);
|
||||
|
||||
// Check engagement heuristic metrics. These are only recorded when the
|
||||
// interstitial isn't enabled by the user pref.
|
||||
if (!IsHttpsFirstModePrefEnabled()) {
|
||||
// Check the heuristic state. The heuristic should enable HFM for
|
||||
// example.com
|
||||
histograms()->ExpectTotalCount(kSiteEngagementHeuristicStateHistogram, 1);
|
||||
histograms()->ExpectBucketCount(kSiteEngagementHeuristicStateHistogram,
|
||||
SiteEngagementHeuristicState::kDisabled,
|
||||
0);
|
||||
histograms()->ExpectBucketCount(kSiteEngagementHeuristicStateHistogram,
|
||||
SiteEngagementHeuristicState::kEnabled,
|
||||
1);
|
||||
// Check host count.
|
||||
histograms()->ExpectTotalCount(kSiteEngagementHeuristicHostCountHistogram,
|
||||
1);
|
||||
histograms()->ExpectBucketCount(
|
||||
kSiteEngagementHeuristicHostCountHistogram, 0,
|
||||
/*expected_count=*/0);
|
||||
histograms()->ExpectBucketCount(
|
||||
kSiteEngagementHeuristicHostCountHistogram, 1,
|
||||
/*expected_count=*/1);
|
||||
// Check accumulated host count.
|
||||
histograms()->ExpectTotalCount(
|
||||
kSiteEngagementHeuristicAccumulatedHostCountHistogram, 1);
|
||||
histograms()->ExpectBucketCount(
|
||||
kSiteEngagementHeuristicAccumulatedHostCountHistogram, 0,
|
||||
/*expected_count=*/0);
|
||||
histograms()->ExpectBucketCount(
|
||||
kSiteEngagementHeuristicAccumulatedHostCountHistogram, 1,
|
||||
/*expected_count=*/1);
|
||||
// Check enforcement duration. Since the host isn't removed from HFM
|
||||
// enforcement list, no duration should be recorded yet.
|
||||
histograms()->ExpectTotalCount(
|
||||
kSiteEngagementHeuristicEnforcementDurationHistogram, 0);
|
||||
} else {
|
||||
histograms()->ExpectTotalCount(kEventHistogramWithEngagementHeuristic, 0);
|
||||
}
|
||||
} else {
|
||||
histograms()->ExpectTotalCount(kEventHistogram, 1);
|
||||
histograms()->ExpectBucketCount(kEventHistogram,
|
||||
Event::kUpgradeNotAttempted, 1);
|
||||
histograms()->ExpectTotalCount(kEventHistogramWithEngagementHeuristic, 0);
|
||||
}
|
||||
}
|
||||
|
||||
IN_PROC_BROWSER_TEST_P(
|
||||
HttpsUpgradesBrowserTest,
|
||||
PRE_UrlWithHttpScheme_BrokenSSL_ShouldInterstitial_TypicallySecureUser) {
|
||||
@ -2422,14 +2573,20 @@ IN_PROC_BROWSER_TEST_P(
|
||||
if (!IsSiteEngagementHeuristicEnabled()) {
|
||||
return;
|
||||
}
|
||||
// Disable the testing port configuration, as this test doesn't use the
|
||||
// EmbeddedTestServer.
|
||||
HttpsUpgradesInterceptor::SetHttpsPortForTesting(0);
|
||||
HttpsUpgradesInterceptor::SetHttpPortForTesting(0);
|
||||
auto url_loader_interceptor = MakeInterceptorForSiteEngagementHeuristic();
|
||||
|
||||
content::WebContents* contents =
|
||||
GetBrowser()->tab_strip_model()->GetActiveWebContents();
|
||||
auto* profile = Profile::FromBrowserContext(contents->GetBrowserContext());
|
||||
|
||||
// Without any policy allowlist, navigate to an HTTP URL. It should show the
|
||||
// HFM+SE interstitial.
|
||||
auto http_url = http_server()->GetURL("bad-https.com", "/simple.html");
|
||||
auto https_url = https_server()->GetURL("bad-https.com", "/simple.html");
|
||||
GURL http_url("http://bad-https.com");
|
||||
GURL https_url("https://bad-https.com");
|
||||
SetSiteEngagementScore(http_url, kLowSiteEngagementScore);
|
||||
SetSiteEngagementScore(https_url, kHighSiteEnagementScore);
|
||||
|
||||
|
@ -268,8 +268,8 @@ void HttpsUpgradesInterceptor::MaybeCreateLoader(
|
||||
security_interstitials::https_only_mode::HttpInterstitialState>();
|
||||
interstitial_state_->enabled_by_pref = http_interstitial_enabled_by_pref_;
|
||||
// StatefulSSLHostStateDelegate can be null during tests.
|
||||
if (state && state->IsHttpsEnforcedForHost(
|
||||
tentative_resource_request.url.host(), storage_partition)) {
|
||||
if (state && state->IsHttpsEnforcedForUrl(tentative_resource_request.url,
|
||||
storage_partition)) {
|
||||
interstitial_state_->enabled_by_engagement_heuristic = true;
|
||||
}
|
||||
|
||||
|
@ -87,8 +87,8 @@ HttpsUpgradesNavigationThrottle::MaybeCreateThrottleFor(
|
||||
}
|
||||
|
||||
// StatefulSSLHostStateDelegate can be null during tests.
|
||||
if (state && state->IsHttpsEnforcedForHost(handle->GetURL().host(),
|
||||
storage_partition)) {
|
||||
if (state &&
|
||||
state->IsHttpsEnforcedForUrl(handle->GetURL(), storage_partition)) {
|
||||
interstitial_state.enabled_by_engagement_heuristic = true;
|
||||
}
|
||||
|
||||
|
@ -1159,9 +1159,8 @@ void PageInfo::ComputeUIInputs(const GURL& url) {
|
||||
// revocation button for HTTP allowlist entries added because HTTPS was
|
||||
// enforced by HTTPS-First Mode.
|
||||
bool is_https_enforced =
|
||||
delegate->IsHttpsEnforcedForHost(
|
||||
url.host(),
|
||||
web_contents_->GetPrimaryMainFrame()->GetStoragePartition()) ||
|
||||
delegate->IsHttpsEnforcedForUrl(
|
||||
url, web_contents_->GetPrimaryMainFrame()->GetStoragePartition()) ||
|
||||
delegate_->IsHttpsFirstModeEnabled();
|
||||
|
||||
bool has_warning_bypass_exception =
|
||||
|
@ -390,14 +390,14 @@ void StatefulSSLHostStateDelegate::SetHttpsEnforcementForHost(
|
||||
}
|
||||
}
|
||||
|
||||
bool StatefulSSLHostStateDelegate::IsHttpsEnforcedForHost(
|
||||
const std::string& host,
|
||||
bool StatefulSSLHostStateDelegate::IsHttpsEnforcedForUrl(
|
||||
const GURL& url,
|
||||
content::StoragePartition* storage_partition) {
|
||||
bool is_nondefault_storage =
|
||||
!storage_partition ||
|
||||
storage_partition != browser_context_->GetDefaultStoragePartition();
|
||||
return https_only_mode_enforcelist_.IsEnforcedForHost(host,
|
||||
is_nondefault_storage);
|
||||
return https_only_mode_enforcelist_.IsEnforcedForUrl(url,
|
||||
is_nondefault_storage);
|
||||
}
|
||||
|
||||
void StatefulSSLHostStateDelegate::ClearHttpsOnlyModeAllowlist() {
|
||||
|
@ -92,8 +92,8 @@ class StatefulSSLHostStateDelegate : public content::SSLHostStateDelegate,
|
||||
const std::string& host,
|
||||
bool enforced,
|
||||
content::StoragePartition* storage_partition) override;
|
||||
bool IsHttpsEnforcedForHost(
|
||||
const std::string& host,
|
||||
bool IsHttpsEnforcedForUrl(
|
||||
const GURL& url,
|
||||
content::StoragePartition* storage_partition) override;
|
||||
|
||||
// Clears all entries from the HTTP allowlist.
|
||||
|
@ -50,7 +50,7 @@ void HttpsOnlyModeEnforcelist::EnforceForHost(const std::string& host,
|
||||
enforce_https_hosts_for_non_default_storage_partitions_.insert(host);
|
||||
return;
|
||||
}
|
||||
DCHECK(!IsEnforcedForHost(host, is_nondefault_storage));
|
||||
DCHECK(!IsEnforcedForUrl(GURL("http://" + host), is_nondefault_storage));
|
||||
|
||||
// We want to count how many HTTPS-enforced hosts accumulate over time, so
|
||||
// use a dictionary here.
|
||||
@ -75,7 +75,7 @@ void HttpsOnlyModeEnforcelist::UnenforceForHost(const std::string& host,
|
||||
enforce_https_hosts_for_non_default_storage_partitions_.erase(host);
|
||||
return;
|
||||
}
|
||||
DCHECK(IsEnforcedForHost(host, is_nondefault_storage));
|
||||
DCHECK(IsEnforcedForUrl(GURL("http://" + host), is_nondefault_storage));
|
||||
|
||||
// We want to count how many HTTPS-enforced hosts accumulate over time, so
|
||||
// don't remove the value, just set it to false.
|
||||
@ -104,17 +104,21 @@ void HttpsOnlyModeEnforcelist::UnenforceForHost(const std::string& host,
|
||||
RecordMetrics(is_nondefault_storage);
|
||||
}
|
||||
|
||||
bool HttpsOnlyModeEnforcelist::IsEnforcedForHost(
|
||||
const std::string& host,
|
||||
bool HttpsOnlyModeEnforcelist::IsEnforcedForUrl(
|
||||
const GURL& url,
|
||||
bool is_nondefault_storage) const {
|
||||
// HTTPS-First Mode is never auto-enabled for URLs with non-default ports.
|
||||
if (!url.port().empty()) {
|
||||
return false;
|
||||
}
|
||||
if (is_nondefault_storage) {
|
||||
return base::Contains(
|
||||
enforce_https_hosts_for_non_default_storage_partitions_, host);
|
||||
enforce_https_hosts_for_non_default_storage_partitions_, url.host());
|
||||
}
|
||||
|
||||
GURL url = GetSecureGURLForHost(host);
|
||||
GURL secure_url = GetSecureGURLForHost(url.host());
|
||||
const base::Value value = host_content_settings_map_->GetWebsiteSetting(
|
||||
url, url, ContentSettingsType::HTTPS_ENFORCED, nullptr);
|
||||
secure_url, secure_url, ContentSettingsType::HTTPS_ENFORCED, nullptr);
|
||||
if (!value.is_dict()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -45,9 +45,10 @@ class HttpsOnlyModeEnforcelist {
|
||||
// enforced for this host.
|
||||
void UnenforceForHost(const std::string& host, bool is_nondefault_storage);
|
||||
|
||||
// Returns true if host is not allowed to be loaded over HTTP.
|
||||
bool IsEnforcedForHost(const std::string& host,
|
||||
bool is_nondefault_storage) const;
|
||||
// Returns true if URL is not allowed to be loaded over HTTP. This check
|
||||
// ignores the scheme of the URL: calling it with http:// or https:// will
|
||||
// return the same result.
|
||||
bool IsEnforcedForUrl(const GURL& url, bool is_nondefault_storage) const;
|
||||
|
||||
// Revokes all HTTPS enforcements for host.
|
||||
void RevokeEnforcements(const std::string& host);
|
||||
|
@ -11,11 +11,13 @@
|
||||
#include "base/functional/callback_forward.h"
|
||||
#include "net/cert/x509_certificate.h"
|
||||
|
||||
class GURL;
|
||||
|
||||
namespace content {
|
||||
|
||||
class StoragePartition;
|
||||
|
||||
// The SSLHostStateDelegate encapulates the host-specific state for SSL errors.
|
||||
// The SSLHostStateDelegate encapsulates the host-specific state for SSL errors.
|
||||
// For example, SSLHostStateDelegate remembers whether the user has whitelisted
|
||||
// a particular broken cert for use with particular host. We separate this
|
||||
// state from the SSLManager because this state is shared across many navigation
|
||||
@ -89,9 +91,11 @@ class SSLHostStateDelegate {
|
||||
const std::string& host,
|
||||
bool enforce,
|
||||
StoragePartition* storage_partition) = 0;
|
||||
// Returns whether HTTPS-First Mode is enabled for the given `host`.
|
||||
virtual bool IsHttpsEnforcedForHost(const std::string& host,
|
||||
StoragePartition* storage_partition) = 0;
|
||||
// Returns whether HTTPS-First Mode is enabled for the given `url`. This check
|
||||
// ignores the scheme of `url`. E.g. http://example.com and
|
||||
// https://example.com will return the same result.
|
||||
virtual bool IsHttpsEnforcedForUrl(const GURL& url,
|
||||
StoragePartition* storage_partition) = 0;
|
||||
|
||||
// Returns whether the user has allowed a certificate error exception or
|
||||
// HTTP exception for |host|. This does not mean that *all* certificate errors
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include "base/containers/contains.h"
|
||||
#include "base/functional/callback.h"
|
||||
#include "url/gurl.h"
|
||||
|
||||
namespace content {
|
||||
|
||||
@ -85,10 +86,14 @@ void MockSSLHostStateDelegate::SetHttpsEnforcementForHost(
|
||||
}
|
||||
}
|
||||
|
||||
bool MockSSLHostStateDelegate::IsHttpsEnforcedForHost(
|
||||
const std::string& host,
|
||||
bool MockSSLHostStateDelegate::IsHttpsEnforcedForUrl(
|
||||
const GURL& url,
|
||||
StoragePartition* storage_partition) {
|
||||
return base::Contains(enforce_https_hosts_, host);
|
||||
// HTTPS-First Mode is never auto-enabled for URLs with non-default ports.
|
||||
if (!url.port().empty()) {
|
||||
return false;
|
||||
}
|
||||
return base::Contains(enforce_https_hosts_, url.host());
|
||||
}
|
||||
|
||||
void MockSSLHostStateDelegate::RevokeUserAllowExceptions(
|
||||
|
@ -46,8 +46,8 @@ class MockSSLHostStateDelegate : public SSLHostStateDelegate {
|
||||
void SetHttpsEnforcementForHost(const std::string& host,
|
||||
bool enforce,
|
||||
StoragePartition* storage_partition) override;
|
||||
bool IsHttpsEnforcedForHost(const std::string& host,
|
||||
StoragePartition* storage_partition) override;
|
||||
bool IsHttpsEnforcedForUrl(const GURL& url,
|
||||
StoragePartition* storage_partition) override;
|
||||
|
||||
void RevokeUserAllowExceptions(const std::string& host) override;
|
||||
|
||||
|
Reference in New Issue
Block a user