blob: 835686407fa7dd4ea395f396026eb7210fe8b21a [file] [log] [blame]
// Copyright 2016 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/permission_reporter.h"
#include <functional>
#include "base/hash.h"
#include "base/memory/ptr_util.h"
#include "base/time/default_clock.h"
#include "chrome/browser/permissions/permission_request.h"
#include "chrome/common/safe_browsing/permission_report.pb.h"
#include "components/variations/active_field_trials.h"
#include "content/public/browser/permission_type.h"
#include "net/url_request/report_sender.h"
using content::PermissionType;
namespace safe_browsing {
namespace {
// URL to upload permission action reports.
const char kPermissionActionReportingUploadUrl[] =
"https://safebrowsing.googleusercontent.com/safebrowsing/clientreport/"
"chrome-permissions";
const int kMaximumReportsPerOriginPerPermissionPerMinute = 5;
PermissionReport::PermissionType PermissionTypeForReport(
PermissionType permission) {
switch (permission) {
case PermissionType::MIDI_SYSEX:
return PermissionReport::MIDI_SYSEX;
case PermissionType::PUSH_MESSAGING:
return PermissionReport::PUSH_MESSAGING;
case PermissionType::NOTIFICATIONS:
return PermissionReport::NOTIFICATIONS;
case PermissionType::GEOLOCATION:
return PermissionReport::GEOLOCATION;
case PermissionType::PROTECTED_MEDIA_IDENTIFIER:
return PermissionReport::PROTECTED_MEDIA_IDENTIFIER;
case PermissionType::MIDI:
return PermissionReport::MIDI;
case PermissionType::DURABLE_STORAGE:
return PermissionReport::DURABLE_STORAGE;
case PermissionType::AUDIO_CAPTURE:
return PermissionReport::AUDIO_CAPTURE;
case PermissionType::VIDEO_CAPTURE:
return PermissionReport::VIDEO_CAPTURE;
case PermissionType::BACKGROUND_SYNC:
return PermissionReport::BACKGROUND_SYNC;
case PermissionType::FLASH:
return PermissionReport::FLASH;
case PermissionType::NUM:
break;
}
NOTREACHED();
return PermissionReport::UNKNOWN_PERMISSION;
}
PermissionReport::Action PermissionActionForReport(PermissionAction action) {
switch (action) {
case GRANTED:
return PermissionReport::GRANTED;
case DENIED:
return PermissionReport::DENIED;
case DISMISSED:
return PermissionReport::DISMISSED;
case IGNORED:
return PermissionReport::IGNORED;
case REVOKED:
return PermissionReport::REVOKED;
case REENABLED:
case REQUESTED:
return PermissionReport::ACTION_UNSPECIFIED;
case PERMISSION_ACTION_NUM:
break;
}
NOTREACHED();
return PermissionReport::ACTION_UNSPECIFIED;
}
PermissionReport::SourceUI SourceUIForReport(PermissionSourceUI source_ui) {
switch (source_ui) {
case PermissionSourceUI::PROMPT:
return PermissionReport::PROMPT;
case PermissionSourceUI::OIB:
return PermissionReport::OIB;
case PermissionSourceUI::SITE_SETTINGS:
return PermissionReport::SITE_SETTINGS;
case PermissionSourceUI::PAGE_ACTION:
return PermissionReport::PAGE_ACTION;
case PermissionSourceUI::SOURCE_UI_NUM:
break;
}
NOTREACHED();
return PermissionReport::SOURCE_UI_UNSPECIFIED;
}
PermissionReport::GestureType GestureTypeForReport(
PermissionRequestGestureType gesture_type) {
switch (gesture_type) {
case PermissionRequestGestureType::UNKNOWN:
return PermissionReport::GESTURE_TYPE_UNSPECIFIED;
case PermissionRequestGestureType::GESTURE:
return PermissionReport::GESTURE;
case PermissionRequestGestureType::NO_GESTURE:
return PermissionReport::NO_GESTURE;
case PermissionRequestGestureType::NUM:
break;
}
NOTREACHED();
return PermissionReport::GESTURE_TYPE_UNSPECIFIED;
}
PermissionReport::PersistDecision PersistDecisionForReport(
PermissionPersistDecision persist_decision) {
switch (persist_decision) {
case PermissionPersistDecision::UNSPECIFIED:
return PermissionReport::PERSIST_DECISION_UNSPECIFIED;
case PermissionPersistDecision::PERSISTED:
return PermissionReport::PERSISTED;
case PermissionPersistDecision::NOT_PERSISTED:
return PermissionReport::NOT_PERSISTED;
}
NOTREACHED();
return PermissionReport::PERSIST_DECISION_UNSPECIFIED;
}
} // namespace
bool PermissionAndOrigin::operator==(const PermissionAndOrigin& other) const {
return (permission == other.permission && origin == other.origin);
}
std::size_t PermissionAndOriginHash::operator()(
const PermissionAndOrigin& permission_and_origin) const {
std::size_t permission_hash =
static_cast<std::size_t>(permission_and_origin.permission);
std::size_t origin_hash =
std::hash<std::string>()(permission_and_origin.origin.spec());
return base::HashInts(permission_hash, origin_hash);
}
PermissionReporter::PermissionReporter(net::URLRequestContext* request_context)
: PermissionReporter(
base::MakeUnique<net::ReportSender>(
request_context,
net::ReportSender::CookiesPreference::DO_NOT_SEND_COOKIES),
base::WrapUnique(new base::DefaultClock)) {}
PermissionReporter::PermissionReporter(
std::unique_ptr<net::ReportSender> report_sender,
std::unique_ptr<base::Clock> clock)
: permission_report_sender_(std::move(report_sender)),
clock_(std::move(clock)) {}
PermissionReporter::~PermissionReporter() {}
void PermissionReporter::SendReport(const PermissionReportInfo& report_info) {
if (IsReportThresholdExceeded(report_info.permission, report_info.origin))
return;
std::string serialized_report;
BuildReport(report_info, &serialized_report);
permission_report_sender_->Send(GURL(kPermissionActionReportingUploadUrl),
"application/octet-stream", serialized_report,
base::Closure(),
base::Callback<void(const GURL&, int)>());
}
// static
bool PermissionReporter::BuildReport(const PermissionReportInfo& report_info,
std::string* output) {
PermissionReport report;
report.set_origin(report_info.origin.spec());
report.set_permission(PermissionTypeForReport(report_info.permission));
report.set_action(PermissionActionForReport(report_info.action));
report.set_source_ui(SourceUIForReport(report_info.source_ui));
report.set_gesture(GestureTypeForReport(report_info.gesture_type));
report.set_persisted(
PersistDecisionForReport(report_info.persist_decision));
report.set_num_prior_dismissals(report_info.num_prior_dismissals);
report.set_num_prior_ignores(report_info.num_prior_ignores);
// Collect platform data.
#if defined(OS_ANDROID)
report.set_platform_type(PermissionReport::ANDROID_PLATFORM);
#elif defined(OS_MACOSX) || defined(OS_WIN) || defined(OS_CHROMEOS) || \
defined(OS_LINUX)
report.set_platform_type(PermissionReport::DESKTOP_PLATFORM);
#else
#error Unsupported platform.
#endif
// Collect field trial data.
std::vector<variations::ActiveGroupId> active_group_ids;
variations::GetFieldTrialActiveGroupIds(&active_group_ids);
for (auto active_group_id : active_group_ids) {
PermissionReport::FieldTrial* field_trial = report.add_field_trials();
field_trial->set_name_id(active_group_id.name);
field_trial->set_group_id(active_group_id.group);
}
return report.SerializeToString(output);
}
bool PermissionReporter::IsReportThresholdExceeded(
content::PermissionType permission,
const GURL& origin) {
std::queue<base::Time>& log = report_logs_[{permission, origin}];
base::Time current_time = clock_->Now();
// Remove entries that are sent more than one minute ago.
while (!log.empty() &&
current_time - log.front() > base::TimeDelta::FromMinutes(1)) {
log.pop();
}
if (log.size() < kMaximumReportsPerOriginPerPermissionPerMinute) {
log.push(current_time);
return false;
} else {
return true;
}
}
} // namespace safe_browsing