blob: b30a779e5840495c7dc0a74a8f0b1acb781a4e5d [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/webui/settings/settings_secure_dns_handler.h"
#include "base/containers/adapters.h"
#include "base/containers/contains.h"
#include "base/feature_list.h"
#include "base/test/metrics/histogram_tester.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/net/dns_probe_test_util.h"
#include "chrome/browser/net/secure_dns_config.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "components/country_codes/country_codes.h"
#include "components/policy/core/browser/browser_policy_connector.h"
#include "components/policy/core/common/mock_configuration_policy_provider.h"
#include "components/policy/policy_constants.h"
#include "components/prefs/pref_service.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/test_web_ui.h"
#include "net/dns/public/resolve_error_info.h"
#include "services/network/public/mojom/clear_data_filter.mojom.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#if BUILDFLAG(IS_WIN)
#include "base/win/win_util.h"
#endif
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
#include "chrome/browser/ash/net/secure_dns_manager.h"
#include "components/account_id/account_id.h"
#include "components/user_manager/scoped_user_manager.h"
#include "components/user_manager/user_type.h"
#endif
using testing::_;
using testing::IsEmpty;
using testing::Return;
namespace settings {
namespace {
constexpr char kGetSecureDnsResolverList[] = "getSecureDnsResolverList";
constexpr char kIsValidConfig[] = "isValidConfig";
constexpr char kProbeConfig[] = "probeConfig";
constexpr char kWebUiFunctionName[] = "webUiCallbackName";
net::DohProviderEntry::List GetDohProviderListForTesting() {
static BASE_FEATURE(kDohProviderFeatureForProvider_Global1,
"DohProviderFeatureForProvider_Global1",
base::FEATURE_ENABLED_BY_DEFAULT);
static const auto global1 = net::DohProviderEntry::ConstructForTesting(
"Provider_Global1", &kDohProviderFeatureForProvider_Global1,
{} /*ip_strs */, {} /* dot_hostnames */,
"https://global1.provider/dns-query{?dns}",
"Global Provider 1" /* ui_name */,
"https://global1.provider/privacy_policy/" /* privacy_policy */,
true /* display_globally */, {} /* display_countries */);
static BASE_FEATURE(kDohProviderFeatureForProvider_NoDisplay,
"DohProviderFeatureForProvider_NoDisplay",
base::FEATURE_ENABLED_BY_DEFAULT);
static const auto no_display = net::DohProviderEntry::ConstructForTesting(
"Provider_NoDisplay", &kDohProviderFeatureForProvider_NoDisplay,
{} /*ip_strs */, {} /* dot_hostnames */,
"https://nodisplay.provider/dns-query{?dns}",
"No Display Provider" /* ui_name */,
"https://nodisplay.provider/privacy_policy/" /* privacy_policy */,
false /* display_globally */, {} /* display_countries */);
static BASE_FEATURE(kDohProviderFeatureForProvider_EE_FR,
"DohProviderFeatureForProvider_EE_FR",
base::FEATURE_ENABLED_BY_DEFAULT);
static const auto ee_fr = net::DohProviderEntry::ConstructForTesting(
"Provider_EE_FR", &kDohProviderFeatureForProvider_EE_FR, {} /*ip_strs */,
{} /* dot_hostnames */, "https://ee.fr.provider/dns-query{?dns}",
"EE/FR Provider" /* ui_name */,
"https://ee.fr.provider/privacy_policy/" /* privacy_policy */,
false /* display_globally */, {"EE", "FR"} /* display_countries */);
static BASE_FEATURE(kDohProviderFeatureForProvider_FR,
"DohProviderFeatureForProvider_FR",
base::FEATURE_ENABLED_BY_DEFAULT);
static const auto fr = net::DohProviderEntry::ConstructForTesting(
"Provider_FR", &kDohProviderFeatureForProvider_FR, {} /*ip_strs */,
{} /* dot_hostnames */, "https://fr.provider/dns-query{?dns}",
"FR Provider" /* ui_name */,
"https://fr.provider/privacy_policy/" /* privacy_policy */,
false /* display_globally */, {"FR"} /* display_countries */);
static BASE_FEATURE(kDohProviderFeatureForProvider_Global2,
"DohProviderFeatureForProvider_Global2",
base::FEATURE_ENABLED_BY_DEFAULT);
static const auto global2 = net::DohProviderEntry::ConstructForTesting(
"Provider_Global2", &kDohProviderFeatureForProvider_Global2,
{} /*ip_strs */, {} /* dot_hostnames */,
"https://global2.provider/dns-query{?dns}",
"Global Provider 2" /* ui_name */,
"https://global2.provider/privacy_policy/" /* privacy_policy */,
true /* display_globally */, {} /* display_countries */);
return {&global1, &no_display, &ee_fr, &fr, &global2};
}
bool FindDropdownItem(const base::Value::List& resolvers,
const std::string& name,
const std::string& value,
const std::string& policy) {
base::Value::Dict dict;
dict.Set("name", name);
dict.Set("value", value);
dict.Set("policy", policy);
return base::Contains(resolvers, dict);
}
} // namespace
class TestSecureDnsHandler : public SecureDnsHandler {
public:
// Pull WebUIMessageHandler::set_web_ui() into public so tests can call it.
using SecureDnsHandler::set_web_ui;
};
class SecureDnsHandlerTest : public InProcessBrowserTest {
public:
SecureDnsHandlerTest(const SecureDnsHandlerTest&) = delete;
SecureDnsHandlerTest& operator=(const SecureDnsHandlerTest&) = delete;
protected:
#if BUILDFLAG(IS_WIN)
SecureDnsHandlerTest()
// Mark as not enterprise managed to prevent the secure DNS mode from
// being downgraded to off.
: scoped_domain_(false) {}
#else
SecureDnsHandlerTest() = default;
#endif
~SecureDnsHandlerTest() override = default;
// InProcessBrowserTest:
void SetUpInProcessBrowserTestFixture() override {
// Initialize user policy.
provider_.SetDefaultReturns(/*is_initialization_complete_return=*/true,
/*is_first_policy_load_complete_return=*/true);
policy::BrowserPolicyConnector::SetPolicyProviderForTesting(&provider_);
}
void SetUpOnMainThread() override {
handler_ = std::make_unique<TestSecureDnsHandler>();
handler_->set_web_ui(&web_ui_);
handler_->RegisterMessages();
handler_->AllowJavascriptForTesting();
base::RunLoop().RunUntilIdle();
}
void TearDownOnMainThread() override { handler_.reset(); }
// Updates out-params from the last message sent to WebUI about a secure DNS
// change. Returns false if the message was invalid or not found.
bool GetLastSettingsChangedMessage(std::string* out_secure_dns_mode,
std::string* out_doh_config,
int* out_management_mode) {
for (const std::unique_ptr<content::TestWebUI::CallData>& data :
base::Reversed(web_ui_.call_data())) {
if (data->function_name() != "cr.webUIListenerCallback" ||
!data->arg1()->is_string() ||
data->arg1()->GetString() != "secure-dns-setting-changed") {
continue;
}
const base::Value::Dict* dict = data->arg2()->GetIfDict();
if (!dict)
return false;
// Get the secure DNS mode.
const std::string* secure_dns_mode = dict->FindString("mode");
if (!secure_dns_mode)
return false;
*out_secure_dns_mode = *secure_dns_mode;
// Get the DoH config string.
const std::string* doh_config = dict->FindString("config");
if (!doh_config)
return false;
*out_doh_config = *doh_config;
// Get the forced management description.
std::optional<int> management_mode = dict->FindInt("managementMode");
if (!management_mode.has_value())
return false;
*out_management_mode = *management_mode;
return true;
}
return false;
}
#if BUILDFLAG(IS_CHROMEOS_ASH)
// Similar to `GetLastSettingsChangedMessage`, but only reads data related to
// template URIs with identifiers. Returns false if the message was invalid
// or not found; in this case the out params may be not set.
bool GetIdentifierConfigsFromLastSettingsChangedMessage(
bool* out_doh_with_identifiers_active,
std::string* out_doh_config_for_display) {
for (const std::unique_ptr<content::TestWebUI::CallData>& data :
base::Reversed(web_ui_.call_data())) {
if (data->function_name() != "cr.webUIListenerCallback" ||
!data->arg1()->is_string() ||
data->arg1()->GetString() != "secure-dns-setting-changed") {
continue;
}
const base::Value::Dict* dict = data->arg2()->GetIfDict();
if (!dict)
return false;
std::optional<bool> doh_with_identifiers_active =
dict->FindBool("dohWithIdentifiersActive");
if (!doh_with_identifiers_active)
return false;
*out_doh_with_identifiers_active = *doh_with_identifiers_active;
const std::string* doh_config_for_display =
dict->FindString("configForDisplay");
if (!doh_config_for_display)
return false;
*out_doh_config_for_display = *doh_config_for_display;
return true;
}
return false;
}
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
// Sets a policy update which will cause power pref managed change.
void SetPolicyForPolicyKey(policy::PolicyMap* policy_map,
const std::string& policy_key,
base::Value value) {
policy_map->Set(policy_key, policy::POLICY_LEVEL_MANDATORY,
policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_CLOUD,
std::move(value), nullptr);
provider_.UpdateChromePolicy(*policy_map);
base::RunLoop().RunUntilIdle();
}
std::unique_ptr<TestSecureDnsHandler> handler_;
content::TestWebUI web_ui_;
testing::NiceMock<policy::MockConfigurationPolicyProvider> provider_;
private:
#if BUILDFLAG(IS_WIN)
base::win::ScopedDomainStateForTesting scoped_domain_;
#endif
};
IN_PROC_BROWSER_TEST_F(SecureDnsHandlerTest, SecureDnsModes) {
PrefService* local_state = g_browser_process->local_state();
std::string secure_dns_mode;
std::string doh_config;
int management_mode;
local_state->SetString(prefs::kDnsOverHttpsMode, SecureDnsConfig::kModeOff);
EXPECT_TRUE(GetLastSettingsChangedMessage(&secure_dns_mode, &doh_config,
&management_mode));
EXPECT_EQ(SecureDnsConfig::kModeOff, secure_dns_mode);
local_state->SetString(prefs::kDnsOverHttpsMode,
SecureDnsConfig::kModeAutomatic);
EXPECT_TRUE(GetLastSettingsChangedMessage(&secure_dns_mode, &doh_config,
&management_mode));
EXPECT_EQ(SecureDnsConfig::kModeAutomatic, secure_dns_mode);
local_state->SetString(prefs::kDnsOverHttpsMode,
SecureDnsConfig::kModeSecure);
EXPECT_TRUE(GetLastSettingsChangedMessage(&secure_dns_mode, &doh_config,
&management_mode));
EXPECT_EQ(SecureDnsConfig::kModeSecure, secure_dns_mode);
local_state->SetString(prefs::kDnsOverHttpsMode, "unknown");
EXPECT_TRUE(GetLastSettingsChangedMessage(&secure_dns_mode, &doh_config,
&management_mode));
EXPECT_EQ(SecureDnsConfig::kModeOff, secure_dns_mode);
}
IN_PROC_BROWSER_TEST_F(SecureDnsHandlerTest, SecureDnsPolicy) {
policy::PolicyMap policy_map;
SetPolicyForPolicyKey(&policy_map, policy::key::kDnsOverHttpsMode,
base::Value(SecureDnsConfig::kModeAutomatic));
PrefService* local_state = g_browser_process->local_state();
local_state->SetString(prefs::kDnsOverHttpsMode,
SecureDnsConfig::kModeSecure);
std::string secure_dns_mode;
std::string doh_config;
int management_mode;
EXPECT_TRUE(GetLastSettingsChangedMessage(&secure_dns_mode, &doh_config,
&management_mode));
EXPECT_EQ(SecureDnsConfig::kModeAutomatic, secure_dns_mode);
EXPECT_EQ(static_cast<int>(SecureDnsConfig::ManagementMode::kNoOverride),
management_mode);
}
IN_PROC_BROWSER_TEST_F(SecureDnsHandlerTest, SecureDnsPolicyChange) {
policy::PolicyMap policy_map;
SetPolicyForPolicyKey(&policy_map, policy::key::kDnsOverHttpsMode,
base::Value(SecureDnsConfig::kModeAutomatic));
std::string secure_dns_mode;
std::string doh_config;
int management_mode;
EXPECT_TRUE(GetLastSettingsChangedMessage(&secure_dns_mode, &doh_config,
&management_mode));
EXPECT_EQ(SecureDnsConfig::kModeAutomatic, secure_dns_mode);
EXPECT_EQ(static_cast<int>(SecureDnsConfig::ManagementMode::kNoOverride),
management_mode);
SetPolicyForPolicyKey(&policy_map, policy::key::kDnsOverHttpsMode,
base::Value(SecureDnsConfig::kModeOff));
EXPECT_TRUE(GetLastSettingsChangedMessage(&secure_dns_mode, &doh_config,
&management_mode));
EXPECT_EQ(SecureDnsConfig::kModeOff, secure_dns_mode);
EXPECT_EQ(static_cast<int>(SecureDnsConfig::ManagementMode::kNoOverride),
management_mode);
}
// On platforms where enterprise policies do not have default values, test
// that DoH is disabled when non-DoH policies are set.
#if !BUILDFLAG(IS_CHROMEOS)
IN_PROC_BROWSER_TEST_F(SecureDnsHandlerTest, OtherPoliciesSet) {
policy::PolicyMap policy_map;
SetPolicyForPolicyKey(&policy_map, policy::key::kIncognitoModeAvailability,
base::Value(1));
PrefService* local_state = g_browser_process->local_state();
local_state->SetString(prefs::kDnsOverHttpsMode,
SecureDnsConfig::kModeSecure);
std::string secure_dns_mode;
std::string doh_config;
int management_mode;
EXPECT_TRUE(GetLastSettingsChangedMessage(&secure_dns_mode, &doh_config,
&management_mode));
EXPECT_EQ(SecureDnsConfig::kModeOff, secure_dns_mode);
EXPECT_EQ(static_cast<int>(SecureDnsConfig::ManagementMode::kDisabledManaged),
management_mode);
}
#endif
// This test makes no assumptions about the country or underlying resolver list.
IN_PROC_BROWSER_TEST_F(SecureDnsHandlerTest, DropdownList) {
base::Value::List args;
args.Append(kWebUiFunctionName);
web_ui_.HandleReceivedMessage(kGetSecureDnsResolverList, args);
const content::TestWebUI::CallData& call_data = *web_ui_.call_data().back();
EXPECT_EQ("cr.webUIResponse", call_data.function_name());
EXPECT_EQ(kWebUiFunctionName, call_data.arg1()->GetString());
ASSERT_TRUE(call_data.arg2()->GetBool());
// Check results (no providers set for testing).
const base::Value::List& resolver_list = call_data.arg3()->GetList();
ASSERT_GE(resolver_list.size(), 0U);
}
IN_PROC_BROWSER_TEST_F(SecureDnsHandlerTest, DropdownListContents) {
const auto entries = GetDohProviderListForTesting();
handler_->SetProvidersForTesting(entries);
const base::Value::List resolver_list = handler_->GetSecureDnsResolverList();
EXPECT_EQ(entries.size(), resolver_list.size());
for (const net::DohProviderEntry* entry : entries) {
EXPECT_TRUE(FindDropdownItem(resolver_list, entry->ui_name,
entry->doh_server_config.server_template(),
entry->privacy_policy));
}
}
IN_PROC_BROWSER_TEST_F(SecureDnsHandlerTest, SecureDnsTemplates) {
#if BUILDFLAG(IS_CHROMEOS)
const std::string kDnsOverHttpsTemplatesPrefName =
prefs::kDnsOverHttpsEffectiveTemplatesChromeOS;
#else
const std::string kDnsOverHttpsTemplatesPrefName =
prefs::kDnsOverHttpsTemplates;
#endif
std::string good_post_template = "https://foo.test/";
std::string good_get_template = "https://bar.test/dns-query{?dns}";
std::string bad_template = "dns-query{?dns}";
std::string secure_dns_mode;
std::string doh_config;
int management_mode;
PrefService* local_state = g_browser_process->local_state();
local_state->SetString(prefs::kDnsOverHttpsMode,
SecureDnsConfig::kModeAutomatic);
local_state->SetString(kDnsOverHttpsTemplatesPrefName, good_post_template);
EXPECT_TRUE(GetLastSettingsChangedMessage(&secure_dns_mode, &doh_config,
&management_mode));
EXPECT_EQ(good_post_template, doh_config);
std::string two_templates = good_post_template + "\n" + good_get_template;
local_state->SetString(kDnsOverHttpsTemplatesPrefName, two_templates);
EXPECT_TRUE(GetLastSettingsChangedMessage(&secure_dns_mode, &doh_config,
&management_mode));
EXPECT_EQ(two_templates, doh_config);
local_state->SetString(kDnsOverHttpsTemplatesPrefName, bad_template);
EXPECT_TRUE(GetLastSettingsChangedMessage(&secure_dns_mode, &doh_config,
&management_mode));
EXPECT_THAT(doh_config, IsEmpty());
local_state->SetString(kDnsOverHttpsTemplatesPrefName,
bad_template + " " + good_post_template);
EXPECT_TRUE(GetLastSettingsChangedMessage(&secure_dns_mode, &doh_config,
&management_mode));
EXPECT_EQ(good_post_template, doh_config);
}
#if BUILDFLAG(IS_CHROMEOS_ASH)
IN_PROC_BROWSER_TEST_F(SecureDnsHandlerTest,
SecureDnsTemplatesWithIdentifiers) {
std::string templatesWithIdentifier =
"https://foo.test-${USER_EMAIL}/dns-query{?dns}";
std::string templatesWithIdentifierDisplay =
"https://foo.test-${testuser@managed.com}/dns-query{?dns}";
std::string templatesWithIdentifierEffective =
"https://"
"foo.test-"
"FD5DFBED0D7B875A6416AFC61A37DBB63B6BA05B627AE9F5BE463A1F858F2D4E/"
"dns-query{?dns}";
std::string templates = "https://bar.test/dns-query{?dns}";
PrefService* local_state = g_browser_process->local_state();
// SecureDnsManager does the identifier placeholder replacement for the
// template URI and maps the final value to the
// prefs::kDnsOverHttpsEffectiveTemplatesChromeOS local state pref.
std::unique_ptr<ash::SecureDnsManager> secure_dns_manager =
std::make_unique<ash::SecureDnsManager>(local_state);
// Create an affiliated user.
auto user_manager = std::make_unique<ash::FakeChromeUserManager>();
const AccountId account_id0(AccountId::FromUserEmail("testuser@managed.com"));
user_manager->AddUserWithAffiliationAndTypeAndProfile(
account_id0, /* is_affiliated=*/true, user_manager::UserType::kRegular,
nullptr);
user_manager::ScopedUserManager user_manager_enabler(std::move(user_manager));
std::string secure_dns_mode;
std::string doh_config, doh_config_for_display;
bool doh_with_identifiers_active;
int management_mode;
local_state->SetString(prefs::kDnsOverHttpsMode,
SecureDnsConfig::kModeSecure);
local_state->SetString(prefs::kDnsOverHttpsTemplatesWithIdentifiers,
templatesWithIdentifier);
local_state->SetString(prefs::kDnsOverHttpsSalt, "salt-for-test");
local_state->SetString(prefs::kDnsOverHttpsTemplates, templates);
EXPECT_TRUE(GetLastSettingsChangedMessage(&secure_dns_mode, &doh_config,
&management_mode));
EXPECT_TRUE(GetIdentifierConfigsFromLastSettingsChangedMessage(
&doh_with_identifiers_active, &doh_config_for_display));
EXPECT_EQ(templatesWithIdentifierEffective, doh_config);
EXPECT_EQ(templatesWithIdentifierDisplay, doh_config_for_display);
EXPECT_TRUE(doh_with_identifiers_active);
local_state->ClearPref(prefs::kDnsOverHttpsTemplatesWithIdentifiers);
EXPECT_TRUE(GetLastSettingsChangedMessage(&secure_dns_mode, &doh_config,
&management_mode));
EXPECT_TRUE(GetIdentifierConfigsFromLastSettingsChangedMessage(
&doh_with_identifiers_active, &doh_config_for_display));
EXPECT_EQ(templates, doh_config);
EXPECT_FALSE(doh_with_identifiers_active);
}
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
IN_PROC_BROWSER_TEST_F(SecureDnsHandlerTest, TemplateValid) {
base::Value::List args;
args.Append(kWebUiFunctionName);
args.Append("https://example.template/dns-query");
base::HistogramTester histograms;
web_ui_.HandleReceivedMessage(kIsValidConfig, args);
const content::TestWebUI::CallData& call_data = *web_ui_.call_data().back();
EXPECT_EQ("cr.webUIResponse", call_data.function_name());
EXPECT_EQ(kWebUiFunctionName, call_data.arg1()->GetString());
// The request should be successful.
ASSERT_TRUE(call_data.arg2()->GetBool());
// The template should be valid.
EXPECT_TRUE(call_data.arg3()->GetBool());
histograms.ExpectBucketCount("Net.DNS.UI.ValidationAttemptSuccess", false, 0);
histograms.ExpectBucketCount("Net.DNS.UI.ValidationAttemptSuccess", true, 1);
}
IN_PROC_BROWSER_TEST_F(SecureDnsHandlerTest, TemplateInvalid) {
base::Value::List args;
args.Append(kWebUiFunctionName);
args.Append("invalid_template");
base::HistogramTester histograms;
web_ui_.HandleReceivedMessage(kIsValidConfig, args);
const content::TestWebUI::CallData& call_data = *web_ui_.call_data().back();
EXPECT_EQ("cr.webUIResponse", call_data.function_name());
EXPECT_EQ(kWebUiFunctionName, call_data.arg1()->GetString());
// The request should be successful.
ASSERT_TRUE(call_data.arg2()->GetBool());
// The template should be invalid.
EXPECT_FALSE(call_data.arg3()->GetBool());
histograms.ExpectBucketCount("Net.DNS.UI.ValidationAttemptSuccess", false, 1);
histograms.ExpectBucketCount("Net.DNS.UI.ValidationAttemptSuccess", true, 0);
}
IN_PROC_BROWSER_TEST_F(SecureDnsHandlerTest, MultipleTemplates) {
base::HistogramTester histograms;
base::Value::List args_valid;
args_valid.Append(kWebUiFunctionName);
args_valid.Append(
"https://example1.template/dns https://example2.template/dns-query");
web_ui_.HandleReceivedMessage(kIsValidConfig, args_valid);
const content::TestWebUI::CallData& call_data_valid =
*web_ui_.call_data().back();
EXPECT_EQ("cr.webUIResponse", call_data_valid.function_name());
EXPECT_EQ(kWebUiFunctionName, call_data_valid.arg1()->GetString());
// The request should be successful.
ASSERT_TRUE(call_data_valid.arg2()->GetBool());
// Both templates should be valid.
EXPECT_TRUE(call_data_valid.arg3()->GetBool());
histograms.ExpectBucketCount("Net.DNS.UI.ValidationAttemptSuccess", false, 0);
histograms.ExpectBucketCount("Net.DNS.UI.ValidationAttemptSuccess", true, 1);
base::Value::List args_invalid;
args_invalid.Append(kWebUiFunctionName);
args_invalid.Append("invalid_template https://example.template/dns");
web_ui_.HandleReceivedMessage(kIsValidConfig, args_invalid);
const content::TestWebUI::CallData& call_data_invalid =
*web_ui_.call_data().back();
EXPECT_EQ("cr.webUIResponse", call_data_invalid.function_name());
EXPECT_EQ(kWebUiFunctionName, call_data_invalid.arg1()->GetString());
// The request should be successful.
ASSERT_TRUE(call_data_invalid.arg2()->GetBool());
// The entry should be invalid.
EXPECT_FALSE(call_data_invalid.arg3()->GetBool());
histograms.ExpectBucketCount("Net.DNS.UI.ValidationAttemptSuccess", false, 1);
histograms.ExpectBucketCount("Net.DNS.UI.ValidationAttemptSuccess", true, 1);
}
IN_PROC_BROWSER_TEST_F(SecureDnsHandlerTest, TemplateProbeSuccess) {
auto network_context_ =
std::make_unique<chrome_browser_net::FakeHostResolverNetworkContext>(
std::vector<chrome_browser_net::FakeHostResolver::SingleResult>(
{chrome_browser_net::FakeHostResolver::SingleResult(
net::OK, net::ResolveErrorInfo(net::OK),
chrome_browser_net::FakeHostResolver::
kOneAddressResponse)}) /* current_config_result_list */,
std::vector<chrome_browser_net::FakeHostResolver::
SingleResult>() /* google_config_result_list */);
handler_->SetNetworkContextForTesting(network_context_.get());
base::HistogramTester histograms;
base::Value::List args_valid;
args_valid.Append(kWebUiFunctionName);
args_valid.Append("https://example.template/dns-query https://example2/");
web_ui_.HandleReceivedMessage(kProbeConfig, args_valid);
base::RunLoop().RunUntilIdle();
const content::TestWebUI::CallData& call_data_valid =
*web_ui_.call_data().back();
EXPECT_EQ("cr.webUIResponse", call_data_valid.function_name());
EXPECT_EQ(kWebUiFunctionName, call_data_valid.arg1()->GetString());
// The request should be successful.
ASSERT_TRUE(call_data_valid.arg2()->GetBool());
// The probe query should have succeeded.
ASSERT_TRUE(call_data_valid.arg3()->GetBool());
histograms.ExpectBucketCount("Net.DNS.UI.ProbeAttemptSuccess", false, 0);
histograms.ExpectBucketCount("Net.DNS.UI.ProbeAttemptSuccess", true, 1);
}
IN_PROC_BROWSER_TEST_F(SecureDnsHandlerTest, TemplateProbeFailure) {
auto network_context_ =
std::make_unique<chrome_browser_net::FakeHostResolverNetworkContext>(
std::vector<chrome_browser_net::FakeHostResolver::SingleResult>(
{chrome_browser_net::FakeHostResolver::SingleResult(
net::ERR_NAME_NOT_RESOLVED,
net::ResolveErrorInfo(net::ERR_DNS_MALFORMED_RESPONSE),
chrome_browser_net::FakeHostResolver::
kNoResponse)}) /* current_config_result_list */,
std::vector<chrome_browser_net::FakeHostResolver::
SingleResult>() /* google_config_result_list */);
handler_->SetNetworkContextForTesting(network_context_.get());
base::HistogramTester histograms;
base::Value::List args_valid;
args_valid.Append(kWebUiFunctionName);
args_valid.Append("https://example.template/dns-query");
web_ui_.HandleReceivedMessage(kProbeConfig, args_valid);
base::RunLoop().RunUntilIdle();
const content::TestWebUI::CallData& call_data_valid =
*web_ui_.call_data().back();
EXPECT_EQ("cr.webUIResponse", call_data_valid.function_name());
EXPECT_EQ(kWebUiFunctionName, call_data_valid.arg1()->GetString());
// The request should be successful.
ASSERT_TRUE(call_data_valid.arg2()->GetBool());
// The probe query should have failed.
ASSERT_FALSE(call_data_valid.arg3()->GetBool());
histograms.ExpectBucketCount("Net.DNS.UI.ProbeAttemptSuccess", false, 1);
histograms.ExpectBucketCount("Net.DNS.UI.ProbeAttemptSuccess", true, 0);
}
IN_PROC_BROWSER_TEST_F(SecureDnsHandlerTest, TemplateProbeDebounce) {
auto network_context_hang =
std::make_unique<chrome_browser_net::HangingHostResolverNetworkContext>();
auto network_context_fail =
std::make_unique<chrome_browser_net::FakeHostResolverNetworkContext>(
std::vector<chrome_browser_net::FakeHostResolver::SingleResult>(
{chrome_browser_net::FakeHostResolver::SingleResult(
net::ERR_NAME_NOT_RESOLVED,
net::ResolveErrorInfo(net::ERR_DNS_MALFORMED_RESPONSE),
chrome_browser_net::FakeHostResolver::
kNoResponse)}) /* current_config_result_list */,
std::vector<chrome_browser_net::FakeHostResolver::
SingleResult>() /* google_config_result_list */);
base::HistogramTester histograms;
base::Value::List args_valid;
args_valid.Append(kWebUiFunctionName);
args_valid.Append("https://example.template/dns-query");
// Request a probe that will hang.
handler_->SetNetworkContextForTesting(network_context_hang.get());
web_ui_.HandleReceivedMessage(kProbeConfig, args_valid);
size_t responses = web_ui_.call_data().size();
base::RunLoop().RunUntilIdle();
// No response yet from the hanging probe.
EXPECT_EQ(responses, web_ui_.call_data().size());
// Request a probe that will fail.
handler_->SetNetworkContextForTesting(network_context_fail.get());
web_ui_.HandleReceivedMessage(kProbeConfig, args_valid);
// The hanging response should now have arrived.
EXPECT_EQ(responses + 1, web_ui_.call_data().size());
const content::TestWebUI::CallData& first_response =
*web_ui_.call_data().back();
ASSERT_EQ("cr.webUIResponse", first_response.function_name());
ASSERT_EQ(kWebUiFunctionName, first_response.arg1()->GetString());
// The cancelled probe should indicate no error.
ASSERT_TRUE(first_response.arg2()->GetBool());
EXPECT_TRUE(first_response.arg3()->GetBool());
// Wait for the second response.
base::RunLoop().RunUntilIdle();
EXPECT_EQ(responses + 2, web_ui_.call_data().size());
const content::TestWebUI::CallData& second_response =
*web_ui_.call_data().back();
ASSERT_EQ("cr.webUIResponse", second_response.function_name());
ASSERT_EQ(kWebUiFunctionName, second_response.arg1()->GetString());
// The second request should have resolved with a failure indication.
ASSERT_TRUE(second_response.arg2()->GetBool());
EXPECT_FALSE(second_response.arg3()->GetBool());
// Only the second response should be counted in the histograms.
histograms.ExpectBucketCount("Net.DNS.UI.ProbeAttemptSuccess", false, 1);
histograms.ExpectBucketCount("Net.DNS.UI.ProbeAttemptSuccess", true, 0);
}
} // namespace settings