blob: e0ec2e2a376a53b5ea7614918a0c68c0bbdbccb7 [file] [log] [blame]
// Copyright 2020 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 <algorithm>
#include <memory>
#include <set>
#include "base/files/file.h"
#include "base/files/file_util.h"
#include "base/path_service.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/bind.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.h"
#include "chrome/browser/enterprise/connectors/analysis/content_analysis_dialog.h"
#include "chrome/browser/enterprise/connectors/common.h"
#include "chrome/browser/enterprise/connectors/connectors_service.h"
#include "chrome/browser/enterprise/connectors/reporting/realtime_reporting_client_factory.h"
#include "chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.h"
#include "chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_factory.h"
#include "chrome/browser/policy/dm_token_utils.h"
#include "chrome/browser/safe_browsing/cloud_content_scanning/cloud_binary_upload_service.h"
#include "chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_browsertest_base.h"
#include "chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_test_utils.h"
#include "chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/common/chrome_paths.h"
#include "components/policy/core/common/cloud/mock_cloud_policy_client.h"
#include "components/policy/core/common/cloud/realtime_reporting_job_configuration.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "components/signin/public/identity_manager/identity_test_environment.h"
#include "content/public/test/browser_test.h"
using extensions::SafeBrowsingPrivateEventRouter;
using safe_browsing::BinaryUploadService;
using safe_browsing::CloudBinaryUploadService;
using ::testing::_;
using ::testing::Mock;
namespace enterprise_connectors {
namespace {
constexpr char kUserName[] = "test@chromium.org";
constexpr char kScanId1[] = "scan id 1";
constexpr char kScanId2[] = "scan id 2";
std::string text() {
return std::string(100, 'a');
}
class FakeBinaryUploadService : public CloudBinaryUploadService {
public:
FakeBinaryUploadService()
: CloudBinaryUploadService(nullptr, nullptr, nullptr) {}
// Sets whether the user is authorized to upload data for Deep Scanning.
void SetAuthorized(bool authorized) {
authorization_result_ = authorized
? BinaryUploadService::Result::SUCCESS
: BinaryUploadService::Result::UNAUTHORIZED;
}
// Finish the authentication request. Called after CreateForWebContents to
// simulate an async callback.
void ReturnAuthorizedResponse() {
FinishRequest(authorization_request_.get(), authorization_result_,
ContentAnalysisResponse());
}
void SetResponseForText(BinaryUploadService::Result result,
const ContentAnalysisResponse& response) {
prepared_text_result_ = result;
prepared_text_response_ = response;
}
void SetResponseForFile(const std::string& path,
BinaryUploadService::Result result,
const ContentAnalysisResponse& response) {
prepared_file_results_[path] = result;
prepared_file_responses_[path] = response;
}
void SetExpectedFinalAction(
enterprise_connectors::ContentAnalysisAcknowledgement::FinalAction
final_action) {
final_action_ = final_action;
}
void SetShouldAutomaticallyAuthorize(bool authorize) {
should_automatically_authorize_ = authorize;
}
int requests_count() const { return requests_count_; }
int ack_count() const { return ack_count_; }
private:
void MaybeAcknowledge(std::unique_ptr<Ack> ack) override {
EXPECT_EQ(final_action_, ack->ack().final_action());
++ack_count_;
ASSERT_NE(requests_tokens_.end(),
std::find(requests_tokens_.begin(), requests_tokens_.end(),
ack->ack().request_token()));
}
void UploadForDeepScanning(std::unique_ptr<Request> request) override {
++requests_count_;
// A request without tags indicates that it's used for authentication
if (request->content_analysis_request().tags().empty()) {
authorization_request_.swap(request);
if (should_automatically_authorize_)
ReturnAuthorizedResponse();
} else {
Request* request_raw = request.get();
std::string file = request->filename();
switch (request->analysis_connector()) {
case AnalysisConnector::FILE_ATTACHED:
ASSERT_FALSE(file.empty());
ASSERT_TRUE(prepared_file_results_.count(file));
ASSERT_TRUE(prepared_file_responses_.count(file));
requests_tokens_.push_back(
prepared_file_responses_[file].request_token());
request->FinishRequest(prepared_file_results_[file],
prepared_file_responses_[file]);
break;
case AnalysisConnector::BULK_DATA_ENTRY:
requests_tokens_.push_back(prepared_text_response_.request_token());
request->FinishRequest(prepared_text_result_,
prepared_text_response_);
break;
case AnalysisConnector::PRINT:
// Since this path is only used for prints that are too large, calling
// GetRequestData should then call FinishRequest with FILE_TOO_LARGE.
request_raw->GetRequestData(base::BindOnce(
[](std::unique_ptr<BinaryUploadService::Request> request,
BinaryUploadService::Result result,
BinaryUploadService::Request::Data data) {
ASSERT_EQ(result, BinaryUploadService::Result::FILE_TOO_LARGE);
request->FinishRequest(result, ContentAnalysisResponse());
},
std::move(request)));
break;
case AnalysisConnector::ANALYSIS_CONNECTOR_UNSPECIFIED:
case AnalysisConnector::FILE_DOWNLOADED:
case AnalysisConnector::FILE_TRANSFER:
NOTREACHED();
}
}
}
BinaryUploadService::Result authorization_result_;
std::unique_ptr<Request> authorization_request_;
BinaryUploadService::Result prepared_text_result_;
ContentAnalysisResponse prepared_text_response_;
std::map<std::string, BinaryUploadService::Result> prepared_file_results_;
std::map<std::string, ContentAnalysisResponse> prepared_file_responses_;
std::vector<std::string> requests_tokens_;
int requests_count_ = 0;
int ack_count_ = 0;
bool should_automatically_authorize_ = false;
ContentAnalysisAcknowledgement::FinalAction final_action_ =
ContentAnalysisAcknowledgement::ACTION_UNSPECIFIED;
};
FakeBinaryUploadService* FakeBinaryUploadServiceStorage() {
static FakeBinaryUploadService service;
return &service;
}
const std::set<std::string>* DocMimeTypes() {
static std::set<std::string> set = {
"application/msword", "text/plain",
// The 50 MB file can result in no mimetype being found.
""};
return &set;
}
const std::set<std::string>* ExeMimeTypes() {
static std::set<std::string> set = {"application/x-msdownload",
"application/x-ms-dos-executable",
"application/octet-stream"};
return &set;
}
const std::set<std::string>* ZipMimeTypes() {
static std::set<std::string> set = {"application/zip",
"application/x-zip-compressed"};
return &set;
}
const std::set<std::string>* PngMimeTypes() {
static std::set<std::string> set = {"image/png"};
return &set;
}
const std::set<std::string>* TextMimeTypes() {
static std::set<std::string> set = {"text/plain"};
return &set;
}
// A fake delegate with minimal overrides to obtain behavior that's as close to
// the real one as possible.
class MinimalFakeContentAnalysisDelegate : public ContentAnalysisDelegate {
public:
MinimalFakeContentAnalysisDelegate(
content::WebContents* web_contents,
ContentAnalysisDelegate::Data data,
ContentAnalysisDelegate::CompletionCallback callback)
: ContentAnalysisDelegate(web_contents,
std::move(data),
std::move(callback),
safe_browsing::DeepScanAccessPoint::UPLOAD) {}
static std::unique_ptr<ContentAnalysisDelegate> Create(
content::WebContents* web_contents,
ContentAnalysisDelegate::Data data,
ContentAnalysisDelegate::CompletionCallback callback) {
return std::make_unique<MinimalFakeContentAnalysisDelegate>(
web_contents, std::move(data), std::move(callback));
}
private:
BinaryUploadService* GetBinaryUploadService() override {
return FakeBinaryUploadServiceStorage();
}
};
constexpr char kBrowserDMToken[] = "browser_dm_token";
constexpr char kProfileDMToken[] = "profile_dm_token";
constexpr char kTestUrl[] = "https://google.com";
} // namespace
// Tests the behavior of the dialog delegate with minimal overriding of methods.
// Only responses obtained via the BinaryUploadService are faked.
class ContentAnalysisDelegateBrowserTestBase
: public safe_browsing::DeepScanningBrowserTestBase,
public ContentAnalysisDialog::TestObserver {
public:
explicit ContentAnalysisDelegateBrowserTestBase(bool machine_scope)
: machine_scope_(machine_scope) {
ContentAnalysisDialog::SetObserverForTesting(this);
}
void EnableUploadsScanningAndReporting() {
#if BUILDFLAG(IS_CHROMEOS_ASH)
SetDMTokenForTesting(
policy::DMToken::CreateValidTokenForTesting(kBrowserDMToken));
#else
if (machine_scope_) {
SetDMTokenForTesting(
policy::DMToken::CreateValidTokenForTesting(kBrowserDMToken));
} else {
safe_browsing::SetProfileDMToken(browser()->profile(), kProfileDMToken);
}
#endif
constexpr char kBlockingScansForDlpAndMalware[] = R"({
"service_provider": "google",
"enable": [
{
"url_list": ["*"],
"tags": ["dlp", "malware"]
}
],
"block_until_verdict": 1
})";
safe_browsing::SetAnalysisConnector(
browser()->profile()->GetPrefs(), FILE_ATTACHED,
kBlockingScansForDlpAndMalware, machine_scope_);
safe_browsing::SetAnalysisConnector(
browser()->profile()->GetPrefs(), BULK_DATA_ENTRY,
kBlockingScansForDlpAndMalware, machine_scope_);
safe_browsing::SetOnSecurityEventReporting(browser()->profile()->GetPrefs(),
/*enabled*/ true,
/*enabled_event_names*/ {},
/*enabled_opt_in_events*/ {},
#if BUILDFLAG(IS_CHROMEOS_ASH)
/*machine_scope*/ false);
#else
machine_scope_);
#endif
client_ = std::make_unique<policy::MockCloudPolicyClient>();
client_->SetDMToken(
#if BUILDFLAG(IS_CHROMEOS_ASH)
kBrowserDMToken);
#else
machine_scope_ ? kBrowserDMToken : kProfileDMToken);
#endif
if (machine_scope_) {
enterprise_connectors::RealtimeReportingClientFactory::GetForProfile(
browser()->profile())
->SetBrowserCloudPolicyClientForTesting(client_.get());
} else {
enterprise_connectors::RealtimeReportingClientFactory::GetForProfile(
browser()->profile())
#if BUILDFLAG(IS_CHROMEOS_ASH)
->SetBrowserCloudPolicyClientForTesting(client_.get());
#else
->SetProfileCloudPolicyClientForTesting(client_.get());
#endif
}
identity_test_environment_ =
std::make_unique<signin::IdentityTestEnvironment>();
identity_test_environment_->MakePrimaryAccountAvailable(
kUserName, signin::ConsentLevel::kSync);
extensions::SafeBrowsingPrivateEventRouterFactory::GetForProfile(
browser()->profile())
->SetIdentityManagerForTesting(
identity_test_environment_->identity_manager());
}
void DestructorCalled(ContentAnalysisDialog* dialog) override {
// The test is over once the views are destroyed.
CallQuitClosure();
}
policy::MockCloudPolicyClient* client() { return client_.get(); }
private:
std::unique_ptr<policy::MockCloudPolicyClient> client_;
std::unique_ptr<signin::IdentityTestEnvironment> identity_test_environment_;
base::ScopedTempDir temp_dir_;
bool machine_scope_;
};
class ContentAnalysisDelegateBrowserTest
: public ContentAnalysisDelegateBrowserTestBase,
public testing::WithParamInterface<bool> {
public:
ContentAnalysisDelegateBrowserTest()
: ContentAnalysisDelegateBrowserTestBase(GetParam()) {}
};
INSTANTIATE_TEST_SUITE_P(, ContentAnalysisDelegateBrowserTest, testing::Bool());
IN_PROC_BROWSER_TEST_P(ContentAnalysisDelegateBrowserTest, Unauthorized) {
// The reading of the browser DM token is blocking and happens in this test
// when checking if the browser is enrolled.
base::ScopedAllowBlockingForTesting allow_blocking;
EnableUploadsScanningAndReporting();
ContentAnalysisDelegate::SetFactoryForTesting(
base::BindRepeating(&MinimalFakeContentAnalysisDelegate::Create));
FakeBinaryUploadServiceStorage()->SetAuthorized(false);
// This causes the DM Token to be rejected, and unauthorized for 24 hours.
client()->SetStatus(policy::DM_STATUS_SERVICE_MANAGEMENT_NOT_SUPPORTED);
client()->NotifyClientError();
bool called = false;
base::RunLoop run_loop;
base::RepeatingClosure quit_closure = run_loop.QuitClosure();
ContentAnalysisDelegate::Data data;
data.text.emplace_back(text());
data.paths.emplace_back(FILE_PATH_LITERAL("/tmp/foo.doc"));
ASSERT_TRUE(ContentAnalysisDelegate::IsEnabled(
browser()->profile(), GURL(kTestUrl), &data, FILE_ATTACHED));
// Nothing should be reported for unauthorized users.
safe_browsing::EventReportValidator validator(client());
validator.ExpectNoReport();
ContentAnalysisDelegate::CreateForWebContents(
browser()->tab_strip_model()->GetActiveWebContents(), std::move(data),
base::BindLambdaForTesting(
[&quit_closure, &called](
const ContentAnalysisDelegate::Data& data,
const ContentAnalysisDelegate::Result& result) {
ASSERT_EQ(result.text_results.size(), 1u);
ASSERT_EQ(result.paths_results.size(), 1u);
ASSERT_TRUE(result.text_results[0]);
ASSERT_TRUE(result.paths_results[0]);
called = true;
quit_closure.Run();
}),
safe_browsing::DeepScanAccessPoint::UPLOAD);
FakeBinaryUploadServiceStorage()->ReturnAuthorizedResponse();
run_loop.Run();
EXPECT_TRUE(called);
// 1 request to authenticate for upload.
ASSERT_EQ(FakeBinaryUploadServiceStorage()->requests_count(), 1);
ASSERT_EQ(FakeBinaryUploadServiceStorage()->ack_count(), 0);
}
IN_PROC_BROWSER_TEST_P(ContentAnalysisDelegateBrowserTest, Files) {
base::ScopedAllowBlockingForTesting allow_blocking;
// Set up delegate and upload service.
EnableUploadsScanningAndReporting();
ContentAnalysisDelegate::SetFactoryForTesting(
base::BindRepeating(&MinimalFakeContentAnalysisDelegate::Create));
FakeBinaryUploadServiceStorage()->SetAuthorized(true);
FakeBinaryUploadServiceStorage()->SetShouldAutomaticallyAuthorize(true);
// Create the files to be opened and scanned.
ContentAnalysisDelegate::Data data;
CreateFilesForTest({"ok.doc", "bad.exe"},
{"ok file content", "bad file content"}, &data);
ASSERT_TRUE(ContentAnalysisDelegate::IsEnabled(
browser()->profile(), GURL(kTestUrl), &data, FILE_ATTACHED));
// The malware verdict means an event should be reported.
safe_browsing::EventReportValidator validator(client());
validator.ExpectDangerousDeepScanningResult(
/*url*/ "about:blank",
/*filename*/ "bad.exe",
// printf "bad file content" | sha256sum | tr '[:lower:]' '[:upper:]'
/*sha*/
"77AE96C38386429D28E53F5005C46C7B4D8D39BE73D757CE61E0AE65CC1A5A5D",
/*threat_type*/ "DANGEROUS",
/*trigger*/ SafeBrowsingPrivateEventRouter::kTriggerFileUpload,
/*mimetypes*/ ExeMimeTypes(),
/*size*/ std::string("bad file content").size(),
/*result*/
safe_browsing::EventResultToString(safe_browsing::EventResult::BLOCKED),
/*username*/ kUserName, /*scan_id*/ kScanId2);
ContentAnalysisResponse ok_response;
ok_response.set_request_token(kScanId1);
auto* ok_result = ok_response.add_results();
ok_result->set_status(ContentAnalysisResponse::Result::SUCCESS);
ok_result->set_tag("malware");
ContentAnalysisResponse bad_response;
bad_response.set_request_token(kScanId2);
auto* bad_result = bad_response.add_results();
bad_result->set_status(ContentAnalysisResponse::Result::SUCCESS);
bad_result->set_tag("malware");
auto* bad_rule = bad_result->add_triggered_rules();
bad_rule->set_action(TriggeredRule::BLOCK);
bad_rule->set_rule_name("malware");
FakeBinaryUploadServiceStorage()->SetResponseForFile(
created_file_paths()[0].AsUTF8Unsafe(),
BinaryUploadService::Result::SUCCESS, ok_response);
FakeBinaryUploadServiceStorage()->SetResponseForFile(
created_file_paths()[1].AsUTF8Unsafe(),
BinaryUploadService::Result::SUCCESS, bad_response);
FakeBinaryUploadServiceStorage()->SetExpectedFinalAction(
ContentAnalysisAcknowledgement::BLOCK);
bool called = false;
base::RunLoop run_loop;
SetQuitClosure(run_loop.QuitClosure());
// Start test.
ContentAnalysisDelegate::CreateForWebContents(
browser()->tab_strip_model()->GetActiveWebContents(), std::move(data),
base::BindLambdaForTesting(
[&called](const ContentAnalysisDelegate::Data& data,
const ContentAnalysisDelegate::Result& result) {
ASSERT_TRUE(result.text_results.empty());
ASSERT_EQ(result.paths_results.size(), 2u);
ASSERT_TRUE(result.paths_results[0]);
ASSERT_FALSE(result.paths_results[1]);
called = true;
}),
safe_browsing::DeepScanAccessPoint::UPLOAD);
run_loop.Run();
EXPECT_TRUE(called);
// There should have been 1 request per file (2 files) and 1 for
// authentication.
ASSERT_EQ(FakeBinaryUploadServiceStorage()->requests_count(), 3);
ASSERT_EQ(FakeBinaryUploadServiceStorage()->ack_count(), 2);
}
IN_PROC_BROWSER_TEST_P(ContentAnalysisDelegateBrowserTest, Texts) {
// The reading of the browser DM token is blocking and happens in this test
// when checking if the browser is enrolled.
base::ScopedAllowBlockingForTesting allow_blocking;
// Set up delegate and upload service.
EnableUploadsScanningAndReporting();
ContentAnalysisDelegate::SetFactoryForTesting(
base::BindRepeating(&MinimalFakeContentAnalysisDelegate::Create));
FakeBinaryUploadServiceStorage()->SetAuthorized(true);
safe_browsing::EventReportValidator validator(client());
// Prepare a complex DLP response to test that the verdict is reported
// correctly in the sensitive data event.
ContentAnalysisResponse response;
response.set_request_token(kScanId1);
auto* result = response.add_results();
result->set_status(ContentAnalysisResponse::Result::SUCCESS);
result->set_tag("dlp");
auto* rule1 = result->add_triggered_rules();
rule1->set_action(TriggeredRule::REPORT_ONLY);
rule1->set_rule_id("1");
rule1->set_rule_name("resource rule 1");
auto* rule2 = result->add_triggered_rules();
rule2->set_action(TriggeredRule::BLOCK);
rule2->set_rule_id("3");
rule2->set_rule_name("resource rule 2");
FakeBinaryUploadServiceStorage()->SetResponseForText(
BinaryUploadService::Result::SUCCESS, response);
FakeBinaryUploadServiceStorage()->SetExpectedFinalAction(
ContentAnalysisAcknowledgement::BLOCK);
// The DLP verdict means an event should be reported. The content size is
// equal to the length of the concatenated texts (2 * 100 * 'a').
validator.ExpectSensitiveDataEvent(
/*url*/ "about:blank",
/*filename*/ "Text data",
// The hash should not be included for string requests.
/*sha*/ "",
/*trigger*/ SafeBrowsingPrivateEventRouter::kTriggerWebContentUpload,
/*dlp_verdict*/ *result,
/*mimetype*/ TextMimeTypes(),
/*size*/ 200,
/*result*/
safe_browsing::EventResultToString(safe_browsing::EventResult::BLOCKED),
/*username*/ kUserName,
/*scan_id*/ kScanId1);
bool called = false;
base::RunLoop run_loop;
SetQuitClosure(run_loop.QuitClosure());
ContentAnalysisDelegate::Data data;
data.text.emplace_back(text());
data.text.emplace_back(text());
ASSERT_TRUE(ContentAnalysisDelegate::IsEnabled(
browser()->profile(), GURL(kTestUrl), &data, BULK_DATA_ENTRY));
// Start test.
ContentAnalysisDelegate::CreateForWebContents(
browser()->tab_strip_model()->GetActiveWebContents(), std::move(data),
base::BindLambdaForTesting(
[&called](const ContentAnalysisDelegate::Data& data,
const ContentAnalysisDelegate::Result& result) {
ASSERT_TRUE(result.paths_results.empty());
ASSERT_EQ(result.text_results.size(), 2u);
ASSERT_FALSE(result.text_results[0]);
ASSERT_FALSE(result.text_results[1]);
called = true;
}),
safe_browsing::DeepScanAccessPoint::PASTE);
FakeBinaryUploadServiceStorage()->ReturnAuthorizedResponse();
run_loop.Run();
EXPECT_TRUE(called);
// There should have been 1 request for all texts,
// 1 for authentication of the scanning request.
ASSERT_EQ(FakeBinaryUploadServiceStorage()->requests_count(), 2);
ASSERT_EQ(FakeBinaryUploadServiceStorage()->ack_count(), 1);
}
IN_PROC_BROWSER_TEST_P(ContentAnalysisDelegateBrowserTest, Throttled) {
base::ScopedAllowBlockingForTesting allow_blocking;
// Set up delegate and upload service.
EnableUploadsScanningAndReporting();
ContentAnalysisDelegate::SetFactoryForTesting(
base::BindRepeating(&MinimalFakeContentAnalysisDelegate::Create));
FakeBinaryUploadServiceStorage()->SetAuthorized(true);
FakeBinaryUploadServiceStorage()->SetShouldAutomaticallyAuthorize(true);
// Create the files to be opened and scanned.
ContentAnalysisDelegate::Data data;
CreateFilesForTest({"a.exe", "b.exe", "c.exe"},
{"a content", "b content", "c content"}, &data);
ASSERT_TRUE(ContentAnalysisDelegate::IsEnabled(
browser()->profile(), GURL(kTestUrl), &data, FILE_ATTACHED));
// The malware verdict means an event should be reported.
safe_browsing::EventReportValidator validator(client());
validator.ExpectUnscannedFileEvents(
/*url*/ "about:blank",
{
created_file_paths()[0].BaseName().AsUTF8Unsafe(),
created_file_paths()[1].BaseName().AsUTF8Unsafe(),
created_file_paths()[2].BaseName().AsUTF8Unsafe(),
},
{
// printf "a content" | sha256sum | tr '[:lower:]' '[:upper:]'
"D2D2ACF640179223BF9E1EB43C5FBF854C4E50FFB6733BC3A9279D3FF7DE9BE1",
// printf "b content" | sha256sum | tr '[:lower:]' '[:upper:]'
"93CB3641ADD6A9A6619D7E2F304EBCF5160B2DB016B27C6E3D641C5306897224",
// printf "c content" | sha256sum | tr '[:lower:]' '[:upper:]'
"2E6D1C4A1F39A02562BF1505AD775C0323D7A04C0C37C9B29D25F532B9972080",
},
/*trigger*/ SafeBrowsingPrivateEventRouter::kTriggerFileUpload,
/*reason*/ "TOO_MANY_REQUESTS",
/*mimetypes*/ ExeMimeTypes(),
/*size*/ 9,
/*result*/
safe_browsing::EventResultToString(safe_browsing::EventResult::ALLOWED),
/*username*/ kUserName);
// While only one file should reach the upload part and get a
// TOO_MANY_REQUEST result, it can be any of them depending on how quickly
// they are opened asynchronously. This means responses must be set up for
// each of them.
for (size_t i = 0; i < 3; ++i) {
FakeBinaryUploadServiceStorage()->SetResponseForFile(
created_file_paths()[i].AsUTF8Unsafe(),
BinaryUploadService::Result::TOO_MANY_REQUESTS,
ContentAnalysisResponse());
}
bool called = false;
base::RunLoop run_loop;
SetQuitClosure(run_loop.QuitClosure());
// Start test.
ContentAnalysisDelegate::CreateForWebContents(
browser()->tab_strip_model()->GetActiveWebContents(), std::move(data),
base::BindLambdaForTesting(
[&called](const ContentAnalysisDelegate::Data& data,
const ContentAnalysisDelegate::Result& result) {
ASSERT_TRUE(result.text_results.empty());
ASSERT_EQ(result.paths_results.size(), 3u);
for (bool paths_result : result.paths_results)
ASSERT_TRUE(paths_result);
called = true;
}),
safe_browsing::DeepScanAccessPoint::UPLOAD);
run_loop.Run();
EXPECT_TRUE(called);
// There should have been 1 request for the first file and 1 for
// authentication. There were no successful requests so no acks.
ASSERT_EQ(FakeBinaryUploadServiceStorage()->requests_count(), 2);
ASSERT_EQ(FakeBinaryUploadServiceStorage()->ack_count(), 0);
}
// This class tests each of the blocking settings used in Connector policies:
// - block_until_verdict
// - block_password_protected
// - block_large_files
// - block_unsupported_file_types
class ContentAnalysisDelegateBlockingSettingBrowserTest
: public ContentAnalysisDelegateBrowserTestBase,
public testing::WithParamInterface<std::tuple<bool, bool>> {
public:
ContentAnalysisDelegateBlockingSettingBrowserTest()
: ContentAnalysisDelegateBrowserTestBase(machine_scope()) {}
bool machine_scope() const { return std::get<0>(GetParam()); }
bool setting_param() const { return std::get<1>(GetParam()); }
// Use a string since the setting value is inserted into a JSON policy.
const char* bool_setting_value() const {
return setting_param() ? "true" : "false";
}
const char* int_setting_value() const { return setting_param() ? "1" : "0"; }
bool expected_result() const { return !setting_param(); }
};
INSTANTIATE_TEST_SUITE_P(,
ContentAnalysisDelegateBlockingSettingBrowserTest,
testing::Combine(testing::Bool(), testing::Bool()));
IN_PROC_BROWSER_TEST_P(ContentAnalysisDelegateBlockingSettingBrowserTest,
BlockPasswordProtected) {
base::ScopedAllowBlockingForTesting allow_blocking;
base::FilePath test_zip;
EXPECT_TRUE(base::PathService::Get(chrome::DIR_TEST_DATA, &test_zip));
test_zip = test_zip.AppendASCII("safe_browsing")
.AppendASCII("download_protection")
.AppendASCII("encrypted.zip");
// Set up delegate and upload service.
EnableUploadsScanningAndReporting();
constexpr char kPasswordProtectedPref[] = R"({
"service_provider": "google",
"enable": [
{
"url_list": ["*"],
"tags": ["dlp"]
}
],
"block_until_verdict": 1,
"block_password_protected": %s
})";
safe_browsing::SetAnalysisConnector(
browser()->profile()->GetPrefs(), FILE_ATTACHED,
base::StringPrintf(kPasswordProtectedPref, bool_setting_value()),
machine_scope());
ContentAnalysisDelegate::SetFactoryForTesting(
base::BindRepeating(&MinimalFakeContentAnalysisDelegate::Create));
FakeBinaryUploadServiceStorage()->SetAuthorized(true);
FakeBinaryUploadServiceStorage()->SetShouldAutomaticallyAuthorize(true);
bool called = false;
base::RunLoop run_loop;
SetQuitClosure(run_loop.QuitClosure());
ContentAnalysisDelegate::Data data;
data.paths.emplace_back(test_zip);
ASSERT_TRUE(ContentAnalysisDelegate::IsEnabled(
browser()->profile(), GURL(kTestUrl), &data, FILE_ATTACHED));
// The file should be reported as unscanned.
safe_browsing::EventReportValidator validator(client());
validator.ExpectUnscannedFileEvent(
/*url*/ "about:blank",
/*filename*/ "encrypted.zip",
// sha256sum < chrome/test/data/safe_browsing/download_protection/\
// encrypted.zip | tr '[:lower:]' '[:upper:]'
/*sha*/
"701FCEA8B2112FFAB257A8A8DFD3382ABCF047689AB028D42903E3B3AA488D9A",
/*trigger*/ SafeBrowsingPrivateEventRouter::kTriggerFileUpload,
/*reason*/ "FILE_PASSWORD_PROTECTED",
/*mimetypes*/ ZipMimeTypes(),
// du chrome/test/data/safe_browsing/download_protection/encrypted.zip -b
/*size*/ 20015,
/*result*/
expected_result() ? safe_browsing::EventResultToString(
safe_browsing::EventResult::ALLOWED)
: safe_browsing::EventResultToString(
safe_browsing::EventResult::BLOCKED),
/*username*/ kUserName);
// Start test.
ContentAnalysisDelegate::CreateForWebContents(
browser()->tab_strip_model()->GetActiveWebContents(), std::move(data),
base::BindLambdaForTesting(
[this, &called](const ContentAnalysisDelegate::Data& data,
const ContentAnalysisDelegate::Result& result) {
ASSERT_TRUE(result.text_results.empty());
ASSERT_EQ(result.paths_results.size(), 1u);
ASSERT_EQ(result.paths_results[0], expected_result());
called = true;
}),
safe_browsing::DeepScanAccessPoint::UPLOAD);
run_loop.Run();
EXPECT_TRUE(called);
ASSERT_EQ(FakeBinaryUploadServiceStorage()->requests_count(), 0);
ASSERT_EQ(FakeBinaryUploadServiceStorage()->ack_count(), 0);
}
IN_PROC_BROWSER_TEST_P(ContentAnalysisDelegateBlockingSettingBrowserTest,
BlockUnsupportedFileTypes) {
base::ScopedAllowBlockingForTesting allow_blocking;
// Set up delegate and upload service.
EnableUploadsScanningAndReporting();
constexpr char kBlockUnsupportedFileTypesPref[] = R"({
"service_provider": "google",
"enable": [
{
"url_list": ["*"],
"tags": ["dlp"]
}
],
"block_until_verdict": 1,
"block_unsupported_file_types": %s
})";
safe_browsing::SetAnalysisConnector(
browser()->profile()->GetPrefs(), FILE_ATTACHED,
base::StringPrintf(kBlockUnsupportedFileTypesPref, bool_setting_value()),
machine_scope());
ContentAnalysisDelegate::SetFactoryForTesting(
base::BindRepeating(&MinimalFakeContentAnalysisDelegate::Create));
FakeBinaryUploadServiceStorage()->SetAuthorized(true);
FakeBinaryUploadServiceStorage()->SetShouldAutomaticallyAuthorize(true);
// Create the files with unsupported types.
std::string png_file_content = "\x89PNG\x0D\x0A\x1A\x0A";
ContentAnalysisDelegate::Data data;
CreateFilesForTest({"a.png"}, {png_file_content}, &data);
ASSERT_TRUE(ContentAnalysisDelegate::IsEnabled(
browser()->profile(), GURL(kTestUrl), &data, FILE_ATTACHED));
// The file should be reported as unscanned.
safe_browsing::EventReportValidator validator(client());
validator.ExpectUnscannedFileEvent(
/*url*/ "about:blank",
/*filename*/ "a.png",
// printf "\x89PNG\x0D\x0A\x1A\x0A" | sha256sum | tr '[:lower:]' \
// '[:upper:]'
"4C4B6A3BE1314AB86138BEF4314DDE022E600960D8689A2C8F8631802D20DAB6",
/*trigger*/ SafeBrowsingPrivateEventRouter::kTriggerFileUpload,
/*reason*/ "DLP_SCAN_UNSUPPORTED_FILE_TYPE",
/*mimetype*/ PngMimeTypes(),
/*size*/ png_file_content.size(),
/*result*/
expected_result() ? safe_browsing::EventResultToString(
safe_browsing::EventResult::ALLOWED)
: safe_browsing::EventResultToString(
safe_browsing::EventResult::BLOCKED),
/*username*/ kUserName);
bool called = false;
base::RunLoop run_loop;
SetQuitClosure(run_loop.QuitClosure());
// Start test.
ContentAnalysisDelegate::CreateForWebContents(
browser()->tab_strip_model()->GetActiveWebContents(), std::move(data),
base::BindLambdaForTesting(
[this, &called](const ContentAnalysisDelegate::Data& data,
const ContentAnalysisDelegate::Result& result) {
ASSERT_TRUE(result.text_results.empty());
ASSERT_EQ(result.paths_results.size(), 1u);
ASSERT_EQ(result.paths_results[0], expected_result());
called = true;
}),
safe_browsing::DeepScanAccessPoint::UPLOAD);
run_loop.Run();
EXPECT_TRUE(called);
}
// Flaky on linux: https://crbug.com/1299762.
#if BUILDFLAG(IS_LINUX)
#define MAYBE_BlockLargeFiles DISABLED_BlockLargeFiles
#else
#define MAYBE_BlockLargeFiles BlockLargeFiles
#endif
IN_PROC_BROWSER_TEST_P(ContentAnalysisDelegateBlockingSettingBrowserTest,
MAYBE_BlockLargeFiles) {
base::ScopedAllowBlockingForTesting allow_blocking;
// Set up delegate and upload service.
EnableUploadsScanningAndReporting();
constexpr char kBlockLargeFilesPref[] = R"({
"service_provider": "google",
"enable": [
{
"url_list": ["*"],
"tags": ["dlp", "malware"]
}
],
"block_until_verdict": 1,
"block_large_files": %s
})";
safe_browsing::SetAnalysisConnector(
browser()->profile()->GetPrefs(), FILE_ATTACHED,
base::StringPrintf(kBlockLargeFilesPref, bool_setting_value()),
machine_scope());
ContentAnalysisDelegate::SetFactoryForTesting(
base::BindRepeating(&MinimalFakeContentAnalysisDelegate::Create));
FakeBinaryUploadServiceStorage()->SetAuthorized(true);
FakeBinaryUploadServiceStorage()->SetShouldAutomaticallyAuthorize(true);
// Create the large file.
ContentAnalysisDelegate::Data data;
CreateFilesForTest({"large.doc"}, {std::string()}, &data);
// Write data to the file in chunks to avoid memory allocation errors.
constexpr int64_t kChunkSize = 50 * 1024 * 1024; // 100 MB
constexpr int64_t kLargeSize = 42 * kChunkSize; // ~2.1 GB, just over maxint
int64_t total_size = 0;
std::string chunk = std::string(kChunkSize, 'a');
base::File file(created_file_paths()[0],
base::File::FLAG_OPEN | base::File::FLAG_WRITE);
while (total_size != kLargeSize) {
file.WriteAtCurrentPos(chunk.data(), chunk.size());
total_size += chunk.size();
}
ASSERT_TRUE(ContentAnalysisDelegate::IsEnabled(
browser()->profile(), GURL(kTestUrl), &data, FILE_ATTACHED));
// The file should be reported as unscanned.
safe_browsing::EventReportValidator validator(client());
validator.ExpectUnscannedFileEvent(
/*url*/ "about:blank",
/*filename*/ "large.doc",
// python3 -c "print('a' * (42 * 50 * 1024 * 1024), end='')" |\
// sha256sum | tr '[:lower:]' '[:upper:]'
/*sha*/
"E061612733D5D991F3BD676A51F77B1F0C824282909B7C1C89BD1612FC52E073",
/*trigger*/ SafeBrowsingPrivateEventRouter::kTriggerFileUpload,
/*reason*/ "FILE_TOO_LARGE",
/*mimetypes*/ DocMimeTypes(),
/*size*/ kLargeSize,
/*result*/
expected_result() ? safe_browsing::EventResultToString(
safe_browsing::EventResult::ALLOWED)
: safe_browsing::EventResultToString(
safe_browsing::EventResult::BLOCKED),
/*username*/ kUserName);
bool called = false;
base::RunLoop run_loop;
SetQuitClosure(run_loop.QuitClosure());
// Start test.
ContentAnalysisDelegate::CreateForWebContents(
browser()->tab_strip_model()->GetActiveWebContents(), std::move(data),
base::BindLambdaForTesting(
[this, &called](const ContentAnalysisDelegate::Data& data,
const ContentAnalysisDelegate::Result& result) {
ASSERT_TRUE(result.text_results.empty());
ASSERT_EQ(result.paths_results.size(), 1u);
ASSERT_EQ(result.paths_results[0], expected_result());
called = true;
}),
safe_browsing::DeepScanAccessPoint::UPLOAD);
run_loop.Run();
EXPECT_TRUE(called);
}
IN_PROC_BROWSER_TEST_P(ContentAnalysisDelegateBlockingSettingBrowserTest,
BlockLargePages) {
base::ScopedAllowBlockingForTesting allow_blocking;
// Set up delegate and upload service.
EnableUploadsScanningAndReporting();
constexpr char kBlockLargePagesPref[] = R"({
"service_provider": "google",
"enable": [
{
"url_list": ["*"],
"tags": ["dlp"]
}
],
"block_until_verdict": 1,
"block_large_files": %s
})";
safe_browsing::SetAnalysisConnector(
browser()->profile()->GetPrefs(), PRINT,
base::StringPrintf(kBlockLargePagesPref, bool_setting_value()),
machine_scope());
ContentAnalysisDelegate::SetFactoryForTesting(
base::BindRepeating(&MinimalFakeContentAnalysisDelegate::Create));
FakeBinaryUploadServiceStorage()->SetAuthorized(true);
// Create the large page.
ContentAnalysisDelegate::Data data;
constexpr int64_t kLargeSize = 51 * 1024 * 1024;
base::MappedReadOnlyRegion page =
base::ReadOnlySharedMemoryRegion::Create(kLargeSize);
memset(page.mapping.memory(), 'a', kLargeSize);
data.page = std::move(page.region);
ASSERT_TRUE(ContentAnalysisDelegate::IsEnabled(browser()->profile(),
GURL(kTestUrl), &data, PRINT));
bool called = false;
base::RunLoop run_loop;
SetQuitClosure(run_loop.QuitClosure());
// Start test.
ContentAnalysisDelegate::CreateForWebContents(
browser()->tab_strip_model()->GetActiveWebContents(), std::move(data),
base::BindLambdaForTesting(
[this, &called](const ContentAnalysisDelegate::Data& data,
const ContentAnalysisDelegate::Result& result) {
ASSERT_TRUE(result.paths_results.empty());
ASSERT_TRUE(result.text_results.empty());
ASSERT_EQ(result.page_result, expected_result());
called = true;
}),
safe_browsing::DeepScanAccessPoint::PRINT);
FakeBinaryUploadServiceStorage()->ReturnAuthorizedResponse();
run_loop.Run();
EXPECT_TRUE(called);
}
IN_PROC_BROWSER_TEST_P(ContentAnalysisDelegateBlockingSettingBrowserTest,
BlockUntilVerdict) {
base::ScopedAllowBlockingForTesting allow_blocking;
// Set up delegate and upload service.
EnableUploadsScanningAndReporting();
constexpr char kBlockUntilVerdictPref[] = R"({
"service_provider": "google",
"enable": [
{
"url_list": ["*"],
"tags": ["dlp", "malware"]
}
],
"block_until_verdict": %s
})";
safe_browsing::SetAnalysisConnector(
browser()->profile()->GetPrefs(), FILE_ATTACHED,
base::StringPrintf(kBlockUntilVerdictPref, int_setting_value()),
machine_scope());
ContentAnalysisDelegate::SetFactoryForTesting(
base::BindRepeating(&MinimalFakeContentAnalysisDelegate::Create));
FakeBinaryUploadServiceStorage()->SetAuthorized(true);
FakeBinaryUploadServiceStorage()->SetShouldAutomaticallyAuthorize(true);
// Create a file.
ContentAnalysisDelegate::Data data;
CreateFilesForTest({"foo.doc"}, {"foo content"}, &data);
ASSERT_TRUE(ContentAnalysisDelegate::IsEnabled(
browser()->profile(), GURL(kTestUrl), &data, FILE_ATTACHED));
// The file should be reported as malware and sensitive content.
safe_browsing::EventReportValidator validator(client());
ContentAnalysisResponse response;
response.set_request_token(kScanId1);
auto* malware_result = response.add_results();
malware_result->set_status(ContentAnalysisResponse::Result::SUCCESS);
malware_result->set_tag("malware");
auto* malware_rule = malware_result->add_triggered_rules();
malware_rule->set_action(TriggeredRule::BLOCK);
malware_rule->set_rule_name("malware");
auto* dlp_result = response.add_results();
dlp_result->set_status(ContentAnalysisResponse::Result::SUCCESS);
dlp_result->set_tag("dlp");
auto* dlp_rule = dlp_result->add_triggered_rules();
dlp_rule->set_action(TriggeredRule::BLOCK);
dlp_rule->set_rule_id("0");
dlp_rule->set_rule_name("some_dlp_rule");
FakeBinaryUploadServiceStorage()->SetResponseForFile(
created_file_paths()[0].AsUTF8Unsafe(),
BinaryUploadService::Result::SUCCESS, response);
FakeBinaryUploadServiceStorage()->SetExpectedFinalAction(
ContentAnalysisAcknowledgement::BLOCK);
validator.ExpectDangerousDeepScanningResultAndSensitiveDataEvent(
/*url*/ "about:blank",
/*filename*/ "foo.doc",
// printf "foo content" | sha256sum | tr '[:lower:]' '[:upper:]'
/*sha*/
"B3A2E2EDBAA3C798B4FC267792B1641B94793DE02D870124E5CBE663750B4CFC",
/*threat_type*/ "DANGEROUS",
/*trigger*/
extensions::SafeBrowsingPrivateEventRouter::kTriggerFileUpload,
/*dlp_verdict*/ *dlp_result,
/*mimetypes*/ DocMimeTypes(),
/*size*/ std::string("foo content").size(),
// If the policy allows immediate delivery of the file, then the result is
// ALLOWED even if the verdict obtained afterwards is BLOCKED.
/*result*/
safe_browsing::EventResultToString(
expected_result() ? safe_browsing::EventResult::ALLOWED
: safe_browsing::EventResult::BLOCKED),
/*username*/ kUserName,
/*scan_id*/ kScanId1);
bool called = false;
base::RunLoop run_loop;
// If the delivery is not delayed, put the quit closure right after the events
// are reported instead of when the dialog closes.
if (expected_result())
validator.SetDoneClosure(run_loop.QuitClosure());
else
SetQuitClosure(run_loop.QuitClosure());
// Start test.
ContentAnalysisDelegate::CreateForWebContents(
browser()->tab_strip_model()->GetActiveWebContents(), std::move(data),
base::BindLambdaForTesting(
[this, &called](const ContentAnalysisDelegate::Data& data,
const ContentAnalysisDelegate::Result& result) {
ASSERT_TRUE(result.text_results.empty());
ASSERT_EQ(result.paths_results.size(), 1u);
ASSERT_EQ(result.paths_results[0], expected_result());
called = true;
}),
safe_browsing::DeepScanAccessPoint::UPLOAD);
run_loop.Run();
EXPECT_TRUE(called);
// Expect 1 request for initial authentication (unspecified type, to be
// removed for crbug.com/1090088, then count should be 1), + 1 to scan the
// file in all cases.
ASSERT_EQ(FakeBinaryUploadServiceStorage()->requests_count(), 2);
ASSERT_EQ(FakeBinaryUploadServiceStorage()->ack_count(), 1);
}
// This class tests that ContentAnalysisDelegate is handled correctly when the
// requests are already unauthorized. The test parameter represents if the scan
// is set to be blocking through policy.
class ContentAnalysisDelegateUnauthorizedBrowserTest
: public ContentAnalysisDelegateBrowserTestBase,
public testing::WithParamInterface<std::tuple<bool, bool>> {
public:
ContentAnalysisDelegateUnauthorizedBrowserTest()
: ContentAnalysisDelegateBrowserTestBase(machine_scope()) {}
bool machine_scope() const { return std::get<0>(GetParam()); }
bool blocking_scan() const { return std::get<1>(GetParam()); }
const char* dm_token() const {
return machine_scope() ? kBrowserDMToken : kProfileDMToken;
}
void SetUpScanning(bool file_scan) {
#if BUILDFLAG(IS_CHROMEOS_ASH)
SetDMTokenForTesting(
policy::DMToken::CreateValidTokenForTesting(dm_token()));
#else
if (machine_scope()) {
SetDMTokenForTesting(
policy::DMToken::CreateValidTokenForTesting(dm_token()));
} else {
safe_browsing::SetProfileDMToken(browser()->profile(), dm_token());
}
#endif
std::string pref = base::StringPrintf(
R"({
"service_provider": "google",
"enable": [
{
"url_list": ["*"],
"tags": ["dlp", "malware"]
}
],
"block_until_verdict": %d
})",
blocking_scan() ? 1 : 0);
safe_browsing::SetAnalysisConnector(
browser()->profile()->GetPrefs(),
file_scan ? FILE_ATTACHED : BULK_DATA_ENTRY, pref, machine_scope());
file_scan_ = file_scan;
}
// The dialog should only appear on file scans since they need to be opened
// asynchronously. This means that the dialog appears when scanning files and
// that all the following overrides should be called. Text scans don't have an
// asynchronous operation needed before being sent for scanning, so in the
// unauthorized case the dialog doesn't need to appear and the assertions will
// fail the test if they are reached.
void ConstructorCalled(ContentAnalysisDialog* dialog,
base::TimeTicks timestamp) override {
ASSERT_TRUE(file_scan_ && blocking_scan());
}
void ViewsFirstShown(ContentAnalysisDialog* dialog,
base::TimeTicks timestamp) override {
ASSERT_TRUE(file_scan_ && blocking_scan());
}
void DialogUpdated(ContentAnalysisDialog* dialog,
FinalContentAnalysisResult result) override {
ASSERT_TRUE(file_scan_ && blocking_scan());
}
void DestructorCalled(ContentAnalysisDialog* dialog) override {
ASSERT_TRUE(file_scan_ && blocking_scan());
CallQuitClosure();
}
protected:
bool file_scan_ = false;
};
INSTANTIATE_TEST_SUITE_P(,
ContentAnalysisDelegateUnauthorizedBrowserTest,
testing::Combine(testing::Bool(), testing::Bool()));
IN_PROC_BROWSER_TEST_P(ContentAnalysisDelegateUnauthorizedBrowserTest, Paste) {
// The reading of the browser DM token is blocking and happens in this test
// when checking if the browser is enrolled.
base::ScopedAllowBlockingForTesting allow_blocking;
SetUpScanning(/*file_scan*/ false);
ContentAnalysisDelegate::SetFactoryForTesting(
base::BindRepeating(&MinimalFakeContentAnalysisDelegate::Create));
FakeBinaryUploadServiceStorage()->SetAuthForTesting(dm_token(), false);
bool called = false;
base::RunLoop run_loop;
base::RepeatingClosure quit_closure = run_loop.QuitClosure();
ContentAnalysisDelegate::Data data;
data.text.emplace_back(text());
ASSERT_TRUE(ContentAnalysisDelegate::IsEnabled(
browser()->profile(), GURL(kTestUrl), &data, BULK_DATA_ENTRY));
ContentAnalysisDelegate::CreateForWebContents(
browser()->tab_strip_model()->GetActiveWebContents(), std::move(data),
base::BindLambdaForTesting(
[&quit_closure, &called](
const ContentAnalysisDelegate::Data& data,
const ContentAnalysisDelegate::Result& result) {
ASSERT_EQ(result.text_results.size(), 1u);
ASSERT_EQ(result.paths_results.size(), 0u);
ASSERT_TRUE(result.text_results[0]);
called = true;
quit_closure.Run();
}),
safe_browsing::DeepScanAccessPoint::PASTE);
run_loop.Run();
EXPECT_TRUE(called);
// No requests should be made since the DM token is unauthorized.
ASSERT_EQ(FakeBinaryUploadServiceStorage()->requests_count(), 0);
ASSERT_EQ(FakeBinaryUploadServiceStorage()->ack_count(), 0);
}
IN_PROC_BROWSER_TEST_P(ContentAnalysisDelegateUnauthorizedBrowserTest, Files) {
base::ScopedAllowBlockingForTesting allow_blocking;
SetUpScanning(/*file_scan*/ true);
ContentAnalysisDelegate::SetFactoryForTesting(
base::BindRepeating(&MinimalFakeContentAnalysisDelegate::Create));
FakeBinaryUploadServiceStorage()->SetAuthForTesting(dm_token(), false);
bool called = false;
base::RunLoop run_loop;
absl::optional<base::RepeatingClosure> quit_closure = absl::nullopt;
// If the scan is blocking, we can call the quit closure when the dialog
// closes. If it's not, call it at the end of the result callback.
if (blocking_scan())
SetQuitClosure(run_loop.QuitClosure());
else
quit_closure = run_loop.QuitClosure();
ContentAnalysisDelegate::Data data;
CreateFilesForTest({"file1.doc", "file2.doc"}, {"content1", "content2"},
&data);
ASSERT_TRUE(ContentAnalysisDelegate::IsEnabled(
browser()->profile(), GURL(kTestUrl), &data, FILE_ATTACHED));
ContentAnalysisDelegate::CreateForWebContents(
browser()->tab_strip_model()->GetActiveWebContents(), std::move(data),
base::BindLambdaForTesting(
[&quit_closure, &called](
const ContentAnalysisDelegate::Data& data,
const ContentAnalysisDelegate::Result& result) {
ASSERT_EQ(result.text_results.size(), 0u);
ASSERT_EQ(result.paths_results.size(), 2u);
ASSERT_TRUE(result.paths_results[0]);
ASSERT_TRUE(result.paths_results[1]);
called = true;
if (quit_closure.has_value())
quit_closure.value().Run();
}),
safe_browsing::DeepScanAccessPoint::UPLOAD);
run_loop.Run();
EXPECT_TRUE(called);
// No requests should be made since the DM token is unauthorized.
ASSERT_EQ(FakeBinaryUploadServiceStorage()->requests_count(), 0);
ASSERT_EQ(FakeBinaryUploadServiceStorage()->ack_count(), 0);
}
} // namespace enterprise_connectors