diff --git a/android/android_browser_tests.gni b/android/android_browser_tests.gni index 09655b11757c..46fbab837be0 100644 --- a/android/android_browser_tests.gni +++ b/android/android_browser_tests.gni @@ -18,6 +18,7 @@ android_test_exception_sources = [ "//brave/browser/brave_shields/cookie_expiry_browsertest.cc", "//brave/browser/brave_shields/domain_block_page_browsertest.cc", "//brave/browser/brave_shields/eventsource_pool_limit_browsertest.cc", + "//brave/browser/brave_shields/https_upgrade_browsertest.cc", "//brave/browser/brave_shields/websockets_pool_limit_browsertest.cc", "//brave/browser/brave_wallet/brave_wallet_ethereum_chain_browsertest.cc", "//brave/browser/brave_wallet/brave_wallet_event_emitter_browsertest.cc", diff --git a/app/brave_settings_strings.grdp b/app/brave_settings_strings.grdp index 406d65a11685..70c1c94a7875 100644 --- a/app/brave_settings_strings.grdp +++ b/app/brave_settings_strings.grdp @@ -269,6 +269,18 @@ Strict, may break sites + + Upgrade connections to HTTPS + + + Standard + + + Disabled + + + Strict + Block cookies diff --git a/browser/BUILD.gn b/browser/BUILD.gn index 97b0fad13b27..3aa2fc1aef53 100644 --- a/browser/BUILD.gn +++ b/browser/BUILD.gn @@ -1,3 +1,8 @@ +# Copyright (c) 2022 The Brave Authors. All rights reserved. +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at https://mozilla.org/MPL/2.0/. + import("//brave/build/config.gni") import("//build/buildflag_header.gni") import("//build/config/features.gni") diff --git a/browser/about_flags.cc b/browser/about_flags.cc index 59dc04ccdf6b..5c9b6c23275f 100644 --- a/browser/about_flags.cc +++ b/browser/about_flags.cc @@ -430,6 +430,11 @@ constexpr char kAllowCertainClientHintsDescription[] = "Allows setting certain request client hints (sec-ch-ua, sec-ch-ua-mobile, " "sec-ch-ua-platform)"; +constexpr char kBraveHttpsByDefaultName[] = "Use HTTPS by Default"; +constexpr char kBraveHttpsByDefaultDescription[] = + "Attempt to connect to all websites using HTTPS before falling back to " + "HTTP."; + #if defined(TOOLKIT_VIEWS) constexpr char kBraveVerticalTabsName[] = "Vertical tabs"; constexpr char kBraveVerticalTabsDescription[] = @@ -813,7 +818,7 @@ constexpr char kRestrictEventSourcePoolDescription[] = flag_descriptions::kBraveTorWindowsHttpsOnlyName, \ flag_descriptions::kBraveTorWindowsHttpsOnlyDescription, \ kOsAll, FEATURE_VALUE_TYPE( \ - blink::features::kBraveTorWindowsHttpsOnly)}, \ + net::features::kBraveTorWindowsHttpsOnly)}, \ {"brave-round-time-stamps", \ flag_descriptions::kBraveRoundTimeStampsName, \ flag_descriptions::kBraveRoundTimeStampsDescription, \ @@ -839,6 +844,11 @@ constexpr char kRestrictEventSourcePoolDescription[] = flag_descriptions::kBraveSyncSendAllHistoryDescription, \ kOsAll, FEATURE_VALUE_TYPE( \ brave_sync::features::kBraveSyncSendAllHistory)}, \ + {"https-by-default", \ + flag_descriptions::kBraveHttpsByDefaultName, \ + flag_descriptions::kBraveHttpsByDefaultDescription, \ + kOsAll, FEATURE_VALUE_TYPE( \ + net::features::kBraveHttpsByDefault)}, \ BRAVE_IPFS_FEATURE_ENTRIES \ BRAVE_NATIVE_WALLET_FEATURE_ENTRIES \ BRAVE_NEWS_FEATURE_ENTRIES \ diff --git a/browser/brave_browser_process.h b/browser/brave_browser_process.h index b656fcc2ff4c..bd02048bc348 100644 --- a/browser/brave_browser_process.h +++ b/browser/brave_browser_process.h @@ -55,6 +55,10 @@ namespace debounce { class DebounceComponentInstaller; } // namespace debounce +namespace https_upgrade_exceptions { +class HttpsUpgradeExceptionsService; +} // namespace https_upgrade_exceptions + namespace misc_metrics { class MenuMetrics; } // namespace misc_metrics @@ -66,7 +70,7 @@ class NTPBackgroundImagesService; namespace tor { class BraveTorClientUpdater; class BraveTorPluggableTransportUpdater; -} +} // namespace tor namespace ipfs { class BraveIpfsClientUpdater; @@ -86,6 +90,8 @@ class BraveBrowserProcess { virtual ~BraveBrowserProcess(); virtual void StartBraveServices() = 0; virtual brave_shields::AdBlockService* ad_block_service() = 0; + virtual https_upgrade_exceptions::HttpsUpgradeExceptionsService* + https_upgrade_exceptions_service() = 0; #if BUILDFLAG(ENABLE_GREASELION) virtual greaselion::GreaselionDownloadService* greaselion_download_service() = 0; diff --git a/browser/brave_browser_process_impl.cc b/browser/brave_browser_process_impl.cc index fc05bcbf0a58..6217be905379 100644 --- a/browser/brave_browser_process_impl.cc +++ b/browser/brave_browser_process_impl.cc @@ -35,6 +35,7 @@ #include "brave/components/constants/pref_names.h" #include "brave/components/debounce/browser/debounce_component_installer.h" #include "brave/components/debounce/common/features.h" +#include "brave/components/https_upgrade_exceptions/browser/https_upgrade_exceptions_service.h" #include "brave/components/misc_metrics/menu_metrics.h" #include "brave/components/ntp_background_images/browser/ntp_background_images_service.h" #include "brave/components/p3a/brave_p3a_service.h" @@ -53,6 +54,7 @@ #include "components/component_updater/timer_update_scheduler.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/child_process_security_policy.h" +#include "net/base/features.h" #include "services/network/public/cpp/resource_request.h" #include "services/network/public/cpp/shared_url_loader_factory.h" #include "url/gurl.h" @@ -200,6 +202,10 @@ void BraveBrowserProcessImpl::StartBraveServices() { https_everywhere_service()->Start(); resource_component(); + if (base::FeatureList::IsEnabled(net::features::kBraveHttpsByDefault)) { + https_upgrade_exceptions_service(); + } + #if BUILDFLAG(ENABLE_GREASELION) greaselion_download_service(); #endif @@ -242,6 +248,16 @@ BraveBrowserProcessImpl::ntp_background_images_service() { return ntp_background_images_service_.get(); } +https_upgrade_exceptions::HttpsUpgradeExceptionsService* +BraveBrowserProcessImpl::https_upgrade_exceptions_service() { + if (!https_upgrade_exceptions_service_) { + https_upgrade_exceptions_service_ = + https_upgrade_exceptions::HttpsUpgradeExceptionsServiceFactory( + local_data_files_service()); + } + return https_upgrade_exceptions_service_.get(); +} + #if BUILDFLAG(ENABLE_GREASELION) greaselion::GreaselionDownloadService* BraveBrowserProcessImpl::greaselion_download_service() { diff --git a/browser/brave_browser_process_impl.h b/browser/brave_browser_process_impl.h index ec4ffb394a76..a172d85a56e4 100644 --- a/browser/brave_browser_process_impl.h +++ b/browser/brave_browser_process_impl.h @@ -38,6 +38,10 @@ class AdBlockService; class HTTPSEverywhereService; } // namespace brave_shields +namespace https_upgrade_exceptions { +class HttpsUpgradeExceptionsService; +} // namespace https_upgrade_exceptions + namespace brave_stats { class BraveStatsUpdater; } // namespace brave_stats @@ -63,7 +67,7 @@ class NTPBackgroundImagesService; namespace tor { class BraveTorClientUpdater; class BraveTorPluggableTransportUpdater; -} +} // namespace tor namespace ipfs { class BraveIpfsClientUpdater; @@ -76,7 +80,7 @@ class SpeedreaderRewriterService; namespace brave_ads { class BraveStatsUpdaterHelper; class ResourceComponent; -} +} // namespace brave_ads class BraveBrowserProcessImpl : public BraveBrowserProcess, public BrowserProcessImpl { @@ -95,6 +99,8 @@ class BraveBrowserProcessImpl : public BraveBrowserProcess, void StartBraveServices() override; brave_shields::AdBlockService* ad_block_service() override; + https_upgrade_exceptions::HttpsUpgradeExceptionsService* + https_upgrade_exceptions_service() override; #if BUILDFLAG(ENABLE_GREASELION) greaselion::GreaselionDownloadService* greaselion_download_service() override; #endif @@ -156,6 +162,8 @@ class BraveBrowserProcessImpl : public BraveBrowserProcess, std::unique_ptr brave_component_updater_delegate_; std::unique_ptr ad_block_service_; + std::unique_ptr + https_upgrade_exceptions_service_; #if BUILDFLAG(ENABLE_GREASELION) std::unique_ptr greaselion_download_service_; diff --git a/browser/brave_shields/https_upgrade_browsertest.cc b/browser/brave_shields/https_upgrade_browsertest.cc new file mode 100644 index 000000000000..d45d7934fdbb --- /dev/null +++ b/browser/brave_shields/https_upgrade_browsertest.cc @@ -0,0 +1,227 @@ +/* Copyright (c) 2022 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "base/test/scoped_feature_list.h" +#include "brave/browser/brave_browser_process.h" +#include "brave/components/brave_shields/browser/brave_shields_util.h" +#include "brave/components/https_upgrade_exceptions/browser/https_upgrade_exceptions_service.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/content_settings/host_content_settings_map_factory.h" +#include "chrome/browser/interstitials/security_interstitial_page_test_utils.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ssl/https_only_mode_upgrade_interceptor.h" +#include "chrome/common/pref_names.h" +#include "chrome/test/base/chrome_test_utils.h" +#include "components/prefs/pref_service.h" +#include "content/public/test/browser_test.h" +#include "content/public/test/browser_test_utils.h" +#include "content/public/test/content_mock_cert_verifier.h" +#include "content/public/test/test_navigation_observer.h" +#include "net/base/features.h" +#include "net/cert/x509_certificate.h" +#include "net/dns/mock_host_resolver.h" +#include "url/gurl.h" + +#if BUILDFLAG(IS_ANDROID) +#include "chrome/test/base/android/android_browser_test.h" +#else +#include "chrome/test/base/in_process_browser_test.h" +#endif + +using brave_shields::ControlType; +using net::features::kBraveHttpsByDefault; + +namespace { + +enum class PageResult { kHttp, kHttps, kInterstitial }; + +struct TestCase { + bool init_secure; + const char* domain; + ControlType control_type; + PageResult expected_result; +}; + +constexpr TestCase kTestCases[] = { + {false, "insecure1.test", ControlType::ALLOW, PageResult::kHttp}, + {false, "insecure2.test", ControlType::BLOCK_THIRD_PARTY, + PageResult::kHttp}, + {false, "insecure3.test", ControlType::BLOCK, PageResult::kInterstitial}, + {false, "upgradable1.test", ControlType::ALLOW, PageResult::kHttp}, + {false, "upgradable2.test", ControlType::BLOCK_THIRD_PARTY, + PageResult::kHttps}, + {false, "upgradable3.test", ControlType::BLOCK, PageResult::kHttps}, + {true, "secure1.test", ControlType::ALLOW, PageResult::kHttps}, + {true, "secure2.test", ControlType::BLOCK_THIRD_PARTY, PageResult::kHttps}, + {true, "secure3.test", ControlType::BLOCK, PageResult::kHttps}}; + +base::FilePath GetTestDataDir() { + return base::FilePath(FILE_PATH_LITERAL("net/data/url_request_unittest")); +} + +} // namespace + +class HttpsUpgradeBrowserTest : public PlatformBrowserTest { + public: + HttpsUpgradeBrowserTest() = default; + ~HttpsUpgradeBrowserTest() override = default; + + void SetUp() override { + feature_list_.InitAndEnableFeature(kBraveHttpsByDefault); + PlatformBrowserTest::SetUp(); + } + + void SetUpOnMainThread() override { + PlatformBrowserTest::SetUpOnMainThread(); + g_brave_browser_process->https_upgrade_exceptions_service() + ->SetIsReadyForTesting(); + // By default allow all hosts on HTTPS. + mock_cert_verifier_.mock_cert_verifier()->set_default_result(net::OK); + host_resolver()->AddRule("*", "127.0.0.1"); + + // Set up "insecure.test" as a hostname with an SSL error. kHttps upgrades + // to this host will fail, (or fall back in some cases). + scoped_refptr cert(https_server_.GetCertificate()); + net::CertVerifyResult verify_result; + verify_result.is_issued_by_known_root = false; + verify_result.verified_cert = cert; + verify_result.cert_status = net::CERT_STATUS_COMMON_NAME_INVALID; + for (const std::string& host : + {"insecure1.test", "insecure2.test", "insecure3.test"}) { + mock_cert_verifier_.mock_cert_verifier()->AddResultForCertAndHost( + cert, host, verify_result, net::ERR_CERT_INVALID); + } + + http_server_.AddDefaultHandlers(GetTestDataDir()); + https_server_.AddDefaultHandlers(GetTestDataDir()); + ASSERT_TRUE(http_server_.Start()); + ASSERT_TRUE(https_server_.Start()); + + HttpsOnlyModeUpgradeInterceptor::SetHttpsPortForTesting( + https_server()->port()); + HttpsOnlyModeUpgradeInterceptor::SetHttpPortForTesting( + http_server()->port()); + } + + void SetUpCommandLine(base::CommandLine* command_line) override { + PlatformBrowserTest::SetUpCommandLine(command_line); + mock_cert_verifier_.SetUpCommandLine(command_line); + } + + void SetUpInProcessBrowserTestFixture() override { + PlatformBrowserTest::SetUpInProcessBrowserTestFixture(); + mock_cert_verifier_.SetUpInProcessBrowserTestFixture(); + } + + void TearDownInProcessBrowserTestFixture() override { + PlatformBrowserTest::TearDownInProcessBrowserTestFixture(); + mock_cert_verifier_.TearDownInProcessBrowserTestFixture(); + } + + void AttemptToNavigateToURL(const GURL& url) { + content::NavigateToURLBlockUntilNavigationsComplete(Contents(), url, 1, + true); + } + + GURL RunTestCaseNavigation(bool shields_enabled, + bool global_setting, + const TestCase& test_case) { + SCOPED_TRACE(testing::Message() + << "global_setting: " << global_setting << ", " + << "test_case.init_secure: " << test_case.init_secure << ", " + << "test_case.domain: " << test_case.domain << ", " + << "test_case.control_type: " << test_case.control_type); + GURL initial_url = + test_case.init_secure + ? https_server()->GetURL(test_case.domain, "/simple.html") + : http_server()->GetURL(test_case.domain, "/simple.html"); + brave_shields::SetBraveShieldsEnabled(ContentSettings(), shields_enabled, + initial_url, nullptr); + brave_shields::SetHttpsUpgradeControlType( + ContentSettings(), test_case.control_type, + global_setting ? GURL() : initial_url, + g_browser_process->local_state()); + AttemptToNavigateToURL(initial_url); + return initial_url; + } + + content::WebContents* Contents() { + return chrome_test_utils::GetActiveWebContents(this); + } + + HostContentSettingsMap* ContentSettings() { + return HostContentSettingsMapFactory::GetForProfile( + chrome_test_utils::GetProfile(this)); + } + + protected: + base::test::ScopedFeatureList feature_list_; + net::EmbeddedTestServer* http_server() { return &http_server_; } + net::EmbeddedTestServer* https_server() { return &https_server_; } + + private: + net::EmbeddedTestServer http_server_{net::EmbeddedTestServer::TYPE_HTTP}; + net::EmbeddedTestServer https_server_{net::EmbeddedTestServer::TYPE_HTTPS}; + content::ContentMockCertVerifier mock_cert_verifier_; +}; + +class HttpsUpgradeBrowserTest_FlagDisabled : public HttpsUpgradeBrowserTest { + public: + HttpsUpgradeBrowserTest_FlagDisabled() = default; + ~HttpsUpgradeBrowserTest_FlagDisabled() override = default; + + void SetUp() override { + feature_list_.InitAndDisableFeature(kBraveHttpsByDefault); + PlatformBrowserTest::SetUp(); + } +}; + +IN_PROC_BROWSER_TEST_F(HttpsUpgradeBrowserTest, CheckUpgrades) { + for (bool global_setting : {true, false}) { + for (const TestCase& test_case : kTestCases) { + RunTestCaseNavigation(true, global_setting, test_case); + bool interstitial_showing = + chrome_browser_interstitials::IsShowingInterstitial(Contents()); + if (test_case.expected_result == PageResult::kInterstitial) { + EXPECT_TRUE(interstitial_showing); + } else { + EXPECT_FALSE(interstitial_showing); + GURL final_url = + (test_case.expected_result == PageResult::kHttp ? http_server() + : https_server()) + ->GetURL(test_case.domain, "/simple.html"); + EXPECT_EQ(final_url, Contents()->GetLastCommittedURL()); + } + } + } +} + +IN_PROC_BROWSER_TEST_F(HttpsUpgradeBrowserTest, CheckUpgradesWithShieldsDown) { + for (bool global_setting : {true, false}) { + for (const TestCase& test_case : kTestCases) { + const GURL initial_url = + RunTestCaseNavigation(false, global_setting, test_case); + // Shields down means no URLs change and no interstitials shown. + EXPECT_EQ(initial_url, Contents()->GetLastCommittedURL()); + bool interstitial_showing = + chrome_browser_interstitials::IsShowingInterstitial(Contents()); + EXPECT_FALSE(interstitial_showing); + } + } +} + +IN_PROC_BROWSER_TEST_F(HttpsUpgradeBrowserTest_FlagDisabled, CheckUpgrades) { + for (bool global_setting : {true, false}) { + for (const TestCase& test_case : kTestCases) { + const GURL initial_url = + RunTestCaseNavigation(true, global_setting, test_case); + // Disabled flag means no URLs change and no interstitials shown. + EXPECT_EQ(initial_url, Contents()->GetLastCommittedURL()); + bool interstitial_showing = + chrome_browser_interstitials::IsShowingInterstitial(Contents()); + EXPECT_FALSE(interstitial_showing); + } + } +} diff --git a/browser/content_settings/brave_content_settings_registry_unittest.cc b/browser/content_settings/brave_content_settings_registry_unittest.cc index 749b0bd1f2c7..4b99a6ef12fd 100644 --- a/browser/content_settings/brave_content_settings_registry_unittest.cc +++ b/browser/content_settings/brave_content_settings_registry_unittest.cc @@ -92,6 +92,7 @@ TEST_F(BraveContentSettingsRegistryTest, Inheritance) { ContentSettingsType::BRAVE_COOKIES, ContentSettingsType::BRAVE_SPEEDREADER, ContentSettingsType::BRAVE_GOOGLE_SIGN_IN, + ContentSettingsType::BRAVE_HTTPS_UPGRADE, }; for (const ContentSettingsInfo* info : *registry()) { diff --git a/browser/profiles/brave_profile_manager.cc b/browser/profiles/brave_profile_manager.cc index 32157196dc12..d1e0ae2ce347 100644 --- a/browser/profiles/brave_profile_manager.cc +++ b/browser/profiles/brave_profile_manager.cc @@ -94,6 +94,7 @@ void BraveProfileManager::InitProfileUserPrefs(Profile* profile) { brave::SetDefaultSearchVersion(profile, profile->IsNewProfile()); brave::SetDefaultThirdPartyCookieBlockValue(profile); perf::MaybeEnableBraveFeatureForPerfTesting(profile); + brave::MigrateHttpsUpgradeSettings(profile); } void BraveProfileManager::DoFinalInitForServices(Profile* profile, @@ -132,8 +133,8 @@ bool BraveProfileManager::IsAllowedProfilePath( } bool BraveProfileManager::LoadProfileByPath(const base::FilePath& profile_path, - bool incognito, - ProfileLoadedCallback callback) { + bool incognito, + ProfileLoadedCallback callback) { #if BUILDFLAG(ENABLE_TOR) // Prevent legacy tor session profile to be loaded so we won't hit // DCHECK(!GetProfileAttributesWithPath(...)). Workaround for legacy tor guest diff --git a/browser/profiles/profile_util.cc b/browser/profiles/profile_util.cc index 34ed83c1c798..bd1058c361ab 100644 --- a/browser/profiles/profile_util.cc +++ b/browser/profiles/profile_util.cc @@ -12,6 +12,7 @@ #include "base/memory/raw_ptr.h" #include "base/metrics/histogram_macros.h" #include "brave/components/brave_shields/browser/brave_shields_p3a.h" +#include "brave/components/brave_shields/common/brave_shield_utils.h" #include "brave/components/constants/brave_constants.h" #include "brave/components/constants/pref_names.h" #include "brave/components/ntp_background_images/common/pref_names.h" @@ -19,13 +20,16 @@ #include "brave/components/tor/buildflags/buildflags.h" #include "chrome/browser/content_settings/host_content_settings_map_factory.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/common/pref_names.h" #include "components/content_settings/core/browser/cookie_settings.h" #include "components/content_settings/core/common/pref_names.h" #include "components/prefs/pref_service.h" #include "components/search_engines/search_engines_pref_names.h" +using brave_shields::ControlType; using ntp_background_images::prefs::kNewTabPageShowBackgroundImage; -using ntp_background_images::prefs::kNewTabPageShowSponsoredImagesBackgroundImage; // NOLINT +using ntp_background_images::prefs:: + kNewTabPageShowSponsoredImagesBackgroundImage; // NOLINT #if BUILDFLAG(ENABLE_TOR) #include "brave/browser/tor/tor_profile_service_factory.h" @@ -42,8 +46,7 @@ bool IsGuestProfile(content::BrowserContext* context) { bool IsTorDisabledForProfile(Profile* profile) { #if BUILDFLAG(ENABLE_TOR) - return TorProfileServiceFactory::IsTorDisabled() || - profile->IsGuestSession(); + return TorProfileServiceFactory::IsTorDisabled() || profile->IsGuestSession(); #else return true; #endif @@ -51,8 +54,7 @@ bool IsTorDisabledForProfile(Profile* profile) { bool IsRegularProfile(content::BrowserContext* context) { auto* profile = Profile::FromBrowserContext(context); - return !context->IsTor() && - !profile->IsGuestSession() && + return !context->IsTor() && !profile->IsGuestSession() && profile->IsRegularProfile(); } @@ -100,4 +102,26 @@ void SetDefaultThirdPartyCookieBlockValue(Profile* profile) { content_settings::CookieControlsMode::kBlockThirdParty))); } +void MigrateHttpsUpgradeSettings(Profile* profile) { + // If user flips the HTTPS by Default feature flag + auto* prefs = profile->GetPrefs(); + auto* map = HostContentSettingsMapFactory::GetForProfile(profile); + if (brave_shields::IsHttpsByDefaultFeatureEnabled()) { + // Migrate forwards from HTTPS-Only Mode to HTTPS Upgrade Strict setting. + if (prefs->GetBoolean(prefs::kHttpsOnlyModeEnabled)) { + brave_shields::SetHttpsUpgradeControlType(map, ControlType::BLOCK, + GURL()); + prefs->SetBoolean(prefs::kHttpsOnlyModeEnabled, false); + } + } else { + // Migrate backwards from HTTPS Upgrade Strict setting to HTTPS-Only Mode. + if (brave_shields::GetHttpsUpgradeControlType(map, GURL()) == + ControlType::BLOCK) { + prefs->SetBoolean(prefs::kHttpsOnlyModeEnabled, true); + brave_shields::SetHttpsUpgradeControlType( + map, ControlType::BLOCK_THIRD_PARTY, GURL()); + } + } +} + } // namespace brave diff --git a/browser/profiles/profile_util.h b/browser/profiles/profile_util.h index 3f373820fe35..45ed6aa8901c 100644 --- a/browser/profiles/profile_util.h +++ b/browser/profiles/profile_util.h @@ -46,6 +46,10 @@ void SetDefaultSearchVersion(Profile* profile, bool is_new_profile); // instead of 'Block Third Party in Incognito' void SetDefaultThirdPartyCookieBlockValue(Profile* profile); +// Checks if the user previously had HTTPS-Only Mode enabled. If so, +// set the HttpsUpgrade default setting to strict. +void MigrateHttpsUpgradeSettings(Profile* profile); + } // namespace brave #endif // BRAVE_BROWSER_PROFILES_PROFILE_UTIL_H_ diff --git a/browser/resources/settings/brave_overrides/security_page.ts b/browser/resources/settings/brave_overrides/security_page.ts index c254d4fdcf05..a8f18ac71621 100644 --- a/browser/resources/settings/brave_overrides/security_page.ts +++ b/browser/resources/settings/brave_overrides/security_page.ts @@ -4,6 +4,7 @@ // you can obtain one at https://mozilla.org/MPL/2.0/. import {RegisterPolymerTemplateModifications} from 'chrome://resources/polymer_overriding.js' +import {loadTimeData} from '//resources/js/load_time_data.js'; RegisterPolymerTemplateModifications({ 'settings-security-page': (templateContent) => { @@ -25,6 +26,23 @@ RegisterPolymerTemplateModifications({ } else { passwordsLeakToggle.setAttribute('hidden', 'true') } + if (loadTimeData.getBoolean("isHttpsByDefaultEnabled")) { + const httpsOnlyModeToggleTemplate = templateContent.querySelector( + `template[if*='showHttpsOnlyModeSetting_']`) + if (!httpsOnlyModeToggleTemplate) { + console.error('[Brave Settings Overrides] Could not find template ' + + 'with if*=showHttpsOnlyModeSetting_ on security page.') + } else { + const httpsOnlyModeToggle = httpsOnlyModeToggleTemplate.content + .getElementById('httpsOnlyModeToggle') + if (!httpsOnlyModeToggle) { + console.error('[Brave Settings Overrides] Could not find' + + 'httpsOnlyModeToggle on security page.') + } else { + httpsOnlyModeToggle.setAttribute('hidden', 'true') + } + } + } const link = templateContent.getElementById('advanced-protection-program-link') if (!link) { console.error('[Brave Settings Overrides] Could not find advanced-protection-program-link id on security page.') diff --git a/browser/resources/settings/default_brave_shields_page/default_brave_shields_browser_proxy.ts b/browser/resources/settings/default_brave_shields_page/default_brave_shields_browser_proxy.ts index 5a247af2f6bf..82d3be905351 100644 --- a/browser/resources/settings/default_brave_shields_page/default_brave_shields_browser_proxy.ts +++ b/browser/resources/settings/default_brave_shields_page/default_brave_shields_browser_proxy.ts @@ -51,6 +51,15 @@ export interface DefaultBraveShieldsBrowserProxy { */ setHTTPSEverywhereEnabled(value) + /** + * @return {!Promise} + */ + getHttpsUpgradeControlType() + /** + * @param {string} value name. + */ + setHttpsUpgradeControlType(value) + /** * @param {string} value name. */ @@ -103,6 +112,16 @@ export class DefaultBraveShieldsBrowserProxyImpl implements DefaultBraveShieldsB chrome.send('setHTTPSEverywhereEnabled', [value]); } + /** @override */ + getHttpsUpgradeControlType() { + return sendWithPromise('getHttpsUpgradeControlType'); + } + + /** @override */ + setHttpsUpgradeControlType(value) { + chrome.send('setHttpsUpgradeControlType', [value]); + } + /** @override */ setNoScriptControlType(value) { chrome.send('setNoScriptControlType', [value]); diff --git a/browser/resources/settings/default_brave_shields_page/default_brave_shields_page.html b/browser/resources/settings/default_brave_shields_page/default_brave_shields_page.html index 2170aa18f30d..19581ba1196a 100644 --- a/browser/resources/settings/default_brave_shields_page/default_brave_shields_page.html +++ b/browser/resources/settings/default_brave_shields_page/default_brave_shields_page.html @@ -1,32 +1,32 @@ @@ -83,6 +83,18 @@ + + $i18n{httpsUpgradeControlLabel} + + + + [[item.name]] + + + + { @@ -117,6 +138,10 @@ class BraveShieldsPage extends BraveShieldsPageBase { this.browserProxy_.getFingerprintingControlType().then(value => { this.fingerprintingControlType_ = value }) + + this.browserProxy_.getHttpsUpgradeControlType().then(value => { + this.httpsUpgradeControlType_ = value + }) } /** @protected */ @@ -153,6 +178,11 @@ class BraveShieldsPage extends BraveShieldsPageBase { this.browserProxy_.setHTTPSEverywhereEnabled(this.$.httpsEverywhereControlType.checked) } + onHttpsUpgradeControlChange_ () { + this.browserProxy_.setHttpsUpgradeControlType( + this.$.httpsUpgradeControlType.value) + } + onNoScriptControlChange_ () { this.browserProxy_.setNoScriptControlType(this.$.noScriptControlType.checked) } diff --git a/browser/sources.gni b/browser/sources.gni index eaf2de5caa8b..7b7857dd1743 100644 --- a/browser/sources.gni +++ b/browser/sources.gni @@ -167,6 +167,7 @@ brave_chrome_browser_deps = [ "//brave/components/decentralized_dns/core", "//brave/components/google_sign_in_permission", "//brave/components/greaselion/browser/buildflags", + "//brave/components/https_upgrade_exceptions/browser", "//brave/components/ipfs/buildflags", "//brave/components/l10n/common", "//brave/components/ntp_background_images/browser", diff --git a/browser/tor/BUILD.gn b/browser/tor/BUILD.gn index 69639f5e9949..7194ec77cef3 100644 --- a/browser/tor/BUILD.gn +++ b/browser/tor/BUILD.gn @@ -1,3 +1,8 @@ +# Copyright (c) 2022 The Brave Authors. All rights reserved. +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at https://mozilla.org/MPL/2.0/. + import("//brave/components/tor/buildflags/buildflags.gni") import("//extensions/buildflags/buildflags.gni") @@ -101,6 +106,7 @@ source_set("browser_tests") { "//brave/common", "//brave/components/brave_ads/browser", "//brave/components/brave_component_updater/browser", + "//brave/components/brave_shields/browser", "//brave/components/constants", "//brave/components/ipfs/buildflags", "//brave/components/l10n/common", diff --git a/browser/tor/brave_tor_browsertest.cc b/browser/tor/brave_tor_browsertest.cc index c8bdc1fd39dd..aeb785f40b6b 100644 --- a/browser/tor/brave_tor_browsertest.cc +++ b/browser/tor/brave_tor_browsertest.cc @@ -20,6 +20,7 @@ #include "brave/browser/tor/tor_profile_manager.h" #include "brave/browser/tor/tor_profile_service_factory.h" #include "brave/components/brave_component_updater/browser/brave_component.h" +#include "brave/components/brave_shields/browser/brave_shields_util.h" #include "brave/components/constants/brave_paths.h" #include "brave/components/tor/brave_tor_client_updater.h" #include "brave/components/tor/brave_tor_pluggable_transport_updater.h" @@ -28,6 +29,7 @@ #include "brave/components/tor/tor_profile_service.h" #include "brave/components/tor/tor_utils.h" #include "build/build_config.h" +#include "chrome/browser/content_settings/host_content_settings_map_factory.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_finder.h" @@ -40,7 +42,7 @@ #include "content/public/test/browser_test.h" #include "content/public/test/test_utils.h" #include "gmock/gmock.h" -#include "third_party/blink/public/common/features.h" +#include "net/base/features.h" namespace { @@ -346,25 +348,51 @@ IN_PROC_BROWSER_TEST_F(BraveTorTest, ResetBridges) { EXPECT_FALSE(CheckComponentExists(tor::kTorPluggableTransportComponentId)); } -class BraveTorTest_EnableTorHttpsOnlyFlag : public BraveTorTest { +class BraveTorTest_EnableTorHttpsOnlyFlag + : public BraveTorTest, + public ::testing::WithParamInterface { public: BraveTorTest_EnableTorHttpsOnlyFlag() { - feature_list_.InitAndEnableFeature( - blink::features::kBraveTorWindowsHttpsOnly); + if (IsBraveHttpsByDefaultEnabled()) { + std::vector enabled_features{ + net::features::kBraveTorWindowsHttpsOnly}; + std::vector disabled_features; + if (IsBraveHttpsByDefaultEnabled()) { + enabled_features.push_back(net::features::kBraveHttpsByDefault); + } else { + disabled_features.push_back(net::features::kBraveHttpsByDefault); + } + scoped_feature_list_.InitWithFeatures(enabled_features, + disabled_features); + } } + ~BraveTorTest_EnableTorHttpsOnlyFlag() override = default; + + bool IsBraveHttpsByDefaultEnabled() { return GetParam(); } + protected: - base::test::ScopedFeatureList feature_list_; + base::test::ScopedFeatureList scoped_feature_list_; }; -IN_PROC_BROWSER_TEST_F(BraveTorTest_EnableTorHttpsOnlyFlag, +IN_PROC_BROWSER_TEST_P(BraveTorTest_EnableTorHttpsOnlyFlag, TorWindowHttpsOnly) { EXPECT_FALSE(TorProfileServiceFactory::IsTorDisabled()); DownloadTorClient(); Profile* tor_profile = OpenTorWindow(); PrefService* prefs = tor_profile->GetPrefs(); - - // Check that HTTPS-Only Mode has been enabled for the Tor window. - EXPECT_TRUE(prefs->GetBoolean(prefs::kHttpsOnlyModeEnabled)); + if (IsBraveHttpsByDefaultEnabled()) { + // Check that HTTPS-Only Mode is not enabled for the Tor window, + // because we already force HTTPS upgrade when the HTTPS by Default + // flag is enabled. + EXPECT_FALSE(prefs->GetBoolean(prefs::kHttpsOnlyModeEnabled)); + } else { + // Check that HTTPS-Only Mode has been enabled for the Tor window. + EXPECT_TRUE(prefs->GetBoolean(prefs::kHttpsOnlyModeEnabled)); + } } + +INSTANTIATE_TEST_SUITE_P(BraveTorTest_EnableTorHttpsOnlyFlag, + BraveTorTest_EnableTorHttpsOnlyFlag, + ::testing::Bool()); diff --git a/browser/tor/tor_profile_manager.cc b/browser/tor/tor_profile_manager.cc index dbe0d1121acf..4372ffd4f914 100644 --- a/browser/tor/tor_profile_manager.cc +++ b/browser/tor/tor_profile_manager.cc @@ -10,10 +10,12 @@ #include "base/feature_list.h" #include "brave/browser/tor/tor_profile_service_factory.h" +#include "brave/components/brave_shields/browser/brave_shields_util.h" #include "brave/components/brave_webtorrent/browser/buildflags/buildflags.h" #include "brave/components/constants/pref_names.h" #include "brave/components/tor/tor_constants.h" #include "brave/components/tor/tor_profile_service.h" +#include "chrome/browser/content_settings/host_content_settings_map_factory.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_window.h" #include "chrome/browser/ui/browser_list.h" @@ -21,7 +23,7 @@ #include "components/prefs/pref_service.h" #include "components/safe_browsing/core/common/safe_browsing_prefs.h" #include "components/translate/core/browser/translate_pref_names.h" -#include "third_party/blink/public/common/features.h" +#include "net/base/features.h" #include "third_party/blink/public/common/peerconnection/webrtc_ip_handling_policy.h" namespace { @@ -118,9 +120,14 @@ void TorProfileManager::InitTorProfileUserPrefs(Profile* profile) { pref_service->SetString(prefs::kWebRTCIPHandlingPolicy, blink::kWebRTCIPHandlingDisableNonProxiedUdp); pref_service->SetBoolean(prefs::kSafeBrowsingEnabled, false); - if (base::FeatureList::IsEnabled( - blink::features::kBraveTorWindowsHttpsOnly)) { - pref_service->SetBoolean(prefs::kHttpsOnlyModeEnabled, true); + if (base::FeatureList::IsEnabled(net::features::kBraveTorWindowsHttpsOnly)) { + if (base::FeatureList::IsEnabled(net::features::kBraveHttpsByDefault)) { + brave_shields::SetHttpsUpgradeControlType( + HostContentSettingsMapFactory::GetForProfile(profile), + brave_shields::ControlType::BLOCK, GURL()); + } else { + pref_service->SetBoolean(prefs::kHttpsOnlyModeEnabled, true); + } } // https://blog.torproject.org/bittorrent-over-tor-isnt-good-idea #if BUILDFLAG(ENABLE_BRAVE_WEBTORRENT) diff --git a/browser/ui/brave_shields_data_controller.cc b/browser/ui/brave_shields_data_controller.cc index bf22a2f4424a..c57b1714b2bb 100644 --- a/browser/ui/brave_shields_data_controller.cc +++ b/browser/ui/brave_shields_data_controller.cc @@ -235,6 +235,20 @@ bool BraveShieldsDataController::GetHTTPSEverywhereEnabled() { GetHostContentSettingsMap(web_contents()), GetCurrentSiteURL()); } +HttpsUpgradeMode BraveShieldsDataController::GetHttpsUpgradeMode() { + ControlType control_type = brave_shields::GetHttpsUpgradeControlType( + GetHostContentSettingsMap(web_contents()), GetCurrentSiteURL()); + if (control_type == ControlType::ALLOW) { + return HttpsUpgradeMode::DISABLED; + } else if (control_type == ControlType::BLOCK) { + return HttpsUpgradeMode::STRICT; + } else if (control_type == ControlType::BLOCK_THIRD_PARTY) { + return HttpsUpgradeMode::STANDARD; + } else { + return HttpsUpgradeMode::STANDARD; + } +} + bool BraveShieldsDataController::GetNoScriptEnabled() { ControlType control_type = brave_shields::GetNoScriptControlType( GetHostContentSettingsMap(web_contents()), GetCurrentSiteURL()); @@ -320,6 +334,24 @@ void BraveShieldsDataController::SetCookieBlockMode(CookieBlockMode mode) { ReloadWebContents(); } +void BraveShieldsDataController::SetHttpsUpgradeMode(HttpsUpgradeMode mode) { + ControlType control_type; + if (mode == HttpsUpgradeMode::DISABLED) { + control_type = ControlType::ALLOW; + } else if (mode == HttpsUpgradeMode::STRICT) { + control_type = ControlType::BLOCK; + } else if (mode == HttpsUpgradeMode::STANDARD) { + control_type = ControlType::BLOCK_THIRD_PARTY; + } else { + control_type = ControlType::DEFAULT; + } + brave_shields::SetHttpsUpgradeControlType( + GetHostContentSettingsMap(web_contents()), control_type, + GetCurrentSiteURL(), g_browser_process->local_state()); + + ReloadWebContents(); +} + void BraveShieldsDataController::SetIsNoScriptEnabled(bool is_enabled) { ControlType control_type; diff --git a/browser/ui/brave_shields_data_controller.h b/browser/ui/brave_shields_data_controller.h index 0c615f5c25f3..b103b3a43c26 100644 --- a/browser/ui/brave_shields_data_controller.h +++ b/browser/ui/brave_shields_data_controller.h @@ -24,6 +24,7 @@ using brave_shields::mojom::AdBlockMode; using brave_shields::mojom::CookieBlockMode; using brave_shields::mojom::FingerprintMode; +using brave_shields::mojom::HttpsUpgradeMode; using content::NavigationEntry; namespace brave_shields { @@ -65,10 +66,12 @@ class BraveShieldsDataController CookieBlockMode GetCookieBlockMode(); bool IsBraveShieldsManaged(); bool GetHTTPSEverywhereEnabled(); + HttpsUpgradeMode GetHttpsUpgradeMode(); bool GetNoScriptEnabled(); void SetAdBlockMode(AdBlockMode mode); void SetFingerprintMode(FingerprintMode mode); void SetCookieBlockMode(CookieBlockMode mode); + void SetHttpsUpgradeMode(HttpsUpgradeMode mode); void SetIsNoScriptEnabled(bool is_enabled); void SetIsHTTPSEverywhereEnabled(bool is_enabled); diff --git a/browser/ui/webui/brave_shields/shields_panel_data_handler.cc b/browser/ui/webui/brave_shields/shields_panel_data_handler.cc index 4c6d71d396bf..cb5e2b26feeb 100644 --- a/browser/ui/webui/brave_shields/shields_panel_data_handler.cc +++ b/browser/ui/webui/brave_shields/shields_panel_data_handler.cc @@ -71,6 +71,8 @@ void ShieldsPanelDataHandler::GetSiteSettings( active_shields_data_controller_->GetCookieBlockMode(); settings.is_https_everywhere_enabled = active_shields_data_controller_->GetHTTPSEverywhereEnabled(); + settings.https_upgrade_mode = + active_shields_data_controller_->GetHttpsUpgradeMode(); settings.is_noscript_enabled = active_shields_data_controller_->GetNoScriptEnabled(); @@ -98,6 +100,14 @@ void ShieldsPanelDataHandler::SetCookieBlockMode(CookieBlockMode mode) { active_shields_data_controller_->SetCookieBlockMode(mode); } +void ShieldsPanelDataHandler::SetHttpsUpgradeMode(HttpsUpgradeMode mode) { + if (!active_shields_data_controller_) { + return; + } + + active_shields_data_controller_->SetHttpsUpgradeMode(mode); +} + void ShieldsPanelDataHandler::SetIsNoScriptsEnabled(bool is_enabled) { if (!active_shields_data_controller_) return; diff --git a/browser/ui/webui/brave_shields/shields_panel_data_handler.h b/browser/ui/webui/brave_shields/shields_panel_data_handler.h index 90b47c206770..58ed4e4bd019 100644 --- a/browser/ui/webui/brave_shields/shields_panel_data_handler.h +++ b/browser/ui/webui/brave_shields/shields_panel_data_handler.h @@ -43,6 +43,7 @@ class ShieldsPanelDataHandler void SetAdBlockMode(AdBlockMode callback) override; void SetFingerprintMode(FingerprintMode mode) override; void SetCookieBlockMode(CookieBlockMode mode) override; + void SetHttpsUpgradeMode(HttpsUpgradeMode mode) override; void SetIsNoScriptsEnabled(bool is_enabled) override; void SetHTTPSEverywhereEnabled(bool is_enabled) override; void SetBraveShieldsEnabled(bool is_enabled) override; diff --git a/browser/ui/webui/brave_shields/shields_panel_ui.cc b/browser/ui/webui/brave_shields/shields_panel_ui.cc index eef4e967f38a..8f6082173c7c 100644 --- a/browser/ui/webui/brave_shields/shields_panel_ui.cc +++ b/browser/ui/webui/brave_shields/shields_panel_ui.cc @@ -9,6 +9,7 @@ #include "base/bind.h" #include "brave/browser/ui/brave_browser_window.h" +#include "brave/components/brave_shields/browser/brave_shields_util.h" #include "brave/components/brave_shields/common/brave_shield_constants.h" #include "brave/components/brave_shields/resources/panel/grit/brave_shields_panel_generated_map.h" #include "brave/components/constants/pref_names.h" @@ -43,6 +44,11 @@ ShieldsPanelUI::ShieldsPanelUI(content::WebUI* web_ui) source->AddBoolean("isAdvancedViewEnabled", profile_->GetPrefs()->GetBoolean( kShieldsAdvancedViewEnabled)); + source->AddBoolean("isHttpsByDefaultEnabled", + brave_shields::IsHttpsByDefaultFeatureEnabled()); + + source->AddBoolean("isTorProfile", profile_->IsTor()); + content::URLDataSource::Add( profile_, std::make_unique( profile_, chrome::FaviconUrlFormat::kFavicon2)); diff --git a/browser/ui/webui/settings/brave_settings_localized_strings_provider.cc b/browser/ui/webui/settings/brave_settings_localized_strings_provider.cc index e738dd3019ea..7f6ad99f0630 100644 --- a/browser/ui/webui/settings/brave_settings_localized_strings_provider.cc +++ b/browser/ui/webui/settings/brave_settings_localized_strings_provider.cc @@ -190,6 +190,8 @@ void BraveAddCommonStrings(content::WebUIDataSource* html_source, {"cookieControlLabel", IDS_SETTINGS_BRAVE_SHIELDS_COOKIE_CONTROL_LABEL}, {"fingerprintingControlLabel", IDS_SETTINGS_BRAVE_SHIELDS_FINGERPRINTING_CONTROL_LABEL}, + {"httpsUpgradeControlLabel", + IDS_SETTINGS_BRAVE_SHIELDS_HTTPS_UPGRADE_CONTROL_LABEL}, {"reduceLanguageControlLabel", IDS_SETTINGS_BRAVE_SHIELDS_REDUCE_LANGUAGE_CONTROL_LABEL}, {"reduceLanguageDesc", IDS_SETTINGS_BRAVE_SHIELDS_REDUCE_LANGUAGE_SUBITEM}, @@ -219,6 +221,9 @@ void BraveAddCommonStrings(content::WebUIDataSource* html_source, {"standardFingerprinting", IDS_SETTINGS_STANDARD_FINGERPRINTING}, {"allowAllFingerprinting", IDS_SETTINGS_ALLOW_ALL_FINGERPRINTING}, {"strictFingerprinting", IDS_SETTINGS_STRICT_FINGERPRINTING}, + {"standardHttpsUpgrade", IDS_SETTINGS_STANDARD_HTTPS_UPGRADE}, + {"disabledHttpsUpgrade", IDS_SETTINGS_DISABLED_HTTPS_UPGRADE}, + {"strictHttpsUpgrade", IDS_SETTINGS_STRICT_HTTPS_UPGRADE}, {"webRTCPolicyLabel", IDS_SETTINGS_WEBRTC_POLICY_LABEL}, {"webRTCPolicySubLabel", IDS_SETTINGS_WEBRTC_POLICY_SUB_LABEL}, {"webRTCDefault", IDS_SETTINGS_WEBRTC_POLICY_DEFAULT}, @@ -736,6 +741,10 @@ void BraveAddLocalizedStrings(content::WebUIDataSource* html_source, base::FeatureList::IsEnabled( brave_wallet::features::kBraveWalletSnsFeature)); + html_source->AddBoolean( + "isHttpsByDefaultEnabled", + base::FeatureList::IsEnabled(net::features::kBraveHttpsByDefault)); + if (base::FeatureList::IsEnabled( net::features::kBraveFirstPartyEphemeralStorage)) { const webui::LocalizedString kSessionOnlyToEphemeralStrings[] = { diff --git a/browser/ui/webui/settings/default_brave_shields_handler.cc b/browser/ui/webui/settings/default_brave_shields_handler.cc index cc248570d31d..cbc7f51dafe1 100644 --- a/browser/ui/webui/settings/default_brave_shields_handler.cc +++ b/browser/ui/webui/settings/default_brave_shields_handler.cc @@ -65,6 +65,16 @@ void DefaultBraveShieldsHandler::RegisterMessages() { base::BindRepeating( &DefaultBraveShieldsHandler::SetHTTPSEverywhereEnabled, base::Unretained(this))); + web_ui()->RegisterMessageCallback( + "getHttpsUpgradeControlType", + base::BindRepeating( + &DefaultBraveShieldsHandler::GetHttpsUpgradeControlType, + base::Unretained(this))); + web_ui()->RegisterMessageCallback( + "setHttpsUpgradeControlType", + base::BindRepeating( + &DefaultBraveShieldsHandler::SetHttpsUpgradeControlType, + base::Unretained(this))); web_ui()->RegisterMessageCallback( "setNoScriptControlType", base::BindRepeating(&DefaultBraveShieldsHandler::SetNoScriptControlType, @@ -182,6 +192,30 @@ void DefaultBraveShieldsHandler::SetHTTPSEverywhereEnabled( g_browser_process->local_state()); } +void DefaultBraveShieldsHandler::GetHttpsUpgradeControlType( + const base::Value::List& args) { + CHECK_EQ(args.size(), 1U); + CHECK(profile_); + + ControlType setting = brave_shields::GetHttpsUpgradeControlType( + HostContentSettingsMapFactory::GetForProfile(profile_), GURL()); + + AllowJavascript(); + ResolveJavascriptCallback(args[0].Clone(), + base::Value(ControlTypeToString(setting))); +} + +void DefaultBraveShieldsHandler::SetHttpsUpgradeControlType( + const base::Value::List& args) { + CHECK_EQ(args.size(), 1U); + CHECK(profile_); + std::string value = args[0].GetString(); + + brave_shields::SetHttpsUpgradeControlType( + HostContentSettingsMapFactory::GetForProfile(profile_), + ControlTypeFromString(value), GURL(), g_browser_process->local_state()); +} + void DefaultBraveShieldsHandler::SetNoScriptControlType( const base::Value::List& args) { CHECK_EQ(args.size(), 1U); diff --git a/browser/ui/webui/settings/default_brave_shields_handler.h b/browser/ui/webui/settings/default_brave_shields_handler.h index fa92d43d47c4..6d936bd8987b 100644 --- a/browser/ui/webui/settings/default_brave_shields_handler.h +++ b/browser/ui/webui/settings/default_brave_shields_handler.h @@ -34,6 +34,8 @@ class DefaultBraveShieldsHandler : public settings::SettingsPageUIHandler { void GetFingerprintingControlType(const base::Value::List& args); void SetHTTPSEverywhereEnabled(const base::Value::List& args); void GetHTTPSEverywhereEnabled(const base::Value::List& args); + void SetHttpsUpgradeControlType(const base::Value::List& args); + void GetHttpsUpgradeControlType(const base::Value::List& args); void SetNoScriptControlType(const base::Value::List& args); void GetNoScriptControlType(const base::Value::List& args); diff --git a/chromium_src/chrome/browser/ssl/https_only_mode_navigation_throttle.cc b/chromium_src/chrome/browser/ssl/https_only_mode_navigation_throttle.cc new file mode 100644 index 000000000000..170cf56b9e96 --- /dev/null +++ b/chromium_src/chrome/browser/ssl/https_only_mode_navigation_throttle.cc @@ -0,0 +1,142 @@ +/* Copyright (c) 2022 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "chrome/browser/ssl/https_only_mode_navigation_throttle.h" + +#include "base/threading/sequenced_task_runner_handle.h" +#include "base/time/time.h" +#include "brave/browser/brave_browser_process.h" +#include "brave/components/brave_shields/browser/brave_shields_util.h" +#include "chrome/browser/content_settings/host_content_settings_map_factory.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ssl/https_only_mode_controller_client.h" +#include "components/prefs/pref_service.h" +#include "components/security_interstitials/content/stateful_ssl_host_state_delegate.h" +#include "content/public/browser/navigation_handle.h" +#include "content/public/browser/page_navigator.h" +#include "content/public/browser/web_contents.h" + +namespace content { +class BrowserContext; +} // namespace content + +namespace { + +// Tor is slow and needs a longer fallback delay +constexpr base::TimeDelta kTorFallbackDelay = base::Seconds(20); + +bool ShouldUpgradeToHttps(content::NavigationHandle* handle) { + if (!brave_shields::IsHttpsByDefaultFeatureEnabled()) { + return false; + } + content::BrowserContext* context = + handle->GetWebContents()->GetBrowserContext(); + const GURL& url = handle->GetURL(); + HostContentSettingsMap* map = + HostContentSettingsMapFactory::GetForProfile(context); + return brave_shields::ShouldUpgradeToHttps( + map, url, g_brave_browser_process->https_upgrade_exceptions_service()); +} + +bool IsTor(content::NavigationHandle* handle) { + auto* context = handle->GetWebContents()->GetBrowserContext(); + Profile* profile = Profile::FromBrowserContext(context); + return profile->IsTor(); +} + +} // namespace + +#define WillFailRequest WillFailRequest_ChromiumImpl +#define GetBoolean(PREF_NAME) \ + GetBooleanOr(PREF_NAME, ShouldUpgradeToHttps(handle)) +#define SetNavigationTimeout(DEFAULT_TIMEOUT) \ + SetNavigationTimeout(IsTor(navigation_handle()) ? kTorFallbackDelay \ + : DEFAULT_TIMEOUT) + +#include "src/chrome/browser/ssl/https_only_mode_navigation_throttle.cc" + +#undef WillFailRequest +#undef GetBoolean +#undef SetNavigationTimeout + +// Called if there is a non-OK net::Error in the completion status. +content::NavigationThrottle::ThrottleCheckResult +HttpsOnlyModeNavigationThrottle::WillFailRequest() { + // Don't fall back if the HTTPS by Default feature flag + // is disabled. + if (!brave_shields::IsHttpsByDefaultFeatureEnabled()) { + return WillFailRequest_ChromiumImpl(); + } + + auto* handle = navigation_handle(); + auto* contents = handle->GetWebContents(); + const GURL& request_url = handle->GetURL(); + + // Don't fall back if we are using HTTPS-Only Mode or the HTTPS-Upgrade + // Strict setting is enabled. + content::BrowserContext* context = + handle->GetWebContents()->GetBrowserContext(); + HostContentSettingsMap* map = + HostContentSettingsMapFactory::GetForProfile(context); + Profile* profile = Profile::FromBrowserContext(context); + auto* prefs = profile->GetPrefs(); + if ((prefs && prefs->GetBoolean(prefs::kHttpsOnlyModeEnabled)) || + brave_shields::ShouldForceHttps(map, request_url)) { + return WillFailRequest_ChromiumImpl(); + } + + // Fall back only on TLS-related errors. + if (handle->GetSSLInfo() && + !net::IsCertStatusError(handle->GetSSLInfo()->cert_status) && + handle->GetNetErrorCode() == net::OK) { + return content::NavigationThrottle::PROCEED; + } + + // Fall back only if the interceptor upgraded this navigation. + auto* tab_helper = HttpsOnlyModeTabHelper::FromWebContents(contents); + if (!tab_helper->is_navigation_upgraded()) { + return content::NavigationThrottle::PROCEED; + } + + // We are going to fall back. + tab_helper->set_is_navigation_upgraded(false); + tab_helper->set_is_navigation_fallback(true); + StatefulSSLHostStateDelegate* state = + static_cast( + profile->GetSSLHostStateDelegate()); + + // StatefulSSLHostStateDelegate can be null during tests. + // Remember not to upgrade this host anymore. + if (state) { + state->AllowHttpForHost( + request_url.host(), + contents->GetPrimaryMainFrame()->GetStoragePartition()); + } + + // Setup the fallback task URL parameters. + content::OpenURLParams url_params = + content::OpenURLParams::FromNavigationHandle(handle); + // Delete the redirect chain that tried to upgrade but hit a dead end. + url_params.redirect_chain.clear(); + // Use the original fallback URL. + url_params.url = tab_helper->fallback_url(); + // Launch a new task to navigate to the fallback URL. + base::SequencedTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce( + [](base::WeakPtr contents, + const content::OpenURLParams& url_params) { + if (!contents) { + return; + } + // This deletes the NavigationThrottle and + // NavigationHandle. + contents->Stop(); + // Navigate to the fallback URL. + contents->OpenURL(url_params); + }, + contents->GetWeakPtr(), std::move(url_params))); + // The NavigationThrottle object will be deleted; nothing more to do here. + return content::NavigationThrottle::CANCEL_AND_IGNORE; +} diff --git a/chromium_src/chrome/browser/ssl/https_only_mode_navigation_throttle.h b/chromium_src/chrome/browser/ssl/https_only_mode_navigation_throttle.h new file mode 100644 index 000000000000..3329e1bc8bf4 --- /dev/null +++ b/chromium_src/chrome/browser/ssl/https_only_mode_navigation_throttle.h @@ -0,0 +1,19 @@ +/* Copyright (c) 2022 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_CHROMIUM_SRC_CHROME_BROWSER_SSL_HTTPS_ONLY_MODE_NAVIGATION_THROTTLE_H_ +#define BRAVE_CHROMIUM_SRC_CHROME_BROWSER_SSL_HTTPS_ONLY_MODE_NAVIGATION_THROTTLE_H_ + +#include "content/public/browser/navigation_throttle.h" + +#define WillFailRequest \ + WillFailRequest_ChromiumImpl(); \ + content::NavigationThrottle::ThrottleCheckResult WillFailRequest + +#include "src/chrome/browser/ssl/https_only_mode_navigation_throttle.h" + +#undef WillFailRequest + +#endif // BRAVE_CHROMIUM_SRC_CHROME_BROWSER_SSL_HTTPS_ONLY_MODE_NAVIGATION_THROTTLE_H_ diff --git a/chromium_src/chrome/browser/ssl/https_only_mode_upgrade_interceptor.cc b/chromium_src/chrome/browser/ssl/https_only_mode_upgrade_interceptor.cc index a0693ce401fe..b5d467bb89af 100644 --- a/chromium_src/chrome/browser/ssl/https_only_mode_upgrade_interceptor.cc +++ b/chromium_src/chrome/browser/ssl/https_only_mode_upgrade_interceptor.cc @@ -5,8 +5,33 @@ #include "chrome/browser/ssl/https_only_mode_upgrade_interceptor.h" +#include "brave/browser/brave_browser_process.h" +#include "brave/components/brave_shields/browser/brave_shields_util.h" +#include "chrome/browser/content_settings/host_content_settings_map_factory.h" +#include "chrome/browser/profiles/profile.h" +#include "components/prefs/pref_service.h" #include "net/base/url_util.h" +class GURL; + +namespace content { +class BrowserContext; +} // namespace content + +namespace { + +bool ShouldUpgradeToHttps(content::BrowserContext* context, const GURL& url) { + if (!brave_shields::IsHttpsByDefaultFeatureEnabled()) { + return false; + } + HostContentSettingsMap* map = + HostContentSettingsMapFactory::GetForProfile(context); + return brave_shields::ShouldUpgradeToHttps( + map, url, g_brave_browser_process->https_upgrade_exceptions_service()); +} + +} // namespace + namespace net { namespace { @@ -22,7 +47,12 @@ bool IsLocalhostOrOnion(const GURL& url) { } // namespace net #define IsLocalhost(URL) IsLocalhostOrOnion(URL) +#define GetBoolean(PREF_NAME) \ + GetBooleanOr( \ + PREF_NAME, \ + ShouldUpgradeToHttps(browser_context, tentative_resource_request.url)) #include "src/chrome/browser/ssl/https_only_mode_upgrade_interceptor.cc" #undef IsLocalHost +#undef GetBoolean diff --git a/chromium_src/chrome/browser/ui/webui/settings/site_settings_helper.cc b/chromium_src/chrome/browser/ui/webui/settings/site_settings_helper.cc index 2f9c8418c58d..a140e1938662 100644 --- a/chromium_src/chrome/browser/ui/webui/settings/site_settings_helper.cc +++ b/chromium_src/chrome/browser/ui/webui/settings/site_settings_helper.cc @@ -26,7 +26,8 @@ {ContentSettingsType::BRAVE_SPEEDREADER, nullptr}, \ {ContentSettingsType::BRAVE_ETHEREUM, nullptr}, \ {ContentSettingsType::BRAVE_SOLANA, nullptr}, \ - {ContentSettingsType::BRAVE_GOOGLE_SIGN_IN, nullptr}, + {ContentSettingsType::BRAVE_GOOGLE_SIGN_IN, nullptr}, \ + {ContentSettingsType::BRAVE_HTTPS_UPGRADE, nullptr} // clang-format on #define BRAVE_SITE_SETTINGS_HELPER_CONTENT_SETTINGS_TYPE_FROM_GROUP_NAME \ diff --git a/chromium_src/components/content_settings/core/browser/content_settings_registry.cc b/chromium_src/components/content_settings/core/browser/content_settings_registry.cc index d1b8a2c7bbb4..00aff706deae 100644 --- a/chromium_src/components/content_settings/core/browser/content_settings_registry.cc +++ b/chromium_src/components/content_settings/core/browser/content_settings_registry.cc @@ -35,6 +35,18 @@ void ContentSettingsRegistry::BraveInit() { ContentSettingsInfo::PERSISTENT, ContentSettingsInfo::EXCEPTIONS_ON_SECURE_AND_INSECURE_ORIGINS); + Register(ContentSettingsType::BRAVE_HTTPS_UPGRADE, + brave_shields::kHTTPSUpgrades, CONTENT_SETTING_ASK, + WebsiteSettingsInfo::SYNCABLE, /*allowlisted_schemes=*/{}, + /*valid_settings=*/ + {CONTENT_SETTING_ALLOW, CONTENT_SETTING_ASK, CONTENT_SETTING_BLOCK}, + WebsiteSettingsInfo::TOP_ORIGIN_ONLY_SCOPE, + WebsiteSettingsRegistry::DESKTOP | + WebsiteSettingsRegistry::PLATFORM_ANDROID, + ContentSettingsInfo::INHERIT_IN_INCOGNITO, + ContentSettingsInfo::PERSISTENT, + ContentSettingsInfo::EXCEPTIONS_ON_SECURE_AND_INSECURE_ORIGINS); + Register(ContentSettingsType::BRAVE_HTTP_UPGRADABLE_RESOURCES, brave_shields::kHTTPUpgradableResources, CONTENT_SETTING_BLOCK, WebsiteSettingsInfo::SYNCABLE, /*allowlisted_schemes=*/{}, diff --git a/chromium_src/components/content_settings/core/common/content_settings.cc b/chromium_src/components/content_settings/core/common/content_settings.cc index 3f767c4629c5..35bcae64b4c4 100644 --- a/chromium_src/components/content_settings/core/common/content_settings.cc +++ b/chromium_src/components/content_settings/core/common/content_settings.cc @@ -33,7 +33,8 @@ constexpr int brave_value(int incr) { {ContentSettingsType::BRAVE_SPEEDREADER, brave_value(8)}, \ {ContentSettingsType::BRAVE_ETHEREUM, brave_value(9)}, \ {ContentSettingsType::BRAVE_SOLANA, brave_value(10)}, \ - {ContentSettingsType::BRAVE_GOOGLE_SIGN_IN, brave_value(11)} + {ContentSettingsType::BRAVE_GOOGLE_SIGN_IN, brave_value(11)}, \ + {ContentSettingsType::BRAVE_HTTPS_UPGRADE, brave_value(12)} // clang-format on #define ContentSettingTypeToHistogramValue \ diff --git a/chromium_src/components/content_settings/core/common/content_settings_types.h b/chromium_src/components/content_settings/core/common/content_settings_types.h index 3fa7cf4f3791..f8199c490362 100644 --- a/chromium_src/components/content_settings/core/common/content_settings_types.h +++ b/chromium_src/components/content_settings/core/common/content_settings_types.h @@ -21,6 +21,7 @@ BRAVE_ETHEREUM, \ BRAVE_SOLANA, \ BRAVE_GOOGLE_SIGN_IN, \ + BRAVE_HTTPS_UPGRADE, \ NUM_TYPES // clang-format on diff --git a/chromium_src/components/prefs/pref_service.cc b/chromium_src/components/prefs/pref_service.cc new file mode 100644 index 000000000000..496c37331897 --- /dev/null +++ b/chromium_src/components/prefs/pref_service.cc @@ -0,0 +1,10 @@ +/* Copyright (c) 2022 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "src/components/prefs/pref_service.cc" + +bool PrefService::GetBooleanOr(const std::string& path, bool other) const { + return GetBoolean(path) || other; +} diff --git a/chromium_src/components/prefs/pref_service.h b/chromium_src/components/prefs/pref_service.h new file mode 100644 index 000000000000..b1ffd6c4ec4e --- /dev/null +++ b/chromium_src/components/prefs/pref_service.h @@ -0,0 +1,17 @@ +/* Copyright (c) 2022 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_CHROMIUM_SRC_COMPONENTS_PREFS_PREF_SERVICE_H_ +#define BRAVE_CHROMIUM_SRC_COMPONENTS_PREFS_PREF_SERVICE_H_ + +#define GetBoolean \ + GetBooleanOr(const std::string& path, bool other) const; \ + bool GetBoolean + +#include "src/components/prefs/pref_service.h" + +#undef GetBoolean + +#endif // BRAVE_CHROMIUM_SRC_COMPONENTS_PREFS_PREF_SERVICE_H_ diff --git a/chromium_src/net/base/features.cc b/chromium_src/net/base/features.cc index 89bb84f00354..377f2e88f332 100644 --- a/chromium_src/net/base/features.cc +++ b/chromium_src/net/base/features.cc @@ -53,5 +53,15 @@ BASE_FEATURE(kBravePartitionHSTS, "BravePartitionHSTS", base::FEATURE_ENABLED_BY_DEFAULT); +// Enables HTTPS-Only Mode in Private Windows with Tor by default. +BASE_FEATURE(kBraveTorWindowsHttpsOnly, + "BraveTorWindowsHttpsOnly", + base::FEATURE_ENABLED_BY_DEFAULT); + +// Enabled HTTPS by Default. +BASE_FEATURE(kBraveHttpsByDefault, + "HttpsByDefault", + base::FEATURE_DISABLED_BY_DEFAULT); + } // namespace features } // namespace net diff --git a/chromium_src/net/base/features.h b/chromium_src/net/base/features.h index 9df9a2feea4c..0a9e627d4477 100644 --- a/chromium_src/net/base/features.h +++ b/chromium_src/net/base/features.h @@ -18,8 +18,10 @@ NET_EXPORT BASE_DECLARE_FEATURE(kBraveEphemeralStorageKeepAlive); NET_EXPORT extern const base::FeatureParam kBraveEphemeralStorageKeepAliveTimeInSeconds; NET_EXPORT BASE_DECLARE_FEATURE(kBraveFirstPartyEphemeralStorage); +NET_EXPORT BASE_DECLARE_FEATURE(kBraveHttpsByDefault); NET_EXPORT BASE_DECLARE_FEATURE(kBravePartitionBlobStorage); NET_EXPORT BASE_DECLARE_FEATURE(kBravePartitionHSTS); +NET_EXPORT BASE_DECLARE_FEATURE(kBraveTorWindowsHttpsOnly); } // namespace features } // namespace net diff --git a/chromium_src/third_party/blink/common/features.cc b/chromium_src/third_party/blink/common/features.cc index ecef7620dac2..979f72ddd52f 100644 --- a/chromium_src/third_party/blink/common/features.cc +++ b/chromium_src/third_party/blink/common/features.cc @@ -70,11 +70,6 @@ BASE_FEATURE(kBraveBlockScreenFingerprinting, "BraveBlockScreenFingerprinting", base::FEATURE_DISABLED_BY_DEFAULT); -// Enables HTTPS-Only Mode in Private Windows with Tor by default. -BASE_FEATURE(kBraveTorWindowsHttpsOnly, - "BraveTorWindowsHttpsOnly", - base::FEATURE_ENABLED_BY_DEFAULT); - // Enables protection against fingerprinting via high-resolution time stamps. BASE_FEATURE(kBraveRoundTimeStamps, "BraveRoundTimeStamps", diff --git a/chromium_src/third_party/blink/public/common/features.h b/chromium_src/third_party/blink/public/common/features.h index c0f3f878a2b7..87248461acc5 100644 --- a/chromium_src/third_party/blink/public/common/features.h +++ b/chromium_src/third_party/blink/public/common/features.h @@ -17,7 +17,6 @@ BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kNavigatorConnectionAttribute); BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kPartitionBlinkMemoryCache); BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kRestrictWebSocketsPool); BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kBraveBlockScreenFingerprinting); -BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kBraveTorWindowsHttpsOnly); BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kBraveRoundTimeStamps); BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kRestrictEventSourcePool); diff --git a/common/BUILD.gn b/common/BUILD.gn index edd675ea3357..e175967174c0 100644 --- a/common/BUILD.gn +++ b/common/BUILD.gn @@ -76,6 +76,7 @@ source_set("common") { deps += [ "//brave/components/brave_component_updater/browser", + "//brave/components/https_upgrade_exceptions/browser", "//extensions/common:common_constants", ] diff --git a/components/brave_component_updater/browser/BUILD.gn b/components/brave_component_updater/browser/BUILD.gn index 46c6f836864c..2c170feed9fd 100644 --- a/components/brave_component_updater/browser/BUILD.gn +++ b/components/brave_component_updater/browser/BUILD.gn @@ -1,3 +1,8 @@ +# Copyright (c) 2022 The Brave Authors. All rights reserved. +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at https://mozilla.org/MPL/2.0/. + import("//extensions/buildflags/buildflags.gni") static_library("browser") { diff --git a/components/brave_shields/browser/BUILD.gn b/components/brave_shields/browser/BUILD.gn index 7dcb79ba6d54..3e058fb73afe 100644 --- a/components/brave_shields/browser/BUILD.gn +++ b/components/brave_shields/browser/BUILD.gn @@ -1,3 +1,8 @@ +# Copyright (c) 2022 The Brave Authors. All rights reserved. +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at https://mozilla.org/MPL/2.0/. + if (!is_ios) { static_library("browser") { sources = [ @@ -71,6 +76,7 @@ if (!is_ios) { "//brave/components/content_settings/core/common", "//brave/components/debounce/common", "//brave/components/ephemeral_storage", + "//brave/components/https_upgrade_exceptions/browser", "//brave/components/l10n/common", "//brave/components/p3a", "//brave/components/p3a_utils", diff --git a/components/brave_shields/browser/brave_shields_util.cc b/components/brave_shields/browser/brave_shields_util.cc index 8c670f6ac1c0..9e9f31dd3b42 100644 --- a/components/brave_shields/browser/brave_shields_util.cc +++ b/components/brave_shields/browser/brave_shields_util.cc @@ -20,6 +20,7 @@ #include "brave/components/constants/pref_names.h" #include "brave/components/content_settings/core/common/content_settings_util.h" #include "brave/components/debounce/common/features.h" +#include "brave/components/https_upgrade_exceptions/browser/https_upgrade_exceptions_service.h" #include "components/content_settings/core/browser/cookie_settings.h" #include "components/content_settings/core/browser/host_content_settings_map.h" #include "components/content_settings/core/common/content_settings.h" @@ -28,6 +29,7 @@ #include "components/content_settings/core/common/pref_names.h" #include "components/prefs/pref_service.h" #include "content/public/browser/browser_thread.h" +#include "content/public/browser/web_contents.h" #include "content/public/common/referrer.h" #include "net/base/features.h" #include "net/base/registry_controlled_domains/registry_controlled_domain.h" @@ -622,6 +624,112 @@ bool GetHTTPSEverywhereEnabled(HostContentSettingsMap* map, const GURL& url) { return setting == CONTENT_SETTING_ALLOW ? false : true; } +bool IsHttpsByDefaultFeatureEnabled() { + return base::FeatureList::IsEnabled(net::features::kBraveHttpsByDefault); +} + +void SetHttpsUpgradeControlType(HostContentSettingsMap* map, + ControlType type, + const GURL& url, + PrefService* local_state) { + if (!url.SchemeIsHTTPOrHTTPS() && !url.is_empty()) { + return; + } + + auto primary_pattern = GetPatternFromURL(url); + if (!primary_pattern.IsValid()) { + return; + } + + ContentSetting setting; + if (type == ControlType::ALLOW) { + // Allow http connections + setting = CONTENT_SETTING_ALLOW; + } else if (type == ControlType::BLOCK) { + // Require https + setting = CONTENT_SETTING_BLOCK; + } else if (type == ControlType::BLOCK_THIRD_PARTY) { + // Prefer https + setting = CONTENT_SETTING_ASK; + } else { + // Fall back to default + setting = CONTENT_SETTING_DEFAULT; + } + map->SetContentSettingCustomScope( + primary_pattern, ContentSettingsPattern::Wildcard(), + ContentSettingsType::BRAVE_HTTPS_UPGRADE, setting); + + // Reset the HTTPS fallback map. + if (url.is_empty()) { + map->ClearSettingsForOneType(ContentSettingsType::HTTP_ALLOWED); + } else { + const GURL& secure_url = GURL("https://" + url.host()); + map->SetWebsiteSettingDefaultScope( + secure_url, GURL(), ContentSettingsType::HTTP_ALLOWED, base::Value()); + } + + RecordShieldsSettingChanged(local_state); +} + +ControlType GetHttpsUpgradeControlType(HostContentSettingsMap* map, + const GURL& url) { + if (!url.SchemeIsHTTPOrHTTPS() && !url.is_empty()) { + // No upgrades happen for non-http(s) URLs. + return ControlType::ALLOW; + } + ContentSetting setting = map->GetContentSetting( + url, GURL(), ContentSettingsType::BRAVE_HTTPS_UPGRADE); + if (setting == CONTENT_SETTING_ALLOW) { + // Disabled (allow http) + return ControlType::ALLOW; + } else if (setting == CONTENT_SETTING_BLOCK) { + // HTTPS Only (require https) + return ControlType::BLOCK; + } else if (setting == CONTENT_SETTING_ASK) { + // HTTPS Only (prefer https) + return ControlType::BLOCK_THIRD_PARTY; + } else { + // HTTPS by default (upgrade when available) + return ControlType::DEFAULT; + } +} + +bool ShouldUpgradeToHttps( + HostContentSettingsMap* map, + const GURL& url, + https_upgrade_exceptions::HttpsUpgradeExceptionsService* + https_upgrade_exceptions_service) { + // Don't upgrade if feature is disabled. + if (!IsHttpsByDefaultFeatureEnabled()) { + return false; + } + if (!url.SchemeIsHTTPOrHTTPS() && !url.is_empty()) { + return false; + } + DCHECK(https_upgrade_exceptions_service); + // Don't upgrade if shields are down. + if (!GetBraveShieldsEnabled(map, url)) { + return false; + } + const ControlType control_type = GetHttpsUpgradeControlType(map, url); + // Always upgrade for Strict HTTPS Upgrade. + if (control_type == ControlType::BLOCK) { + return true; + } + // Upgrade for Standard HTTPS upgrade if host is not on the exceptions list. + if (control_type == ControlType::BLOCK_THIRD_PARTY && + https_upgrade_exceptions_service && + https_upgrade_exceptions_service->CanUpgradeToHTTPS(url)) { + return true; + } + return false; +} + +bool ShouldForceHttps(HostContentSettingsMap* map, const GURL& url) { + return GetBraveShieldsEnabled(map, url) && + GetHttpsUpgradeControlType(map, url) == ControlType::BLOCK; +} + void SetNoScriptControlType(HostContentSettingsMap* map, ControlType type, const GURL& url, diff --git a/components/brave_shields/browser/brave_shields_util.h b/components/brave_shields/browser/brave_shields_util.h index 75ef665793d1..7a0b34c8fde0 100644 --- a/components/brave_shields/browser/brave_shields_util.h +++ b/components/brave_shields/browser/brave_shields_util.h @@ -1,7 +1,7 @@ -/* Copyright 2019 The Brave Authors. All rights reserved. +/* Copyright (c) 2019 The Brave Authors. All rights reserved. * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ + * You can obtain one at https://mozilla.org/MPL/2.0/. */ #ifndef BRAVE_COMPONENTS_BRAVE_SHIELDS_BROWSER_BRAVE_SHIELDS_UTIL_H_ #define BRAVE_COMPONENTS_BRAVE_SHIELDS_BROWSER_BRAVE_SHIELDS_UTIL_H_ @@ -13,6 +13,10 @@ #include "components/content_settings/core/common/content_settings_types.h" #include "services/network/public/mojom/referrer_policy.mojom.h" +namespace https_upgrade_exceptions { +class HttpsUpgradeExceptionsService; +} + namespace content { struct Referrer; } @@ -111,6 +115,20 @@ void SetHTTPSEverywhereEnabled(HostContentSettingsMap* map, void ResetHTTPSEverywhereEnabled(HostContentSettingsMap* map, const GURL& url); bool GetHTTPSEverywhereEnabled(HostContentSettingsMap* map, const GURL& url); +bool IsHttpsByDefaultFeatureEnabled(); +void SetHttpsUpgradeControlType(HostContentSettingsMap* map, + ControlType type, + const GURL& url, + PrefService* local_state = nullptr); +ControlType GetHttpsUpgradeControlType(HostContentSettingsMap* map, + const GURL& url); +bool ShouldUpgradeToHttps( + HostContentSettingsMap* map, + const GURL& url, + https_upgrade_exceptions::HttpsUpgradeExceptionsService* + https_upgrade_exceptions_service); +bool ShouldForceHttps(HostContentSettingsMap* map, const GURL& url); + void SetNoScriptControlType(HostContentSettingsMap* map, ControlType type, const GURL& url, diff --git a/components/brave_shields/common/brave_shield_constants.h b/components/brave_shields/common/brave_shield_constants.h index e2668dda946d..338c22700209 100644 --- a/components/brave_shields/common/brave_shield_constants.h +++ b/components/brave_shields/common/brave_shield_constants.h @@ -17,6 +17,7 @@ const char kAds[] = "shieldsAds"; const char kCosmeticFiltering[] = "cosmeticFiltering"; const char kTrackers[] = "trackers"; const char kHTTPUpgradableResources[] = "httpUpgradableResources"; +const char kHTTPSUpgrades[] = "httpsUpgrades"; const char kJavaScript[] = "javascript"; const char kFingerprintingV2[] = "fingerprintingV2"; const char kBraveShields[] = "braveShields"; @@ -83,6 +84,12 @@ constexpr webui::LocalizedString kLocalizedStrings[] = { IDS_BRAVE_SHIELDS_FINGERPRINTING_BLOCKED_AGG}, {"braveShieldsFingerprintingAllowAll", IDS_BRAVE_SHIELDS_FINGERPRINTING_ALLOW_ALL}, + {"braveShieldsHttpsUpgradeModeDisabled", + IDS_BRAVE_SHIELDS_HTTPS_UPGRADE_MODE_DISABLED}, + {"braveShieldsHttpsUpgradeModeStandard", + IDS_BRAVE_SHIELDS_HTTPS_UPGRADE_MODE_STANDARD}, + {"braveShieldsHttpsUpgradeModeStrict", + IDS_BRAVE_SHIELDS_HTTPS_UPGRADE_MODE_STRICT}, {"braveShieldsReportSite", IDS_BRAVE_SHIELDS_REPORT_SITE}, {"braveShieldsReportSiteDesc", IDS_BRAVE_SHIELDS_REPORT_SITE_DESC}, {"braveShieldsDownDesc", IDS_BRAVE_SHIELDS_DOWN_DESC}, diff --git a/components/brave_shields/common/brave_shields_panel.mojom b/components/brave_shields/common/brave_shields_panel.mojom index 22c58fec9fd4..8b0ae6afb68a 100644 --- a/components/brave_shields/common/brave_shields_panel.mojom +++ b/components/brave_shields/common/brave_shields_panel.mojom @@ -40,6 +40,7 @@ interface DataHandler { SetAdBlockMode(AdBlockMode mode); SetFingerprintMode(FingerprintMode mode); SetCookieBlockMode(CookieBlockMode mode); + SetHttpsUpgradeMode(HttpsUpgradeMode mode); SetIsNoScriptsEnabled(bool is_enabled); SetHTTPSEverywhereEnabled(bool is_enabled); SetBraveShieldsEnabled(bool is_enabled); @@ -64,6 +65,7 @@ struct SiteSettings { FingerprintMode fingerprint_mode; CookieBlockMode cookie_block_mode; bool is_https_everywhere_enabled; + HttpsUpgradeMode https_upgrade_mode; bool is_noscript_enabled; }; @@ -84,3 +86,9 @@ enum CookieBlockMode { BLOCKED, ALLOW }; + +enum HttpsUpgradeMode { + STRICT, + STANDARD, + DISABLED +}; diff --git a/components/brave_shields/resources/panel/components/advanced-controls-content/index.tsx b/components/brave_shields/resources/panel/components/advanced-controls-content/index.tsx index 23e795ab522d..c7bf6acad63d 100644 --- a/components/brave_shields/resources/panel/components/advanced-controls-content/index.tsx +++ b/components/brave_shields/resources/panel/components/advanced-controls-content/index.tsx @@ -8,9 +8,10 @@ import * as S from './style' import Toggle from '../../../../../web-components/toggle' import Select from '../../../../../web-components/select' import { getLocale } from '../../../../../common/locale' -import getPanelBrowserAPI, { AdBlockMode, CookieBlockMode, FingerprintMode } from '../../api/panel_browser_api' +import getPanelBrowserAPI, { AdBlockMode, CookieBlockMode, FingerprintMode, HttpsUpgradeMode } from '../../api/panel_browser_api' import DataContext from '../../state/context' import { ViewType } from '../../state/component_types' +import { loadTimeData } from '../../../../../common/loadTimeData' const adBlockModeOptions = [ { value: AdBlockMode.AGGRESSIVE, text: getLocale('braveShieldsTrackersAndAdsBlockedAgg') }, @@ -30,6 +31,12 @@ const fingerprintModeOptions = [ { value: FingerprintMode.ALLOW, text: getLocale('braveShieldsFingerprintingAllowAll') } ] +const httpsUpgradeModeOptions = [ + { value: HttpsUpgradeMode.STRICT, text: getLocale('braveShieldsHttpsUpgradeModeStrict') }, + { value: HttpsUpgradeMode.STANDARD, text: getLocale('braveShieldsHttpsUpgradeModeStandard') }, + { value: HttpsUpgradeMode.DISABLED, text: getLocale('braveShieldsHttpsUpgradeModeDisabled') } +] + function GlobalSettings () { const onAdBlockListsClick = () => { chrome.tabs.create({ url: 'chrome://settings/shields/filters', active: true }) @@ -80,6 +87,11 @@ function AdvancedControlsContent () { if (getSiteSettings) getSiteSettings() } + const handleHttpsUpgradeModeChange = (value: string) => { + getPanelBrowserAPI().dataHandler.setHttpsUpgradeMode(parseInt(value)) + if (getSiteSettings) getSiteSettings() + } + const handleIsNoScriptEnabledChange = (isEnabled: boolean) => { getPanelBrowserAPI().dataHandler.setIsNoScriptsEnabled(isEnabled) if (getSiteSettings) getSiteSettings() @@ -93,6 +105,8 @@ function AdvancedControlsContent () { const adsListCount = siteBlockInfo?.adsList.length ?? 0 const httpRedirectsListCount = siteBlockInfo?.httpRedirectsList.length ?? 0 const jsListCount = siteBlockInfo?.jsList.length ?? 0 + const isHttpsByDefaultEnabled = loadTimeData.getBoolean('isHttpsByDefaultEnabled') + const isTorProfile = loadTimeData.getBoolean('isTorProfile') return ( {adsListCount > 99 ? '99+' : adsListCount} - + {!isHttpsByDefaultEnabled && {getLocale('braveShieldsConnectionsUpgraded')} {httpRedirectsListCount > 99 ? '99+' : httpRedirectsListCount} - + } + {(isHttpsByDefaultEnabled && !isTorProfile) && + + + {httpsUpgradeModeOptions.map(entry => { + return ( + {entry.text} + ) + })} + + + } {getLocale('braveShieldsScriptsBlocked')} diff --git a/components/brave_shields/resources/panel/stories/component-panel.tsx b/components/brave_shields/resources/panel/stories/component-panel.tsx index 35512c83d271..a6e144613c4f 100644 --- a/components/brave_shields/resources/panel/stories/component-panel.tsx +++ b/components/brave_shields/resources/panel/stories/component-panel.tsx @@ -13,7 +13,7 @@ import shieldsDarkTheme from '../theme/shields-dark' import shieldsLightTheme from '../theme/shields-light' import ThemeProvider from '../../../../common/BraveCoreThemeProvider' import DataContext from '../state/context' -import { AdBlockMode, FingerprintMode, CookieBlockMode } from '../api/panel_browser_api' +import { AdBlockMode, FingerprintMode, CookieBlockMode, HttpsUpgradeMode } from '../api/panel_browser_api' import { ViewType } from '../state/component_types' import { getLocale } from '../../../../common/locale' @@ -67,6 +67,7 @@ export default { fingerprintMode: FingerprintMode.ALLOW, cookieBlockMode: CookieBlockMode.ALLOW, isHttpsEverywhereEnabled: true, + httpsUpgradeMode: HttpsUpgradeMode.DISABLED, isNoscriptEnabled: false }, viewType: ViewType.Main diff --git a/components/brave_shields/resources/panel/stories/locale.ts b/components/brave_shields/resources/panel/stories/locale.ts index 8173f9cac78a..fc8cdafcd142 100644 --- a/components/brave_shields/resources/panel/stories/locale.ts +++ b/components/brave_shields/resources/panel/stories/locale.ts @@ -20,6 +20,9 @@ provideStrings({ braveShieldsCustomizeAdblockLists: 'Filter lists', braveShieldsConnectionsUpgraded: 'Upgrade connections to HTTPS', braveShieldsHTTPSEnable: 'Enable HTTPS', + braveShieldsHttpsUpgradeModeDisabled: 'Don\'t upgrade connections to HTTPS', + braveShieldsHttpsUpgradeModeStandard: 'Upgrade connections to HTTPS', + braveShieldsHttpsUpgradeModeStrict: 'Only use HTTPS connections', braveShieldsScriptsBlocked: 'Block scripts', braveShieldsScriptsBlockedEnable: 'Enable Scripts Blocked', braveShieldsTrackersAndAds: 'Trackers & ads', diff --git a/components/https_upgrade_exceptions/browser/BUILD.gn b/components/https_upgrade_exceptions/browser/BUILD.gn new file mode 100644 index 000000000000..8b18067c3120 --- /dev/null +++ b/components/https_upgrade_exceptions/browser/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (c) 2023 The Brave Authors. All rights reserved. +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at https://mozilla.org/MPL/2.0/. + +import("//brave/build/config.gni") + +static_library("browser") { + sources = [ + "https_upgrade_exceptions_service.cc", + "https_upgrade_exceptions_service.h", + ] + + deps = [ + "//base", + "//brave/components/brave_component_updater/browser", + ] +} diff --git a/components/https_upgrade_exceptions/browser/https_upgrade_exceptions_service.cc b/components/https_upgrade_exceptions/browser/https_upgrade_exceptions_service.cc new file mode 100644 index 000000000000..1d2a5b8b6385 --- /dev/null +++ b/components/https_upgrade_exceptions/browser/https_upgrade_exceptions_service.cc @@ -0,0 +1,86 @@ +/* Copyright (c) 2022 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "brave/components/https_upgrade_exceptions/browser/https_upgrade_exceptions_service.h" + +#include +#include +#include +#include +#include + +#include "base/containers/contains.h" +#include "base/files/file_path.h" +#include "base/strings/string_split.h" +#include "base/task/thread_pool.h" +#include "brave/components/brave_component_updater/browser/dat_file_util.h" +#include "brave/components/brave_component_updater/browser/local_data_files_observer.h" + +#define HTTPS_UPGRADE_EXCEPTIONS_TXT_FILE "https-upgrade-exceptions-list.txt" +#define HTTPS_UPGRADE_EXCEPTIONS_TXT_FILE_VERSION "1" + +namespace https_upgrade_exceptions { + +using brave_component_updater::LocalDataFilesObserver; +using brave_component_updater::LocalDataFilesService; + +HttpsUpgradeExceptionsService::HttpsUpgradeExceptionsService( + LocalDataFilesService* local_data_files_service) + : LocalDataFilesObserver(local_data_files_service) {} + +void HttpsUpgradeExceptionsService::LoadHTTPSUpgradeExceptions( + const base::FilePath& install_dir) { + base::FilePath txt_file_path = + install_dir.AppendASCII(HTTPS_UPGRADE_EXCEPTIONS_TXT_FILE_VERSION) + .AppendASCII(HTTPS_UPGRADE_EXCEPTIONS_TXT_FILE); + base::ThreadPool::PostTaskAndReplyWithResult( + FROM_HERE, {base::MayBlock()}, + base::BindOnce(&brave_component_updater::GetDATFileAsString, + txt_file_path), + base::BindOnce(&HttpsUpgradeExceptionsService::OnDATFileDataReady, + weak_factory_.GetWeakPtr())); +} + +void HttpsUpgradeExceptionsService::OnDATFileDataReady( + const std::string& contents) { + std::vector lines = base::SplitString( + contents, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); + for (const auto& line : lines) { + exceptional_domains_.insert(std::move(line)); + } + is_ready_ = true; + return; +} + +bool HttpsUpgradeExceptionsService::CanUpgradeToHTTPS(const GURL& url) { + if (!is_ready_) { + // We don't have the exceptions list loaded yet. To avoid breakage, + // don't upgrade any websites yet. + return false; + } + // Allow upgrade only if the domain is not on the exceptions list. + return !base::Contains(exceptional_domains_, url.host()); +} + +// implementation of LocalDataFilesObserver +void HttpsUpgradeExceptionsService::OnComponentReady( + const std::string& component_id, + const base::FilePath& install_dir, + const std::string& manifest) { + LoadHTTPSUpgradeExceptions(install_dir); +} + +HttpsUpgradeExceptionsService::~HttpsUpgradeExceptionsService() { + exceptional_domains_.clear(); +} + +std::unique_ptr +HttpsUpgradeExceptionsServiceFactory( + LocalDataFilesService* local_data_files_service) { + return std::make_unique( + local_data_files_service); +} + +} // namespace https_upgrade_exceptions diff --git a/components/https_upgrade_exceptions/browser/https_upgrade_exceptions_service.h b/components/https_upgrade_exceptions/browser/https_upgrade_exceptions_service.h new file mode 100644 index 000000000000..89f3ddc2fc4f --- /dev/null +++ b/components/https_upgrade_exceptions/browser/https_upgrade_exceptions_service.h @@ -0,0 +1,50 @@ +/* Copyright (c) 2022 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_COMPONENTS_HTTPS_UPGRADE_EXCEPTIONS_BROWSER_HTTPS_UPGRADE_EXCEPTIONS_SERVICE_H_ +#define BRAVE_COMPONENTS_HTTPS_UPGRADE_EXCEPTIONS_BROWSER_HTTPS_UPGRADE_EXCEPTIONS_SERVICE_H_ + +#include +#include +#include + +#include "base/files/file_path.h" +#include "base/strings/string_piece.h" +#include "brave/components/brave_component_updater/browser/local_data_files_observer.h" +#include "brave/components/brave_component_updater/browser/local_data_files_service.h" + +namespace https_upgrade_exceptions { + +class HttpsUpgradeExceptionsService + : public brave_component_updater::LocalDataFilesObserver { + public: + explicit HttpsUpgradeExceptionsService( + brave_component_updater::LocalDataFilesService* local_data_files_service); + + // implementation of brave_component_updater::LocalDataFilesObserver + void OnComponentReady(const std::string& component_id, + const base::FilePath& install_dir, + const std::string& manifest) override; + + bool CanUpgradeToHTTPS(const GURL& url); + ~HttpsUpgradeExceptionsService() override; + void SetIsReadyForTesting() { is_ready_ = true; } + void OnDATFileDataReady(const std::string& contents); + + private: + void LoadHTTPSUpgradeExceptions(const base::FilePath& install_dir); + std::set exceptional_domains_; + bool is_ready_ = false; + base::WeakPtrFactory weak_factory_{this}; +}; + +// Creates the HttpsUpgradeExceptionsService +std::unique_ptr +HttpsUpgradeExceptionsServiceFactory( + brave_component_updater::LocalDataFilesService* local_data_files_service); + +} // namespace https_upgrade_exceptions + +#endif // BRAVE_COMPONENTS_HTTPS_UPGRADE_EXCEPTIONS_BROWSER_HTTPS_UPGRADE_EXCEPTIONS_SERVICE_H_ diff --git a/components/resources/brave_shields_strings.grdp b/components/resources/brave_shields_strings.grdp index 81db6f2c89f3..6319b2dcb1dd 100644 --- a/components/resources/brave_shields_strings.grdp +++ b/components/resources/brave_shields_strings.grdp @@ -117,6 +117,18 @@ Allow fingerprinting + + Don't upgrade HTTP connections + + + + Upgrade connections to HTTPS + + + + Only connect with HTTPS + + Report site diff --git a/test/BUILD.gn b/test/BUILD.gn index 0b61cd8d88f6..def7dbb9b47f 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -696,6 +696,7 @@ test("brave_browser_tests") { "//brave/browser/brave_shields/cookie_pref_service_browsertest.cc", "//brave/browser/brave_shields/domain_block_page_browsertest.cc", "//brave/browser/brave_shields/eventsource_pool_limit_browsertest.cc", + "//brave/browser/brave_shields/https_upgrade_browsertest.cc", "//brave/browser/brave_shields/websockets_pool_limit_browsertest.cc", "//brave/browser/brave_stats/brave_stats_updater_browsertest.cc", "//brave/browser/brave_wallet/brave_wallet_ethereum_chain_browsertest.cc", @@ -867,6 +868,7 @@ test("brave_browser_tests") { "//brave/components/de_amp/common:common", "//brave/components/debounce/browser", "//brave/components/debounce/common", + "//brave/components/https_upgrade_exceptions/browser:browser", "//brave/components/ipfs/buildflags", "//brave/components/l10n/common", "//brave/components/l10n/common:test_support", diff --git a/test/base/testing_brave_browser_process.cc b/test/base/testing_brave_browser_process.cc index b60091651cc8..07f603cc94ae 100644 --- a/test/base/testing_brave_browser_process.cc +++ b/test/base/testing_brave_browser_process.cc @@ -69,6 +69,11 @@ TestingBraveBrowserProcess::URLSanitizerComponentInstaller() { return nullptr; } +https_upgrade_exceptions::HttpsUpgradeExceptionsService* +TestingBraveBrowserProcess::https_upgrade_exceptions_service() { + return nullptr; +} + brave_shields::HTTPSEverywhereService* TestingBraveBrowserProcess::https_everywhere_service() { NOTREACHED(); diff --git a/test/base/testing_brave_browser_process.h b/test/base/testing_brave_browser_process.h index 6b74da19db29..5b5c5f6fcbba 100644 --- a/test/base/testing_brave_browser_process.h +++ b/test/base/testing_brave_browser_process.h @@ -49,6 +49,8 @@ class TestingBraveBrowserProcess : public BraveBrowserProcess { brave::URLSanitizerComponentInstaller* URLSanitizerComponentInstaller() override; brave_shields::HTTPSEverywhereService* https_everywhere_service() override; + https_upgrade_exceptions::HttpsUpgradeExceptionsService* + https_upgrade_exceptions_service() override; brave_component_updater::LocalDataFilesService* local_data_files_service() override; #if BUILDFLAG(ENABLE_TOR)