Home | History | Annotate | Download | only in incident_reporting
      1 // Copyright 2014 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/incident_reporting/last_download_finder.h"
      6 
      7 #include <string>
      8 #include <vector>
      9 
     10 #include "base/bind.h"
     11 #include "base/callback.h"
     12 #include "base/files/file_util.h"
     13 #include "base/run_loop.h"
     14 #include "base/strings/string_number_conversions.h"
     15 #include "base/strings/utf_string_conversions.h"
     16 #include "chrome/browser/history/chrome_history_client.h"
     17 #include "chrome/browser/history/chrome_history_client_factory.h"
     18 #include "chrome/browser/history/download_row.h"
     19 #include "chrome/browser/history/history_service.h"
     20 #include "chrome/browser/history/history_service_factory.h"
     21 #include "chrome/browser/history/web_history_service_factory.h"
     22 #include "chrome/browser/prefs/browser_prefs.h"
     23 #include "chrome/browser/profiles/profile_manager.h"
     24 #include "chrome/common/chrome_constants.h"
     25 #include "chrome/common/pref_names.h"
     26 #include "chrome/common/safe_browsing/csd.pb.h"
     27 #include "chrome/test/base/testing_browser_process.h"
     28 #include "chrome/test/base/testing_pref_service_syncable.h"
     29 #include "chrome/test/base/testing_profile.h"
     30 #include "chrome/test/base/testing_profile_manager.h"
     31 #include "content/public/test/test_browser_thread_bundle.h"
     32 #include "content/public/test/test_utils.h"
     33 #include "testing/gtest/include/gtest/gtest.h"
     34 
     35 namespace {
     36 
     37 // A BrowserContextKeyedServiceFactory::TestingFactoryFunction that creates a
     38 // HistoryService for a TestingProfile.
     39 KeyedService* BuildHistoryService(content::BrowserContext* context) {
     40   TestingProfile* profile = static_cast<TestingProfile*>(context);
     41 
     42   // Delete the file before creating the service.
     43   base::FilePath history_path(
     44       profile->GetPath().Append(chrome::kHistoryFilename));
     45   if (!base::DeleteFile(history_path, false) ||
     46       base::PathExists(history_path)) {
     47     ADD_FAILURE() << "failed to delete history db file "
     48                   << history_path.value();
     49     return NULL;
     50   }
     51 
     52   HistoryService* history_service = new HistoryService(
     53       ChromeHistoryClientFactory::GetForProfile(profile), profile);
     54   if (history_service->Init(profile->GetPath()))
     55     return history_service;
     56 
     57   ADD_FAILURE() << "failed to initialize history service";
     58   delete history_service;
     59   return NULL;
     60 }
     61 
     62 }  // namespace
     63 
     64 class LastDownloadFinderTest : public testing::Test {
     65  public:
     66   void NeverCalled(scoped_ptr<
     67       safe_browsing::ClientIncidentReport_DownloadDetails> download) {
     68     FAIL();
     69   }
     70 
     71   // Creates a new profile that participates in safe browsing and adds a
     72   // download to its history.
     73   void CreateProfileWithDownload() {
     74     TestingProfile* profile = CreateProfile(SAFE_BROWSING_OPT_IN);
     75     HistoryService* history_service =
     76         HistoryServiceFactory::GetForProfile(profile, Profile::EXPLICIT_ACCESS);
     77     history_service->CreateDownload(
     78         CreateTestDownloadRow(),
     79         base::Bind(&LastDownloadFinderTest::OnDownloadCreated,
     80                    base::Unretained(this)));
     81   }
     82 
     83   // safe_browsing::LastDownloadFinder::LastDownloadCallback implementation that
     84   // passes the found download to |result| and then runs a closure.
     85   void OnLastDownload(
     86       scoped_ptr<safe_browsing::ClientIncidentReport_DownloadDetails>* result,
     87       const base::Closure& quit_closure,
     88       scoped_ptr<safe_browsing::ClientIncidentReport_DownloadDetails>
     89           download) {
     90     *result = download.Pass();
     91     quit_closure.Run();
     92   }
     93 
     94  protected:
     95   // A type for specifying whether or not a profile created by CreateProfile
     96   // participates in safe browsing.
     97   enum SafeBrowsingDisposition {
     98     SAFE_BROWSING_OPT_OUT,
     99     SAFE_BROWSING_OPT_IN,
    100   };
    101 
    102   LastDownloadFinderTest() : profile_number_() {}
    103 
    104   virtual void SetUp() OVERRIDE {
    105     testing::Test::SetUp();
    106     profile_manager_.reset(
    107         new TestingProfileManager(TestingBrowserProcess::GetGlobal()));
    108     ASSERT_TRUE(profile_manager_->SetUp());
    109   }
    110 
    111   virtual void TearDown() OVERRIDE {
    112     // Shut down the history service on all profiles.
    113     std::vector<Profile*> profiles(
    114         profile_manager_->profile_manager()->GetLoadedProfiles());
    115     for (size_t i = 0; i < profiles.size(); ++i) {
    116       profiles[0]->AsTestingProfile()->DestroyHistoryService();
    117     }
    118     profile_manager_.reset();
    119     TestingBrowserProcess::DeleteInstance();
    120     testing::Test::TearDown();
    121   }
    122 
    123   TestingProfile* CreateProfile(SafeBrowsingDisposition safe_browsing_opt_in) {
    124     std::string profile_name("profile");
    125     profile_name.append(base::IntToString(++profile_number_));
    126 
    127     // Set up keyed service factories.
    128     TestingProfile::TestingFactories factories;
    129     // Build up a custom history service.
    130     factories.push_back(std::make_pair(HistoryServiceFactory::GetInstance(),
    131                                        &BuildHistoryService));
    132     // Suppress WebHistoryService since it makes network requests.
    133     factories.push_back(std::make_pair(
    134         WebHistoryServiceFactory::GetInstance(),
    135         static_cast<BrowserContextKeyedServiceFactory::TestingFactoryFunction>(
    136             NULL)));
    137 
    138     // Create prefs for the profile with safe browsing enabled or not.
    139     scoped_ptr<TestingPrefServiceSyncable> prefs(
    140         new TestingPrefServiceSyncable);
    141     chrome::RegisterUserProfilePrefs(prefs->registry());
    142     prefs->SetBoolean(prefs::kSafeBrowsingEnabled,
    143                       safe_browsing_opt_in == SAFE_BROWSING_OPT_IN);
    144 
    145     TestingProfile* profile = profile_manager_->CreateTestingProfile(
    146         profile_name,
    147         prefs.PassAs<PrefServiceSyncable>(),
    148         base::UTF8ToUTF16(profile_name),  // user_name
    149         0,                                // avatar_id
    150         std::string(),                    // supervised_user_id
    151         factories);
    152 
    153     return profile;
    154   }
    155 
    156   void AddDownload(Profile* profile, const history::DownloadRow& download) {
    157     base::RunLoop run_loop;
    158 
    159     HistoryService* history_service =
    160         HistoryServiceFactory::GetForProfile(profile, Profile::EXPLICIT_ACCESS);
    161     history_service->CreateDownload(
    162         download,
    163         base::Bind(&LastDownloadFinderTest::ContinueOnDownloadCreated,
    164                    base::Unretained(this),
    165                    run_loop.QuitClosure()));
    166     run_loop.Run();
    167   }
    168 
    169   // Wait for the history backend thread to process any outstanding tasks.
    170   // This is needed because HistoryService::QueryDownloads uses PostTaskAndReply
    171   // to do work on the backend thread and then invoke the caller's callback on
    172   // the originating thread. The PostTaskAndReplyRelay holds a reference to the
    173   // backend until its RunReplyAndSelfDestruct is called on the originating
    174   // thread. This reference MUST be released (on the originating thread,
    175   // remember) _before_ calling DestroyHistoryService in TearDown(). See the
    176   // giant comment in HistoryService::Cleanup explaining where the backend's
    177   // dtor must be run.
    178   void FlushHistoryBackend(Profile* profile) {
    179     base::RunLoop run_loop;
    180     HistoryServiceFactory::GetForProfile(profile, Profile::EXPLICIT_ACCESS)
    181         ->FlushForTest(run_loop.QuitClosure());
    182     run_loop.Run();
    183     // Then make sure anything bounced back to the main thread has been handled.
    184     base::RunLoop().RunUntilIdle();
    185   }
    186 
    187   // Runs the last download finder on all loaded profiles, returning the found
    188   // download or an empty pointer if none was found.
    189   scoped_ptr<safe_browsing::ClientIncidentReport_DownloadDetails>
    190   RunLastDownloadFinder() {
    191     base::RunLoop run_loop;
    192 
    193     scoped_ptr<safe_browsing::ClientIncidentReport_DownloadDetails>
    194         last_download;
    195 
    196     scoped_ptr<safe_browsing::LastDownloadFinder> finder(
    197         safe_browsing::LastDownloadFinder::Create(
    198             base::Bind(&LastDownloadFinderTest::OnLastDownload,
    199                        base::Unretained(this),
    200                        &last_download,
    201                        run_loop.QuitClosure())));
    202 
    203     if (finder)
    204       run_loop.Run();
    205 
    206     return last_download.Pass();
    207   }
    208 
    209   history::DownloadRow CreateTestDownloadRow() {
    210     base::Time now(base::Time::Now());
    211     return history::DownloadRow(
    212         base::FilePath(FILE_PATH_LITERAL("spam.exe")),
    213         base::FilePath(FILE_PATH_LITERAL("spam.exe")),
    214         std::vector<GURL>(1, GURL("http://www.google.com")),  // url_chain
    215         GURL(),                                               // referrer
    216         "application/octet-stream",                           // mime_type
    217         "application/octet-stream",                   // original_mime_type
    218         now - base::TimeDelta::FromMinutes(10),       // start
    219         now - base::TimeDelta::FromMinutes(9),        // end
    220         std::string(),                                // etag
    221         std::string(),                                // last_modified
    222         47LL,                                         // received
    223         47LL,                                         // total
    224         content::DownloadItem::COMPLETE,              // download_state
    225         content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,  // danger_type
    226         content::DOWNLOAD_INTERRUPT_REASON_NONE,      // interrupt_reason,
    227         1,                                            // id
    228         false,                                        // download_opened
    229         std::string(),                                // ext_id
    230         std::string());                               // ext_name
    231   }
    232 
    233   void ExpectNoDownloadFound(scoped_ptr<
    234       safe_browsing::ClientIncidentReport_DownloadDetails> download) {
    235     EXPECT_FALSE(download);
    236   }
    237 
    238   void ExpectFoundTestDownload(scoped_ptr<
    239       safe_browsing::ClientIncidentReport_DownloadDetails> download) {
    240     ASSERT_TRUE(download);
    241   }
    242 
    243   content::TestBrowserThreadBundle browser_thread_bundle_;
    244   scoped_ptr<TestingProfileManager> profile_manager_;
    245 
    246  private:
    247   // A HistoryService::DownloadCreateCallback that asserts that the download was
    248   // created and runs |closure|.
    249   void ContinueOnDownloadCreated(const base::Closure& closure, bool created) {
    250     ASSERT_TRUE(created);
    251     closure.Run();
    252   }
    253 
    254   // A HistoryService::DownloadCreateCallback that asserts that the download was
    255   // created.
    256   void OnDownloadCreated(bool created) { ASSERT_TRUE(created); }
    257 
    258   int profile_number_;
    259 };
    260 
    261 // Tests that nothing happens if there are no profiles at all.
    262 TEST_F(LastDownloadFinderTest, NoProfiles) {
    263   ExpectNoDownloadFound(RunLastDownloadFinder());
    264 }
    265 
    266 // Tests that nothing happens other than the callback being invoked if there are
    267 // no profiles participating in safe browsing.
    268 TEST_F(LastDownloadFinderTest, NoParticipatingProfiles) {
    269   // Create a profile with a history service that is opted-out
    270   TestingProfile* profile = CreateProfile(SAFE_BROWSING_OPT_OUT);
    271 
    272   // Add a download.
    273   AddDownload(profile, CreateTestDownloadRow());
    274 
    275   ExpectNoDownloadFound(RunLastDownloadFinder());
    276 }
    277 
    278 // Tests that a download is found from a single profile.
    279 TEST_F(LastDownloadFinderTest, SimpleEndToEnd) {
    280   // Create a profile with a history service that is opted-in.
    281   TestingProfile* profile = CreateProfile(SAFE_BROWSING_OPT_IN);
    282 
    283   // Add a download.
    284   AddDownload(profile, CreateTestDownloadRow());
    285 
    286   ExpectFoundTestDownload(RunLastDownloadFinder());
    287 }
    288 
    289 // Tests that there is no crash if the finder is deleted before results arrive.
    290 TEST_F(LastDownloadFinderTest, DeleteBeforeResults) {
    291   // Create a profile with a history service that is opted-in.
    292   TestingProfile* profile = CreateProfile(SAFE_BROWSING_OPT_IN);
    293 
    294   // Add a download.
    295   AddDownload(profile, CreateTestDownloadRow());
    296 
    297   // Start a finder and kill it before the search completes.
    298   safe_browsing::LastDownloadFinder::Create(
    299       base::Bind(&LastDownloadFinderTest::NeverCalled, base::Unretained(this)))
    300       .reset();
    301 
    302   // Flush tasks on the history backend thread.
    303   FlushHistoryBackend(profile);
    304 }
    305 
    306 // Tests that a download in profile added after the search is begun is found.
    307 TEST_F(LastDownloadFinderTest, AddProfileAfterStarting) {
    308   // Create a profile with a history service that is opted-in.
    309   CreateProfile(SAFE_BROWSING_OPT_IN);
    310 
    311   scoped_ptr<safe_browsing::ClientIncidentReport_DownloadDetails> last_download;
    312   base::RunLoop run_loop;
    313 
    314   // Post a task that will create a second profile once the main loop is run.
    315   base::MessageLoop::current()->PostTask(
    316       FROM_HERE,
    317       base::Bind(&LastDownloadFinderTest::CreateProfileWithDownload,
    318                  base::Unretained(this)));
    319 
    320   // Create a finder that we expect will find a download in the second profile.
    321   scoped_ptr<safe_browsing::LastDownloadFinder> finder(
    322       safe_browsing::LastDownloadFinder::Create(
    323           base::Bind(&LastDownloadFinderTest::OnLastDownload,
    324                      base::Unretained(this),
    325                      &last_download,
    326                      run_loop.QuitClosure())));
    327 
    328   run_loop.Run();
    329 
    330   ExpectFoundTestDownload(last_download.Pass());
    331 }
    332