| 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..af6c2563e547b50cb8c35bdbe972d21dbace4efa 100644
|
| --- a/components/safe_browsing/password_protection/password_protection_service.cc
|
| +++ b/components/safe_browsing/password_protection/password_protection_service.cc
|
| @@ -6,14 +6,64 @@
|
|
|
| #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_split.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(
|
| + const 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;
|
| +}
|
| +
|
| +// Returns the number of path segments |cache_expression| has.
|
| +// For example, return 0 for "www.123.com", since there is no path after the
|
| +// origin; return 3 for www.123.com/abc/def/gh.html since there are three
|
| +// path segments after its origin.
|
| +size_t GetPathDepth(const base::StringPiece& cache_expression) {
|
| + return base::SplitString(cache_expression, "/", base::KEEP_WHITESPACE,
|
| + base::SPLIT_WANT_NONEMPTY)
|
| + .size();
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| PasswordProtectionService::PasswordProtectionService(
|
| const scoped_refptr<SafeBrowsingDatabaseManager>& database_manager)
|
| : database_manager_(database_manager), weak_factory_(this) {
|
| @@ -38,6 +88,90 @@ void PasswordProtectionService::RecordPasswordReuse(const GURL& url) {
|
| GetWeakPtr()));
|
| }
|
|
|
| +LoginReputationClientResponse::VerdictType
|
| +PasswordProtectionService::GetCachedVerdict(
|
| + const HostContentSettingsMap* settings,
|
| + const GURL& url) {
|
| + // TODO(jialiul): Add UMA metrics to track if verdict is available, not
|
| + // available, or expired.
|
| + DCHECK(settings);
|
| + if (!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.
|
| + std::vector<std::string> hosts;
|
| + std::vector<std::string> paths;
|
| + V4ProtocolManagerUtil::GenerateHostAndPathVariants(url, &hosts, &paths);
|
| + int cache_creation_time;
|
| + int cache_duration;
|
| + int verdict_type_value;
|
| + base::DictionaryValue* verdict_entry = nullptr;
|
| + int max_path_depth = 0;
|
| + LoginReputationClientResponse::VerdictType most_matching_verdict;
|
| + for (base::DictionaryValue::Iterator it(*verdict_dictionary.get());
|
| + !it.IsAtEnd(); it.Advance()) {
|
| + base::StringPiece cache_expression_trimed(
|
| + base::TrimString(it.key(), "/", base::TrimPositions::TRIM_TRAILING));
|
| + int path_depth = GetPathDepth(cache_expression_trimed);
|
| + if (path_depth <= max_path_depth)
|
| + continue;
|
| + DCHECK(verdict_dictionary->GetDictionaryWithoutPathExpansion(
|
| + it.key(), &verdict_entry));
|
| + DCHECK(ParseVerdictEntry(verdict_entry, &cache_creation_time,
|
| + &cache_duration, &verdict_type_value));
|
| +
|
| + if (HostPathVariantsMatchCacheExpression(
|
| + hosts, paths, cache_expression_trimed /* cache_expression */) &&
|
| + !IsCacheExpired(cache_creation_time, cache_duration)) {
|
| + max_path_depth = path_depth;
|
| + most_matching_verdict =
|
| + static_cast<LoginReputationClientResponse::VerdictType>(
|
| + verdict_type_value);
|
| + }
|
| + }
|
| + return most_matching_verdict;
|
| +}
|
| +
|
| +void PasswordProtectionService::CacheVerdict(
|
| + const GURL& url,
|
| + LoginReputationClientResponse* verdict,
|
| + const base::Time& receive_time,
|
| + HostContentSettingsMap* settings) {
|
| + DCHECK(verdict);
|
| +
|
| + 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.
|
| + 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 +180,85 @@ 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(), all_history, deleted_rows, setting_map));
|
| +}
|
| +
|
| +void PasswordProtectionService::RemoveContentSettingsOnURLsDeleted(
|
| + bool all_history,
|
| + const history::URLRows& deleted_rows,
|
| + HostContentSettingsMap* setting_map) {
|
| + 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(
|
| + const 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::HostPathVariantsMatchCacheExpression(
|
| + const std::vector<std::string>& hosts,
|
| + const std::vector<std::string>& paths,
|
| + const base::StringPiece& cache_expression) {
|
| + for (const auto& host : hosts)
|
| + for (const auto& path : paths) {
|
| + std::string host_path_combine(host);
|
| + host_path_combine.append(path);
|
| + if (cache_expression ==
|
| + 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
|
|
|