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

Unified Diff: components/safe_browsing/password_protection/password_protection_service.cc

Issue 2747313002: PasswordProtectionService verdict cache management (Closed)
Patch Set: fix build Created 3 years, 9 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: components/safe_browsing/password_protection/password_protection_service.cc
diff --git a/components/safe_browsing/password_protection/password_protection_service.cc b/components/safe_browsing/password_protection/password_protection_service.cc
index d0331fc4d30f12a84578b937d2c861b52006be93..dff530b13438ee9d75ae4bf3162e86f89fa7fd7c 100644
--- a/components/safe_browsing/password_protection/password_protection_service.cc
+++ b/components/safe_browsing/password_protection/password_protection_service.cc
@@ -6,14 +6,53 @@
#include "base/bind.h"
#include "base/callback.h"
+#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_macros.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
#include "components/safe_browsing_db/database_manager.h"
+#include "components/safe_browsing_db/v4_protocol_manager_util.h"
#include "content/public/browser/browser_thread.h"
using content::BrowserThread;
namespace safe_browsing {
+namespace {
+
+// Keys for storing password protection verdict into a DictionaryValue.
+static const char kCacheTTL[] = "cache_ttl";
+static const char kCacheCreationTime[] = "cache_creation_time";
+static const char kVerdictType[] = "verdict";
+
+// Helper function to determine if the given origin matches content settings
+// map's patterns.
+bool OriginMatchPrimaryPattern(
+ const GURL& origin,
+ const ContentSettingsPattern& primary_pattern,
+ const ContentSettingsPattern& secondary_pattern_unused) {
+ return ContentSettingsPattern::FromURLNoWildcard(origin) == primary_pattern;
+}
+
+// Convert a LoginReputationClientResponse proto into a DictionaryValue.
+std::unique_ptr<base::DictionaryValue> CreateDictionaryFromVerdict(
+ LoginReputationClientResponse* verdict,
+ const base::Time& receive_time) {
+ std::unique_ptr<base::DictionaryValue> result =
+ base::MakeUnique<base::DictionaryValue>();
+ if (verdict) {
+ result->SetIntegerWithoutPathExpansion(kCacheTTL,
+ verdict->cache_duration_sec());
+ result->SetIntegerWithoutPathExpansion(
+ kCacheCreationTime, static_cast<int>(receive_time.ToDoubleT()));
+ result->SetIntegerWithoutPathExpansion(kVerdictType,
+ verdict->verdict_type());
+ }
+ return result;
+}
+
+} // namespace
+
PasswordProtectionService::PasswordProtectionService(
const scoped_refptr<SafeBrowsingDatabaseManager>& database_manager)
: database_manager_(database_manager), weak_factory_(this) {
@@ -38,6 +77,79 @@ void PasswordProtectionService::RecordPasswordReuse(const GURL& url) {
GetWeakPtr()));
}
+LoginReputationClientResponse::VerdictType
+PasswordProtectionService::GetCachedVerdict(HostContentSettingsMap* settings,
+ const GURL& url) {
+ // TODO(jialiul): Add UMA metrics to track if verdict is available, not
+ // available, or expired.
+ if (!settings || !url.is_valid()) {
+ return LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED;
+ }
+
+ GURL origin_url = url.GetOrigin();
+ std::unique_ptr<base::DictionaryValue> verdict_dictionary =
+ base::DictionaryValue::From(settings->GetWebsiteSetting(
+ origin_url, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION,
+ std::string(), nullptr));
+ // Return early if there is no verdict cached for this |origin_url|.
+ if (!verdict_dictionary.get() || verdict_dictionary->empty()) {
+ return LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED;
+ }
+
+ // For all the verdicts of the same origin, we key them by cache_expression
+ // field. And its corresponding value is a DictionaryValue contains its ttl,
+ // creation time, and verdict type info.
+ int cache_creation_time;
+ int cache_duration;
+ int verdict_type_value;
+ for (base::DictionaryValue::Iterator it(*verdict_dictionary.get());
+ !it.IsAtEnd(); it.Advance()) {
+ base::DictionaryValue* verdict_entry = nullptr;
+ DCHECK(verdict_dictionary->GetDictionaryWithoutPathExpansion(
+ it.key(), &verdict_entry));
lpz 2017/03/15 15:14:58 nit: alignment?
Jialiu Lin 2017/03/15 17:47:31 Acknowledged. git cl format insists this is the b
+ DCHECK(ParseVerdictEntry(verdict_entry, &cache_creation_time,
+ &cache_duration, &verdict_type_value));
+ if (UrlMatchCacheExpression(url, it.key() /* cache_expression */) &&
+ !IsCacheExpired(cache_creation_time, cache_duration)) {
+ return static_cast<LoginReputationClientResponse::VerdictType>(
+ verdict_type_value);
+ }
+ }
+ // We don't find any valid and matching verdict.
+ return LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED;
+}
+
+void PasswordProtectionService::CacheVerdict(
+ HostContentSettingsMap* settings,
+ const GURL& url,
+ LoginReputationClientResponse* verdict,
+ const base::Time& receive_time) {
+ if (!verdict)
+ return;
+
+ GURL origin_url = url.GetOrigin();
+ std::unique_ptr<base::DictionaryValue> verdict_dictionary =
+ base::DictionaryValue::From(settings->GetWebsiteSetting(
+ origin_url, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION,
+ std::string(), nullptr));
+
+ if (!verdict_dictionary.get())
+ verdict_dictionary = base::MakeUnique<base::DictionaryValue>();
+
+ std::unique_ptr<base::DictionaryValue> verdict_entry =
+ CreateDictionaryFromVerdict(verdict, receive_time);
+ // If same cache_expression is already in this verdict_dictionary, we simply
+ // override it with a fresher verdict.
+ // TODO(jialiul): We assume cache expressions returned by CSD server are
+ // not overlapping with each other and are stable. Need to confirm with SB
+ // backend if this assumption is always true.
+ verdict_dictionary->SetWithoutPathExpansion(verdict->cache_expression(),
+ std::move(verdict_entry));
+ settings->SetWebsiteSettingDefaultScope(
+ origin_url, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION,
+ std::string(), std::move(verdict_dictionary));
+}
+
void PasswordProtectionService::OnMatchCsdWhiteListResult(
bool match_whitelist) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
@@ -46,4 +158,104 @@ void PasswordProtectionService::OnMatchCsdWhiteListResult(
match_whitelist);
}
+HostContentSettingsMap*
+PasswordProtectionService::GetSettingMapForActiveProfile() {
+ return nullptr;
+}
+
+void PasswordProtectionService::OnURLsDeleted(
+ history::HistoryService* history_service,
+ bool all_history,
+ bool expired,
+ const history::URLRows& deleted_rows,
+ const std::set<GURL>& favicon_urls) {
+ HostContentSettingsMap* setting_map = GetSettingMapForActiveProfile();
+ if (!setting_map)
+ return;
+
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&PasswordProtectionService::RemoveContentSettingsOnURLsDeleted,
+ GetWeakPtr(), setting_map, all_history, deleted_rows));
+}
+
+void PasswordProtectionService::RemoveContentSettingsOnURLsDeleted(
+ HostContentSettingsMap* setting_map,
+ bool all_history,
+ const history::URLRows& deleted_rows) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ if (all_history) {
+ setting_map->ClearSettingsForOneType(
+ CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION);
+ return;
+ }
+
+ // For now, if a URL is deleted from history, we simply remove all the
+ // cached verdicts of the same origin. This is a pretty aggressive deletion.
+ // We might revisit this logic later to decide if we want to only delete the
+ // cached verdict whose cache expression matches this URL.
+ for (const history::URLRow& row : deleted_rows) {
+ setting_map->ClearSettingsForOneTypeWithPredicate(
+ CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION,
+ base::Bind(&OriginMatchPrimaryPattern, row.url().GetOrigin()));
+ }
+}
+
+// static
+bool PasswordProtectionService::ParseVerdictEntry(
+ base::DictionaryValue* verdict_entry,
+ int* out_cache_creation_time,
+ int* out_cache_duration,
+ int* out_verdict_type) {
+ return verdict_entry &&
+ verdict_entry->GetInteger(kCacheCreationTime,
+ out_cache_creation_time) &&
+ verdict_entry->GetInteger(kCacheTTL, out_cache_duration) &&
+ verdict_entry->GetInteger(kVerdictType, out_verdict_type);
+}
+
+bool PasswordProtectionService::UrlMatchCacheExpression(
+ const GURL& url,
+ const std::string& cache_expression) {
+ std::string canon_host;
+ std::string canon_path;
+ std::string canon_query;
+ V4ProtocolManagerUtil::CanonicalizeUrl(url, &canon_host, &canon_path,
+ &canon_query);
+
+ std::vector<std::string> hosts;
+ if (url.HostIsIPAddress()) {
+ hosts.push_back(url.host());
+ } else {
+ V4ProtocolManagerUtil::GenerateHostVariantsToCheck(canon_host, &hosts);
+ }
+
+ std::vector<std::string> paths;
+ V4ProtocolManagerUtil::GeneratePathVariantsToCheck(canon_path, canon_query,
+ &paths);
+
+ base::StringPiece cache_expression_trimed(base::TrimString(
+ cache_expression, "/", base::TrimPositions::TRIM_TRAILING));
+ for (const std::string& host : hosts) {
+ for (const std::string& path : paths) {
+ std::string host_path_combine(host);
+ host_path_combine.append(path);
+ if (cache_expression_trimed ==
+ base::TrimString(host_path_combine, "/",
+ base::TrimPositions::TRIM_TRAILING)) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool PasswordProtectionService::IsCacheExpired(int cache_creation_time,
+ int cache_duration) {
+ // TODO(jialiul): For now, we assume client's clock is accurate or almost
+ // accurate. Need some logic to handle cases where client's clock is way off.
+ return base::Time::Now().ToDoubleT() >
+ static_cast<double>(cache_creation_time + cache_duration);
+}
+
} // namespace safe_browsing

Powered by Google App Engine
This is Rietveld 408576698