Commit 1250f181 authored by Livvie Lin's avatar Livvie Lin Committed by Commit Bot

[iOS] Call CancelAndDisplayError from ShouldAllowResponse

For lookalike interstitials, call CancelAndDisplayError from
ShouldAllowResponse rather than at the request stage, so that the
provisional load has already started. This will allow PrepareErrorPage
to trigger.

This CL also puts a placeholder method in ChromeWebClient for preparing
the lookalike interstitial HTML when it sees a lookalike error code.

Bug: 1058898
Change-Id: Iff830bb48cdee60cc1e2b8b5f74ebe858c2d5993
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2261614Reviewed-by: default avatarAli Juma <ajuma@chromium.org>
Reviewed-by: default avatarEugene But <eugenebut@chromium.org>
Commit-Queue: Livvie Lin <livvielin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#783085}
parent dfd63ce6
...@@ -262,6 +262,7 @@ source_set("web_internal") { ...@@ -262,6 +262,7 @@ source_set("web_internal") {
"//ios/chrome/browser/ui/util", "//ios/chrome/browser/ui/util",
"//ios/chrome/browser/web:feature_flags", "//ios/chrome/browser/web:feature_flags",
"//ios/components/security_interstitials", "//ios/components/security_interstitials",
"//ios/components/security_interstitials/lookalikes",
"//ios/components/webui:url_constants", "//ios/components/webui:url_constants",
"//ios/public/provider/chrome/browser", "//ios/public/provider/chrome/browser",
"//ios/public/provider/chrome/browser/voice", "//ios/public/provider/chrome/browser/voice",
......
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#import "ios/chrome/browser/web/error_page_util.h" #import "ios/chrome/browser/web/error_page_util.h"
#include "ios/chrome/browser/web/features.h" #include "ios/chrome/browser/web/features.h"
#import "ios/components/security_interstitials/ios_blocking_page_tab_helper.h" #import "ios/components/security_interstitials/ios_blocking_page_tab_helper.h"
#import "ios/components/security_interstitials/lookalikes/lookalike_url_error.h"
#include "ios/components/webui/web_ui_url_constants.h" #include "ios/components/webui/web_ui_url_constants.h"
#include "ios/public/provider/chrome/browser/browser_url_rewriter_provider.h" #include "ios/public/provider/chrome/browser/browser_url_rewriter_provider.h"
#import "ios/public/provider/chrome/browser/chrome_browser_provider.h" #import "ios/public/provider/chrome/browser/chrome_browser_provider.h"
...@@ -96,6 +97,13 @@ NSString* GetSafeBrowsingErrorPageHTML(web::WebState* web_state, ...@@ -96,6 +97,13 @@ NSString* GetSafeBrowsingErrorPageHTML(web::WebState* web_state,
return base::SysUTF8ToNSString(error_page_content); return base::SysUTF8ToNSString(error_page_content);
} }
// Returns the lookalike error page HTML.
NSString* GetLookalikeErrorPageHTML(web::WebState* web_state,
int64_t navigation_id) {
std::string error_page_content = "lookalike error";
return base::SysUTF8ToNSString(error_page_content);
}
// Returns a string describing the product name and version, of the // Returns a string describing the product name and version, of the
// form "productname/version". Used as part of the user agent string. // form "productname/version". Used as part of the user agent string.
std::string GetMobileProduct() { std::string GetMobileProduct() {
...@@ -299,6 +307,11 @@ void ChromeWebClient::PrepareErrorPage( ...@@ -299,6 +307,11 @@ void ChromeWebClient::PrepareErrorPage(
DCHECK_EQ(kUnsafeResourceErrorCode, final_underlying_error.code); DCHECK_EQ(kUnsafeResourceErrorCode, final_underlying_error.code);
std::move(error_html_callback) std::move(error_html_callback)
.Run(GetSafeBrowsingErrorPageHTML(web_state, navigation_id)); .Run(GetSafeBrowsingErrorPageHTML(web_state, navigation_id));
} else if ([final_underlying_error.domain isEqual:kLookalikeUrlErrorDomain]) {
// Only kLookalikeUrlErrorCode is supported.
DCHECK_EQ(kLookalikeUrlErrorCode, final_underlying_error.code);
std::move(error_html_callback)
.Run(GetLookalikeErrorPageHTML(web_state, navigation_id));
} else if (info.has_value()) { } else if (info.has_value()) {
base::OnceCallback<void(bool)> proceed_callback; base::OnceCallback<void(bool)> proceed_callback;
base::OnceCallback<void(NSString*)> blocking_page_callback = base::OnceCallback<void(NSString*)> blocking_page_callback =
......
...@@ -7,6 +7,8 @@ source_set("lookalikes") { ...@@ -7,6 +7,8 @@ source_set("lookalikes") {
sources = [ sources = [
"lookalike_url_container.h", "lookalike_url_container.h",
"lookalike_url_container.mm", "lookalike_url_container.mm",
"lookalike_url_error.h",
"lookalike_url_error.mm",
"lookalike_url_tab_allow_list.h", "lookalike_url_tab_allow_list.h",
"lookalike_url_tab_allow_list.mm", "lookalike_url_tab_allow_list.mm",
"lookalike_url_tab_helper.h", "lookalike_url_tab_helper.h",
...@@ -31,6 +33,7 @@ source_set("unit_tests") { ...@@ -31,6 +33,7 @@ source_set("unit_tests") {
deps = [ deps = [
":lookalikes", ":lookalikes",
"//ios/web/public/test", "//ios/web/public/test",
"//net",
"//testing/gtest", "//testing/gtest",
] ]
} }
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef IOS_COMPONENTS_SECURITY_INTERSTITIALS_LOOKALIKES_LOOKALIKE_URL_ERROR_H_
#define IOS_COMPONENTS_SECURITY_INTERSTITIALS_LOOKALIKES_LOOKALIKE_URL_ERROR_H_
#import <Foundation/Foundation.h>
// The error domain for lookalike URL errors.
extern const NSErrorDomain kLookalikeUrlErrorDomain;
// Error code for navigations to lookalike URLs.
extern const NSInteger kLookalikeUrlErrorCode;
#endif // IOS_COMPONENTS_SECURITY_INTERSTITIALS_LOOKALIKES_LOOKALIKE_URL_ERROR_H_
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/components/security_interstitials/lookalikes/lookalike_url_error.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
const NSErrorDomain kLookalikeUrlErrorDomain =
@"com.google.chrome.lookalike_url";
const NSInteger kLookalikeUrlErrorCode = -1003;
...@@ -24,9 +24,10 @@ class LookalikeUrlTabHelper ...@@ -24,9 +24,10 @@ class LookalikeUrlTabHelper
WEB_STATE_USER_DATA_KEY_DECL(); WEB_STATE_USER_DATA_KEY_DECL();
// web::WebStatePolicyDecider implementation // web::WebStatePolicyDecider implementation
web::WebStatePolicyDecider::PolicyDecision ShouldAllowRequest( void ShouldAllowResponse(
NSURLRequest* request, NSURLResponse* response,
const web::WebStatePolicyDecider::RequestInfo& request_info) override; bool for_main_frame,
web::WebStatePolicyDecider::PolicyDecisionCallback callback) override;
}; };
#endif // IOS_COMPONENTS_SECURITY_INTERSTITIALS_LOOKALIKES_LOOKALIKE_URL_TAB_HELPER_H_ #endif // IOS_COMPONENTS_SECURITY_INTERSTITIALS_LOOKALIKES_LOOKALIKE_URL_TAB_HELPER_H_
...@@ -7,27 +7,45 @@ ...@@ -7,27 +7,45 @@
#include "components/lookalikes/core/lookalike_url_util.h" #include "components/lookalikes/core/lookalike_url_util.h"
#include "components/url_formatter/spoof_checks/top_domains/top_domain_util.h" #include "components/url_formatter/spoof_checks/top_domains/top_domain_util.h"
#include "ios/components/security_interstitials/lookalikes/lookalike_url_container.h" #include "ios/components/security_interstitials/lookalikes/lookalike_url_container.h"
#include "ios/components/security_interstitials/lookalikes/lookalike_url_error.h"
#include "ios/components/security_interstitials/lookalikes/lookalike_url_tab_allow_list.h" #include "ios/components/security_interstitials/lookalikes/lookalike_url_tab_allow_list.h"
#import "ios/net/protocol_handler_util.h" #import "ios/net/protocol_handler_util.h"
#import "net/base/mac/url_conversions.h" #import "net/base/mac/url_conversions.h"
#include "net/base/net_errors.h"
#if !defined(__has_feature) || !__has_feature(objc_arc) #if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support." #error "This file requires ARC support."
#endif #endif
namespace {
// Creates a PolicyDecision that cancels a navigation to show a lookalike
// error.
web::WebStatePolicyDecider::PolicyDecision CreateLookalikeErrorDecision() {
return web::WebStatePolicyDecider::PolicyDecision::CancelAndDisplayError(
[NSError errorWithDomain:kLookalikeUrlErrorDomain
code:kLookalikeUrlErrorCode
userInfo:nil]);
}
// Creates a PolicyDecision that allows the navigation.
web::WebStatePolicyDecider::PolicyDecision CreateAllowDecision() {
return web::WebStatePolicyDecider::PolicyDecision::Allow();
}
} // namespace
LookalikeUrlTabHelper::~LookalikeUrlTabHelper() = default; LookalikeUrlTabHelper::~LookalikeUrlTabHelper() = default;
LookalikeUrlTabHelper::LookalikeUrlTabHelper(web::WebState* web_state) LookalikeUrlTabHelper::LookalikeUrlTabHelper(web::WebState* web_state)
: web::WebStatePolicyDecider(web_state) {} : web::WebStatePolicyDecider(web_state) {}
web::WebStatePolicyDecider::PolicyDecision void LookalikeUrlTabHelper::ShouldAllowResponse(
LookalikeUrlTabHelper::ShouldAllowRequest( NSURLResponse* response,
NSURLRequest* request, bool for_main_frame,
const web::WebStatePolicyDecider::RequestInfo& request_info) { base::OnceCallback<void(web::WebStatePolicyDecider::PolicyDecision)>
callback) {
// Ignore subframe navigations. // Ignore subframe navigations.
if (!request_info.target_frame_is_main) { if (!for_main_frame) {
return web::WebStatePolicyDecider::PolicyDecision::Allow(); std::move(callback).Run(CreateAllowDecision());
return;
} }
// Get stored interstitial parameters early. Doing so ensures that a // Get stored interstitial parameters early. Doing so ensures that a
...@@ -45,25 +63,28 @@ LookalikeUrlTabHelper::ShouldAllowRequest( ...@@ -45,25 +63,28 @@ LookalikeUrlTabHelper::ShouldAllowRequest(
std::unique_ptr<LookalikeUrlContainer::InterstitialParams> std::unique_ptr<LookalikeUrlContainer::InterstitialParams>
interstitial_params = lookalike_container->ReleaseInterstitialParams(); interstitial_params = lookalike_container->ReleaseInterstitialParams();
GURL request_url = net::GURLWithNSURL(request.URL); GURL response_url = net::GURLWithNSURL(response.URL);
// If the URL is not an HTTP or HTTPS page, don't show any warning. // If the URL is not an HTTP or HTTPS page, don't show any warning.
if (!request_url.SchemeIsHTTPOrHTTPS()) { if (!response_url.SchemeIsHTTPOrHTTPS()) {
return web::WebStatePolicyDecider::PolicyDecision::Allow(); std::move(callback).Run(CreateAllowDecision());
return;
} }
// If the URL is in the allowlist, don't show any warning. // If the URL is in the allowlist, don't show any warning.
LookalikeUrlTabAllowList* allow_list = LookalikeUrlTabAllowList* allow_list =
LookalikeUrlTabAllowList::FromWebState(web_state()); LookalikeUrlTabAllowList::FromWebState(web_state());
if (allow_list->IsDomainAllowed(request_url.host())) { if (allow_list->IsDomainAllowed(response_url.host())) {
return web::WebStatePolicyDecider::PolicyDecision::Allow(); std::move(callback).Run(CreateAllowDecision());
return;
} }
const DomainInfo navigated_domain = GetDomainInfo(request_url); const DomainInfo navigated_domain = GetDomainInfo(response_url);
// Empty domain_and_registry happens on private domains. // Empty domain_and_registry happens on private domains.
if (navigated_domain.domain_and_registry.empty() || if (navigated_domain.domain_and_registry.empty() ||
IsTopDomain(navigated_domain)) { IsTopDomain(navigated_domain)) {
return web::WebStatePolicyDecider::PolicyDecision::Allow(); std::move(callback).Run(CreateAllowDecision());
return;
} }
// TODO(crbug.com/1058898): After site engagement has been componentized, // TODO(crbug.com/1058898): After site engagement has been componentized,
...@@ -80,7 +101,8 @@ LookalikeUrlTabHelper::ShouldAllowRequest( ...@@ -80,7 +101,8 @@ LookalikeUrlTabHelper::ShouldAllowRequest(
}); });
if (!GetMatchingDomain(navigated_domain, engaged_sites, in_target_allowlist, if (!GetMatchingDomain(navigated_domain, engaged_sites, in_target_allowlist,
&matched_domain, &match_type)) { &matched_domain, &match_type)) {
return web::WebStatePolicyDecider::PolicyDecision::Allow(); std::move(callback).Run(CreateAllowDecision());
return;
} }
DCHECK(!matched_domain.empty()); DCHECK(!matched_domain.empty());
...@@ -92,18 +114,13 @@ LookalikeUrlTabHelper::ShouldAllowRequest( ...@@ -92,18 +114,13 @@ LookalikeUrlTabHelper::ShouldAllowRequest(
GURL::Replacements replace_host; GURL::Replacements replace_host;
replace_host.SetHostStr(suggested_domain); replace_host.SetHostStr(suggested_domain);
const GURL suggested_url = const GURL suggested_url =
request_url.ReplaceComponents(replace_host).GetWithEmptyPath(); response_url.ReplaceComponents(replace_host).GetWithEmptyPath();
// TODO(crbug.com/1058898): Instead of a net error, pass a custom NSError std::move(callback).Run(CreateLookalikeErrorDecision());
// for lookalikes. GetErrorPage will first need to be updated to accept return;
// non-net errors.
return web::WebStatePolicyDecider::PolicyDecision::CancelAndDisplayError(
[NSError errorWithDomain:net::kNSErrorDomain
code:net::ERR_BLOCKED_BY_CLIENT
userInfo:nil]);
} }
return web::WebStatePolicyDecider::PolicyDecision::Allow(); std::move(callback).Run(CreateAllowDecision());
} }
WEB_STATE_USER_DATA_KEY_IMPL(LookalikeUrlTabHelper) WEB_STATE_USER_DATA_KEY_IMPL(LookalikeUrlTabHelper)
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "ios/components/security_interstitials/lookalikes/lookalike_url_tab_allow_list.h" #include "ios/components/security_interstitials/lookalikes/lookalike_url_tab_allow_list.h"
#import "ios/web/public/navigation/web_state_policy_decider.h" #import "ios/web/public/navigation/web_state_policy_decider.h"
#import "ios/web/public/test/fakes/test_web_state.h" #import "ios/web/public/test/fakes/test_web_state.h"
#import "net/base/mac/url_conversions.h"
#include "testing/platform_test.h" #include "testing/platform_test.h"
#if !defined(__has_feature) || !__has_feature(objc_arc) #if !defined(__has_feature) || !__has_feature(objc_arc)
...@@ -23,15 +24,29 @@ class LookalikeUrlTabHelperTest : public PlatformTest { ...@@ -23,15 +24,29 @@ class LookalikeUrlTabHelperTest : public PlatformTest {
allow_list_ = LookalikeUrlTabAllowList::FromWebState(&web_state_); allow_list_ = LookalikeUrlTabAllowList::FromWebState(&web_state_);
} }
bool RequestAllowed(NSString* url_string, bool main_frame) { // Helper function that calls into WebState::ShouldAllowResponse with the
web::WebStatePolicyDecider::RequestInfo request_info( // given |url| and |for_main_frame|, waits for the callback with the decision
ui::PageTransition::PAGE_TRANSITION_LINK, main_frame, // to be called, and returns the decision.
/*has_user_gesture=*/false); web::WebStatePolicyDecider::PolicyDecision ShouldAllowResponseUrl(
web::WebStatePolicyDecider::PolicyDecision request_policy = const GURL& url,
web_state_.ShouldAllowRequest( bool for_main_frame) {
[NSURLRequest requestWithURL:[NSURL URLWithString:url_string]], NSURLResponse* response =
request_info); [[NSURLResponse alloc] initWithURL:net::NSURLWithGURL(url)
return request_policy.ShouldAllowNavigation(); MIMEType:@"text/html"
expectedContentLength:0
textEncodingName:nil];
__block bool callback_called = false;
__block web::WebStatePolicyDecider::PolicyDecision policy_decision =
web::WebStatePolicyDecider::PolicyDecision::Allow();
auto callback =
base::Bind(^(web::WebStatePolicyDecider::PolicyDecision decision) {
policy_decision = decision;
callback_called = true;
});
web_state_.ShouldAllowResponse(response, for_main_frame,
std::move(callback));
EXPECT_TRUE(callback_called);
return policy_decision;
} }
LookalikeUrlTabAllowList* allow_list() { return allow_list_; } LookalikeUrlTabAllowList* allow_list() { return allow_list_; }
...@@ -41,22 +56,27 @@ class LookalikeUrlTabHelperTest : public PlatformTest { ...@@ -41,22 +56,27 @@ class LookalikeUrlTabHelperTest : public PlatformTest {
LookalikeUrlTabAllowList* allow_list_; LookalikeUrlTabAllowList* allow_list_;
}; };
// Tests that ShouldAllowRequest properly blocks lookalike navigations and // Tests that ShouldAllowResponse properly blocks lookalike navigations and
// allows subframe navigations, non-HTTP/S navigations, and navigations // allows subframe navigations, non-HTTP/S navigations, and navigations
// to allowed domains. // to allowed domains. ShouldAllowRequest should always allow the navigation.
TEST_F(LookalikeUrlTabHelperTest, ShouldAllowRequest) { TEST_F(LookalikeUrlTabHelperTest, ShouldAllowResponse) {
NSString* lookalike_url = @"https://xn--googl-fsa.com/"; GURL lookalike_url("https://xn--googl-fsa.com/");
// Lookalike IDNs should be blocked. // Lookalike IDNs should be blocked.
EXPECT_FALSE(RequestAllowed(lookalike_url, /*main_frame=*/true)); EXPECT_FALSE(ShouldAllowResponseUrl(lookalike_url, /*main_frame=*/true)
.ShouldAllowNavigation());
// Non-main frame navigations should be allowed. // Non-main frame navigations should be allowed.
EXPECT_TRUE(RequestAllowed(lookalike_url, /*main_frame=*/false)); EXPECT_TRUE(ShouldAllowResponseUrl(lookalike_url, /*main_frame=*/false)
.ShouldAllowNavigation());
// Non-HTTP/S navigations should be allowed. // Non-HTTP/S navigations should be allowed.
EXPECT_TRUE( GURL file_url("file://xn--googl-fsa.com/");
RequestAllowed(@"file://xn--googl-fsa.com/", /*main_frame=*/true)); EXPECT_TRUE(ShouldAllowResponseUrl(file_url, /*main_frame=*/true)
.ShouldAllowNavigation());
// Lookalike IDNs that have been allowlisted should not be blocked. // Lookalike IDNs that have been allowlisted should not be blocked.
allow_list()->AllowDomain("xn--googl-fsa.com"); allow_list()->AllowDomain("xn--googl-fsa.com");
EXPECT_TRUE(RequestAllowed(lookalike_url, /*main_frame=*/true)); EXPECT_TRUE(ShouldAllowResponseUrl(lookalike_url, /*main_frame=*/true)
.ShouldAllowNavigation());
} }
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