blob: c54307488cd205440542200ac1d018cc8aea3c71 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include <set>
#include <string>
#include "base/bind.h"
#include "base/files/file_path.h"
#include "base/files/scoped_temp_dir.h"
#include "base/optional.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/bind_test_util.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/enterprise/connectors/connectors_manager.h"
#include "chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.h"
#include "chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_test_utils.h"
#include "chrome/browser/safe_browsing/cloud_content_scanning/fake_deep_scanning_dialog_delegate.h"
#include "chrome/browser/safe_browsing/dm_token_utils.h"
#include "chrome/browser/ui/tab_contents/chrome_web_contents_view_handle_drop.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
#include "chrome/test/base/testing_profile_manager.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
#include "components/safe_browsing/core/features.h"
#include "components/safe_browsing/core/proto/webprotect.pb.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/drop_data.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
class ChromeWebContentsViewDelegateHandleOnPerformDrop
: public testing::TestWithParam<bool> {
public:
ChromeWebContentsViewDelegateHandleOnPerformDrop() {
EXPECT_TRUE(profile_manager_.SetUp());
profile_ = profile_manager_.CreateTestingProfile("test-user");
if (use_legacy_policies()) {
scoped_feature_list_.InitAndEnableFeature(
safe_browsing::kContentComplianceEnabled);
} else {
scoped_feature_list_.InitWithFeatures(
{safe_browsing::kContentComplianceEnabled,
enterprise_connectors::kEnterpriseConnectorsEnabled},
{});
}
}
void SetUp() override {
if (!use_legacy_policies()) {
enterprise_connectors::ConnectorsManager::GetInstance()
->SetUpForTesting();
}
}
void TearDown() override {
if (!use_legacy_policies()) {
enterprise_connectors::ConnectorsManager::GetInstance()
->TearDownForTesting();
}
}
bool use_legacy_policies() const { return GetParam(); }
void RunUntilDone() { run_loop_->Run(); }
content::WebContents* contents() {
if (!web_contents_) {
content::WebContents::CreateParams params(profile_);
web_contents_ = content::WebContents::Create(params);
}
return web_contents_.get();
}
void EnableDeepScanning(bool enable, bool scan_succeeds) {
SetScanPolicies(enable ? safe_browsing::CHECK_UPLOADS
: safe_browsing::CHECK_NONE);
run_loop_.reset(new base::RunLoop());
using FakeDelegate = safe_browsing::FakeDeepScanningDialogDelegate;
using Verdict = safe_browsing::DlpDeepScanningVerdict;
auto is_encrypted_callback =
base::BindRepeating([](const base::FilePath&) { return false; });
safe_browsing::SetDMTokenForTesting(
policy::DMToken::CreateValidTokenForTesting("dm_token"));
if (use_legacy_policies()) {
auto callback = base::BindLambdaForTesting(
[this, scan_succeeds](const base::FilePath&)
-> safe_browsing::DeepScanningClientResponse {
current_requests_count_++;
return scan_succeeds ? FakeDelegate::SuccessfulResponse()
: FakeDelegate::DlpResponse(
Verdict::SUCCESS, "block_rule",
Verdict::TriggeredRule::BLOCK);
});
safe_browsing::DeepScanningDialogDelegate::SetFactoryForTesting(
base::BindRepeating(
&safe_browsing::FakeDeepScanningDialogDelegate::Create,
run_loop_->QuitClosure(), callback, is_encrypted_callback,
"dm_token"));
} else {
auto callback = base::BindLambdaForTesting(
[this, scan_succeeds](const base::FilePath&)
-> enterprise_connectors::ContentAnalysisResponse {
std::set<std::string> dlp_tag = {"dlp"};
current_requests_count_++;
return scan_succeeds
? FakeDelegate::SuccessfulResponse(std::move(dlp_tag))
: FakeDelegate::DlpResponse(
enterprise_connectors::ContentAnalysisResponse::
Result::SUCCESS,
"block_rule",
enterprise_connectors::ContentAnalysisResponse::
Result::TriggeredRule::BLOCK);
});
safe_browsing::DeepScanningDialogDelegate::SetFactoryForTesting(
base::BindRepeating(&safe_browsing::FakeDeepScanningDialogDelegate::
CreateForConnectors,
run_loop_->QuitClosure(), callback,
is_encrypted_callback, "dm_token"));
}
safe_browsing::DeepScanningDialogDelegate::DisableUIForTesting();
}
// Common code for running the test cases.
void RunTest(const content::DropData& data,
bool enable,
bool scan_succeeds = false) {
current_requests_count_ = 0;
EnableDeepScanning(enable, scan_succeeds);
content::WebContentsViewDelegate::DropCompletionResult result =
scan_succeeds
? content::WebContentsViewDelegate::DropCompletionResult::kContinue
: content::WebContentsViewDelegate::DropCompletionResult::kAbort;
bool called = false;
HandleOnPerformDrop(
contents(), data,
base::BindOnce(
[](content::WebContentsViewDelegate::DropCompletionResult
expected_result,
bool* called,
content::WebContentsViewDelegate::DropCompletionResult result) {
EXPECT_EQ(expected_result, result);
*called = true;
},
result, &called));
if (enable)
RunUntilDone();
EXPECT_TRUE(called);
ASSERT_EQ(expected_requests_count_, current_requests_count_);
}
void SetExpectedRequestsCount(int count) { expected_requests_count_ = count; }
// Helpers to get text with sizes relative to the minimum required size of 100
// bytes for scans to trigger.
std::string large_text() const { return std::string(100, 'a'); }
std::string small_text() const { return "random small text"; }
private:
void SetScanPolicies(safe_browsing::CheckContentComplianceValues state) {
if (use_legacy_policies()) {
PrefService* pref_service =
TestingBrowserProcess::GetGlobal()->local_state();
pref_service->SetInteger(prefs::kCheckContentCompliance, state);
pref_service->SetInteger(prefs::kDelayDeliveryUntilVerdict,
safe_browsing::DELAY_UPLOADS);
} else {
safe_browsing::SetDlpPolicyForConnectors(state);
safe_browsing::SetDelayDeliveryUntilVerdictPolicyForConnectors(
safe_browsing::DELAY_UPLOADS);
}
}
content::BrowserTaskEnvironment task_environment_;
base::test::ScopedFeatureList scoped_feature_list_;
TestingProfileManager profile_manager_{TestingBrowserProcess::GetGlobal()};
TestingProfile* profile_;
std::unique_ptr<base::RunLoop> run_loop_;
std::unique_ptr<content::WebContents> web_contents_;
int expected_requests_count_ = 0;
int current_requests_count_ = 0;
};
INSTANTIATE_TEST_SUITE_P(,
ChromeWebContentsViewDelegateHandleOnPerformDrop,
testing::Bool());
// When no drop data is specified, HandleOnPerformDrop() should indicate
// the caller can proceed, whether scanning is enabled or not.
TEST_P(ChromeWebContentsViewDelegateHandleOnPerformDrop, NoData) {
content::DropData data;
SetExpectedRequestsCount(0);
RunTest(data, /*enable=*/false, /*scan_succeeds=*/true);
RunTest(data, /*enable=*/true, /*scan_succeeds=*/true);
}
// Make sure DropData::url_title is handled correctly.
TEST_P(ChromeWebContentsViewDelegateHandleOnPerformDrop, UrlTitle) {
content::DropData data;
data.url_title = base::UTF8ToUTF16(large_text());
SetExpectedRequestsCount(0);
RunTest(data, /*enable=*/false, /*scan_succeeds=*/true);
SetExpectedRequestsCount(1);
RunTest(data, /*enable=*/true, /*scan_succeeds=*/false);
RunTest(data, /*enable=*/true, /*scan_succeeds=*/true);
data.url_title = base::UTF8ToUTF16(small_text());
SetExpectedRequestsCount(0);
RunTest(data, /*enable=*/true, /*scan_succeeds=*/true);
}
// Make sure DropData::text is handled correctly.
TEST_P(ChromeWebContentsViewDelegateHandleOnPerformDrop, Text) {
content::DropData data;
data.text = base::UTF8ToUTF16(large_text());
SetExpectedRequestsCount(0);
RunTest(data, /*enable=*/false, /*scan_succeeds=*/true);
SetExpectedRequestsCount(1);
RunTest(data, /*enable=*/true, /*scan_succeeds=*/false);
RunTest(data, /*enable=*/true, /*scan_succeeds=*/true);
data.text = base::UTF8ToUTF16(small_text());
SetExpectedRequestsCount(0);
RunTest(data, /*enable=*/true, /*scan_succeeds=*/true);
}
// Make sure DropData::html is handled correctly.
TEST_P(ChromeWebContentsViewDelegateHandleOnPerformDrop, Html) {
content::DropData data;
data.html = base::UTF8ToUTF16(large_text());
SetExpectedRequestsCount(0);
RunTest(data, /*enable=*/false, /*scan_succeeds=*/true);
SetExpectedRequestsCount(1);
RunTest(data, /*enable=*/true, /*scan_succeeds=*/false);
RunTest(data, /*enable=*/true, /*scan_succeeds=*/true);
data.html = base::UTF8ToUTF16(small_text());
SetExpectedRequestsCount(0);
RunTest(data, /*enable=*/true, /*scan_succeeds=*/true);
}
// Make sure DropData::file_contents is handled correctly.
TEST_P(ChromeWebContentsViewDelegateHandleOnPerformDrop, FileContents) {
content::DropData data;
data.file_contents = large_text();
SetExpectedRequestsCount(0);
RunTest(data, /*enable=*/false, /*scan_succeeds=*/true);
SetExpectedRequestsCount(1);
RunTest(data, /*enable=*/true, /*scan_succeeds=*/false);
RunTest(data, /*enable=*/true, /*scan_succeeds=*/true);
data.file_contents = small_text();
SetExpectedRequestsCount(0);
RunTest(data, /*enable=*/true, /*scan_succeeds=*/true);
}
// Make sure DropData::filenames is handled correctly.
TEST_P(ChromeWebContentsViewDelegateHandleOnPerformDrop, Files) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
base::FilePath path_1 = temp_dir.GetPath().AppendASCII("Foo.doc");
base::FilePath path_2 = temp_dir.GetPath().AppendASCII("Bar.doc");
base::File file_1(path_1, base::File::FLAG_CREATE | base::File::FLAG_READ);
base::File file_2(path_2, base::File::FLAG_CREATE | base::File::FLAG_READ);
ASSERT_TRUE(file_1.IsValid());
ASSERT_TRUE(file_2.IsValid());
content::DropData data;
data.filenames.emplace_back(path_1, path_1);
data.filenames.emplace_back(path_2, path_2);
SetExpectedRequestsCount(0);
RunTest(data, /*enable=*/false, /*scan_succeeds=*/true);
SetExpectedRequestsCount(2);
RunTest(data, /*enable=*/true, /*scan_succeeds=*/false);
RunTest(data, /*enable=*/true, /*scan_succeeds=*/true);
}
// Make sure DropData::filenames directories are handled correctly.
TEST_P(ChromeWebContentsViewDelegateHandleOnPerformDrop, Directories) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
base::FilePath path_1 = temp_dir.GetPath().AppendASCII("Foo.doc");
base::FilePath path_2 = temp_dir.GetPath().AppendASCII("Bar.doc");
base::FilePath path_3 = temp_dir.GetPath().AppendASCII("Baz.doc");
base::File file_1(path_1, base::File::FLAG_CREATE | base::File::FLAG_READ);
base::File file_2(path_2, base::File::FLAG_CREATE | base::File::FLAG_READ);
base::File file_3(path_3, base::File::FLAG_CREATE | base::File::FLAG_READ);
ASSERT_TRUE(file_1.IsValid());
ASSERT_TRUE(file_2.IsValid());
ASSERT_TRUE(file_3.IsValid());
content::DropData data;
data.filenames.emplace_back(temp_dir.GetPath(), temp_dir.GetPath());
SetExpectedRequestsCount(0);
RunTest(data, /*enable=*/false, /*scan_succeeds=*/true);
SetExpectedRequestsCount(3);
RunTest(data, /*enable=*/true, /*scan_succeeds=*/false);
RunTest(data, /*enable=*/true, /*scan_succeeds=*/true);
}