Home | History | Annotate | Download | only in safe_browsing
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/safe_browsing/client_side_detection_service.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "base/bind.h"
     10 #include "base/command_line.h"
     11 #include "base/logging.h"
     12 #include "base/memory/scoped_ptr.h"
     13 #include "base/message_loop/message_loop.h"
     14 #include "base/metrics/histogram.h"
     15 #include "base/metrics/sparse_histogram.h"
     16 #include "base/prefs/pref_service.h"
     17 #include "base/stl_util.h"
     18 #include "base/strings/string_util.h"
     19 #include "base/time/time.h"
     20 #include "chrome/browser/browser_process.h"
     21 #include "chrome/browser/profiles/profile.h"
     22 #include "chrome/common/chrome_switches.h"
     23 #include "chrome/common/pref_names.h"
     24 #include "chrome/common/safe_browsing/client_model.pb.h"
     25 #include "chrome/common/safe_browsing/csd.pb.h"
     26 #include "chrome/common/safe_browsing/safebrowsing_messages.h"
     27 #include "content/public/browser/browser_thread.h"
     28 #include "content/public/browser/notification_service.h"
     29 #include "content/public/browser/notification_types.h"
     30 #include "content/public/browser/render_process_host.h"
     31 #include "crypto/sha2.h"
     32 #include "google_apis/google_api_keys.h"
     33 #include "net/base/escape.h"
     34 #include "net/base/load_flags.h"
     35 #include "net/base/net_util.h"
     36 #include "net/http/http_response_headers.h"
     37 #include "net/http/http_status_code.h"
     38 #include "net/url_request/url_fetcher.h"
     39 #include "net/url_request/url_request_context_getter.h"
     40 #include "net/url_request/url_request_status.h"
     41 #include "url/gurl.h"
     42 
     43 using content::BrowserThread;
     44 
     45 namespace safe_browsing {
     46 
     47 namespace {
     48 
     49   // malware report type for UMA histogram counting.
     50   enum MalwareReportTypes {
     51     REPORT_SENT,
     52     REPORT_HIT_LIMIT,
     53     REPORT_FAILED_SERIALIZATION,
     54 
     55     // Always at the end
     56     REPORT_RESULT_MAX
     57   };
     58 
     59   void UpdateEnumUMAHistogram(MalwareReportTypes report_type) {
     60     DCHECK(report_type >= 0 && report_type < REPORT_RESULT_MAX);
     61     UMA_HISTOGRAM_ENUMERATION("SBClientMalware.SentReports",
     62                               report_type, REPORT_RESULT_MAX);
     63   }
     64 
     65 }  // namespace
     66 
     67 const size_t ClientSideDetectionService::kMaxModelSizeBytes = 90 * 1024;
     68 const int ClientSideDetectionService::kMaxReportsPerInterval = 3;
     69 // TODO(noelutz): once we know this mechanism works as intended we should fetch
     70 // the model much more frequently.  E.g., every 5 minutes or so.
     71 const int ClientSideDetectionService::kClientModelFetchIntervalMs = 3600 * 1000;
     72 const int ClientSideDetectionService::kInitialClientModelFetchDelayMs = 10000;
     73 
     74 const int ClientSideDetectionService::kReportsIntervalDays = 1;
     75 const int ClientSideDetectionService::kNegativeCacheIntervalDays = 1;
     76 const int ClientSideDetectionService::kPositiveCacheIntervalMinutes = 30;
     77 
     78 const char ClientSideDetectionService::kClientReportPhishingUrl[] =
     79     "https://sb-ssl.google.com/safebrowsing/clientreport/phishing";
     80 const char ClientSideDetectionService::kClientReportMalwareUrl[] =
     81     "https://sb-ssl.google.com/safebrowsing/clientreport/malware-check";
     82 const char ClientSideDetectionService::kClientModelUrl[] =
     83     "https://ssl.gstatic.com/safebrowsing/csd/client_model_v5.pb";
     84 
     85 struct ClientSideDetectionService::ClientReportInfo {
     86   ClientReportPhishingRequestCallback callback;
     87   GURL phishing_url;
     88 };
     89 
     90 struct ClientSideDetectionService::ClientMalwareReportInfo {
     91   ClientReportMalwareRequestCallback callback;
     92   // This is the original landing url, may not be the malware url.
     93   GURL original_url;
     94 };
     95 
     96 ClientSideDetectionService::CacheState::CacheState(bool phish, base::Time time)
     97     : is_phishing(phish),
     98       timestamp(time) {}
     99 
    100 ClientSideDetectionService::ClientSideDetectionService(
    101     net::URLRequestContextGetter* request_context_getter)
    102     : enabled_(false),
    103       request_context_getter_(request_context_getter),
    104       weak_factory_(this) {
    105   registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CREATED,
    106                  content::NotificationService::AllBrowserContextsAndSources());
    107 }
    108 
    109 ClientSideDetectionService::~ClientSideDetectionService() {
    110   weak_factory_.InvalidateWeakPtrs();
    111   STLDeleteContainerPairPointers(client_phishing_reports_.begin(),
    112                                  client_phishing_reports_.end());
    113   client_phishing_reports_.clear();
    114   STLDeleteContainerPairPointers(client_malware_reports_.begin(),
    115                                  client_malware_reports_.end());
    116   client_malware_reports_.clear();
    117 }
    118 
    119 // static
    120 ClientSideDetectionService* ClientSideDetectionService::Create(
    121     net::URLRequestContextGetter* request_context_getter) {
    122   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    123   return new ClientSideDetectionService(request_context_getter);
    124 }
    125 
    126 void ClientSideDetectionService::SetEnabledAndRefreshState(bool enabled) {
    127   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    128   SendModelToRenderers();  // always refresh the renderer state
    129   if (enabled == enabled_)
    130     return;
    131   enabled_ = enabled;
    132   if (enabled_) {
    133     // Refresh the model when the service is enabled.  This can happen when the
    134     // preference is toggled, or early during startup if the preference is
    135     // already enabled. In a lot of cases the model will be in the cache so it
    136     // won't actually be fetched from the network.
    137     // We delay the first model fetch to avoid slowing down browser startup.
    138     ScheduleFetchModel(kInitialClientModelFetchDelayMs);
    139   } else {
    140     // Cancel pending requests.
    141     model_fetcher_.reset();
    142     // Invoke pending callbacks with a false verdict.
    143     for (std::map<const net::URLFetcher*, ClientReportInfo*>::iterator it =
    144              client_phishing_reports_.begin();
    145          it != client_phishing_reports_.end(); ++it) {
    146       ClientReportInfo* info = it->second;
    147       if (!info->callback.is_null())
    148         info->callback.Run(info->phishing_url, false);
    149     }
    150     STLDeleteContainerPairPointers(client_phishing_reports_.begin(),
    151                                    client_phishing_reports_.end());
    152     client_phishing_reports_.clear();
    153     for (std::map<const net::URLFetcher*, ClientMalwareReportInfo*>::iterator it
    154              = client_malware_reports_.begin();
    155          it != client_malware_reports_.end(); ++it) {
    156       ClientMalwareReportInfo* info = it->second;
    157       if (!info->callback.is_null())
    158         info->callback.Run(info->original_url, info->original_url, false);
    159     }
    160     STLDeleteContainerPairPointers(client_malware_reports_.begin(),
    161                                    client_malware_reports_.end());
    162     client_malware_reports_.clear();
    163     cache_.clear();
    164   }
    165 }
    166 
    167 void ClientSideDetectionService::SendClientReportPhishingRequest(
    168     ClientPhishingRequest* verdict,
    169     const ClientReportPhishingRequestCallback& callback) {
    170   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    171   base::MessageLoop::current()->PostTask(
    172       FROM_HERE,
    173       base::Bind(&ClientSideDetectionService::StartClientReportPhishingRequest,
    174                  weak_factory_.GetWeakPtr(), verdict, callback));
    175 }
    176 
    177 void ClientSideDetectionService::SendClientReportMalwareRequest(
    178     ClientMalwareRequest* verdict,
    179     const ClientReportMalwareRequestCallback& callback) {
    180   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    181   base::MessageLoop::current()->PostTask(
    182       FROM_HERE,
    183       base::Bind(&ClientSideDetectionService::StartClientReportMalwareRequest,
    184                  weak_factory_.GetWeakPtr(), verdict, callback));
    185 }
    186 
    187 bool ClientSideDetectionService::IsPrivateIPAddress(
    188     const std::string& ip_address) const {
    189   net::IPAddressNumber ip_number;
    190   if (!net::ParseIPLiteralToNumber(ip_address, &ip_number)) {
    191     VLOG(2) << "Unable to parse IP address: '" << ip_address << "'";
    192     // Err on the side of safety and assume this might be private.
    193     return true;
    194   }
    195 
    196   return net::IsIPAddressReserved(ip_number);
    197 }
    198 
    199 void ClientSideDetectionService::OnURLFetchComplete(
    200     const net::URLFetcher* source) {
    201   std::string data;
    202   source->GetResponseAsString(&data);
    203   if (source == model_fetcher_.get()) {
    204     HandleModelResponse(
    205         source, source->GetURL(), source->GetStatus(),
    206         source->GetResponseCode(), source->GetCookies(), data);
    207   } else if (client_phishing_reports_.find(source) !=
    208              client_phishing_reports_.end()) {
    209     HandlePhishingVerdict(
    210         source, source->GetURL(), source->GetStatus(),
    211         source->GetResponseCode(), source->GetCookies(), data);
    212   } else if (client_malware_reports_.find(source) !=
    213              client_malware_reports_.end()) {
    214     HandleMalwareVerdict(
    215         source, source->GetURL(), source->GetStatus(),
    216         source->GetResponseCode(), source->GetCookies(), data);
    217   } else {
    218     NOTREACHED();
    219   }
    220 }
    221 
    222 void ClientSideDetectionService::Observe(
    223     int type,
    224     const content::NotificationSource& source,
    225     const content::NotificationDetails& details) {
    226   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    227   DCHECK(type == content::NOTIFICATION_RENDERER_PROCESS_CREATED);
    228   if (!model_.get()) {
    229     // Model might not be ready or maybe there was an error.
    230     return;
    231   }
    232   SendModelToProcess(
    233       content::Source<content::RenderProcessHost>(source).ptr());
    234 }
    235 
    236 void ClientSideDetectionService::SendModelToProcess(
    237     content::RenderProcessHost* process) {
    238   // The ClientSideDetectionService is enabled if _any_ active profile has
    239   // SafeBrowsing turned on.  Here we check the profile for each renderer
    240   // process and only send the model to those that have SafeBrowsing enabled.
    241   Profile* profile = Profile::FromBrowserContext(process->GetBrowserContext());
    242   std::string model;
    243   if (profile->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled)) {
    244     VLOG(2) << "Sending phishing model to RenderProcessHost @" << process;
    245     model = model_str_;
    246   } else {
    247     VLOG(2) << "Disabling client-side phishing detection for "
    248             << "RenderProcessHost @" << process;
    249   }
    250   process->Send(new SafeBrowsingMsg_SetPhishingModel(model));
    251 }
    252 
    253 void ClientSideDetectionService::SendModelToRenderers() {
    254   for (content::RenderProcessHost::iterator i(
    255           content::RenderProcessHost::AllHostsIterator());
    256        !i.IsAtEnd(); i.Advance()) {
    257     SendModelToProcess(i.GetCurrentValue());
    258   }
    259 }
    260 
    261 void ClientSideDetectionService::ScheduleFetchModel(int64 delay_ms) {
    262   if (CommandLine::ForCurrentProcess()->HasSwitch(
    263       switches::kSbDisableAutoUpdate))
    264     return;
    265   base::MessageLoop::current()->PostDelayedTask(
    266       FROM_HERE,
    267       base::Bind(&ClientSideDetectionService::StartFetchModel,
    268                  weak_factory_.GetWeakPtr()),
    269       base::TimeDelta::FromMilliseconds(delay_ms));
    270 }
    271 
    272 void ClientSideDetectionService::StartFetchModel() {
    273   if (enabled_) {
    274     // Start fetching the model either from the cache or possibly from the
    275     // network if the model isn't in the cache.
    276     model_fetcher_.reset(net::URLFetcher::Create(
    277         0 /* ID used for testing */, GURL(kClientModelUrl),
    278         net::URLFetcher::GET, this));
    279     model_fetcher_->SetRequestContext(request_context_getter_.get());
    280     model_fetcher_->Start();
    281   }
    282 }
    283 
    284 void ClientSideDetectionService::EndFetchModel(ClientModelStatus status) {
    285   UMA_HISTOGRAM_ENUMERATION("SBClientPhishing.ClientModelStatus",
    286                             status,
    287                             MODEL_STATUS_MAX);
    288   if (status == MODEL_SUCCESS) {
    289     SetBadSubnets(*model_, &bad_subnets_);
    290     SendModelToRenderers();
    291   }
    292   int delay_ms = kClientModelFetchIntervalMs;
    293   // If the most recently fetched model had a valid max-age and the model was
    294   // valid we're scheduling the next model update for after the max-age expired.
    295   if (model_max_age_.get() &&
    296       (status == MODEL_SUCCESS || status == MODEL_NOT_CHANGED)) {
    297     // We're adding 60s of additional delay to make sure we're past
    298     // the model's age.
    299     *model_max_age_ += base::TimeDelta::FromMinutes(1);
    300     delay_ms = model_max_age_->InMilliseconds();
    301   }
    302   model_max_age_.reset();
    303 
    304   // Schedule the next model reload.
    305   ScheduleFetchModel(delay_ms);
    306 }
    307 
    308 void ClientSideDetectionService::StartClientReportPhishingRequest(
    309     ClientPhishingRequest* verdict,
    310     const ClientReportPhishingRequestCallback& callback) {
    311   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    312   scoped_ptr<ClientPhishingRequest> request(verdict);
    313 
    314   if (!enabled_) {
    315     if (!callback.is_null())
    316       callback.Run(GURL(request->url()), false);
    317     return;
    318   }
    319 
    320   std::string request_data;
    321   if (!request->SerializeToString(&request_data)) {
    322     UMA_HISTOGRAM_COUNTS("SBClientPhishing.RequestNotSerialized", 1);
    323     VLOG(1) << "Unable to serialize the CSD request. Proto file changed?";
    324     if (!callback.is_null())
    325       callback.Run(GURL(request->url()), false);
    326     return;
    327   }
    328 
    329   net::URLFetcher* fetcher = net::URLFetcher::Create(
    330       0 /* ID used for testing */,
    331       GetClientReportUrl(kClientReportPhishingUrl),
    332       net::URLFetcher::POST, this);
    333 
    334   // Remember which callback and URL correspond to the current fetcher object.
    335   ClientReportInfo* info = new ClientReportInfo;
    336   info->callback = callback;
    337   info->phishing_url = GURL(request->url());
    338   client_phishing_reports_[fetcher] = info;
    339 
    340   fetcher->SetLoadFlags(net::LOAD_DISABLE_CACHE);
    341   fetcher->SetRequestContext(request_context_getter_.get());
    342   fetcher->SetUploadData("application/octet-stream", request_data);
    343   fetcher->Start();
    344 
    345   // Record that we made a request
    346   phishing_report_times_.push(base::Time::Now());
    347 }
    348 
    349 void ClientSideDetectionService::StartClientReportMalwareRequest(
    350     ClientMalwareRequest* verdict,
    351     const ClientReportMalwareRequestCallback& callback) {
    352   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    353   scoped_ptr<ClientMalwareRequest> request(verdict);
    354 
    355   if (!enabled_) {
    356     if (!callback.is_null())
    357       callback.Run(GURL(request->url()), GURL(request->url()), false);
    358     return;
    359   }
    360 
    361   std::string request_data;
    362   if (!request->SerializeToString(&request_data)) {
    363     UpdateEnumUMAHistogram(REPORT_FAILED_SERIALIZATION);
    364     DVLOG(1) << "Unable to serialize the CSD request. Proto file changed?";
    365     if (!callback.is_null())
    366       callback.Run(GURL(request->url()), GURL(request->url()), false);
    367     return;
    368   }
    369 
    370   net::URLFetcher* fetcher = net::URLFetcher::Create(
    371       0 /* ID used for testing */,
    372       GetClientReportUrl(kClientReportMalwareUrl),
    373       net::URLFetcher::POST, this);
    374 
    375   // Remember which callback and URL correspond to the current fetcher object.
    376   ClientMalwareReportInfo* info = new ClientMalwareReportInfo;
    377   info->callback = callback;
    378   info->original_url = GURL(request->url());
    379   client_malware_reports_[fetcher] = info;
    380 
    381   fetcher->SetLoadFlags(net::LOAD_DISABLE_CACHE);
    382   fetcher->SetRequestContext(request_context_getter_.get());
    383   fetcher->SetUploadData("application/octet-stream", request_data);
    384   fetcher->Start();
    385 
    386   UMA_HISTOGRAM_ENUMERATION("SBClientMalware.SentReports",
    387                             REPORT_SENT, REPORT_RESULT_MAX);
    388 
    389   UMA_HISTOGRAM_COUNTS("SBClientMalware.IPBlacklistRequestPayloadSize",
    390                        request_data.size());
    391 
    392   // Record that we made a malware request
    393   malware_report_times_.push(base::Time::Now());
    394 }
    395 
    396 void ClientSideDetectionService::HandleModelResponse(
    397     const net::URLFetcher* source,
    398     const GURL& url,
    399     const net::URLRequestStatus& status,
    400     int response_code,
    401     const net::ResponseCookies& cookies,
    402     const std::string& data) {
    403   base::TimeDelta max_age;
    404   if (status.is_success() && net::HTTP_OK == response_code &&
    405       source->GetResponseHeaders() &&
    406       source->GetResponseHeaders()->GetMaxAgeValue(&max_age)) {
    407     model_max_age_.reset(new base::TimeDelta(max_age));
    408   }
    409   scoped_ptr<ClientSideModel> model(new ClientSideModel());
    410   ClientModelStatus model_status;
    411   if (!status.is_success() || net::HTTP_OK != response_code) {
    412     model_status = MODEL_FETCH_FAILED;
    413   } else if (data.empty()) {
    414     model_status = MODEL_EMPTY;
    415   } else if (data.size() > kMaxModelSizeBytes) {
    416     model_status = MODEL_TOO_LARGE;
    417   } else if (!model->ParseFromString(data)) {
    418     model_status = MODEL_PARSE_ERROR;
    419   } else if (!model->IsInitialized() || !model->has_version()) {
    420     model_status = MODEL_MISSING_FIELDS;
    421   } else if (!ModelHasValidHashIds(*model)) {
    422     model_status = MODEL_BAD_HASH_IDS;
    423   } else if (model->version() < 0 ||
    424              (model_.get() && model->version() < model_->version())) {
    425     model_status = MODEL_INVALID_VERSION_NUMBER;
    426   } else if (model_.get() && model->version() == model_->version()) {
    427     model_status = MODEL_NOT_CHANGED;
    428   } else {
    429     // The model is valid => replace the existing model with the new one.
    430     model_str_.assign(data);
    431     model_.swap(model);
    432     model_status = MODEL_SUCCESS;
    433   }
    434   EndFetchModel(model_status);
    435 }
    436 
    437 void ClientSideDetectionService::HandlePhishingVerdict(
    438     const net::URLFetcher* source,
    439     const GURL& url,
    440     const net::URLRequestStatus& status,
    441     int response_code,
    442     const net::ResponseCookies& cookies,
    443     const std::string& data) {
    444   ClientPhishingResponse response;
    445   scoped_ptr<ClientReportInfo> info(client_phishing_reports_[source]);
    446   bool is_phishing = false;
    447   if (status.is_success() && net::HTTP_OK == response_code &&
    448       response.ParseFromString(data)) {
    449     // Cache response, possibly flushing an old one.
    450     cache_[info->phishing_url] =
    451         make_linked_ptr(new CacheState(response.phishy(), base::Time::Now()));
    452     is_phishing = response.phishy();
    453   } else {
    454     DLOG(ERROR) << "Unable to get the server verdict for URL: "
    455                 << info->phishing_url << " status: " << status.status() << " "
    456                 << "response_code:" << response_code;
    457   }
    458   if (!info->callback.is_null())
    459     info->callback.Run(info->phishing_url, is_phishing);
    460   client_phishing_reports_.erase(source);
    461   delete source;
    462 }
    463 
    464 void ClientSideDetectionService::HandleMalwareVerdict(
    465     const net::URLFetcher* source,
    466     const GURL& url,
    467     const net::URLRequestStatus& status,
    468     int response_code,
    469     const net::ResponseCookies& cookies,
    470     const std::string& data) {
    471   if (status.is_success()) {
    472     UMA_HISTOGRAM_SPARSE_SLOWLY(
    473         "SBClientMalware.IPBlacklistRequestResponseCode", response_code);
    474   }
    475   // status error is negative, so we put - in front of it.
    476   UMA_HISTOGRAM_SPARSE_SLOWLY(
    477       "SBClientMalware.IPBlacklistRequestNetError", -status.error());
    478 
    479   ClientMalwareResponse response;
    480   scoped_ptr<ClientMalwareReportInfo> info(client_malware_reports_[source]);
    481   bool should_blacklist = false;
    482   if (status.is_success() && net::HTTP_OK == response_code &&
    483       response.ParseFromString(data)) {
    484     should_blacklist = response.blacklist();
    485   } else {
    486     DLOG(ERROR) << "Unable to get the server verdict for URL: "
    487                 << info->original_url << " status: " << status.status() << " "
    488                 << "response_code:" << response_code;
    489   }
    490 
    491   if (!info->callback.is_null()) {
    492     if (response.has_bad_url())
    493       info->callback.Run(info->original_url, GURL(response.bad_url()),
    494                          should_blacklist);
    495     else
    496       info->callback.Run(info->original_url, info->original_url, false);
    497   }
    498 
    499   client_malware_reports_.erase(source);
    500   delete source;
    501 }
    502 
    503 bool ClientSideDetectionService::IsInCache(const GURL& url) {
    504   UpdateCache();
    505 
    506   return cache_.find(url) != cache_.end();
    507 }
    508 
    509 bool ClientSideDetectionService::GetValidCachedResult(const GURL& url,
    510                                                       bool* is_phishing) {
    511   UpdateCache();
    512 
    513   PhishingCache::iterator it = cache_.find(url);
    514   if (it == cache_.end()) {
    515     return false;
    516   }
    517 
    518   // We still need to check if the result is valid.
    519   const CacheState& cache_state = *it->second;
    520   if (cache_state.is_phishing ?
    521       cache_state.timestamp > base::Time::Now() -
    522           base::TimeDelta::FromMinutes(kPositiveCacheIntervalMinutes) :
    523       cache_state.timestamp > base::Time::Now() -
    524           base::TimeDelta::FromDays(kNegativeCacheIntervalDays)) {
    525     *is_phishing = cache_state.is_phishing;
    526     return true;
    527   }
    528   return false;
    529 }
    530 
    531 void ClientSideDetectionService::UpdateCache() {
    532   // Since we limit the number of requests but allow pass-through for cache
    533   // refreshes, we don't want to remove elements from the cache if they
    534   // could be used for this purpose even if we will not use the entry to
    535   // satisfy the request from the cache.
    536   base::TimeDelta positive_cache_interval =
    537       std::max(base::TimeDelta::FromMinutes(kPositiveCacheIntervalMinutes),
    538                base::TimeDelta::FromDays(kReportsIntervalDays));
    539   base::TimeDelta negative_cache_interval =
    540       std::max(base::TimeDelta::FromDays(kNegativeCacheIntervalDays),
    541                base::TimeDelta::FromDays(kReportsIntervalDays));
    542 
    543   // Remove elements from the cache that will no longer be used.
    544   for (PhishingCache::iterator it = cache_.begin(); it != cache_.end();) {
    545     const CacheState& cache_state = *it->second;
    546     if (cache_state.is_phishing ?
    547         cache_state.timestamp > base::Time::Now() - positive_cache_interval :
    548         cache_state.timestamp > base::Time::Now() - negative_cache_interval) {
    549       ++it;
    550     } else {
    551       cache_.erase(it++);
    552     }
    553   }
    554 }
    555 
    556 bool ClientSideDetectionService::OverMalwareReportLimit() {
    557   return GetMalwareNumReports() > kMaxReportsPerInterval;
    558 }
    559 
    560 bool ClientSideDetectionService::OverPhishingReportLimit() {
    561   return GetPhishingNumReports() > kMaxReportsPerInterval;
    562 }
    563 
    564 int ClientSideDetectionService::GetMalwareNumReports() {
    565   return GetNumReports(&malware_report_times_);
    566 }
    567 
    568 int ClientSideDetectionService::GetPhishingNumReports() {
    569   return GetNumReports(&phishing_report_times_);
    570 }
    571 
    572 int ClientSideDetectionService::GetNumReports(
    573     std::queue<base::Time>* report_times) {
    574   base::Time cutoff =
    575       base::Time::Now() - base::TimeDelta::FromDays(kReportsIntervalDays);
    576 
    577   // Erase items older than cutoff because we will never care about them again.
    578   while (!report_times->empty() &&
    579          report_times->front() < cutoff) {
    580     report_times->pop();
    581   }
    582 
    583   // Return the number of elements that are above the cutoff.
    584   return report_times->size();
    585 }
    586 
    587 // static
    588 void ClientSideDetectionService::SetBadSubnets(const ClientSideModel& model,
    589                                                BadSubnetMap* bad_subnets) {
    590   bad_subnets->clear();
    591   for (int i = 0; i < model.bad_subnet_size(); ++i) {
    592     int size = model.bad_subnet(i).size();
    593     if (size < 0 || size > static_cast<int>(net::kIPv6AddressSize) * 8) {
    594       DLOG(ERROR) << "Invalid bad subnet size: " << size;
    595       continue;
    596     }
    597     if (model.bad_subnet(i).prefix().size() != crypto::kSHA256Length) {
    598       DLOG(ERROR) << "Invalid bad subnet prefix length: "
    599                   << model.bad_subnet(i).prefix().size();
    600       continue;
    601     }
    602     // We precompute the mask for the given subnet size to speed up lookups.
    603     // Basically we need to create a 16B long string which has the highest
    604     // |size| bits sets to one.
    605     std::string mask(net::kIPv6AddressSize, '\x00');
    606     mask.replace(0, size / 8, size / 8, '\xFF');
    607     if (size % 8) {
    608       mask[size / 8] = 0xFF << (8 - (size % 8));
    609     }
    610     (*bad_subnets)[mask].insert(model.bad_subnet(i).prefix());
    611   }
    612 }
    613 
    614 // static
    615 bool ClientSideDetectionService::ModelHasValidHashIds(
    616     const ClientSideModel& model) {
    617   const int max_index = model.hashes_size() - 1;
    618   for (int i = 0; i < model.rule_size(); ++i) {
    619     for (int j = 0; j < model.rule(i).feature_size(); ++j) {
    620       if (model.rule(i).feature(j) < 0 ||
    621           model.rule(i).feature(j) > max_index) {
    622         return false;
    623       }
    624     }
    625   }
    626   for (int i = 0; i < model.page_term_size(); ++i) {
    627     if (model.page_term(i) < 0 || model.page_term(i) > max_index) {
    628       return false;
    629     }
    630   }
    631   return true;
    632 }
    633 
    634 // static
    635 GURL ClientSideDetectionService::GetClientReportUrl(
    636     const std::string& report_url) {
    637   GURL url(report_url);
    638   std::string api_key = google_apis::GetAPIKey();
    639   if (!api_key.empty())
    640     url = url.Resolve("?key=" + net::EscapeQueryParamValue(api_key, true));
    641 
    642   return url;
    643 }
    644 }  // namespace safe_browsing
    645