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.
5 #include "chrome/browser/safe_browsing/incident_reporting/last_download_finder.h"
10 #include "base/bind.h"
11 #include "base/callback.h"
12 #include "base/files/file_path.h"
13 #include "base/files/file_util.h"
14 #include "base/location.h"
15 #include "base/memory/scoped_ptr.h"
16 #include "base/run_loop.h"
17 #include "base/single_thread_task_runner.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "base/thread_task_runner_handle.h"
21 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
22 #include "chrome/browser/history/chrome_history_client.h"
23 #include "chrome/browser/history/history_service_factory.h"
24 #include "chrome/browser/history/web_history_service_factory.h"
25 #include "chrome/browser/prefs/browser_prefs.h"
26 #include "chrome/browser/profiles/profile_manager.h"
27 #include "chrome/common/chrome_constants.h"
28 #include "chrome/common/pref_names.h"
29 #include "chrome/common/safe_browsing/csd.pb.h"
30 #include "chrome/test/base/testing_browser_process.h"
31 #include "chrome/test/base/testing_profile.h"
32 #include "chrome/test/base/testing_profile_manager.h"
33 #include "components/history/content/browser/content_visit_delegate.h"
34 #include "components/history/content/browser/download_constants_utils.h"
35 #include "components/history/content/browser/history_database_helper.h"
36 #include "components/history/core/browser/download_constants.h"
37 #include "components/history/core/browser/download_row.h"
38 #include "components/history/core/browser/history_constants.h"
39 #include "components/history/core/browser/history_database_params.h"
40 #include "components/history/core/browser/history_service.h"
41 #include "components/syncable_prefs/testing_pref_service_syncable.h"
42 #include "content/public/test/test_browser_thread_bundle.h"
43 #include "content/public/test/test_utils.h"
44 #include "testing/gtest/include/gtest/gtest.h"
48 // A BrowserContextKeyedServiceFactory::TestingFactoryFunction that creates a
49 // HistoryService for a TestingProfile.
50 scoped_ptr
<KeyedService
> BuildHistoryService(content::BrowserContext
* context
) {
51 TestingProfile
* profile
= static_cast<TestingProfile
*>(context
);
53 // Delete the file before creating the service.
54 base::FilePath
history_path(
55 profile
->GetPath().Append(history::kHistoryFilename
));
56 if (!base::DeleteFile(history_path
, false) ||
57 base::PathExists(history_path
)) {
58 ADD_FAILURE() << "failed to delete history db file "
59 << history_path
.value();
63 scoped_ptr
<history::HistoryService
> history_service(
64 new history::HistoryService(
65 make_scoped_ptr(new ChromeHistoryClient(
66 BookmarkModelFactory::GetForProfile(profile
))),
67 scoped_ptr
<history::VisitDelegate
>()));
68 if (history_service
->Init(
69 profile
->GetPrefs()->GetString(prefs::kAcceptLanguages
),
70 history::HistoryDatabaseParamsForPath(profile
->GetPath()))) {
71 return history_service
.Pass();
74 ADD_FAILURE() << "failed to initialize history service";
79 static const base::FilePath::CharType kBinaryFileName
[] =
80 FILE_PATH_LITERAL("spam.exe");
81 static const base::FilePath::CharType kBinaryFileNameForOtherOS
[] =
82 FILE_PATH_LITERAL("spam.dmg");
83 #elif defined(OS_MACOSX)
84 static const base::FilePath::CharType kBinaryFileName
[] =
85 FILE_PATH_LITERAL("spam.dmg");
86 static const base::FilePath::CharType kBinaryFileNameForOtherOS
[] =
87 FILE_PATH_LITERAL("spam.apk");
88 #elif defined(OS_ANDROID)
89 static const base::FilePath::CharType kBinaryFileName
[] =
90 FILE_PATH_LITERAL("spam.apk");
91 static const base::FilePath::CharType kBinaryFileNameForOtherOS
[] =
92 FILE_PATH_LITERAL("spam.dmg");
94 static const base::FilePath::CharType kBinaryFileName
[] =
95 FILE_PATH_LITERAL("spam.exe");
100 namespace safe_browsing
{
102 class LastDownloadFinderTest
: public testing::Test
{
104 void NeverCalled(scoped_ptr
<ClientIncidentReport_DownloadDetails
> download
) {
108 // Creates a new profile that participates in safe browsing and adds a
109 // download to its history.
110 void CreateProfileWithDownload() {
111 TestingProfile
* profile
= CreateProfile(SAFE_BROWSING_OPT_IN
);
112 history::HistoryService
* history_service
=
113 HistoryServiceFactory::GetForProfile(
114 profile
, ServiceAccessType::EXPLICIT_ACCESS
);
115 history_service
->CreateDownload(
116 CreateTestDownloadRow(kBinaryFileName
),
117 base::Bind(&LastDownloadFinderTest::OnDownloadCreated
,
118 base::Unretained(this)));
121 // LastDownloadFinder::LastDownloadCallback implementation that
122 // passes the found download to |result| and then runs a closure.
124 scoped_ptr
<ClientIncidentReport_DownloadDetails
>* result
,
125 const base::Closure
& quit_closure
,
126 scoped_ptr
<ClientIncidentReport_DownloadDetails
> download
) {
127 *result
= download
.Pass();
132 // A type for specifying whether or not a profile created by CreateProfile
133 // participates in safe browsing.
134 enum SafeBrowsingDisposition
{
135 SAFE_BROWSING_OPT_OUT
,
136 SAFE_BROWSING_OPT_IN
,
139 LastDownloadFinderTest() : profile_number_() {}
141 void SetUp() override
{
142 testing::Test::SetUp();
143 profile_manager_
.reset(
144 new TestingProfileManager(TestingBrowserProcess::GetGlobal()));
145 ASSERT_TRUE(profile_manager_
->SetUp());
148 void TearDown() override
{
149 // Shut down the history service on all profiles.
150 std::vector
<Profile
*> profiles(
151 profile_manager_
->profile_manager()->GetLoadedProfiles());
152 for (size_t i
= 0; i
< profiles
.size(); ++i
) {
153 profiles
[0]->AsTestingProfile()->DestroyHistoryService();
155 profile_manager_
.reset();
156 TestingBrowserProcess::DeleteInstance();
157 testing::Test::TearDown();
160 TestingProfile
* CreateProfile(SafeBrowsingDisposition safe_browsing_opt_in
) {
161 std::string
profile_name("profile");
162 profile_name
.append(base::IntToString(++profile_number_
));
164 // Set up keyed service factories.
165 TestingProfile::TestingFactories factories
;
166 // Build up a custom history service.
167 factories
.push_back(std::make_pair(HistoryServiceFactory::GetInstance(),
168 &BuildHistoryService
));
169 // Suppress WebHistoryService since it makes network requests.
170 factories
.push_back(std::make_pair(
171 WebHistoryServiceFactory::GetInstance(),
172 static_cast<BrowserContextKeyedServiceFactory::TestingFactoryFunction
>(
175 // Create prefs for the profile with safe browsing enabled or not.
176 scoped_ptr
<syncable_prefs::TestingPrefServiceSyncable
> prefs(
177 new syncable_prefs::TestingPrefServiceSyncable
);
178 chrome::RegisterUserProfilePrefs(prefs
->registry());
179 prefs
->SetBoolean(prefs::kSafeBrowsingEnabled
,
180 safe_browsing_opt_in
== SAFE_BROWSING_OPT_IN
);
182 TestingProfile
* profile
= profile_manager_
->CreateTestingProfile(
185 base::UTF8ToUTF16(profile_name
), // user_name
187 std::string(), // supervised_user_id
193 LastDownloadFinder::DownloadDetailsGetter
GetDownloadDetailsGetter() {
194 return base::Bind(&LastDownloadFinderTest::GetDownloadDetails
,
195 base::Unretained(this));
198 void AddDownload(Profile
* profile
, const history::DownloadRow
& download
) {
199 base::RunLoop run_loop
;
201 history::HistoryService
* history_service
=
202 HistoryServiceFactory::GetForProfile(
203 profile
, ServiceAccessType::EXPLICIT_ACCESS
);
204 history_service
->CreateDownload(
206 base::Bind(&LastDownloadFinderTest::ContinueOnDownloadCreated
,
207 base::Unretained(this),
208 run_loop
.QuitClosure()));
212 // Wait for the history backend thread to process any outstanding tasks.
213 // This is needed because HistoryService::QueryDownloads uses PostTaskAndReply
214 // to do work on the backend thread and then invoke the caller's callback on
215 // the originating thread. The PostTaskAndReplyRelay holds a reference to the
216 // backend until its RunReplyAndSelfDestruct is called on the originating
217 // thread. This reference MUST be released (on the originating thread,
218 // remember) _before_ calling DestroyHistoryService in TearDown(). See the
219 // giant comment in HistoryService::Cleanup explaining where the backend's
221 void FlushHistoryBackend(Profile
* profile
) {
222 base::RunLoop run_loop
;
223 HistoryServiceFactory::GetForProfile(profile
,
224 ServiceAccessType::EXPLICIT_ACCESS
)
225 ->FlushForTest(run_loop
.QuitClosure());
227 // Then make sure anything bounced back to the main thread has been handled.
228 base::RunLoop().RunUntilIdle();
231 // Runs the last download finder on all loaded profiles, returning the found
232 // download or an empty pointer if none was found.
233 scoped_ptr
<ClientIncidentReport_DownloadDetails
> RunLastDownloadFinder() {
234 base::RunLoop run_loop
;
236 scoped_ptr
<ClientIncidentReport_DownloadDetails
> last_download
;
238 scoped_ptr
<LastDownloadFinder
> finder(LastDownloadFinder::Create(
239 GetDownloadDetailsGetter(),
240 base::Bind(&LastDownloadFinderTest::OnLastDownload
,
241 base::Unretained(this),
243 run_loop
.QuitClosure())));
248 return last_download
.Pass();
251 history::DownloadRow
CreateTestDownloadRow(
252 const base::FilePath::CharType
* file_path
) {
253 base::Time
now(base::Time::Now());
254 return history::DownloadRow(
255 base::FilePath(file_path
), base::FilePath(file_path
),
256 std::vector
<GURL
>(1, GURL("http://www.google.com")), // url_chain
258 "application/octet-stream", // mime_type
259 "application/octet-stream", // original_mime_type
260 now
- base::TimeDelta::FromMinutes(10), // start
261 now
- base::TimeDelta::FromMinutes(9), // end
262 std::string(), // etag
263 std::string(), // last_modified
266 history::DownloadState::COMPLETE
, // download_state
267 history::DownloadDangerType::NOT_DANGEROUS
, // danger_type
268 history::ToHistoryDownloadInterruptReason(
269 content::DOWNLOAD_INTERRUPT_REASON_NONE
), // interrupt_reason,
271 false, // download_opened
272 std::string(), // ext_id
273 std::string()); // ext_name
276 void ExpectNoDownloadFound(
277 scoped_ptr
<ClientIncidentReport_DownloadDetails
> download
) {
278 EXPECT_FALSE(download
);
281 void ExpectFoundTestDownload(
282 scoped_ptr
<ClientIncidentReport_DownloadDetails
> download
) {
283 ASSERT_TRUE(download
);
286 content::TestBrowserThreadBundle browser_thread_bundle_
;
287 scoped_ptr
<TestingProfileManager
> profile_manager_
;
290 // A HistoryService::DownloadCreateCallback that asserts that the download was
291 // created and runs |closure|.
292 void ContinueOnDownloadCreated(const base::Closure
& closure
, bool created
) {
293 ASSERT_TRUE(created
);
297 // A HistoryService::DownloadCreateCallback that asserts that the download was
299 void OnDownloadCreated(bool created
) { ASSERT_TRUE(created
); }
301 void GetDownloadDetails(
302 content::BrowserContext
* context
,
303 const DownloadMetadataManager::GetDownloadDetailsCallback
& callback
) {
304 callback
.Run(scoped_ptr
<ClientIncidentReport_DownloadDetails
>());
310 // Tests that nothing happens if there are no profiles at all.
311 TEST_F(LastDownloadFinderTest
, NoProfiles
) {
312 ExpectNoDownloadFound(RunLastDownloadFinder());
315 // Tests that nothing happens other than the callback being invoked if there are
316 // no profiles participating in safe browsing.
317 TEST_F(LastDownloadFinderTest
, NoParticipatingProfiles
) {
318 // Create a profile with a history service that is opted-out
319 TestingProfile
* profile
= CreateProfile(SAFE_BROWSING_OPT_OUT
);
322 AddDownload(profile
, CreateTestDownloadRow(kBinaryFileName
));
324 ExpectNoDownloadFound(RunLastDownloadFinder());
327 // Tests that a download is found from a single profile.
328 TEST_F(LastDownloadFinderTest
, SimpleEndToEnd
) {
329 // Create a profile with a history service that is opted-in.
330 TestingProfile
* profile
= CreateProfile(SAFE_BROWSING_OPT_IN
);
333 AddDownload(profile
, CreateTestDownloadRow(kBinaryFileName
));
335 ExpectFoundTestDownload(RunLastDownloadFinder());
338 #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_ANDROID)
339 // Tests that nothing happens if the binary is an executable for a different OS.
340 TEST_F(LastDownloadFinderTest
, DownloadForDifferentOs
) {
341 // Create a profile with a history service that is opted-in.
342 TestingProfile
* profile
= CreateProfile(SAFE_BROWSING_OPT_IN
);
345 AddDownload(profile
, CreateTestDownloadRow(kBinaryFileNameForOtherOS
));
347 ExpectNoDownloadFound(RunLastDownloadFinder());
351 // Tests that there is no crash if the finder is deleted before results arrive.
352 TEST_F(LastDownloadFinderTest
, DeleteBeforeResults
) {
353 // Create a profile with a history service that is opted-in.
354 TestingProfile
* profile
= CreateProfile(SAFE_BROWSING_OPT_IN
);
357 AddDownload(profile
, CreateTestDownloadRow(kBinaryFileName
));
359 // Start a finder and kill it before the search completes.
360 LastDownloadFinder::Create(GetDownloadDetailsGetter(),
361 base::Bind(&LastDownloadFinderTest::NeverCalled
,
362 base::Unretained(this))).reset();
364 // Flush tasks on the history backend thread.
365 FlushHistoryBackend(profile
);
368 // Tests that a download in profile added after the search is begun is found.
369 TEST_F(LastDownloadFinderTest
, AddProfileAfterStarting
) {
370 // Create a profile with a history service that is opted-in.
371 CreateProfile(SAFE_BROWSING_OPT_IN
);
373 scoped_ptr
<ClientIncidentReport_DownloadDetails
> last_download
;
374 base::RunLoop run_loop
;
376 // Post a task that will create a second profile once the main loop is run.
377 base::ThreadTaskRunnerHandle::Get()->PostTask(
378 FROM_HERE
, base::Bind(&LastDownloadFinderTest::CreateProfileWithDownload
,
379 base::Unretained(this)));
381 // Create a finder that we expect will find a download in the second profile.
382 scoped_ptr
<LastDownloadFinder
> finder(LastDownloadFinder::Create(
383 GetDownloadDetailsGetter(),
384 base::Bind(&LastDownloadFinderTest::OnLastDownload
,
385 base::Unretained(this),
387 run_loop
.QuitClosure())));
391 ExpectFoundTestDownload(last_download
.Pass());
394 } // namespace safe_browsing