blob: e725f8903e608d79d0c6f0bb2c2fbf4edaa97d2e [file] [log] [blame]
// Copyright 2015 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.
#include "chrome/browser/safe_browsing/incident_reporting/resource_request_detector.h"
#include <utility>
#include "base/bind.h"
#include "base/task/post_task.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/safe_browsing/incident_reporting/incident_receiver.h"
#include "chrome/browser/safe_browsing/incident_reporting/resource_request_incident.h"
#include "components/safe_browsing/core/proto/csd.pb.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/site_instance.h"
#include "crypto/sha2.h"
#include "net/url_request/url_request.h"
#include "url/gurl.h"
namespace {
Profile* GetProfileForRenderProcessId(int render_process_id) {
// How to get a profile from a RenderProcess id:
// 1) Get the RenderProcessHost
// 2) From 1) Get the BrowserContext
// 3) From 2) Get the Profile.
Profile* profile = nullptr;
content::RenderProcessHost* render_process_host =
content::RenderProcessHost::FromID(render_process_id);
if (render_process_host) {
content::BrowserContext* browser_context =
render_process_host->GetBrowserContext();
if (browser_context)
profile = Profile::FromBrowserContext(browser_context);
}
return profile;
}
GURL GetUrlForRenderFrameId(int render_process_id, int render_frame_id) {
content::RenderFrameHost* render_frame_host =
content::RenderFrameHost::FromID(render_process_id, render_frame_id);
if (render_frame_host)
return render_frame_host->GetLastCommittedURL();
return GURL();
}
} // namespace
namespace safe_browsing {
namespace {
// Implementation of SafeBrowsingDatabaseManager::Client that is used to lookup
// a resource blacklist. Can be constructed on any thread.
class ResourceRequestDetectorClient
: public SafeBrowsingDatabaseManager::Client,
public base::RefCountedThreadSafe<ResourceRequestDetectorClient> {
public:
using ResourceRequestIncidentMessage =
ClientIncidentReport::IncidentData::ResourceRequestIncident;
using OnResultCallback =
base::OnceCallback<void(std::unique_ptr<ResourceRequestIncidentMessage>)>;
static void Start(
const GURL& resource_url,
const scoped_refptr<SafeBrowsingDatabaseManager>& database_manager,
OnResultCallback callback) {
auto client = base::WrapRefCounted(new ResourceRequestDetectorClient(
std::move(database_manager), std::move(callback)));
base::PostTask(FROM_HERE, {content::BrowserThread::IO},
base::BindOnce(&ResourceRequestDetectorClient::StartCheck,
client, resource_url));
}
private:
ResourceRequestDetectorClient(
scoped_refptr<SafeBrowsingDatabaseManager> database_manager,
OnResultCallback callback)
: database_manager_(std::move(database_manager)),
callback_(std::move(callback)) {}
friend class base::RefCountedThreadSafe<ResourceRequestDetectorClient>;
~ResourceRequestDetectorClient() override {}
void StartCheck(const GURL& resource_url) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
if (!database_manager_)
return;
AddRef();
// If database_manager_->CheckResourceUrl returns false, the resource might
// be blacklisted and the response will come in OnCheckResourceUrlResult
// callback, where AddRef() is balanced by Release().
// If the check returns true, the resource is not blacklisted and the
// client object may be destroyed immediately.
if (database_manager_->CheckResourceUrl(resource_url, this)) {
Release();
}
}
void OnCheckResourceUrlResult(const GURL& url,
SBThreatType threat_type,
const std::string& threat_hash) override {
if (threat_type == SB_THREAT_TYPE_BLACKLISTED_RESOURCE) {
std::unique_ptr<ResourceRequestIncidentMessage> incident_data(
new ResourceRequestIncidentMessage());
incident_data->set_type(ResourceRequestIncidentMessage::TYPE_PATTERN);
incident_data->set_digest(threat_hash);
base::PostTask(
FROM_HERE, {content::BrowserThread::UI},
base::BindOnce(std::move(callback_), std::move(incident_data)));
}
Release(); // Balanced in StartCheck.
}
scoped_refptr<SafeBrowsingDatabaseManager> database_manager_;
OnResultCallback callback_;
DISALLOW_COPY_AND_ASSIGN(ResourceRequestDetectorClient);
};
} // namespace
ResourceRequestDetector::ResourceRequestDetector(
scoped_refptr<SafeBrowsingDatabaseManager> database_manager,
std::unique_ptr<IncidentReceiver> incident_receiver)
: incident_receiver_(std::move(incident_receiver)),
database_manager_(database_manager),
allow_null_profile_for_testing_(false) {}
ResourceRequestDetector::~ResourceRequestDetector() {
}
void ResourceRequestDetector::ProcessResourceRequest(
const ResourceRequestInfo* request) {
// Only look at actual net requests (e.g., not chrome-extensions://id/foo.js).
if (!request->url.SchemeIsHTTPOrHTTPS())
return;
if (request->resource_type == content::ResourceType::kSubFrame ||
request->resource_type == content::ResourceType::kScript ||
request->resource_type == content::ResourceType::kObject) {
ResourceRequestDetectorClient::Start(
request->url, database_manager_,
base::BindOnce(&ResourceRequestDetector::ReportIncidentOnUIThread,
weak_ptr_factory_.GetWeakPtr(),
request->render_process_id, request->render_frame_id));
}
}
void ResourceRequestDetector::set_allow_null_profile_for_testing(
bool allow_null_profile_for_testing) {
allow_null_profile_for_testing_ = allow_null_profile_for_testing;
}
void ResourceRequestDetector::ReportIncidentOnUIThread(
int render_process_id,
int render_frame_id,
std::unique_ptr<ClientIncidentReport_IncidentData_ResourceRequestIncident>
incident_data) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
Profile* profile = GetProfileForRenderProcessId(render_process_id);
if (profile || allow_null_profile_for_testing_) {
// Add the URL obtained from the RenderFrameHost, if available.
GURL host_url = GetUrlForRenderFrameId(render_process_id, render_frame_id);
if (host_url.is_valid())
incident_data->set_origin(host_url.GetOrigin().spec());
incident_receiver_->AddIncidentForProfile(
profile,
std::make_unique<ResourceRequestIncident>(std::move(incident_data)));
}
}
} // namespace safe_browsing