Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(375)

Unified Diff: chrome/browser/safe_browsing/incident_reporting_service.cc

Issue 411793004: More fine-grained pruning in safe browsing incident reporting service. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: gab comments Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/safe_browsing/incident_reporting_service.cc
diff --git a/chrome/browser/safe_browsing/incident_reporting_service.cc b/chrome/browser/safe_browsing/incident_reporting_service.cc
index 46cb9f3a35d4507215a42bea61766fe747feb069..6a2d776d49596f8bc3a48fd19c84610da9672a71 100644
--- a/chrome/browser/safe_browsing/incident_reporting_service.cc
+++ b/chrome/browser/safe_browsing/incident_reporting_service.cc
@@ -9,11 +9,15 @@
#include <algorithm>
#include <vector>
+#include "base/hash.h"
#include "base/metrics/histogram.h"
#include "base/prefs/pref_service.h"
+#include "base/prefs/scoped_user_pref_update.h"
#include "base/process/process_info.h"
#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
#include "base/threading/sequenced_worker_pool.h"
+#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/prefs/tracked/tracked_preference_validation_delegate.h"
@@ -45,16 +49,94 @@ enum IncidentDisposition {
ACCEPTED,
};
+// The state persisted for a specific instance of an incident to enable pruning
+// of previously-reported incidents.
+struct PersistentIncidentState {
+ // The type of the incident.
+ IncidentType type;
+
+ // The key for a specific instance of an incident.
+ std::string key;
+
+ // A hash digest representing a specific instance of an incident.
+ uint32_t digest;
+};
Scott Hess - ex-Googler 2014/07/23 22:05:51 Is there any system to how these are organized? T
grt (UTC plus 2) 2014/07/24 18:58:40 I was attempting to roughly follow the declaration
+
const int64 kDefaultUploadDelayMs = 1000 * 60; // one minute
-void LogIncidentDataType(
- IncidentDisposition disposition,
+// Handlers for a specific type of incident.
+struct IncidentTypeHandlers {
+ // The type of incident to which these handlers apply.
+ IncidentType type;
+
+ // A function that returns a unique key representing an incident.
+ std::string (*get_key_fn)(const ClientIncidentReport_IncidentData&);
+
+ // A function that returns a hash digest of an incident's data.
+ uint32_t (*get_digest_fn)(const ClientIncidentReport_IncidentData&);
+};
+
+// Forward declarations of incident type handler functions.
+std::string GetTrackedPreferenceIncidentKey(
+ const ClientIncidentReport_IncidentData& incident_data);
+uint32_t GetTrackedPreferenceIncidentDigest(
+ const ClientIncidentReport_IncidentData& incident_data);
+
+// The collection of handlers for all incident types.
+const IncidentTypeHandlers kIncidentTypeHandlers[] = {
+ {TRACKED_PREFERENCE,
+ &GetTrackedPreferenceIncidentKey,
+ &GetTrackedPreferenceIncidentDigest},
+};
+
+// Returns the type of incident contained in |incident_data|.
+IncidentType GetIncidentType(
const ClientIncidentReport_IncidentData& incident_data) {
- IncidentType type = TRACKED_PREFERENCE;
+ // Add logic once other types are supported.
+ DCHECK(incident_data.has_tracked_preference());
+
+ return TRACKED_PREFERENCE;
+}
+
+// Returns the handlers for a given type of incident.
+const IncidentTypeHandlers* GetIncidentTypeHandlers(IncidentType type) {
Scott Hess - ex-Googler 2014/07/23 22:05:50 This seems like a kind of elaborate setup to repla
grt (UTC plus 2) 2014/07/24 18:58:40 There will be more incident types in the future. I
Scott Hess - ex-Googler 2014/07/24 21:22:19 I like the tests, my main complaint here was mostl
grt (UTC plus 2) 2014/07/24 22:58:11 You've convinced me. While I liked the idea that d
+ for (size_t i = 0; i < arraysize(kIncidentTypeHandlers); ++i) {
+ if (kIncidentTypeHandlers[i].type == type)
+ return &kIncidentTypeHandlers[i];
+ }
+ NOTREACHED();
+ return NULL;
+}
- // Add a switch statement once other types are supported.
+// Computes a simple hash digest over the serialized form of |message|.
+uint32_t HashMessage(const google::protobuf::MessageLite& message) {
+ std::string message_string;
+ if (!message.SerializeToString(&message_string)) {
+ NOTREACHED();
+ return 0;
+ }
+ return base::Hash(message_string);
+}
Scott Hess - ex-Googler 2014/07/23 22:05:51 This seems questionable, to me. I think that if y
grt (UTC plus 2) 2014/07/24 18:58:40 Ah, yes. I hadn't thought about optional values wh
Scott Hess - ex-Googler 2014/07/24 21:22:19 It's plausible that the code paths which lead here
grt (UTC plus 2) 2014/07/24 22:58:11 I'm conflicted about this. In the current implemen
+
+// Returns the path of the tracked preference.
+std::string GetTrackedPreferenceIncidentKey(
+ const ClientIncidentReport_IncidentData& incident_data) {
DCHECK(incident_data.has_tracked_preference());
+ return incident_data.tracked_preference().path();
+}
+// Returns a digest computed over the tracked preference incident data.
+uint32_t GetTrackedPreferenceIncidentDigest(
+ const ClientIncidentReport_IncidentData& incident_data) {
+ DCHECK(incident_data.has_tracked_preference());
+ return HashMessage(incident_data.tracked_preference());
+}
+
+// Logs the type of incident in |incident_data| to a user metrics histogram.
+void LogIncidentDataType(
+ IncidentDisposition disposition,
+ const ClientIncidentReport_IncidentData& incident_data) {
+ IncidentType type = GetIncidentType(incident_data);
if (disposition == ACCEPTED) {
UMA_HISTOGRAM_ENUMERATION("SBIRS.Incident", type, NUM_INCIDENT_TYPES);
} else {
@@ -64,6 +146,52 @@ void LogIncidentDataType(
}
}
+// Computes the persistent state for an incident.
+PersistentIncidentState ComputeIncidentState(
+ const ClientIncidentReport_IncidentData& incident) {
+ PersistentIncidentState state;
+
+ state.type = GetIncidentType(incident);
+ const IncidentTypeHandlers* handlers = GetIncidentTypeHandlers(state.type);
+ state.key = handlers->get_key_fn(incident);
+ state.digest = handlers->get_digest_fn(incident);
+
+ return state;
+}
+
+// Returns true if the incident described by |state| has already been reported
+// based on the bookkeeping in the |incidents_sent| preference dictionary.
+bool IncidentHasBeenReported(const base::DictionaryValue* incidents_sent,
+ const PersistentIncidentState& state) {
+ const base::DictionaryValue* type_dict = NULL;
+ std::string digest_string;
+ uint32_t digest = 0;
+ return (incidents_sent &&
+ incidents_sent->GetDictionaryWithoutPathExpansion(
+ base::IntToString(state.type), &type_dict) &&
+ type_dict->GetStringWithoutPathExpansion(state.key, &digest_string) &&
+ base::StringToUint(digest_string, &digest) &&
+ digest == state.digest);
Scott Hess - ex-Googler 2014/07/23 22:05:51 Could the last two lines be: digest_string ==
grt (UTC plus 2) 2014/07/24 18:58:40 Rock on.
Scott Hess - ex-Googler 2014/07/24 21:22:19 I wouldn't expect there to be any cases where thin
grt (UTC plus 2) 2014/07/24 22:58:11 Ah. I'm not concerned about that. If code outside
+}
+
+// Marks the incidents described by |states| as having been reported
+// in |incidents_set|.
+void MarkIncidentsAsReported(const std::vector<PersistentIncidentState>& states,
+ base::DictionaryValue* incidents_sent) {
+ for (size_t i = 0; i < states.size(); ++i) {
+ const PersistentIncidentState& data = states[i];
+ base::DictionaryValue* type_dict = NULL;
+ const std::string type_string(base::IntToString(data.type));
+ if (!incidents_sent->GetDictionaryWithoutPathExpansion(type_string,
+ &type_dict)) {
+ type_dict = new base::DictionaryValue();
+ incidents_sent->SetWithoutPathExpansion(type_string, type_dict);
+ }
+ type_dict->SetStringWithoutPathExpansion(data.key,
+ base::UintToString(data.digest));
+ }
+}
+
} // namespace
struct IncidentReportingService::ProfileContext {
@@ -82,6 +210,9 @@ struct IncidentReportingService::ProfileContext {
class IncidentReportingService::UploadContext {
public:
+ typedef std::map<Profile*, std::vector<PersistentIncidentState> >
+ PersistentIncidentStateCollection;
+
explicit UploadContext(scoped_ptr<ClientIncidentReport> report);
~UploadContext();
@@ -91,8 +222,8 @@ class IncidentReportingService::UploadContext {
// The uploader in use. This is NULL until the CSD killswitch is checked.
scoped_ptr<IncidentReportUploader> uploader;
- // The set of profiles from which incidents in |report| originated.
- std::vector<Profile*> profiles;
+ // A mapping of profiles to the data to be persisted upon successful upload.
+ PersistentIncidentStateCollection profiles_to_state;
private:
DISALLOW_COPY_AND_ASSIGN(UploadContext);
@@ -261,17 +392,9 @@ void IncidentReportingService::OnProfileDestroyed(Profile* profile) {
delete it->second;
profiles_.erase(it);
- // Remove the association with this profile from any pending uploads.
- for (size_t i = 0; i < uploads_.size(); ++i) {
- UploadContext* upload = uploads_[i];
- std::vector<Profile*>::iterator it =
- std::find(upload->profiles.begin(), upload->profiles.end(), profile);
- if (it != upload->profiles.end()) {
- *it = upload->profiles.back();
- upload->profiles.resize(upload->profiles.size() - 1);
- break;
- }
- }
+ // Remove the association with this profile from all pending uploads.
+ for (size_t i = 0; i < uploads_.size(); ++i)
+ uploads_[i]->profiles_to_state.erase(profile);
}
void IncidentReportingService::AddIncident(
@@ -516,10 +639,10 @@ void IncidentReportingService::UploadIfCollectionComplete() {
process->set_extended_consent(false);
// Collect incidents across all profiles participating in safe browsing. Drop
// incidents if the profile stopped participating before collection completed.
- // Prune incidents if the profile has already submitted any incidents.
- // Associate the participating profiles with the upload.
+ // Prune previously submitted incidents.
+ // Associate the profiles and their incident data with the upload.
size_t prune_count = 0;
- std::vector<Profile*> profiles;
+ std::map<Profile*, std::vector<PersistentIncidentState> > profiles_to_state;
Scott Hess - ex-Googler 2014/07/23 22:05:51 PersistentIncidentStateCollection? The code below
grt (UTC plus 2) 2014/07/24 18:58:40 Done.
for (ProfileContextCollection::iterator scan = profiles_.begin();
scan != profiles_.end();
++scan) {
@@ -537,21 +660,48 @@ void IncidentReportingService::UploadIfCollectionComplete() {
LogIncidentDataType(DROPPED, *context->incidents[i]);
}
context->incidents.clear();
- } else if (prefs->GetBoolean(prefs::kSafeBrowsingIncidentReportSent)) {
- // Prune all incidents.
- // TODO(grt): Only prune previously submitted incidents;
- // http://crbug.com/383043.
- prune_count += context->incidents.size();
+ continue;
+ }
+ std::vector<PersistentIncidentState> states;
+ const base::DictionaryValue* incidents_sent =
+ prefs->GetDictionary(prefs::kSafeBrowsingIncidentsSent);
+ // Prep persistent data and prune any incidents already sent.
+ for (size_t i = 0; i < context->incidents.size(); ++i) {
+ ClientIncidentReport_IncidentData* incident = context->incidents[i];
+ const PersistentIncidentState state = ComputeIncidentState(*incident);
+ if (IncidentHasBeenReported(incidents_sent, state)) {
+ ++prune_count;
+ delete context->incidents[i];
+ context->incidents[i] = NULL;
+ } else {
+ states.push_back(state);
+ }
+ }
+ if (prefs->GetBoolean(prefs::kSafeBrowsingIncidentReportSent)) {
+ // Prune all incidents as if they had been reported, migrating to the new
+ // technique. TODO(grt): remove this branch after it has shipped.
+ for (size_t i = 0; i < context->incidents.size(); ++i) {
+ if (context->incidents[i])
+ ++prune_count;
+ }
context->incidents.clear();
+ prefs->ClearPref(prefs::kSafeBrowsingIncidentReportSent);
+ DictionaryPrefUpdate pref_update(prefs,
+ prefs::kSafeBrowsingIncidentsSent);
+ MarkIncidentsAsReported(states, pref_update.Get());
} else {
for (size_t i = 0; i < context->incidents.size(); ++i) {
ClientIncidentReport_IncidentData* incident = context->incidents[i];
- LogIncidentDataType(ACCEPTED, *incident);
- // Ownership of the incident is passed to the report.
- report->mutable_incident()->AddAllocated(incident);
+ if (incident) {
+ LogIncidentDataType(ACCEPTED, *incident);
+ // Ownership of the incident is passed to the report.
+ report->mutable_incident()->AddAllocated(incident);
+ }
}
context->incidents.weak_clear();
- profiles.push_back(scan->first);
+ std::vector<PersistentIncidentState>& profile_states =
+ profiles_to_state[scan->first];
+ profile_states.swap(states);
}
}
@@ -573,7 +723,7 @@ void IncidentReportingService::UploadIfCollectionComplete() {
return;
scoped_ptr<UploadContext> context(new UploadContext(report.Pass()));
- context->profiles.swap(profiles);
+ context->profiles_to_state.swap(profiles_to_state);
if (!database_manager_) {
// No database manager during testing. Take ownership of the context and
// continue processing.
@@ -628,9 +778,13 @@ void IncidentReportingService::OnKillSwitchResult(UploadContext* context,
}
void IncidentReportingService::HandleResponse(const UploadContext& context) {
- for (size_t i = 0; i < context.profiles.size(); ++i) {
- context.profiles[i]->GetPrefs()->SetBoolean(
- prefs::kSafeBrowsingIncidentReportSent, true);
+ for (UploadContext::PersistentIncidentStateCollection::const_iterator scan =
+ context.profiles_to_state.begin();
+ scan != context.profiles_to_state.end();
+ ++scan) {
+ DictionaryPrefUpdate pref_update(scan->first->GetPrefs(),
+ prefs::kSafeBrowsingIncidentsSent);
+ MarkIncidentsAsReported(scan->second, pref_update.Get());
}
}
« no previous file with comments | « chrome/browser/profiles/profile.cc ('k') | chrome/browser/safe_browsing/incident_reporting_service_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698