#ifdef UNSAFE_BUFFERS_BUILD
#pragma allow_unsafe_buffers
#endif
#include "chrome/browser/download/download_target_determiner.h"
#include <stddef.h>
#include <stdint.h>
#include <optional>
#include <string>
#include <vector>
#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "base/json/values_util.h"
#include "base/location.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/raw_ref.h"
#include "base/observer_list.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/strings/string_util.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/test_future.h"
#include "build/build_config.h"
#include "chrome/browser/download/chrome_download_manager_delegate.h"
#include "chrome/browser/download/download_confirmation_result.h"
#include "chrome/browser/download/download_crx_util.h"
#include "chrome/browser/download/download_prefs.h"
#include "chrome/browser/download/download_prompt_status.h"
#include "chrome/browser/download/download_stats.h"
#include "chrome/browser/history/history_service_factory.h"
#include "chrome/common/buildflags.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "chrome/test/base/testing_profile.h"
#include "components/download/public/common/download_interrupt_reasons.h"
#include "components/download/public/common/download_target_info.h"
#include "components/download/public/common/mock_download_item.h"
#include "components/history/core/browser/history_service.h"
#include "components/history/core/browser/history_types.h"
#include "components/prefs/pref_service.h"
#include "components/safe_browsing/content/common/file_type_policies.h"
#include "components/safe_browsing/content/common/file_type_policies_test_util.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "components/user_manager/scoped_user_manager.h"
#include "content/public/browser/download_item_utils.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_delegate.h"
#include "content/public/test/test_renderer_host.h"
#include "content/public/test/web_contents_tester.h"
#include "extensions/buildflags/buildflags.h"
#include "net/base/mime_util.h"
#include "ppapi/buildflags/buildflags.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/shell_dialogs/selected_file_info.h"
#include "url/origin.h"
#if BUILDFLAG(ENABLE_PLUGINS)
#include "content/public/browser/plugin_service.h"
#include "content/public/browser/plugin_service_filter.h"
#include "content/public/common/webplugininfo.h"
#endif
#if BUILDFLAG(ENABLE_EXTENSIONS)
#include "extensions/common/extension.h"
#endif
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
#include "chrome/browser/ash/policy/dlp/dlp_files_controller_ash.h"
#include "chrome/browser/chromeos/policy/dlp/dlp_file_destination.h"
#include "chrome/browser/chromeos/policy/dlp/dlp_rules_manager.h"
#include "chrome/browser/chromeos/policy/dlp/dlp_rules_manager_factory.h"
#include "chrome/browser/chromeos/policy/dlp/test/mock_dlp_rules_manager.h"
#endif
DownloadItem;
DownloadPathReservationTracker;
_;
AnyNumber;
Invoke;
Ref;
Return;
ReturnRef;
ReturnRefOfCopy;
Truly;
WithArg;
ConflictAction;
DownloadFileType;
FileTypePolicies;
namespace {
const char kTransientPathGenerationHistogram[] = …;
const char kTransientPathValidationHistogram[] = …;
template <typename T>
base::HistogramBase::Sample ToHistogramSample(T t) { … }
class NullWebContentsDelegate : public content::WebContentsDelegate { … };
ACTION_P(ScheduleCallback, result0) { … }
ACTION_P2(ScheduleCallback2, result0, result1) { … }
enum TestCaseType { … };
enum TestCaseExpectIntermediate { … };
struct DownloadTestCase { … };
class MockDownloadTargetDeterminerDelegate
: public DownloadTargetDeterminerDelegate { … };
class DownloadTargetDeterminerTest : public ChromeRenderViewHostTestHarness { … };
void DownloadTargetDeterminerTest::SetUp() { … }
void DownloadTargetDeterminerTest::TearDown() { … }
std::unique_ptr<download::MockDownloadItem>
DownloadTargetDeterminerTest::CreateActiveDownloadItem(
int32_t id,
const DownloadTestCase& test_case) { … }
void DownloadTargetDeterminerTest::EnableAutoOpenByUserBasedOnExtension(
const base::FilePath& path) { … }
void DownloadTargetDeterminerTest::SetManagedDownloadPath(
const base::FilePath& path) { … }
void DownloadTargetDeterminerTest::SetPromptForDownload(bool prompt) { … }
base::FilePath DownloadTargetDeterminerTest::GetPathInDownloadDir(
const base::FilePath::StringType& relative_path) { … }
void DownloadTargetDeterminerTest::RunTestCase(
const DownloadTestCase& test_case,
const base::FilePath& initial_virtual_path,
download::MockDownloadItem* item) { … }
DownloadTargetDeterminerTest::TargetInfoAndDangerLevel
DownloadTargetDeterminerTest::RunDownloadTargetDeterminer(
const base::FilePath& initial_virtual_path,
download::MockDownloadItem* item) { … }
void DownloadTargetDeterminerTest::RunTestCasesWithActiveItem(
const DownloadTestCase test_cases[],
size_t test_case_count) { … }
void DownloadTargetDeterminerTest::VerifyDownloadTarget(
const DownloadTestCase& test_case,
TargetInfoAndDangerLevel info) { … }
void DownloadTargetDeterminerTest::SetUpFileTypePolicies() { … }
void MockDownloadTargetDeterminerDelegate::NullPromptUser(
DownloadItem* download,
const base::FilePath& suggested_path,
DownloadConfirmationReason reason,
ConfirmationCallback& callback) { … }
void MockDownloadTargetDeterminerDelegate::NullDetermineLocalPath(
DownloadItem* download,
const base::FilePath& virtual_path,
download::LocalPathCallback& callback) { … }
void NotifyExtensionsOverridePath(
download::DownloadItem* download,
const base::FilePath& path,
DownloadTargetDeterminerDelegate::NotifyExtensionsCallback& callback) { … }
TEST_F(DownloadTargetDeterminerTest, Basic) { … }
TEST_F(DownloadTargetDeterminerTest, CancelSaveAs) { … }
TEST_F(DownloadTargetDeterminerTest, DangerousUrl) { … }
TEST_F(DownloadTargetDeterminerTest, MaybeDangerousContent) { … }
TEST_F(DownloadTargetDeterminerTest, LastSavePath) { … }
TEST_F(DownloadTargetDeterminerTest, DefaultVirtual) { … }
#if BUILDFLAG(ENABLE_PLUGINS)
TEST_F(DownloadTargetDeterminerTest,
DetermineIfHandledSafelyHelperSynchronous) { … }
#endif
TEST_F(DownloadTargetDeterminerTest, InactiveDownload) { … }
TEST_F(DownloadTargetDeterminerTest, ReservationFailed_Confirmation) { … }
TEST_F(DownloadTargetDeterminerTest, LocalPathFailed) { … }
TEST_F(DownloadTargetDeterminerTest, VisitedReferrer) { … }
TEST_F(DownloadTargetDeterminerTest, TransitionType) { … }
TEST_F(DownloadTargetDeterminerTest, PromptAlways_SafeAutomatic) { … }
TEST_F(DownloadTargetDeterminerTest, PromptAlways_SafeSaveAs) { … }
TEST_F(DownloadTargetDeterminerTest, PromptAlways_SafeForced) { … }
TEST_F(DownloadTargetDeterminerTest, PromptAlways_AutoOpen) { … }
TEST_F(DownloadTargetDeterminerTest, ContinueWithoutConfirmation_SaveAs) { … }
TEST_F(DownloadTargetDeterminerTest, ContinueWithConfirmation_SaveAs) { … }
#if BUILDFLAG(ENABLE_EXTENSIONS)
TEST_F(DownloadTargetDeterminerTest, PromptAlways_NonTrustedExtension) { … }
TEST_F(DownloadTargetDeterminerTest, PromptAlways_TrustedExtension) { … }
#endif
TEST_F(DownloadTargetDeterminerTest, ManagedPath) { … }
TEST_F(DownloadTargetDeterminerTest, BlockDownloads) { … }
TEST_F(DownloadTargetDeterminerTest, NotifyExtensionsSafe) { … }
TEST_F(DownloadTargetDeterminerTest, NotifyExtensionsUnsafe) { … }
TEST_F(DownloadTargetDeterminerTest, NotifyExtensionsConflict) { … }
TEST_F(DownloadTargetDeterminerTest, NotifyExtensionsDefaultPath) { … }
TEST_F(DownloadTargetDeterminerTest, NotifyExtensionsLocalFile) { … }
TEST_F(DownloadTargetDeterminerTest, InitialVirtualPathUnsafe) { … }
TEST_F(DownloadTargetDeterminerTest, ResumedNoPrompt) { … }
TEST_F(DownloadTargetDeterminerTest, ResumedForcedDownload) { … }
TEST_F(DownloadTargetDeterminerTest, ResumedWithPrompt) { … }
TEST_F(DownloadTargetDeterminerTest, IntermediateNameForResumed) { … }
TEST_F(DownloadTargetDeterminerTest, MIMETypeDetermination) { … }
TEST_F(DownloadTargetDeterminerTest, MimeTypeFileExtension) { … }
TEST_F(DownloadTargetDeterminerTest, ResumedWithUserValidatedDownload) { … }
TEST_F(DownloadTargetDeterminerTest, TransientDownload) { … }
TEST_F(DownloadTargetDeterminerTest, TransientDownloadResumption) { … }
#if BUILDFLAG(IS_WIN)
TEST_F(DownloadTargetDeterminerTest, TestSanitizeEnvVariable) {
const DownloadTestCase kSaveEnvPathTestCases[] = {
{
SAVE_AS, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS, "http://example.com/f%oo%.tx%xyz%t",
"text/plain", FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("f.txt"), DownloadItem::TARGET_DISPOSITION_PROMPT,
EXPECT_CRDOWNLOAD},
{
SAVE_AS, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.ln%xyz%k",
"application/octet-stream", FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("foo.download"),
DownloadItem::TARGET_DISPOSITION_PROMPT,
EXPECT_CRDOWNLOAD},
{
SAVE_AS, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS, "http://example.com/foo2.lnk.%%",
"application/octet-stream", FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("foo2.download"),
DownloadItem::TARGET_DISPOSITION_PROMPT,
EXPECT_CRDOWNLOAD},
{
SAVE_AS, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS, "http://example.com/%foo.txt%",
"text/plain", FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("download"), DownloadItem::TARGET_DISPOSITION_PROMPT,
EXPECT_CRDOWNLOAD}};
RunTestCasesWithActiveItem(kSaveEnvPathTestCases,
std::size(kSaveEnvPathTestCases));
}
#endif
#if BUILDFLAG(ENABLE_PLUGINS)
void DummyGetPluginsCallback(
base::OnceClosure closure,
const std::vector<content::WebPluginInfo>& plugins) { … }
void ForceRefreshOfPlugins() { … }
class MockPluginServiceFilter : public content::PluginServiceFilter { … };
class ScopedRegisterInternalPlugin { … };
class DownloadTargetDeterminerTestWithPlugin
: public DownloadTargetDeterminerTest { … };
TEST_F(DownloadTargetDeterminerTestWithPlugin, CheckForSecureHandling_PPAPI) { … }
TEST_F(DownloadTargetDeterminerTestWithPlugin,
CheckForSecureHandling_BrowserPlugin) { … }
#endif
#if BUILDFLAG(IS_ANDROID)
TEST_F(DownloadTargetDeterminerTest, DetermineLocalPathReturnsContentUri) {
const DownloadTestCase kLocalPathContentUriCase = {
AUTOMATIC,
download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS,
"http://example.com/foo.txt",
"text/plain",
FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("content://media/123"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECT_LOCAL_PATH};
std::unique_ptr<download::MockDownloadItem> item =
CreateActiveDownloadItem(0, kLocalPathContentUriCase);
download_prefs()->SetDownloadPath(test_virtual_dir());
EXPECT_CALL(
*delegate(),
DetermineLocalPath_(
_, GetPathInDownloadDir(FILE_PATH_LITERAL("virtual/foo.txt")), _))
.WillOnce(WithArg<2>(ScheduleCallback2(
base::FilePath("content://media/123"), base::FilePath("foor.txt"))));
TargetInfoAndDangerLevel info =
RunDownloadTargetDeterminer(base::FilePath(), item.get());
EXPECT_EQ(info.target_info.display_name.value(), "foor.txt");
EXPECT_EQ(info.target_info.target_path.value(), "content://media/123");
}
#endif
#if BUILDFLAG(IS_CHROMEOS_ASH)
class DownloadTargetDeterminerDlpTest : public DownloadTargetDeterminerTest {
protected:
DownloadTargetDeterminerDlpTest()
: profile_(std::make_unique<TestingProfile>()),
user_manager_(new ash::FakeChromeUserManager()),
scoped_user_manager_(std::make_unique<user_manager::ScopedUserManager>(
base::WrapUnique(user_manager_.get()))) {}
class MockFilesController : public policy::DlpFilesControllerAsh {
public:
explicit MockFilesController(const policy::DlpRulesManager& rules_manager,
Profile* profile)
: DlpFilesControllerAsh(rules_manager, profile) {}
~MockFilesController() override = default;
MOCK_METHOD(bool,
ShouldPromptBeforeDownload,
(const policy::DlpFileDestination&, const base::FilePath&),
(override));
};
void SetUp() override {
DownloadTargetDeterminerTest::SetUp();
AccountId account_id =
AccountId::FromUserEmailGaiaId("[email protected]", "12345");
profile_->SetIsNewProfile(true);
user_manager::User* user =
user_manager_->AddUserWithAffiliationAndTypeAndProfile(
account_id, false,
user_manager::UserType::kRegular, profile_.get());
user_manager_->UserLoggedIn(account_id, user->username_hash(),
false,
false);
user_manager_->SimulateUserProfileLoad(account_id);
}
void TearDown() override {
mock_files_controller_.reset();
scoped_user_manager_.reset();
profile_.reset();
DownloadTargetDeterminerTest::TearDown();
}
std::unique_ptr<KeyedService> SetDlpRulesManager(
content::BrowserContext* context) {
auto dlp_rules_manager =
std::make_unique<testing::NiceMock<policy::MockDlpRulesManager>>(
Profile::FromBrowserContext(context));
rules_manager_ = dlp_rules_manager.get();
return dlp_rules_manager;
}
void SetupRulesManager() {
policy::DlpRulesManagerFactory::GetInstance()->SetTestingFactory(
profile_.get(),
base::BindRepeating(
&DownloadTargetDeterminerDlpTest::SetDlpRulesManager,
base::Unretained(this)));
ASSERT_TRUE(policy::DlpRulesManagerFactory::GetForPrimaryProfile());
ON_CALL(*rules_manager_, IsFilesPolicyEnabled)
.WillByDefault(testing::Return(true));
mock_files_controller_ =
std::make_unique<MockFilesController>(*rules_manager_, profile_.get());
ON_CALL(*rules_manager_, GetDlpFilesController)
.WillByDefault(testing::Return(mock_files_controller_.get()));
}
std::unique_ptr<TestingProfile> profile_;
raw_ptr<ash::FakeChromeUserManager, DanglingUntriaged> user_manager_;
std::unique_ptr<user_manager::ScopedUserManager> scoped_user_manager_;
raw_ptr<policy::MockDlpRulesManager, DanglingUntriaged> rules_manager_ =
nullptr;
std::unique_ptr<MockFilesController> mock_files_controller_ = nullptr;
};
TEST_F(DownloadTargetDeterminerDlpTest, InvalidUrl) {
SetupRulesManager();
const DownloadTestCase kManagedPathTestCase = {
AUTOMATIC,
download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS,
"",
"text/plain",
FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("download.txt"),
DownloadItem::TARGET_DISPOSITION_PROMPT,
EXPECT_CRDOWNLOAD};
SetManagedDownloadPath(test_download_dir());
ASSERT_TRUE(download_prefs()->IsDownloadPathManaged());
EXPECT_CALL(*delegate(),
RequestConfirmation_(
_, GetPathInDownloadDir(FILE_PATH_LITERAL("download.txt")),
DownloadConfirmationReason::DLP_BLOCKED, _));
EXPECT_CALL(*mock_files_controller_, ShouldPromptBeforeDownload).Times(0);
RunTestCasesWithActiveItem(&kManagedPathTestCase, 1);
}
TEST_F(DownloadTargetDeterminerDlpTest, ManagedPath_ShouldPrompt) {
SetupRulesManager();
const DownloadTestCase kManagedPathTestCase = {
AUTOMATIC,
download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS,
"http://example.com/foo.txt",
"text/plain",
FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("foo.txt"),
DownloadItem::TARGET_DISPOSITION_PROMPT,
EXPECT_CRDOWNLOAD};
SetManagedDownloadPath(test_download_dir());
ASSERT_TRUE(download_prefs()->IsDownloadPathManaged());
EXPECT_CALL(*delegate(),
RequestConfirmation_(
_, GetPathInDownloadDir(FILE_PATH_LITERAL("foo.txt")),
DownloadConfirmationReason::DLP_BLOCKED, _));
EXPECT_CALL(*mock_files_controller_, ShouldPromptBeforeDownload)
.WillOnce(testing::Return(true));
RunTestCasesWithActiveItem(&kManagedPathTestCase, 1);
}
TEST_F(DownloadTargetDeterminerDlpTest, PromptAlways_SafeAutomatic) {
SetupRulesManager();
const DownloadTestCase kSafeAutomatic = {
AUTOMATIC,
download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS,
"http://example.com/automatic.txt",
"text/plain",
FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("automatic.txt"),
DownloadItem::TARGET_DISPOSITION_PROMPT,
EXPECT_CRDOWNLOAD};
SetPromptForDownload(false);
EXPECT_CALL(*mock_files_controller_, ShouldPromptBeforeDownload)
.WillOnce(testing::Return(true));
EXPECT_CALL(*delegate(),
RequestConfirmation_(
_, GetPathInDownloadDir(FILE_PATH_LITERAL("automatic.txt")),
DownloadConfirmationReason::DLP_BLOCKED, _));
RunTestCasesWithActiveItem(&kSafeAutomatic, 1);
}
#endif
}