blob: 356314c322e57cbecf126f3f5806888e7b71715b [file] [log] [blame]
// Copyright 2019 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/download_protection/download_reporter.h"
#include "base/strings/string_number_conversions.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/download/simple_download_manager_coordinator_factory.h"
#include "chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.h"
#include "chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/safe_browsing/safe_browsing_service.h"
#include "components/download/public/common/download_danger_type.h"
#include "components/download/public/common/download_item.h"
#include "components/download/public/common/simple_download_manager_coordinator.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/download_item_utils.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_service.h"
namespace safe_browsing {
namespace {
bool DangerTypeIsDangerous(download::DownloadDangerType danger_type) {
return (danger_type == download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE ||
danger_type == download::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL ||
danger_type == download::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT ||
danger_type == download::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT ||
danger_type == download::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST ||
danger_type == download::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED);
}
std::string DangerTypeToThreatType(download::DownloadDangerType danger_type) {
switch (danger_type) {
case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE:
return "DANGEROUS_FILE_TYPE";
case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL:
return "DANGEROUS_URL";
case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT:
return "DANGEROUS";
case download::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT:
return "UNCOMMON";
case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST:
return "DANGEROUS_HOST";
case download::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED:
return "POTENTIALLY_UNWANTED";
default:
// Expects to only be called with the dangerous threat types listed above.
NOTREACHED() << "Unexpected danger type: " << danger_type;
return "UNKNOWN";
}
}
void ReportDangerousDownloadWarning(download::DownloadItem* download) {
content::BrowserContext* browser_context =
content::DownloadItemUtils::GetBrowserContext(download);
Profile* profile = Profile::FromBrowserContext(browser_context);
if (profile) {
std::string raw_digest_sha256 = download->GetHash();
extensions::SafeBrowsingPrivateEventRouterFactory::GetForProfile(profile)
->OnDangerousDownloadWarning(
download->GetURL(), download->GetTargetFilePath().AsUTF8Unsafe(),
base::HexEncode(raw_digest_sha256.data(), raw_digest_sha256.size()),
DangerTypeToThreatType(download->GetDangerType()),
download->GetMimeType(), download->GetTotalBytes());
}
}
void ReportDangerousDownloadWarningBypassed(
download::DownloadItem* download,
download::DownloadDangerType original_danger_type) {
content::BrowserContext* browser_context =
content::DownloadItemUtils::GetBrowserContext(download);
Profile* profile = Profile::FromBrowserContext(browser_context);
if (profile) {
std::string raw_digest_sha256 = download->GetHash();
extensions::SafeBrowsingPrivateEventRouterFactory::GetForProfile(profile)
->OnDangerousDownloadWarningBypassed(
download->GetURL(), download->GetTargetFilePath().AsUTF8Unsafe(),
base::HexEncode(raw_digest_sha256.data(), raw_digest_sha256.size()),
DangerTypeToThreatType(original_danger_type),
download->GetMimeType(), download->GetTotalBytes());
}
}
} // namespace
DownloadReporter::DownloadReporter() {
profiles_registrar_.Add(this, chrome::NOTIFICATION_PROFILE_CREATED,
content::NotificationService::AllSources());
profiles_registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
content::NotificationService::AllSources());
}
DownloadReporter::~DownloadReporter() {
profiles_registrar_.RemoveAll();
for (download::SimpleDownloadManagerCoordinator* coordinator :
observed_coordinators_) {
coordinator->RemoveObserver(this);
}
for (download::DownloadItem* download_item : observed_downloads_) {
download_item->RemoveObserver(this);
}
}
void DownloadReporter::Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
switch (type) {
case chrome::NOTIFICATION_PROFILE_CREATED: {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
Profile* profile = content::Source<Profile>(source).ptr();
download::SimpleDownloadManagerCoordinator* coordinator =
SimpleDownloadManagerCoordinatorFactory::GetForKey(
profile->GetProfileKey());
coordinator->AddObserver(this);
observed_coordinators_.insert(coordinator);
break;
}
case chrome::NOTIFICATION_PROFILE_DESTROYED: {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
Profile* profile = content::Source<Profile>(source).ptr();
download::SimpleDownloadManagerCoordinator* coordinator =
SimpleDownloadManagerCoordinatorFactory::GetForKey(
profile->GetProfileKey());
coordinator->RemoveObserver(this);
observed_coordinators_.erase(coordinator);
break;
}
default:
NOTREACHED();
}
}
void DownloadReporter::OnDownloadCreated(download::DownloadItem* download) {
danger_types_[download] = download->GetDangerType();
download->AddObserver(this);
observed_downloads_.insert(download);
}
void DownloadReporter::OnDownloadDestroyed(download::DownloadItem* download) {
download->RemoveObserver(this);
danger_types_.erase(download);
observed_downloads_.erase(download);
}
void DownloadReporter::OnDownloadUpdated(download::DownloadItem* download) {
// If the update isn't a change in danger type, we can ignore it.
if (danger_types_[download] == download->GetDangerType())
return;
download::DownloadDangerType old_danger_type = danger_types_[download];
download::DownloadDangerType current_danger_type = download->GetDangerType();
if (!DangerTypeIsDangerous(old_danger_type) &&
DangerTypeIsDangerous(current_danger_type)) {
ReportDangerousDownloadWarning(download);
}
if (DangerTypeIsDangerous(old_danger_type) &&
current_danger_type == download::DOWNLOAD_DANGER_TYPE_USER_VALIDATED) {
ReportDangerousDownloadWarningBypassed(download, old_danger_type);
}
danger_types_[download] = current_danger_type;
}
} // namespace safe_browsing