| OLD | NEW |
| 1 // Copyright (c) 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "components/safe_browsing/base_resource_throttle.h" | 5 #include "components/safe_browsing/request_checker.h" |
| 6 |
| 7 #include <memory> |
| 8 #include <utility> |
| 6 | 9 |
| 7 #include "base/metrics/histogram_macros.h" | 10 #include "base/metrics/histogram_macros.h" |
| 8 #include "base/trace_event/trace_event.h" | 11 #include "base/trace_event/trace_event.h" |
| 9 #include "base/values.h" | 12 #include "base/values.h" |
| 10 #include "components/safe_browsing/base_ui_manager.h" | 13 #include "components/safe_browsing/base_ui_manager.h" |
| 14 #include "components/safe_browsing_db/database_manager.h" |
| 11 #include "components/safe_browsing_db/util.h" | 15 #include "components/safe_browsing_db/util.h" |
| 12 #include "components/security_interstitials/content/unsafe_resource.h" | 16 #include "components/security_interstitials/content/unsafe_resource.h" |
| 13 #include "content/public/browser/browser_thread.h" | 17 #include "content/public/browser/browser_thread.h" |
| 14 #include "content/public/browser/resource_request_info.h" | |
| 15 #include "content/public/browser/web_contents.h" | |
| 16 #include "net/base/load_flags.h" | 18 #include "net/base/load_flags.h" |
| 17 #include "net/log/net_log_capture_mode.h" | 19 #include "net/log/net_log_capture_mode.h" |
| 18 #include "net/log/net_log_source.h" | 20 #include "net/log/net_log_source.h" |
| 19 #include "net/log/net_log_source_type.h" | 21 #include "net/log/net_log_source_type.h" |
| 20 #include "net/url_request/redirect_info.h" | 22 #include "net/url_request/redirect_info.h" |
| 21 #include "net/url_request/url_request.h" | 23 #include "net/url_request/url_request.h" |
| 22 | 24 |
| 23 using net::NetLogEventType; | 25 using net::NetLogEventType; |
| 24 using net::NetLogSourceType; | 26 using net::NetLogSourceType; |
| 25 | 27 |
| 26 namespace safe_browsing { | 28 namespace safe_browsing { |
| 27 | 29 |
| 28 namespace { | 30 namespace { |
| 29 | 31 |
| 30 // Maximum time in milliseconds to wait for the safe browsing service to | 32 // Maximum time in milliseconds to wait for the safe browsing service to |
| 31 // verify a URL. After this amount of time the outstanding check will be | 33 // verify a URL. After this amount of time the outstanding check will be |
| 32 // aborted, and the URL will be treated as if it were safe. | 34 // aborted, and the URL will be treated as if it were safe. |
| 33 const int kCheckUrlTimeoutMs = 5000; | 35 // Mutable for testing purposes. |
| 36 int g_check_url_timeout_ms = 5000; |
| 34 | 37 |
| 35 // Return a dictionary with "url"=|url-spec| and optionally | 38 // Return a dictionary with "url"=|url-spec| and optionally |
| 36 // |name|=|value| (if not null), for netlogging. | 39 // |name|=|value| (if not null), for netlogging. |
| 37 // This will also add a reference to the original request's net_log ID. | 40 // This will also add a reference to the original request's net_log ID. |
| 38 std::unique_ptr<base::Value> NetLogUrlCallback( | 41 std::unique_ptr<base::Value> NetLogUrlCallback( |
| 39 const net::URLRequest* request, | 42 const net::URLRequest* request, |
| 40 const GURL& url, | 43 const GURL& url, |
| 41 const char* name, | 44 const char* name, |
| 42 const char* value, | 45 const char* value, |
| 43 net::NetLogCaptureMode /* capture_mode */) { | 46 net::NetLogCaptureMode /* capture_mode */) { |
| (...skipping 12 matching lines...) Expand all Loading... |
| 56 net::NetLogCaptureMode) { | 59 net::NetLogCaptureMode) { |
| 57 std::unique_ptr<base::DictionaryValue> event_params( | 60 std::unique_ptr<base::DictionaryValue> event_params( |
| 58 new base::DictionaryValue()); | 61 new base::DictionaryValue()); |
| 59 if (name && value) | 62 if (name && value) |
| 60 event_params->SetString(name, value); | 63 event_params->SetString(name, value); |
| 61 return std::move(event_params); | 64 return std::move(event_params); |
| 62 } | 65 } |
| 63 | 66 |
| 64 } // namespace | 67 } // namespace |
| 65 | 68 |
| 66 // TODO(eroman): Downgrade these CHECK()s to DCHECKs once there is more | 69 RequestChecker::RequestChecker( |
| 67 // unit test coverage. | |
| 68 | |
| 69 BaseResourceThrottle::BaseResourceThrottle( | |
| 70 const net::URLRequest* request, | 70 const net::URLRequest* request, |
| 71 content::ResourceType resource_type, | 71 content::ResourceType resource_type, |
| 72 scoped_refptr<SafeBrowsingDatabaseManager> database_manager, | 72 scoped_refptr<SafeBrowsingDatabaseManager> database_manager, |
| 73 scoped_refptr<BaseUIManager> ui_manager) | 73 scoped_refptr<BaseUIManager> ui_manager, |
| 74 : ui_manager_(ui_manager), | 74 Delegate* delegate) |
| 75 threat_type_(SB_THREAT_TYPE_SAFE), | 75 : state_(STATE_NONE), |
| 76 database_manager_(database_manager), | |
| 77 request_(request), | |
| 78 state_(STATE_NONE), | |
| 79 defer_state_(DEFERRED_NONE), | 76 defer_state_(DEFERRED_NONE), |
| 80 resource_type_(resource_type), | 77 resource_type_(resource_type), |
| 78 threat_type_(SB_THREAT_TYPE_SAFE), |
| 79 request_(request), |
| 80 ui_manager_(std::move(ui_manager)), |
| 81 database_manager_(std::move(database_manager)), |
| 81 net_log_with_source_( | 82 net_log_with_source_( |
| 82 net::NetLogWithSource::Make(request->net_log().net_log(), | 83 net::NetLogWithSource::Make(request->net_log().net_log(), |
| 83 NetLogSourceType::SAFE_BROWSING)) {} | 84 NetLogSourceType::SAFE_BROWSING)), |
| 85 delegate_(std::move(delegate)), |
| 86 weak_factory_(this) {} |
| 84 | 87 |
| 85 // static | 88 RequestChecker::~RequestChecker() { |
| 86 BaseResourceThrottle* BaseResourceThrottle::MaybeCreate( | |
| 87 net::URLRequest* request, | |
| 88 content::ResourceType resource_type, | |
| 89 scoped_refptr<SafeBrowsingDatabaseManager> database_manager, | |
| 90 scoped_refptr<BaseUIManager> ui_manager) { | |
| 91 if (database_manager->IsSupported()) { | |
| 92 return new BaseResourceThrottle(request, resource_type, | |
| 93 database_manager, ui_manager); | |
| 94 } | |
| 95 return nullptr; | |
| 96 } | |
| 97 | |
| 98 BaseResourceThrottle::~BaseResourceThrottle() { | |
| 99 if (defer_state_ != DEFERRED_NONE) { | 89 if (defer_state_ != DEFERRED_NONE) { |
| 100 EndNetLogEvent(NetLogEventType::SAFE_BROWSING_DEFERRED, nullptr, nullptr); | 90 EndNetLogEvent(NetLogEventType::SAFE_BROWSING_DEFERRED, nullptr, nullptr); |
| 101 } | 91 } |
| 102 | 92 |
| 103 if (state_ == STATE_CHECKING_URL) { | 93 if (state_ == STATE_CHECKING_URL) { |
| 104 database_manager_->CancelCheck(this); | 94 database_manager_->CancelCheck(this); |
| 105 EndNetLogEvent(NetLogEventType::SAFE_BROWSING_CHECKING_URL, "result", | 95 EndNetLogEvent(NetLogEventType::SAFE_BROWSING_CHECKING_URL, "result", |
| 106 "request_canceled"); | 96 "request_canceled"); |
| 107 } | 97 } |
| 108 } | 98 } |
| 109 | 99 |
| 110 // Note on net_log calls: SAFE_BROWSING_DEFERRED events must be wholly | 100 RequestChecker::DeferDecision RequestChecker::CheckNewRequest() { |
| 111 // nested within SAFE_BROWSING_CHECKING_URL events. Synchronous checks | |
| 112 // are not logged at all. | |
| 113 void BaseResourceThrottle::BeginNetLogEvent(NetLogEventType type, | |
| 114 const GURL& url, | |
| 115 const char* name, | |
| 116 const char* value) { | |
| 117 net_log_with_source_.BeginEvent( | |
| 118 type, base::Bind(&NetLogUrlCallback, request_, url, name, value)); | |
| 119 request_->net_log().AddEvent( | |
| 120 type, net_log_with_source_.source().ToEventParametersCallback()); | |
| 121 } | |
| 122 | |
| 123 void BaseResourceThrottle::EndNetLogEvent(NetLogEventType type, | |
| 124 const char* name, | |
| 125 const char* value) { | |
| 126 net_log_with_source_.EndEvent(type, | |
| 127 base::Bind(&NetLogStringCallback, name, value)); | |
| 128 request_->net_log().AddEvent( | |
| 129 type, net_log_with_source_.source().ToEventParametersCallback()); | |
| 130 } | |
| 131 | |
| 132 void BaseResourceThrottle::WillStartRequest(bool* defer) { | |
| 133 // We need to check the new URL before starting the request. | 101 // We need to check the new URL before starting the request. |
| 134 if (CheckUrl(request_->url())) | 102 DeferDecision decision = CheckUrl(request_->url()); |
| 135 return; | 103 DCHECK_NE(decision, PROCEED_ALWAYS_ASYNC); |
| 104 if (decision != DEFER) |
| 105 return decision; |
| 136 | 106 |
| 137 // We let the check run in parallel with resource load only if this | 107 // We let the check run in parallel with resource load only if this |
| 138 // db_manager only supports asynchronous checks, like on mobile. | 108 // db_manager only supports asynchronous checks, like on mobile. |
| 139 // Otherwise, we defer now. | 109 // Otherwise, we defer now. |
| 140 if (database_manager_->ChecksAreAlwaysAsync()) | 110 if (database_manager_->ChecksAreAlwaysAsync()) |
| 141 return; | 111 return PROCEED_ALWAYS_ASYNC; |
| 142 | 112 |
| 143 // If the URL couldn't be verified synchronously, defer starting the | 113 // If the URL couldn't be verified synchronously, defer starting the |
| 144 // request until the check has completed. | 114 // request until the check has completed. |
| 145 defer_state_ = DEFERRED_START; | 115 defer_state_ = DEFERRED_START; |
| 146 defer_start_time_ = base::TimeTicks::Now(); | 116 defer_start_time_ = base::TimeTicks::Now(); |
| 147 *defer = true; | |
| 148 BeginNetLogEvent(NetLogEventType::SAFE_BROWSING_DEFERRED, request_->url(), | 117 BeginNetLogEvent(NetLogEventType::SAFE_BROWSING_DEFERRED, request_->url(), |
| 149 "defer_reason", "at_start"); | 118 "defer_reason", "at_start"); |
| 119 return DEFER; |
| 150 } | 120 } |
| 151 | 121 |
| 152 void BaseResourceThrottle::WillProcessResponse(bool* defer) { | 122 RequestChecker::DeferDecision RequestChecker::CheckRedirect( |
| 153 CHECK_EQ(defer_state_, DEFERRED_NONE); | 123 const GURL& new_url) { |
| 154 // TODO(nparker): Maybe remove this check, since it should have no effect. | |
| 155 if (!database_manager_->ChecksAreAlwaysAsync()) | |
| 156 return; | |
| 157 | |
| 158 if (state_ == STATE_CHECKING_URL || | |
| 159 state_ == STATE_DISPLAYING_BLOCKING_PAGE) { | |
| 160 defer_state_ = DEFERRED_PROCESSING; | |
| 161 defer_start_time_ = base::TimeTicks::Now(); | |
| 162 *defer = true; | |
| 163 BeginNetLogEvent(NetLogEventType::SAFE_BROWSING_DEFERRED, request_->url(), | |
| 164 "defer_reason", "at_response"); | |
| 165 } | |
| 166 } | |
| 167 | |
| 168 bool BaseResourceThrottle::MustProcessResponseBeforeReadingBody() { | |
| 169 // On Android, SafeBrowsing may only decide to cancel the request when the | |
| 170 // response has been received. Therefore, no part of it should be cached | |
| 171 // until this ResourceThrottle has been able to check the response. This | |
| 172 // prevents the following scenario: | |
| 173 // 1) A request is made for foo.com which has been hacked. | |
| 174 // 2) The request is only canceled at WillProcessResponse stage, but part of | |
| 175 // it has been cached. | |
| 176 // 3) foo.com is no longer hacked and removed from the SafeBrowsing list. | |
| 177 // 4) The user requests foo.com, which is not on the SafeBrowsing list. This | |
| 178 // is deemed safe. However, the resource is actually served from cache, | |
| 179 // using the version that was previously stored. | |
| 180 // 5) This results in the user accessing an unsafe resource without being | |
| 181 // notified that it's dangerous. | |
| 182 // TODO(clamy): Add a browser test that checks this specific scenario. | |
| 183 return true; | |
| 184 } | |
| 185 | |
| 186 void BaseResourceThrottle::WillRedirectRequest( | |
| 187 const net::RedirectInfo& redirect_info, | |
| 188 bool* defer) { | |
| 189 CHECK_EQ(defer_state_, DEFERRED_NONE); | 124 CHECK_EQ(defer_state_, DEFERRED_NONE); |
| 190 | 125 |
| 191 // Prev check completed and was safe. | 126 // Prev check completed and was safe. |
| 192 if (state_ == STATE_NONE) { | 127 if (state_ == STATE_NONE) { |
| 193 // Save the redirect urls for possible malware detail reporting later. | 128 // Save the redirect urls for possible malware detail reporting later. |
| 194 redirect_urls_.push_back(redirect_info.new_url); | 129 redirect_urls_.push_back(new_url); |
| 195 | 130 |
| 196 // We need to check the new URL before following the redirect. | 131 // We need to check the new URL before following the redirect. |
| 197 if (CheckUrl(redirect_info.new_url)) | 132 DeferDecision decision = CheckUrl(new_url); |
| 198 return; | 133 if (decision == PROCEED_SAFE) |
| 134 return PROCEED_SAFE; |
| 135 DCHECK_EQ(decision, DEFER); |
| 199 defer_state_ = DEFERRED_REDIRECT; | 136 defer_state_ = DEFERRED_REDIRECT; |
| 200 } else { | 137 } else { |
| 201 CHECK(state_ == STATE_CHECKING_URL || | 138 DCHECK(state_ == STATE_CHECKING_URL || |
| 202 state_ == STATE_DISPLAYING_BLOCKING_PAGE); | 139 state_ == STATE_DISPLAYING_BLOCKING_PAGE); |
| 140 DCHECK(database_manager_->ChecksAreAlwaysAsync()); |
| 203 // We can't check this new URL until we have finished checking | 141 // We can't check this new URL until we have finished checking |
| 204 // the prev one, or resumed from the blocking page. | 142 // the prev one, or resumed from the blocking page. |
| 205 unchecked_redirect_url_ = redirect_info.new_url; | 143 unchecked_redirect_url_ = new_url; |
| 206 defer_state_ = DEFERRED_UNCHECKED_REDIRECT; | 144 defer_state_ = DEFERRED_UNCHECKED_REDIRECT; |
| 207 } | 145 } |
| 208 | 146 |
| 209 defer_start_time_ = base::TimeTicks::Now(); | 147 defer_start_time_ = base::TimeTicks::Now(); |
| 210 *defer = true; | |
| 211 BeginNetLogEvent( | 148 BeginNetLogEvent( |
| 212 NetLogEventType::SAFE_BROWSING_DEFERRED, redirect_info.new_url, | 149 NetLogEventType::SAFE_BROWSING_DEFERRED, new_url, "defer_reason", |
| 213 "defer_reason", | |
| 214 defer_state_ == DEFERRED_REDIRECT ? "redirect" : "unchecked_redirect"); | 150 defer_state_ == DEFERRED_REDIRECT ? "redirect" : "unchecked_redirect"); |
| 151 return DEFER; |
| 215 } | 152 } |
| 216 | 153 |
| 217 const char* BaseResourceThrottle::GetNameForLogging() const { | 154 RequestChecker::DeferDecision RequestChecker::ShouldDeferResponse() { |
| 218 return "BaseResourceThrottle"; | 155 CHECK_EQ(defer_state_, DEFERRED_NONE); |
| 156 if (state_ == STATE_CHECKING_URL || |
| 157 state_ == STATE_DISPLAYING_BLOCKING_PAGE) { |
| 158 DCHECK(database_manager_->ChecksAreAlwaysAsync()); |
| 159 defer_state_ = DEFERRED_PROCESSING; |
| 160 defer_start_time_ = base::TimeTicks::Now(); |
| 161 BeginNetLogEvent(NetLogEventType::SAFE_BROWSING_DEFERRED, request_->url(), |
| 162 "defer_reason", "at_response"); |
| 163 return DEFER; |
| 164 } |
| 165 |
| 166 return PROCEED_SAFE; |
| 219 } | 167 } |
| 220 | 168 |
| 221 void BaseResourceThrottle::MaybeDestroyPrerenderContents( | |
| 222 const content::ResourceRequestInfo* info) {} | |
| 223 | |
| 224 // SafeBrowsingService::Client implementation, called on the IO thread once | 169 // SafeBrowsingService::Client implementation, called on the IO thread once |
| 225 // the URL has been classified. | 170 // the URL has been classified. |
| 226 void BaseResourceThrottle::OnCheckBrowseUrlResult( | 171 void RequestChecker::OnCheckBrowseUrlResult(const GURL& url, |
| 227 const GURL& url, | 172 SBThreatType threat_type, |
| 228 SBThreatType threat_type, | 173 const ThreatMetadata& metadata) { |
| 229 const ThreatMetadata& metadata) { | 174 DCHECK_EQ(state_, STATE_CHECKING_URL); |
| 230 CHECK_EQ(state_, STATE_CHECKING_URL); | 175 DCHECK(url.is_valid()); |
| 231 CHECK(url.is_valid()); | 176 DCHECK(url_being_checked_.is_valid()); |
| 232 CHECK(url_being_checked_.is_valid()); | 177 DCHECK_EQ(url, url_being_checked_); |
| 233 CHECK_EQ(url, url_being_checked_); | |
| 234 | 178 |
| 235 timer_.Stop(); // Cancel the timeout timer. | 179 timer_.Stop(); // Cancel the timeout timer. |
| 236 threat_type_ = threat_type; | 180 threat_type_ = threat_type; |
| 237 state_ = STATE_NONE; | 181 state_ = STATE_NONE; |
| 238 | 182 |
| 239 if (defer_state_ != DEFERRED_NONE) { | 183 if (defer_state_ != DEFERRED_NONE) { |
| 240 EndNetLogEvent(NetLogEventType::SAFE_BROWSING_DEFERRED, nullptr, nullptr); | 184 EndNetLogEvent(NetLogEventType::SAFE_BROWSING_DEFERRED, nullptr, nullptr); |
| 241 } | 185 } |
| 242 EndNetLogEvent( | 186 EndNetLogEvent(NetLogEventType::SAFE_BROWSING_CHECKING_URL, "result", |
| 243 NetLogEventType::SAFE_BROWSING_CHECKING_URL, "result", | 187 threat_type_ == SB_THREAT_TYPE_SAFE ? "safe" : "unsafe"); |
| 244 threat_type_ == SB_THREAT_TYPE_SAFE ? "safe" : "unsafe"); | |
| 245 | 188 |
| 246 if (threat_type == SB_THREAT_TYPE_SAFE) { | 189 if (threat_type == SB_THREAT_TYPE_SAFE) { |
| 247 if (defer_state_ != DEFERRED_NONE) { | 190 if (defer_state_ != DEFERRED_NONE) { |
| 248 // Log how much time the safe browsing check cost us. | 191 // Log how much time the safe browsing check cost us. |
| 249 ui_manager_->LogPauseDelay(base::TimeTicks::Now() - defer_start_time_); | 192 ui_manager_->LogPauseDelay(base::TimeTicks::Now() - defer_start_time_); |
| 250 ResumeRequest(); | 193 ResumeRequest(); |
| 251 } else { | 194 } else { |
| 252 ui_manager_->LogPauseDelay(base::TimeDelta()); | 195 ui_manager_->LogPauseDelay(base::TimeDelta()); |
| 253 } | 196 } |
| 254 return; | 197 return; |
| 255 } | 198 } |
| 256 | 199 |
| 257 const content::ResourceRequestInfo* info = | |
| 258 content::ResourceRequestInfo::ForRequest(request_); | |
| 259 | |
| 260 if (request_->load_flags() & net::LOAD_PREFETCH) { | 200 if (request_->load_flags() & net::LOAD_PREFETCH) { |
| 261 // Destroy the prefetch with FINAL_STATUS_SAFEBROSWING. | 201 // Destroy the prefetch with FINAL_STATUS_SAFEBROWSING. |
| 262 if (resource_type_ == content::RESOURCE_TYPE_MAIN_FRAME) { | 202 if (resource_type_ == content::RESOURCE_TYPE_MAIN_FRAME) { |
| 263 MaybeDestroyPrerenderContents(info); | 203 delegate_->MaybeDestroyPrerenderContents(request_); |
| 264 } | 204 } |
| 265 // Don't prefetch resources that fail safe browsing, disallow them. | 205 // Don't prefetch resources that fail safe browsing, disallow them. |
| 266 Cancel(); | 206 delegate_->CancelResourceLoad(); |
| 267 UMA_HISTOGRAM_ENUMERATION("SB2.ResourceTypes2.UnsafePrefetchCanceled", | 207 UMA_HISTOGRAM_ENUMERATION("SB2.ResourceTypes2.UnsafePrefetchCanceled", |
| 268 resource_type_, content::RESOURCE_TYPE_LAST_TYPE); | 208 resource_type_, content::RESOURCE_TYPE_LAST_TYPE); |
| 269 return; | 209 return; |
| 270 } | 210 } |
| 271 | 211 |
| 272 UMA_HISTOGRAM_ENUMERATION("SB2.ResourceTypes2.Unsafe", resource_type_, | 212 UMA_HISTOGRAM_ENUMERATION("SB2.ResourceTypes2.Unsafe", resource_type_, |
| 273 content::RESOURCE_TYPE_LAST_TYPE); | 213 content::RESOURCE_TYPE_LAST_TYPE); |
| 274 | 214 |
| 275 security_interstitials::UnsafeResource resource; | 215 security_interstitials::UnsafeResource resource; |
| 276 resource.url = url; | 216 resource.url = url; |
| 277 resource.original_url = request_->original_url(); | 217 resource.original_url = request_->original_url(); |
| 278 resource.redirect_urls = redirect_urls_; | 218 resource.redirect_urls = redirect_urls_; |
| 279 resource.is_subresource = resource_type_ != content::RESOURCE_TYPE_MAIN_FRAME; | 219 resource.is_subresource = resource_type_ != content::RESOURCE_TYPE_MAIN_FRAME; |
| 280 resource.is_subframe = resource_type_ == content::RESOURCE_TYPE_SUB_FRAME; | 220 resource.is_subframe = resource_type_ == content::RESOURCE_TYPE_SUB_FRAME; |
| 281 resource.threat_type = threat_type; | 221 resource.threat_type = threat_type; |
| 282 resource.threat_metadata = metadata; | 222 resource.threat_metadata = metadata; |
| 283 resource.callback = base::Bind( | 223 resource.callback = base::Bind(&RequestChecker::OnBlockingPageComplete, |
| 284 &BaseResourceThrottle::OnBlockingPageComplete, AsWeakPtr()); | 224 weak_factory_.GetWeakPtr()); |
| 285 resource.callback_thread = content::BrowserThread::GetTaskRunnerForThread( | 225 resource.callback_thread = content::BrowserThread::GetTaskRunnerForThread( |
| 286 content::BrowserThread::IO); | 226 content::BrowserThread::IO); |
| 287 resource.web_contents_getter = info->GetWebContentsGetterForRequest(); | 227 resource.web_contents_getter = |
| 228 delegate_->GetWebContentsGetterForRequest(request_); |
| 288 resource.threat_source = database_manager_->GetThreatSource(); | 229 resource.threat_source = database_manager_->GetThreatSource(); |
| 289 | 230 |
| 290 state_ = STATE_DISPLAYING_BLOCKING_PAGE; | 231 state_ = STATE_DISPLAYING_BLOCKING_PAGE; |
| 291 | 232 |
| 292 StartDisplayingBlockingPageHelper(resource); | 233 delegate_->StartDisplayingBlockingPage(std::move(resource), ui_manager_); |
| 293 } | 234 } |
| 294 | 235 |
| 295 void BaseResourceThrottle::StartDisplayingBlockingPageHelper( | 236 void RequestChecker::OnBlockingPageComplete(bool proceed) { |
| 296 security_interstitials::UnsafeResource resource) { | |
| 297 content::BrowserThread::PostTask( | |
| 298 content::BrowserThread::UI, FROM_HERE, | |
| 299 base::Bind(&BaseResourceThrottle::StartDisplayingBlockingPage, | |
| 300 AsWeakPtr(), ui_manager_, resource)); | |
| 301 } | |
| 302 | |
| 303 // Static | |
| 304 void BaseResourceThrottle::StartDisplayingBlockingPage( | |
| 305 const base::WeakPtr<BaseResourceThrottle>& throttle, | |
| 306 scoped_refptr<BaseUIManager> ui_manager, | |
| 307 const security_interstitials::UnsafeResource& resource) { | |
| 308 content::WebContents* web_contents = resource.web_contents_getter.Run(); | |
| 309 if (web_contents) { | |
| 310 ui_manager->DisplayBlockingPage(resource); | |
| 311 return; | |
| 312 } | |
| 313 | |
| 314 // Tab is gone or it's being prerendered. | |
| 315 content::BrowserThread::PostTask( | |
| 316 content::BrowserThread::IO, FROM_HERE, | |
| 317 base::Bind(&BaseResourceThrottle::Cancel, throttle)); | |
| 318 } | |
| 319 | |
| 320 void BaseResourceThrottle::OnBlockingPageComplete(bool proceed) { | |
| 321 CHECK_EQ(state_, STATE_DISPLAYING_BLOCKING_PAGE); | 237 CHECK_EQ(state_, STATE_DISPLAYING_BLOCKING_PAGE); |
| 322 state_ = STATE_NONE; | 238 state_ = STATE_NONE; |
| 323 | 239 |
| 324 if (proceed) { | 240 if (proceed) { |
| 325 threat_type_ = SB_THREAT_TYPE_SAFE; | 241 threat_type_ = SB_THREAT_TYPE_SAFE; |
| 326 if (defer_state_ != DEFERRED_NONE) { | 242 if (defer_state_ != DEFERRED_NONE) { |
| 327 ResumeRequest(); | 243 ResumeRequest(); |
| 328 } | 244 } |
| 329 } else { | 245 } else { |
| 330 CancelResourceLoad(); | 246 delegate_->CancelResourceLoad(); |
| 331 } | 247 } |
| 332 } | 248 } |
| 333 | 249 |
| 334 void BaseResourceThrottle::CancelResourceLoad() { | 250 RequestChecker::DeferDecision RequestChecker::CheckUrl(const GURL& url) { |
| 335 Cancel(); | 251 TRACE_EVENT1("loader", "RequestChecker::CheckUrl", "url", url.spec()); |
| 336 } | |
| 337 | |
| 338 scoped_refptr<BaseUIManager> BaseResourceThrottle::ui_manager() { | |
| 339 return ui_manager_; | |
| 340 } | |
| 341 | |
| 342 bool BaseResourceThrottle::CheckUrl(const GURL& url) { | |
| 343 TRACE_EVENT1("loader", "BaseResourceThrottle::CheckUrl", "url", | |
| 344 url.spec()); | |
| 345 CHECK_EQ(state_, STATE_NONE); | 252 CHECK_EQ(state_, STATE_NONE); |
| 346 // To reduce aggregate latency on mobile, check only the most dangerous | 253 // To reduce aggregate latency on mobile, check only the most dangerous |
| 347 // resource types. | 254 // resource types. |
| 348 if (!database_manager_->CanCheckResourceType(resource_type_)) { | 255 if (!database_manager_->CanCheckResourceType(resource_type_)) { |
| 349 // TODO(vakh): Consider changing this metric to SafeBrowsing.V4ResourceType | 256 // TODO(vakh): Consider changing this metric to SafeBrowsing.V4ResourceType |
| 350 // to be consistent with the other PVer4 metrics. | 257 // to be consistent with the other PVer4 metrics. |
| 351 UMA_HISTOGRAM_ENUMERATION("SB2.ResourceTypes2.Skipped", resource_type_, | 258 UMA_HISTOGRAM_ENUMERATION("SB2.ResourceTypes2.Skipped", resource_type_, |
| 352 content::RESOURCE_TYPE_LAST_TYPE); | 259 content::RESOURCE_TYPE_LAST_TYPE); |
| 353 return true; | 260 return PROCEED_SKIPPED; |
| 354 } | 261 } |
| 355 | 262 |
| 356 // TODO(vakh): Consider changing this metric to SafeBrowsing.V4ResourceType to | 263 // TODO(vakh): Consider changing this metric to SafeBrowsing.V4ResourceType to |
| 357 // be consistent with the other PVer4 metrics. | 264 // be consistent with the other PVer4 metrics. |
| 358 UMA_HISTOGRAM_ENUMERATION("SB2.ResourceTypes2.Checked", resource_type_, | 265 UMA_HISTOGRAM_ENUMERATION("SB2.ResourceTypes2.Checked", resource_type_, |
| 359 content::RESOURCE_TYPE_LAST_TYPE); | 266 content::RESOURCE_TYPE_LAST_TYPE); |
| 360 | 267 |
| 361 if (database_manager_->CheckBrowseUrl(url, this)) { | 268 if (database_manager_->CheckBrowseUrl(url, this)) { |
| 362 threat_type_ = SB_THREAT_TYPE_SAFE; | 269 threat_type_ = SB_THREAT_TYPE_SAFE; |
| 363 ui_manager_->LogPauseDelay(base::TimeDelta()); // No delay. | 270 ui_manager_->LogPauseDelay(base::TimeDelta()); // No delay. |
| 364 return true; | 271 return PROCEED_SAFE; |
| 365 } | 272 } |
| 366 | 273 |
| 367 state_ = STATE_CHECKING_URL; | 274 state_ = STATE_CHECKING_URL; |
| 368 url_being_checked_ = url; | 275 url_being_checked_ = url; |
| 369 BeginNetLogEvent(NetLogEventType::SAFE_BROWSING_CHECKING_URL, url, nullptr, | 276 BeginNetLogEvent(NetLogEventType::SAFE_BROWSING_CHECKING_URL, url, nullptr, |
| 370 nullptr); | 277 nullptr); |
| 371 | 278 |
| 372 // Start a timer to abort the check if it takes too long. | 279 // Start a timer to abort the check if it takes too long. |
| 373 // TODO(nparker): Set this only when we defer, based on remaining time, | 280 // TODO(nparker): Set this only when we defer, based on remaining time, |
| 374 // so we don't cancel earlier than necessary. | 281 // so we don't cancel earlier than necessary. |
| 375 timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(kCheckUrlTimeoutMs), | 282 timer_.Start(FROM_HERE, |
| 376 this, &BaseResourceThrottle::OnCheckUrlTimeout); | 283 base::TimeDelta::FromMilliseconds(g_check_url_timeout_ms), this, |
| 284 &RequestChecker::OnCheckUrlTimeout); |
| 377 | 285 |
| 378 return false; | 286 return DEFER; |
| 379 } | 287 } |
| 380 | 288 |
| 381 void BaseResourceThrottle::OnCheckUrlTimeout() { | 289 void RequestChecker::OnCheckUrlTimeout() { |
| 382 CHECK_EQ(state_, STATE_CHECKING_URL); | 290 CHECK_EQ(state_, STATE_CHECKING_URL); |
| 383 | 291 |
| 384 database_manager_->CancelCheck(this); | 292 database_manager_->CancelCheck(this); |
| 385 | 293 |
| 386 OnCheckBrowseUrlResult(url_being_checked_, safe_browsing::SB_THREAT_TYPE_SAFE, | 294 OnCheckBrowseUrlResult(url_being_checked_, safe_browsing::SB_THREAT_TYPE_SAFE, |
| 387 ThreatMetadata()); | 295 ThreatMetadata()); |
| 388 } | 296 } |
| 389 | 297 |
| 390 void BaseResourceThrottle::ResumeRequest() { | 298 void RequestChecker::ResumeRequest() { |
| 391 CHECK_EQ(state_, STATE_NONE); | 299 CHECK_EQ(state_, STATE_NONE); |
| 392 CHECK_NE(defer_state_, DEFERRED_NONE); | 300 CHECK_NE(defer_state_, DEFERRED_NONE); |
| 393 | 301 |
| 394 bool resume = true; | 302 bool resume = true; |
| 395 if (defer_state_ == DEFERRED_UNCHECKED_REDIRECT) { | 303 if (defer_state_ == DEFERRED_UNCHECKED_REDIRECT) { |
| 304 DCHECK(unchecked_redirect_url_.is_valid()); |
| 305 GURL unchecked_redirect_url; |
| 306 unchecked_redirect_url_.Swap(&unchecked_redirect_url); |
| 396 // Save the redirect urls for possible malware detail reporting later. | 307 // Save the redirect urls for possible malware detail reporting later. |
| 397 redirect_urls_.push_back(unchecked_redirect_url_); | 308 redirect_urls_.push_back(unchecked_redirect_url); |
| 398 if (!CheckUrl(unchecked_redirect_url_)) { | 309 if (!CheckUrl(unchecked_redirect_url)) { |
| 399 // We're now waiting for the unchecked_redirect_url_. | 310 // We're now waiting for the unchecked_redirect_url. |
| 400 defer_state_ = DEFERRED_REDIRECT; | 311 defer_state_ = DEFERRED_REDIRECT; |
| 401 resume = false; | 312 resume = false; |
| 402 BeginNetLogEvent(NetLogEventType::SAFE_BROWSING_DEFERRED, | 313 BeginNetLogEvent(NetLogEventType::SAFE_BROWSING_DEFERRED, |
| 403 unchecked_redirect_url_, "defer_reason", | 314 unchecked_redirect_url, "defer_reason", |
| 404 "resumed_redirect"); | 315 "resumed_redirect"); |
| 405 } | 316 } |
| 406 } | 317 } |
| 407 | 318 |
| 408 if (resume) { | 319 if (resume) { |
| 409 defer_state_ = DEFERRED_NONE; | 320 defer_state_ = DEFERRED_NONE; |
| 410 Resume(); | 321 delegate_->ResumeResourceRequest(); |
| 411 } | 322 } |
| 412 } | 323 } |
| 413 | 324 |
| 325 // Note on net_log calls: SAFE_BROWSING_DEFERRED events must be wholly |
| 326 // nested within SAFE_BROWSING_CHECKING_URL events. Synchronous checks |
| 327 // are not logged at all. |
| 328 void RequestChecker::BeginNetLogEvent(NetLogEventType type, |
| 329 const GURL& url, |
| 330 const char* name, |
| 331 const char* value) { |
| 332 net_log_with_source_.BeginEvent( |
| 333 type, base::Bind(&NetLogUrlCallback, request_, url, name, value)); |
| 334 request_->net_log().AddEvent( |
| 335 type, net_log_with_source_.source().ToEventParametersCallback()); |
| 336 } |
| 337 |
| 338 void RequestChecker::EndNetLogEvent(NetLogEventType type, |
| 339 const char* name, |
| 340 const char* value) { |
| 341 net_log_with_source_.EndEvent(type, |
| 342 base::Bind(&NetLogStringCallback, name, value)); |
| 343 request_->net_log().AddEvent( |
| 344 type, net_log_with_source_.source().ToEventParametersCallback()); |
| 345 } |
| 346 |
| 347 ScopedTimeoutForTesting::ScopedTimeoutForTesting(int new_timeout_ms) |
| 348 : original_timeout_ms_(g_check_url_timeout_ms) { |
| 349 g_check_url_timeout_ms = new_timeout_ms; |
| 350 } |
| 351 |
| 352 ScopedTimeoutForTesting::ScopedTimeoutForTesting(ScopedTimeoutForTesting&& rhs) |
| 353 : original_timeout_ms_(rhs.original_timeout_ms_) { |
| 354 rhs.original_timeout_ms_ = -1; |
| 355 } |
| 356 |
| 357 ScopedTimeoutForTesting::~ScopedTimeoutForTesting() { |
| 358 if (original_timeout_ms_ != -1) |
| 359 g_check_url_timeout_ms = original_timeout_ms_; |
| 360 } |
| 361 |
| 414 } // namespace safe_browsing | 362 } // namespace safe_browsing |
| OLD | NEW |