Commit 4e55899c authored by dalyk's avatar dalyk Committed by Commit Bot

Send test query for secure DNS setting custom text field entries.

This cl adds a new error message when the custom text field entry
passes the formatting check but a test DNS query fails.

Bug: 1040145
Change-Id: If25737013e942814d1f7bf0a0ac5d5c72c72e757
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2082357Reviewed-by: default avatarEric Orth <ericorth@chromium.org>
Reviewed-by: default avatarEsmael Elmoslimany <aee@chromium.org>
Commit-Queue: Katharine Daly <dalyk@google.com>
Cr-Commit-Position: refs/heads/master@{#748985}
parent 5da27796
...@@ -1748,10 +1748,10 @@ ...@@ -1748,10 +1748,10 @@
See this provider's <ph name="BEGIN_LINK">&lt;a target="_blank" href="$1<ex>https://google.com/</ex>"&gt;</ph>privacy policy<ph name="END_LINK">&lt;/a&gt;</ph> See this provider's <ph name="BEGIN_LINK">&lt;a target="_blank" href="$1<ex>https://google.com/</ex>"&gt;</ph>privacy policy<ph name="END_LINK">&lt;/a&gt;</ph>
</message> </message>
<message name="IDS_SETTINGS_SECURE_DNS_DISABLED_FOR_MANAGED_ENVIRONMENT" desc="Substring of the secure DNS setting when secure DNS is disabled due to detection of a managed environment"> <message name="IDS_SETTINGS_SECURE_DNS_DISABLED_FOR_MANAGED_ENVIRONMENT" desc="Substring of the secure DNS setting when secure DNS is disabled due to detection of a managed environment">
This setting is disabled on managed browsers. This setting is disabled on managed browsers
</message> </message>
<message name="IDS_SETTINGS_SECURE_DNS_DISABLED_FOR_PARENTAL_CONTROL" desc="Substring of the secure DNS setting when secure DNS is disabled due to detection of OS-level parental controls"> <message name="IDS_SETTINGS_SECURE_DNS_DISABLED_FOR_PARENTAL_CONTROL" desc="Substring of the secure DNS setting when secure DNS is disabled due to detection of OS-level parental controls">
This setting is disabled because parental controls are on. This setting is disabled because parental controls are on
</message> </message>
<message name="IDS_SETTINGS_SECURE_DNS_CUSTOM_PLACEHOLDER" desc="Placeholder text for a textbox where users can enter a custom secure DNS provider"> <message name="IDS_SETTINGS_SECURE_DNS_CUSTOM_PLACEHOLDER" desc="Placeholder text for a textbox where users can enter a custom secure DNS provider">
Enter custom provider Enter custom provider
...@@ -1759,6 +1759,9 @@ ...@@ -1759,6 +1759,9 @@
<message name="IDS_SETTINGS_SECURE_DNS_CUSTOM_FORMAT_ERROR" desc="Error text for an incorrectly formatted entry for the custom secure DNS provider"> <message name="IDS_SETTINGS_SECURE_DNS_CUSTOM_FORMAT_ERROR" desc="Error text for an incorrectly formatted entry for the custom secure DNS provider">
Enter a correctly formatted URL Enter a correctly formatted URL
</message> </message>
<message name="IDS_SETTINGS_SECURE_DNS_CUSTOM_CONNECTION_ERROR" desc="Error text for a custom secure DNS provider entry to which a probe connection fails">
Please verify that this is a valid provider or try again later
</message>
<message name="IDS_SETTINGS_CONTENT_SETTINGS" desc="Text of the button that takes a user to settings page thats allows users to modify site settings. Also the title of that settings page."> <message name="IDS_SETTINGS_CONTENT_SETTINGS" desc="Text of the button that takes a user to settings page thats allows users to modify site settings. Also the title of that settings page.">
Content settings Content settings
</message> </message>
......
...@@ -69,11 +69,18 @@ cr.define('settings', function() { ...@@ -69,11 +69,18 @@ cr.define('settings', function() {
getSecureDnsSetting() {} getSecureDnsSetting() {}
/** /**
* Determines whether the entry contains at least one valid URL template. * Returns the first valid URL template, if any.
* @param {string} entry * @param {string} entry
* @return {!Promise<boolean>} * @return {!Promise<string>}
*/ */
validateCustomDnsEntry(entry) {} validateCustomDnsEntry(entry) {}
/**
* Returns whether a test query to the secure DNS template succeeded.
* @param {string} template
* @return {!Promise<boolean>}
*/
probeCustomDnsTemplate(template) {}
} }
/** /**
...@@ -119,6 +126,11 @@ cr.define('settings', function() { ...@@ -119,6 +126,11 @@ cr.define('settings', function() {
validateCustomDnsEntry(entry) { validateCustomDnsEntry(entry) {
return cr.sendWithPromise('validateCustomDnsEntry', entry); return cr.sendWithPromise('validateCustomDnsEntry', entry);
} }
/** @override */
probeCustomDnsTemplate(template) {
return cr.sendWithPromise('probeCustomDnsTemplate', template);
}
} }
cr.addSingletonGetter(PrivacyPageBrowserProxyImpl); cr.addSingletonGetter(PrivacyPageBrowserProxyImpl);
......
...@@ -49,20 +49,31 @@ Polymer({ ...@@ -49,20 +49,31 @@ Polymer({
/** /**
* When the custom input field loses focus, validate the current value and * When the custom input field loses focus, validate the current value and
* trigger an event with the result. Show an error message if the validated * trigger an event with the result. If the value is valid, also attempt a
* value is still the most recent value, is invalid, and is non-empty. * test query. Show an error message if the tested value is still the most
* recent value, is non-empty, and was either invalid or failed the test
* query.
* @private
*/ */
validate: function() { validate: async function() {
this.showError_ = false;
const valueToValidate = this.value; const valueToValidate = this.value;
this.browserProxy_.validateCustomDnsEntry(valueToValidate).then(valid => { const validTemplate =
this.showError_ = await this.browserProxy_.validateCustomDnsEntry(valueToValidate);
valueToValidate === this.value && !valid && valueToValidate !== ''; const successfulProbe = validTemplate &&
if (this.showError_) { await this.browserProxy_.probeCustomDnsTemplate(validTemplate);
this.errorText_ = loadTimeData.getString('secureDnsCustomFormatError'); // If there was no valid template or a valid template doesn't successfully
// answer a probe query, show an error as long as the input field value
// hasn't changed and is non-empty.
if (valueToValidate === this.value && this.value !== '' &&
!successfulProbe) {
this.errorText_ = loadTimeData.getString(
validTemplate ? 'secureDnsCustomConnectionError' :
'secureDnsCustomFormatError');
this.showError_ = true;
} }
this.fire(
this.fire('value-update', {isValid: valid, text: valueToValidate}); 'value-update', {isValid: !!validTemplate, text: valueToValidate});
});
}, },
/** /**
......
...@@ -1094,6 +1094,8 @@ void AddPrivacyStrings(content::WebUIDataSource* html_source, ...@@ -1094,6 +1094,8 @@ void AddPrivacyStrings(content::WebUIDataSource* html_source,
IDS_SETTINGS_SECURE_DNS_CUSTOM_PLACEHOLDER}, IDS_SETTINGS_SECURE_DNS_CUSTOM_PLACEHOLDER},
{"secureDnsCustomFormatError", {"secureDnsCustomFormatError",
IDS_SETTINGS_SECURE_DNS_CUSTOM_FORMAT_ERROR}, IDS_SETTINGS_SECURE_DNS_CUSTOM_FORMAT_ERROR},
{"secureDnsCustomConnectionError",
IDS_SETTINGS_SECURE_DNS_CUSTOM_CONNECTION_ERROR},
{"contentSettings", IDS_SETTINGS_CONTENT_SETTINGS}, {"contentSettings", IDS_SETTINGS_CONTENT_SETTINGS},
{"siteSettings", IDS_SETTINGS_SITE_SETTINGS}, {"siteSettings", IDS_SETTINGS_SITE_SETTINGS},
{"siteSettingsDescription", IDS_SETTINGS_SITE_SETTINGS_DESCRIPTION}, {"siteSettingsDescription", IDS_SETTINGS_SITE_SETTINGS_DESCRIPTION},
......
...@@ -16,6 +16,9 @@ ...@@ -16,6 +16,9 @@
#include "chrome/common/pref_names.h" #include "chrome/common/pref_names.h"
#include "chrome/grit/generated_resources.h" #include "chrome/grit/generated_resources.h"
#include "components/country_codes/country_codes.h" #include "components/country_codes/country_codes.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui.h" #include "content/public/browser/web_ui.h"
#include "net/dns/public/doh_provider_list.h" #include "net/dns/public/doh_provider_list.h"
#include "net/dns/public/util.h" #include "net/dns/public/util.h"
...@@ -27,6 +30,8 @@ namespace settings { ...@@ -27,6 +30,8 @@ namespace settings {
namespace { namespace {
const char kProbeHostname[] = "google.com";
std::unique_ptr<base::DictionaryValue> CreateSecureDnsSettingDict() { std::unique_ptr<base::DictionaryValue> CreateSecureDnsSettingDict() {
// Fetch the current host resolver configuration. It is not sufficient to read // Fetch the current host resolver configuration. It is not sufficient to read
// the secure DNS prefs directly since the host resolver configuration takes // the secure DNS prefs directly since the host resolver configuration takes
...@@ -91,6 +96,11 @@ void SecureDnsHandler::RegisterMessages() { ...@@ -91,6 +96,11 @@ void SecureDnsHandler::RegisterMessages() {
"validateCustomDnsEntry", "validateCustomDnsEntry",
base::BindRepeating(&SecureDnsHandler::HandleValidateCustomDnsEntry, base::BindRepeating(&SecureDnsHandler::HandleValidateCustomDnsEntry,
base::Unretained(this))); base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"probeCustomDnsTemplate",
base::BindRepeating(&SecureDnsHandler::HandleProbeCustomDnsTemplate,
base::Unretained(this)));
} }
void SecureDnsHandler::OnJavascriptAllowed() { void SecureDnsHandler::OnJavascriptAllowed() {
...@@ -156,6 +166,11 @@ base::Value SecureDnsHandler::GetSecureDnsResolverListForCountry( ...@@ -156,6 +166,11 @@ base::Value SecureDnsHandler::GetSecureDnsResolverListForCountry(
return resolvers; return resolvers;
} }
void SecureDnsHandler::SetNetworkContextForTesting(
network::mojom::NetworkContext* network_context) {
network_context_for_testing_ = network_context;
}
void SecureDnsHandler::HandleGetSecureDnsResolverList( void SecureDnsHandler::HandleGetSecureDnsResolverList(
const base::ListValue* args) { const base::ListValue* args) {
AllowJavascript(); AllowJavascript();
...@@ -188,14 +203,75 @@ void SecureDnsHandler::HandleValidateCustomDnsEntry( ...@@ -188,14 +203,75 @@ void SecureDnsHandler::HandleValidateCustomDnsEntry(
SplitString(server_templates, " ", base::TRIM_WHITESPACE, SplitString(server_templates, " ", base::TRIM_WHITESPACE,
base::SPLIT_WANT_NONEMPTY)) { base::SPLIT_WANT_NONEMPTY)) {
if (net::dns_util::IsValidDohTemplate(server_template, &server_method)) { if (net::dns_util::IsValidDohTemplate(server_template, &server_method)) {
ResolveJavascriptCallback(*callback_id, base::Value(true)); ResolveJavascriptCallback(*callback_id, base::Value(server_template));
return; return;
} }
} }
ResolveJavascriptCallback(*callback_id, base::Value(false)); ResolveJavascriptCallback(*callback_id, base::Value(std::string()));
return; return;
} }
void SecureDnsHandler::HandleProbeCustomDnsTemplate(
const base::ListValue* args) {
AllowJavascript();
receiver_.reset();
host_resolver_.reset();
std::string server_template;
CHECK(args->GetString(0, &probe_callback_id_));
CHECK(args->GetString(1, &server_template));
net::DnsConfigOverrides overrides;
overrides.search = std::vector<std::string>();
overrides.attempts = 1;
overrides.randomize_ports = false;
overrides.secure_dns_mode = net::DnsConfig::SecureDnsMode::SECURE;
std::string server_method;
// We only send probe queries to templates that have already passed a format
// validation check.
CHECK(net::dns_util::IsValidDohTemplate(server_template, &server_method));
overrides.dns_over_https_servers.emplace(
{net::DnsConfig::DnsOverHttpsServerConfig(server_template,
server_method == "POST")});
auto* network_context =
network_context_for_testing_
? network_context_for_testing_
: content::BrowserContext::GetDefaultStoragePartition(
web_ui()->GetWebContents()->GetBrowserContext())
->GetNetworkContext();
network_context->CreateHostResolver(
overrides, host_resolver_.BindNewPipeAndPassReceiver());
network::mojom::ResolveHostParametersPtr parameters =
network::mojom::ResolveHostParameters::New();
parameters->dns_query_type = net::DnsQueryType::A;
parameters->source = net::HostResolverSource::DNS;
parameters->cache_usage =
network::mojom::ResolveHostParameters::CacheUsage::DISALLOWED;
host_resolver_->ResolveHost(net::HostPortPair(kProbeHostname, 80),
net::NetworkIsolationKey::CreateTransient(),
std::move(parameters),
receiver_.BindNewPipeAndPassRemote());
receiver_.set_disconnect_handler(base::BindOnce(
&SecureDnsHandler::OnMojoConnectionError, base::Unretained(this)));
}
// network::ResolveHostClientBase impl:
void SecureDnsHandler::OnComplete(
int result,
const net::ResolveErrorInfo& resolve_error_info,
const base::Optional<net::AddressList>& resolved_addresses) {
receiver_.reset();
host_resolver_.reset();
ResolveJavascriptCallback(base::Value(probe_callback_id_),
base::Value(result == 0));
}
void SecureDnsHandler::OnMojoConnectionError() {
OnComplete(net::ERR_NAME_NOT_RESOLVED, net::ResolveErrorInfo(net::ERR_FAILED),
base::nullopt);
}
void SecureDnsHandler::SendSecureDnsSettingUpdatesToJavascript() { void SecureDnsHandler::SendSecureDnsSettingUpdatesToJavascript() {
FireWebUIListener("secure-dns-setting-changed", FireWebUIListener("secure-dns-setting-changed",
*CreateSecureDnsSettingDict()); *CreateSecureDnsSettingDict());
......
...@@ -9,12 +9,18 @@ ...@@ -9,12 +9,18 @@
#include "base/values.h" #include "base/values.h"
#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h" #include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
#include "components/prefs/pref_change_registrar.h" #include "components/prefs/pref_change_registrar.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "net/dns/public/doh_provider_list.h" #include "net/dns/public/doh_provider_list.h"
#include "services/network/public/cpp/resolve_host_client_base.h"
#include "services/network/public/mojom/host_resolver.mojom.h"
#include "services/network/public/mojom/network_context.mojom.h"
namespace settings { namespace settings {
// Handler for the Secure DNS setting. // Handler for the Secure DNS setting.
class SecureDnsHandler : public SettingsPageUIHandler { class SecureDnsHandler : public SettingsPageUIHandler,
network::ResolveHostClientBase {
public: public:
SecureDnsHandler(); SecureDnsHandler();
~SecureDnsHandler() override; ~SecureDnsHandler() override;
...@@ -32,6 +38,9 @@ class SecureDnsHandler : public SettingsPageUIHandler { ...@@ -32,6 +38,9 @@ class SecureDnsHandler : public SettingsPageUIHandler {
int country_id, int country_id,
const std::vector<net::DohProviderEntry>& providers); const std::vector<net::DohProviderEntry>& providers);
void SetNetworkContextForTesting(
network::mojom::NetworkContext* network_context);
protected: protected:
// Retrieves all pre-approved secure resolvers and returns them to WebUI. // Retrieves all pre-approved secure resolvers and returns them to WebUI.
void HandleGetSecureDnsResolverList(const base::ListValue* args); void HandleGetSecureDnsResolverList(const base::ListValue* args);
...@@ -42,11 +51,26 @@ class SecureDnsHandler : public SettingsPageUIHandler { ...@@ -42,11 +51,26 @@ class SecureDnsHandler : public SettingsPageUIHandler {
// Returns whether or not a custom entry is valid. // Returns whether or not a custom entry is valid.
void HandleValidateCustomDnsEntry(const base::ListValue* args); void HandleValidateCustomDnsEntry(const base::ListValue* args);
// Returns whether or not a test query to the resolver succeeds.
void HandleProbeCustomDnsTemplate(const base::ListValue* args);
// Retrieves the current host resolver configuration, computes the // Retrieves the current host resolver configuration, computes the
// corresponding UI representation, and sends it to javascript. // corresponding UI representation, and sends it to javascript.
void SendSecureDnsSettingUpdatesToJavascript(); void SendSecureDnsSettingUpdatesToJavascript();
private: private:
// network::ResolveHostClientBase impl:
void OnComplete(
int result,
const net::ResolveErrorInfo& resolve_error_info,
const base::Optional<net::AddressList>& resolved_addresses) override;
void OnMojoConnectionError();
network::mojom::NetworkContext* network_context_for_testing_ = nullptr;
mojo::Receiver<network::mojom::ResolveHostClient> receiver_{this};
mojo::Remote<network::mojom::HostResolver> host_resolver_;
std::string probe_callback_id_;
PrefChangeRegistrar pref_registrar_; PrefChangeRegistrar pref_registrar_;
DISALLOW_COPY_AND_ASSIGN(SecureDnsHandler); DISALLOW_COPY_AND_ASSIGN(SecureDnsHandler);
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include "build/build_config.h" #include "build/build_config.h"
#include "chrome/browser/browser_process.h" #include "chrome/browser/browser_process.h"
#include "chrome/browser/net/dns_probe_test_util.h"
#include "chrome/browser/net/dns_util.h" #include "chrome/browser/net/dns_util.h"
#include "chrome/common/chrome_features.h" #include "chrome/common/chrome_features.h"
#include "chrome/common/pref_names.h" #include "chrome/common/pref_names.h"
...@@ -16,6 +17,7 @@ ...@@ -16,6 +17,7 @@
#include "components/policy/policy_constants.h" #include "components/policy/policy_constants.h"
#include "components/prefs/pref_service.h" #include "components/prefs/pref_service.h"
#include "content/public/test/test_web_ui.h" #include "content/public/test/test_web_ui.h"
#include "net/dns/public/resolve_error_info.h"
#include "testing/gmock/include/gmock/gmock.h" #include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
...@@ -33,6 +35,7 @@ namespace { ...@@ -33,6 +35,7 @@ namespace {
constexpr char kGetSecureDnsResolverList[] = "getSecureDnsResolverList"; constexpr char kGetSecureDnsResolverList[] = "getSecureDnsResolverList";
constexpr char kValidateCustomDnsEntry[] = "validateCustomDnsEntry"; constexpr char kValidateCustomDnsEntry[] = "validateCustomDnsEntry";
constexpr char kProbeCustomDnsTemplate[] = "probeCustomDnsTemplate";
constexpr char kWebUiFunctionName[] = "webUiCallbackName"; constexpr char kWebUiFunctionName[] = "webUiCallbackName";
const std::vector<DohProviderEntry>& GetDohProviderListForTesting() { const std::vector<DohProviderEntry>& GetDohProviderListForTesting() {
...@@ -454,7 +457,8 @@ IN_PROC_BROWSER_TEST_F(SecureDnsHandlerTest, TemplateValid) { ...@@ -454,7 +457,8 @@ IN_PROC_BROWSER_TEST_F(SecureDnsHandlerTest, TemplateValid) {
// The request should be successful. // The request should be successful.
ASSERT_TRUE(call_data.arg2()->GetBool()); ASSERT_TRUE(call_data.arg2()->GetBool());
// The template should be valid. // The template should be valid.
ASSERT_TRUE(call_data.arg3()->GetBool()); ASSERT_EQ("https://example.template/dns-query",
call_data.arg3()->GetString());
} }
IN_PROC_BROWSER_TEST_F(SecureDnsHandlerTest, TemplateInvalid) { IN_PROC_BROWSER_TEST_F(SecureDnsHandlerTest, TemplateInvalid) {
...@@ -469,7 +473,7 @@ IN_PROC_BROWSER_TEST_F(SecureDnsHandlerTest, TemplateInvalid) { ...@@ -469,7 +473,7 @@ IN_PROC_BROWSER_TEST_F(SecureDnsHandlerTest, TemplateInvalid) {
// The request should be successful. // The request should be successful.
ASSERT_TRUE(call_data.arg2()->GetBool()); ASSERT_TRUE(call_data.arg2()->GetBool());
// The template should be invalid. // The template should be invalid.
ASSERT_FALSE(call_data.arg3()->GetBool()); ASSERT_EQ(std::string(), call_data.arg3()->GetString());
} }
IN_PROC_BROWSER_TEST_F(SecureDnsHandlerTest, MultipleTemplates) { IN_PROC_BROWSER_TEST_F(SecureDnsHandlerTest, MultipleTemplates) {
...@@ -484,8 +488,9 @@ IN_PROC_BROWSER_TEST_F(SecureDnsHandlerTest, MultipleTemplates) { ...@@ -484,8 +488,9 @@ IN_PROC_BROWSER_TEST_F(SecureDnsHandlerTest, MultipleTemplates) {
EXPECT_EQ(kWebUiFunctionName, call_data_valid.arg1()->GetString()); EXPECT_EQ(kWebUiFunctionName, call_data_valid.arg1()->GetString());
// The request should be successful. // The request should be successful.
ASSERT_TRUE(call_data_valid.arg2()->GetBool()); ASSERT_TRUE(call_data_valid.arg2()->GetBool());
// The entry should be valid. // The second template should be valid.
ASSERT_TRUE(call_data_valid.arg3()->GetBool()); ASSERT_EQ("https://example.template/dns-query",
call_data_valid.arg3()->GetString());
base::ListValue args_invalid; base::ListValue args_invalid;
args_invalid.AppendString(kWebUiFunctionName); args_invalid.AppendString(kWebUiFunctionName);
...@@ -498,7 +503,62 @@ IN_PROC_BROWSER_TEST_F(SecureDnsHandlerTest, MultipleTemplates) { ...@@ -498,7 +503,62 @@ IN_PROC_BROWSER_TEST_F(SecureDnsHandlerTest, MultipleTemplates) {
// The request should be successful. // The request should be successful.
ASSERT_TRUE(call_data_invalid.arg2()->GetBool()); ASSERT_TRUE(call_data_invalid.arg2()->GetBool());
// The entry should be invalid. // The entry should be invalid.
ASSERT_FALSE(call_data_invalid.arg3()->GetBool()); ASSERT_EQ(std::string(), call_data_invalid.arg3()->GetString());
}
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::ListValue args_valid;
args_valid.AppendString(kWebUiFunctionName);
args_valid.AppendString("https://example.template/dns-query");
web_ui_.HandleReceivedMessage(kProbeCustomDnsTemplate, &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());
}
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::ListValue args_valid;
args_valid.AppendString(kWebUiFunctionName);
args_valid.AppendString("https://example.template/dns-query");
web_ui_.HandleReceivedMessage(kProbeCustomDnsTemplate, &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());
} }
} // namespace settings } // namespace settings
...@@ -65,6 +65,9 @@ suite('SettingsSecureDnsInteractive', function() { ...@@ -65,6 +65,9 @@ suite('SettingsSecureDnsInteractive', function() {
}, },
]; ];
const invalidEntry = 'invalid_template';
const validEntry = 'https://example.doh.server/dns-query';
suiteSetup(function() { suiteSetup(function() {
loadTimeData.overrideValues({showSecureDnsSetting: true}); loadTimeData.overrideValues({showSecureDnsSetting: true});
}); });
...@@ -127,10 +130,13 @@ suite('SettingsSecureDnsInteractive', function() { ...@@ -127,10 +130,13 @@ suite('SettingsSecureDnsInteractive', function() {
// click outside the text field. The mode pref should be updated to // click outside the text field. The mode pref should be updated to
// 'secure'. // 'secure'.
secureDnsInput.focus(); secureDnsInput.focus();
secureDnsInput.value = 'https://example.doh.server/dns-query'; secureDnsInput.value = validEntry;
testBrowserProxy.setIsEntryValid(true); testBrowserProxy.setValidEntry(validEntry);
secureDnsInput.blur(); secureDnsInput.blur();
await testBrowserProxy.whenCalled('validateCustomDnsEntry'); await Promise.all([
testBrowserProxy.whenCalled('validateCustomDnsEntry'),
testBrowserProxy.whenCalled('probeCustomDnsTemplate')
]);
assertEquals( assertEquals(
settings.SecureDnsMode.SECURE, settings.SecureDnsMode.SECURE,
testElement.prefs.dns_over_https.mode.value); testElement.prefs.dns_over_https.mode.value);
...@@ -146,12 +152,15 @@ suite('SettingsSecureDnsInteractive', function() { ...@@ -146,12 +152,15 @@ suite('SettingsSecureDnsInteractive', function() {
secureDnsToggle.click(); secureDnsToggle.click();
assertEquals(settings.SecureDnsMode.SECURE, secureDnsRadioGroup.selected); assertEquals(settings.SecureDnsMode.SECURE, secureDnsRadioGroup.selected);
assertTrue(secureDnsInput.matches(':focus-within')); assertTrue(secureDnsInput.matches(':focus-within'));
assertEquals('https://example.doh.server/dns-query', secureDnsInput.value); assertEquals(validEntry, secureDnsInput.value);
assertEquals( assertEquals(
settings.SecureDnsMode.OFF, settings.SecureDnsMode.OFF,
testElement.prefs.dns_over_https.mode.value); testElement.prefs.dns_over_https.mode.value);
secureDnsInput.blur(); secureDnsInput.blur();
await testBrowserProxy.whenCalled('validateCustomDnsEntry'); await Promise.all([
testBrowserProxy.whenCalled('validateCustomDnsEntry'),
testBrowserProxy.whenCalled('probeCustomDnsTemplate')
]);
assertEquals( assertEquals(
settings.SecureDnsMode.SECURE, settings.SecureDnsMode.SECURE,
testElement.prefs.dns_over_https.mode.value); testElement.prefs.dns_over_https.mode.value);
...@@ -323,8 +332,8 @@ suite('SettingsSecureDnsInteractive', function() { ...@@ -323,8 +332,8 @@ suite('SettingsSecureDnsInteractive', function() {
// Make the template invalid and check that the mode pref changes to // Make the template invalid and check that the mode pref changes to
// 'automatic'. // 'automatic'.
secureDnsInput.focus(); secureDnsInput.focus();
secureDnsInput.value = 'invalid_template'; secureDnsInput.value = invalidEntry;
testBrowserProxy.setIsEntryValid(false); testBrowserProxy.setValidEntry('');
secureDnsInput.blur(); secureDnsInput.blur();
await testBrowserProxy.whenCalled('validateCustomDnsEntry'); await testBrowserProxy.whenCalled('validateCustomDnsEntry');
assertFalse(secureDnsInput.matches(':focus-within')); assertFalse(secureDnsInput.matches(':focus-within'));
...@@ -347,17 +356,21 @@ suite('SettingsSecureDnsInteractive', function() { ...@@ -347,17 +356,21 @@ suite('SettingsSecureDnsInteractive', function() {
assertFalse(secureDnsInput.hasAttribute('hidden')); assertFalse(secureDnsInput.hasAttribute('hidden'));
assertFalse(secureDnsInput.matches(':focus-within')); assertFalse(secureDnsInput.matches(':focus-within'));
assertTrue(secureDnsInput.isInvalid()); assertTrue(secureDnsInput.isInvalid());
assertEquals('invalid_template', secureDnsInput.value); assertEquals(invalidEntry, secureDnsInput.value);
assertEquals( assertEquals(
settings.SecureDnsMode.AUTOMATIC, secureDnsRadioGroup.selected); settings.SecureDnsMode.AUTOMATIC, secureDnsRadioGroup.selected);
// Make the template valid but don't change the radio button yet. // Make the template valid, but don't change the radio button yet.
secureDnsInput.focus(); secureDnsInput.focus();
secureDnsInput.value = secureDnsInput.value =
'https://dns.ex/dns-query invalid https://dns.ex.another/dns-query'; `${validEntry} ${invalidEntry} https://dns.ex.another/dns-query`;
testBrowserProxy.setIsEntryValid(true); testBrowserProxy.setValidEntry(validEntry);
testBrowserProxy.setProbeSuccess(true);
secureDnsInput.blur(); secureDnsInput.blur();
await testBrowserProxy.whenCalled('validateCustomDnsEntry'); await Promise.all([
testBrowserProxy.whenCalled('validateCustomDnsEntry'),
testBrowserProxy.whenCalled('probeCustomDnsTemplate')
]);
assertFalse(secureDnsInput.matches(':focus-within')); assertFalse(secureDnsInput.matches(':focus-within'));
assertFalse(secureDnsInput.isInvalid()); assertFalse(secureDnsInput.isInvalid());
assertEquals( assertEquals(
...@@ -373,7 +386,10 @@ suite('SettingsSecureDnsInteractive', function() { ...@@ -373,7 +386,10 @@ suite('SettingsSecureDnsInteractive', function() {
testElement.prefs.dns_over_https.mode.value); testElement.prefs.dns_over_https.mode.value);
assertEquals('', testElement.prefs.dns_over_https.templates.value); assertEquals('', testElement.prefs.dns_over_https.templates.value);
secureDnsInput.blur(); secureDnsInput.blur();
await testBrowserProxy.whenCalled('validateCustomDnsEntry'); await Promise.all([
testBrowserProxy.whenCalled('validateCustomDnsEntry'),
testBrowserProxy.whenCalled('probeCustomDnsTemplate')
]);
assertFalse(secureDnsInput.matches(':focus-within')); assertFalse(secureDnsInput.matches(':focus-within'));
assertFalse(secureDnsInput.isInvalid()); assertFalse(secureDnsInput.isInvalid());
assertEquals(settings.SecureDnsMode.SECURE, secureDnsRadioGroup.selected); assertEquals(settings.SecureDnsMode.SECURE, secureDnsRadioGroup.selected);
...@@ -381,7 +397,7 @@ suite('SettingsSecureDnsInteractive', function() { ...@@ -381,7 +397,7 @@ suite('SettingsSecureDnsInteractive', function() {
settings.SecureDnsMode.SECURE, settings.SecureDnsMode.SECURE,
testElement.prefs.dns_over_https.mode.value); testElement.prefs.dns_over_https.mode.value);
assertEquals( assertEquals(
'https://dns.ex/dns-query invalid https://dns.ex.another/dns-query', `${validEntry} ${invalidEntry} https://dns.ex.another/dns-query`,
testElement.prefs.dns_over_https.templates.value); testElement.prefs.dns_over_https.templates.value);
// Make sure the input field updates with a change in the underlying // Make sure the input field updates with a change in the underlying
......
...@@ -14,12 +14,21 @@ suite('SettingsSecureDnsInput', function() { ...@@ -14,12 +14,21 @@ suite('SettingsSecureDnsInput', function() {
/** @type {SecureDnsInputElement} */ /** @type {SecureDnsInputElement} */
let testElement; let testElement;
/** @type {CrInputElement} */
let crInput;
// Possible error messages // Possible error messages
const invalidFormat = 'invalid format description'; const invalidFormat = 'invalid format description';
const probeFail = 'probe fail description';
const invalidEntry = 'invalid_entry';
const validFailEntry = 'https://example.server/dns-query';
const validSuccessEntry = 'https://example.server.another/dns-query';
suiteSetup(function() { suiteSetup(function() {
loadTimeData.overrideValues({ loadTimeData.overrideValues({
secureDnsCustomFormatError: invalidFormat, secureDnsCustomFormatError: invalidFormat,
secureDnsCustomConnectionError: probeFail,
}); });
}); });
...@@ -30,37 +39,66 @@ suite('SettingsSecureDnsInput', function() { ...@@ -30,37 +39,66 @@ suite('SettingsSecureDnsInput', function() {
testElement = document.createElement('secure-dns-input'); testElement = document.createElement('secure-dns-input');
document.body.appendChild(testElement); document.body.appendChild(testElement);
Polymer.dom.flush(); Polymer.dom.flush();
crInput = testElement.$$('#input');
assertFalse(crInput.invalid);
assertEquals('', testElement.value);
}); });
teardown(function() { teardown(function() {
testElement.remove(); testElement.remove();
}); });
test('SecureDnsInputError', async function() { test('SecureDnsInputEmpty', async function() {
const crInput = testElement.$$('#input');
assertFalse(crInput.invalid);
assertEquals('', testElement.value);
// Trigger validation on an empty input. // Trigger validation on an empty input.
testBrowserProxy.setIsEntryValid(false); testBrowserProxy.setValidEntry('');
testElement.validate(); testElement.validate();
await testBrowserProxy.whenCalled('validateCustomDnsEntry'); assertEquals(
'', await testBrowserProxy.whenCalled('validateCustomDnsEntry'));
assertFalse(crInput.invalid); assertFalse(crInput.invalid);
assertFalse(testElement.isInvalid()); assertFalse(testElement.isInvalid());
});
test('SecureDnsInputValidFormatAndProbeFail', async function() {
// Enter two valid servers but make the first one fail the test query.
testElement.value = `${validFailEntry} ${validSuccessEntry}`;
testBrowserProxy.setValidEntry(validFailEntry);
testBrowserProxy.setProbeSuccess(false);
testElement.validate();
assertEquals(
`${validFailEntry} ${validSuccessEntry}`,
await testBrowserProxy.whenCalled('validateCustomDnsEntry'));
assertEquals(
validFailEntry,
await testBrowserProxy.whenCalled('probeCustomDnsTemplate'));
assertTrue(crInput.invalid);
assertTrue(testElement.isInvalid());
assertEquals(probeFail, crInput.errorMessage);
});
// Enter a valid input and trigger validation. test('SecureDnsInputValidFormatAndProbeSuccess', async function() {
testElement.value = 'https://example.server/dns-query'; // Enter a valid input and make the test query succeed.
testBrowserProxy.setIsEntryValid(true); testElement.value = validSuccessEntry;
testBrowserProxy.setValidEntry(validSuccessEntry);
testBrowserProxy.setProbeSuccess(true);
testElement.validate(); testElement.validate();
await testBrowserProxy.whenCalled('validateCustomDnsEntry'); assertEquals(
validSuccessEntry,
await testBrowserProxy.whenCalled('validateCustomDnsEntry'));
assertEquals(
validSuccessEntry,
await testBrowserProxy.whenCalled('probeCustomDnsTemplate'));
assertFalse(crInput.invalid); assertFalse(crInput.invalid);
assertFalse(testElement.isInvalid()); assertFalse(testElement.isInvalid());
});
test('SecureDnsInputInvalid', async function() {
// Enter an invalid input and trigger validation. // Enter an invalid input and trigger validation.
testElement.value = 'invalid_template'; testElement.value = invalidEntry;
testBrowserProxy.setIsEntryValid(false); testBrowserProxy.setValidEntry('');
testElement.validate(); testElement.validate();
await testBrowserProxy.whenCalled('validateCustomDnsEntry'); assertEquals(
invalidEntry,
await testBrowserProxy.whenCalled('validateCustomDnsEntry'));
assertTrue(crInput.invalid); assertTrue(crInput.invalid);
assertTrue(testElement.isInvalid()); assertTrue(testElement.isInvalid());
assertEquals(invalidFormat, crInput.errorMessage); assertEquals(invalidFormat, crInput.errorMessage);
...@@ -69,7 +107,7 @@ suite('SettingsSecureDnsInput', function() { ...@@ -69,7 +107,7 @@ suite('SettingsSecureDnsInput', function() {
crInput.fire('input'); crInput.fire('input');
assertFalse(crInput.invalid); assertFalse(crInput.invalid);
assertFalse(testElement.isInvalid()); assertFalse(testElement.isInvalid());
assertEquals('invalid_template', testElement.value); assertEquals(invalidEntry, testElement.value);
}); });
}); });
......
...@@ -14,6 +14,7 @@ class TestPrivacyPageBrowserProxy extends TestBrowserProxy { ...@@ -14,6 +14,7 @@ class TestPrivacyPageBrowserProxy extends TestBrowserProxy {
'getSecureDnsResolverList', 'getSecureDnsResolverList',
'getSecureDnsSetting', 'getSecureDnsSetting',
'validateCustomDnsEntry', 'validateCustomDnsEntry',
'probeCustomDnsTemplate',
]); ]);
/** @type {!MetricsReporting} */ /** @type {!MetricsReporting} */
...@@ -94,15 +95,29 @@ class TestPrivacyPageBrowserProxy extends TestBrowserProxy { ...@@ -94,15 +95,29 @@ class TestPrivacyPageBrowserProxy extends TestBrowserProxy {
/** /**
* Sets the return value for the next validateCustomDnsEntry call. * Sets the return value for the next validateCustomDnsEntry call.
* @param {boolean} isEntryValid * @param {string} validEntry
*/ */
setIsEntryValid(isEntryValid) { setValidEntry(validEntry) {
this.isEntryValid_ = isEntryValid; this.validEntry_ = validEntry;
} }
/** @override */ /** @override */
validateCustomDnsEntry() { validateCustomDnsEntry(entry) {
this.methodCalled('validateCustomDnsEntry'); this.methodCalled('validateCustomDnsEntry', entry);
return Promise.resolve(this.isEntryValid_); return Promise.resolve(this.validEntry_);
}
/**
* Sets the return value for the next probeCustomDnsTemplate call.
* @param {boolean} success
*/
setProbeSuccess(success) {
this.probeSuccess_ = success;
}
/** @override */
probeCustomDnsTemplate(template) {
this.methodCalled('probeCustomDnsTemplate', template);
return Promise.resolve(this.probeSuccess_);
} }
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment