Commit a8f62560 authored by Daniel Rubery's avatar Daniel Rubery Committed by Commit Bot

Separate FileSourceRequest from DeepScanningDialogDelegate

This CL breaks out FileSourceRequest into it's own public class. Future
CLs will use this class for download deep scanning, as well as save
package deep scanning.

Bug: 1012710
Change-Id: Ic9826c743d86c92834dd9163f3df8aca4bfd0e75
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2092062
Commit-Queue: Daniel Rubery <drubery@chromium.org>
Reviewed-by: default avatarXinghui Lu <xinghuilu@chromium.org>
Reviewed-by: default avatarDominique Fauteux-Chapleau <domfc@chromium.org>
Cr-Commit-Position: refs/heads/master@{#748809}
parent 1a55a9d6
......@@ -151,6 +151,8 @@ jumbo_static_library("safe_browsing") {
"cloud_content_scanning/deep_scanning_dialog_views.h",
"cloud_content_scanning/deep_scanning_utils.cc",
"cloud_content_scanning/deep_scanning_utils.h",
"cloud_content_scanning/file_source_request.cc",
"cloud_content_scanning/file_source_request.h",
"cloud_content_scanning/multipart_uploader.cc",
"cloud_content_scanning/multipart_uploader.h",
"dm_token_utils.cc",
......
......@@ -456,10 +456,12 @@ void BinaryUploadService::IsAuthorized(AuthorizationCallback callback) {
if (!can_upload_enterprise_data_.has_value()) {
// Send a request to check if the browser can upload data.
authorization_callbacks_.push_back(std::move(callback));
if (!pending_validate_data_upload_request_) {
auto dm_token = GetDMToken(profile_);
if (!dm_token.is_valid()) {
std::move(callback).Run(false);
can_upload_enterprise_data_ = false;
RunAuthorizationCallbacks();
return;
}
......@@ -470,7 +472,6 @@ void BinaryUploadService::IsAuthorized(AuthorizationCallback callback) {
request->set_dm_token(dm_token.value());
UploadForDeepScanning(std::move(request));
}
authorization_callbacks_.push_back(std::move(callback));
return;
}
std::move(callback).Run(can_upload_enterprise_data_.value());
......
......@@ -99,7 +99,17 @@ class BinaryUploadService {
// Structure of data returned in the callback to GetRequestData().
struct Data {
Data();
// The data content.
std::string contents;
// The SHA256 of the data.
std::string hash;
// The size of the data. This can differ from |contents.size()| when the
// file is too large for deep scanning. This field will contain the true
// size.
uint64_t size = 0;
};
// Asynchronously returns the file contents to upload.
......
......@@ -28,8 +28,10 @@
#include "chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.h"
#include "chrome/browser/file_util_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h"
#include "chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_views.h"
#include "chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.h"
#include "chrome/browser/safe_browsing/cloud_content_scanning/file_source_request.h"
#include "chrome/browser/safe_browsing/dm_token_utils.h"
#include "chrome/browser/safe_browsing/download_protection/check_client_download_request.h"
#include "chrome/grit/generated_resources.h"
......@@ -68,58 +70,6 @@ bool WaitForVerdict() {
return state == DELAY_UPLOADS || state == DELAY_UPLOADS_AND_DOWNLOADS;
}
DeepScanningDialogDelegate::FileContents GetFileContentsForLargeFile(
const base::FilePath& path,
base::File* file) {
size_t file_size = file->GetLength();
DeepScanningDialogDelegate::FileContents file_contents;
file_contents.result = BinaryUploadService::Result::FILE_TOO_LARGE;
file_contents.size = file_size;
// Only read 50MB at a time to avoid having very large files in memory.
std::unique_ptr<crypto::SecureHash> secure_hash =
crypto::SecureHash::Create(crypto::SecureHash::SHA256);
size_t bytes_read = 0;
std::string buf;
buf.reserve(BinaryUploadService::kMaxUploadSizeBytes);
while (bytes_read < file_size) {
int64_t bytes_currently_read = file->ReadAtCurrentPos(
&buf[0], BinaryUploadService::kMaxUploadSizeBytes);
if (bytes_currently_read == -1)
return DeepScanningDialogDelegate::FileContents();
secure_hash->Update(buf.data(), bytes_currently_read);
bytes_read += bytes_currently_read;
}
file_contents.sha256.resize(crypto::kSHA256Length);
secure_hash->Finish(base::data(file_contents.sha256), crypto::kSHA256Length);
return file_contents;
}
DeepScanningDialogDelegate::FileContents GetFileContentsForNormalFile(
const base::FilePath& path,
base::File* file) {
size_t file_size = file->GetLength();
DeepScanningDialogDelegate::FileContents file_contents;
file_contents.result = BinaryUploadService::Result::SUCCESS;
file_contents.size = file_size;
file_contents.data.contents.resize(file_size);
int64_t bytes_currently_read =
file->ReadAtCurrentPos(&file_contents.data.contents[0], file_size);
if (bytes_currently_read == -1)
return DeepScanningDialogDelegate::FileContents();
DCHECK_EQ(static_cast<size_t>(bytes_currently_read), file_size);
file_contents.sha256 = crypto::SHA256HashString(file_contents.data.contents);
return file_contents;
}
// A BinaryUploadService::Request implementation that gets the data to scan
// from a string.
class StringSourceRequest : public BinaryUploadService::Request {
......@@ -224,41 +174,6 @@ bool* UIEnabledStorage() {
} // namespace
DeepScanningDialogDelegate::FileSourceRequest::FileSourceRequest(
base::WeakPtr<DeepScanningDialogDelegate> delegate,
base::FilePath path,
BinaryUploadService::Callback callback)
: Request(std::move(callback)),
delegate_(delegate),
path_(std::move(path)) {
set_filename(path_.BaseName().AsUTF8Unsafe());
}
DeepScanningDialogDelegate::FileSourceRequest::~FileSourceRequest() = default;
void DeepScanningDialogDelegate::FileSourceRequest::GetRequestData(
DataCallback callback) {
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, {base::TaskPriority::USER_VISIBLE, base::MayBlock()},
base::BindOnce(&DeepScanningDialogDelegate::GetFileContentsSHA256Blocking,
path_),
base::BindOnce(&FileSourceRequest::OnGotFileContents,
weakptr_factory_.GetWeakPtr(), std::move(callback)));
}
void DeepScanningDialogDelegate::FileSourceRequest::OnGotFileContents(
DataCallback callback,
FileContents file_contents) {
if (delegate_)
delegate_->SetFileInfo(path_, std::move(file_contents.sha256),
file_contents.size);
set_digest(base::HexEncode(file_contents.sha256.data(),
file_contents.sha256.size()));
std::move(callback).Run(file_contents.result, file_contents.data);
}
DeepScanningDialogDelegate::Data::Data() = default;
DeepScanningDialogDelegate::Data::Data(Data&& other) = default;
DeepScanningDialogDelegate::Data::~Data() = default;
......@@ -310,9 +225,7 @@ void DeepScanningDialogDelegate::BypassWarnings() {
ReportSensitiveDataWarningBypass(
Profile::FromBrowserContext(web_contents_->GetBrowserContext()),
web_contents_->GetLastCommittedURL(), data_.paths[index].AsUTF8Unsafe(),
base::HexEncode(file_info_[index].sha256.data(),
file_info_[index].sha256.size()),
file_info_[index].mime_type,
file_info_[index].sha256, file_info_[index].mime_type,
extensions::SafeBrowsingPrivateEventRouter::kTriggerFileUpload,
file_info_[index].size);
}
......@@ -365,20 +278,6 @@ bool DeepScanningDialogDelegate::ResultShouldAllowDataUse(
}
}
// static
DeepScanningDialogDelegate::FileContents
DeepScanningDialogDelegate::GetFileContentsSHA256Blocking(
const base::FilePath& path) {
base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ);
if (!file.IsValid())
return FileContents();
return static_cast<size_t>(file.GetLength()) >
BinaryUploadService::kMaxUploadSizeBytes
? GetFileContentsForLargeFile(path, &file)
: GetFileContentsForNormalFile(path, &file);
}
// static
bool DeepScanningDialogDelegate::IsEnabled(Profile* profile,
GURL url,
......@@ -561,9 +460,8 @@ void DeepScanningDialogDelegate::CompleteFileRequestCallback(
MaybeReportDeepScanningVerdict(
Profile::FromBrowserContext(web_contents_->GetBrowserContext()),
web_contents_->GetLastCommittedURL(), path.AsUTF8Unsafe(),
base::HexEncode(file_info_[index].sha256.data(),
file_info_[index].sha256.size()),
mime_type, extensions::SafeBrowsingPrivateEventRouter::kTriggerFileUpload,
file_info_[index].sha256, mime_type,
extensions::SafeBrowsingPrivateEventRouter::kTriggerFileUpload,
file_info_[index].size, result, response);
bool dlp_ok = DlpTriggeredRulesOK(response.dlp_scan_verdict());
......@@ -691,12 +589,14 @@ void DeepScanningDialogDelegate::AnalyzerCallback(
}
auto request = std::make_unique<FileSourceRequest>(
weak_ptr_factory_.GetWeakPtr(), data_.paths[index],
data_.paths[index],
base::BindOnce(&DeepScanningDialogDelegate::FileRequestCallback,
weak_ptr_factory_.GetWeakPtr(), data_.paths[index]));
PrepareRequest(DlpDeepScanningClientRequest::FILE_UPLOAD, request.get());
UploadFileForDeepScanning(data_.paths[index], std::move(request));
FileSourceRequest* request_raw = request.get();
request_raw->GetRequestData(base::BindOnce(
&DeepScanningDialogDelegate::OnGotFileInfo,
weak_ptr_factory_.GetWeakPtr(), std::move(request), data_.paths[index]));
}
void DeepScanningDialogDelegate::PrepareRequest(
......@@ -781,14 +681,19 @@ void DeepScanningDialogDelegate::RunCallback() {
std::move(callback_).Run(data_, result_);
}
void DeepScanningDialogDelegate::SetFileInfo(const base::FilePath& path,
std::string sha256,
int64_t size) {
void DeepScanningDialogDelegate::OnGotFileInfo(
std::unique_ptr<BinaryUploadService::Request> request,
const base::FilePath& path,
BinaryUploadService::Result result,
const BinaryUploadService::Request::Data& data) {
auto it = std::find(data_.paths.begin(), data_.paths.end(), path);
DCHECK(it != data_.paths.end());
size_t index = std::distance(data_.paths.begin(), it);
file_info_[index].sha256 = std::move(sha256);
file_info_[index].size = size;
file_info_[index].sha256 = data.hash;
file_info_[index].size = data.size;
PrepareRequest(DlpDeepScanningClientRequest::FILE_UPLOAD, request.get());
UploadFileForDeepScanning(data_.paths[index], std::move(request));
}
void DeepScanningDialogDelegate::UpdateFinalResult(
......
......@@ -111,7 +111,7 @@ class DeepScanningDialogDelegate {
FileInfo(FileInfo&& other);
~FileInfo();
// SHA256 hash for the given file.
// Hex-encoded SHA256 hash for the given file.
std::string sha256;
// File size in bytes. -1 represents an unknown size.
......@@ -222,31 +222,6 @@ class DeepScanningDialogDelegate {
// block it.
static bool ResultShouldAllowDataUse(BinaryUploadService::Result result);
// Callback used by FileSourceRequest to read file data on a blocking thread.
static FileContents GetFileContentsSHA256Blocking(const base::FilePath& path);
// A BinaryUploadService::Request implementation that gets the data to scan
// from the contents of a file.
class FileSourceRequest : public BinaryUploadService::Request {
public:
FileSourceRequest(base::WeakPtr<DeepScanningDialogDelegate> delegate,
base::FilePath path,
BinaryUploadService::Callback callback);
FileSourceRequest(const FileSourceRequest&) = delete;
FileSourceRequest& operator=(const FileSourceRequest&) = delete;
~FileSourceRequest() override;
// BinaryUploadService::Request implementation.
void GetRequestData(DataCallback callback) override;
private:
void OnGotFileContents(DataCallback callback, FileContents file_contents);
base::WeakPtr<DeepScanningDialogDelegate> delegate_;
base::FilePath path_;
base::WeakPtrFactory<FileSourceRequest> weakptr_factory_{this};
};
protected:
DeepScanningDialogDelegate(content::WebContents* web_contents,
Data data,
......@@ -310,10 +285,12 @@ class DeepScanningDialogDelegate {
// |callback_| is cleared after being run.
void RunCallback();
// Sets the FileInfo the given file.
void SetFileInfo(const base::FilePath& path,
std::string sha256,
int64_t size);
// Called when the file info for |path| has been fetched. Also begins the
// upload process.
void OnGotFileInfo(std::unique_ptr<BinaryUploadService::Request> request,
const base::FilePath& path,
BinaryUploadService::Result result,
const BinaryUploadService::Request::Data& data);
// Completion of |FileRequestCallback| once the mime type is obtained
// asynchronously.
......
......@@ -46,6 +46,10 @@ class FakeBinaryUploadService : public BinaryUploadService {
prepared_file_responses_[path] = response;
}
void SetShouldAutomaticallyAuthorize(bool authorize) {
should_automatically_authorize_ = authorize;
}
int requests_count() const { return requests_count_; }
private:
......@@ -53,6 +57,9 @@ class FakeBinaryUploadService : public BinaryUploadService {
// The first uploaded request is the authentication one.
if (++requests_count_ == 1) {
authorization_request_.swap(request);
if (should_automatically_authorize_)
ReturnAuthorizedResponse();
} else {
std::string file = request->deep_scanning_request().filename();
if (file.empty()) {
......@@ -73,6 +80,7 @@ class FakeBinaryUploadService : public BinaryUploadService {
std::map<std::string, BinaryUploadService::Result> prepared_file_results_;
std::map<std::string, DeepScanningClientResponse> prepared_file_responses_;
int requests_count_ = 0;
bool should_automatically_authorize_ = false;
};
FakeBinaryUploadService* FakeBinaryUploadServiceStorage() {
......@@ -198,6 +206,8 @@ IN_PROC_BROWSER_TEST_F(DeepScanningDialogDelegateBrowserTest, Files) {
ok_file.WriteAtCurrentPos(ok_content.data(), ok_content.size());
bad_file.WriteAtCurrentPos(bad_content.data(), bad_content.size());
FakeBinaryUploadServiceStorage()->SetShouldAutomaticallyAuthorize(true);
// Set up delegate and upload service.
EnableUploadScanning();
......@@ -246,9 +256,8 @@ IN_PROC_BROWSER_TEST_F(DeepScanningDialogDelegateBrowserTest, Files) {
}),
DeepScanAccessPoint::UPLOAD);
FakeBinaryUploadServiceStorage()->ReturnAuthorizedResponse();
run_loop.Run();
EXPECT_TRUE(called);
// There should have been 1 request per file and 1 for authentication.
......
......@@ -1532,150 +1532,4 @@ TEST_F(DeepScanningDialogDelegatePolicyResultsTest,
BinaryUploadService::Result::FILE_ENCRYPTED));
}
class DeepScanningFileContentsTest : public testing::Test {
public:
void TestFile(const std::string& file_contents,
const std::string& expected_sha256,
BinaryUploadService::Result expected_result) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
base::FilePath file_path = temp_dir.GetPath().AppendASCII("foo.doc");
// Create the file.
base::File file(file_path,
base::File::FLAG_CREATE | base::File::FLAG_WRITE);
file.WriteAtCurrentPos(file_contents.data(), file_contents.size());
// Obtain its contents and validate the returned value.
DeepScanningDialogDelegate::FileContents contents =
DeepScanningDialogDelegate::GetFileContentsSHA256Blocking(file_path);
ASSERT_EQ(contents.result, expected_result);
ASSERT_EQ(static_cast<size_t>(contents.size), file_contents.size());
if (expected_result == BinaryUploadService::Result::FILE_TOO_LARGE) {
ASSERT_TRUE(contents.data.contents.empty());
} else {
ASSERT_EQ(contents.data.contents, file_contents);
}
ASSERT_EQ(contents.sha256, expected_sha256);
}
static constexpr int kLargeFileThreshold =
BinaryUploadService::kMaxUploadSizeBytes;
};
TEST_F(DeepScanningFileContentsTest, InvalidFiles) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
// Non-existent files should return UNKNOWN and have no information set.
DeepScanningDialogDelegate::FileContents contents =
DeepScanningDialogDelegate::GetFileContentsSHA256Blocking(
temp_dir.GetPath().AppendASCII("not_a_real.doc"));
ASSERT_EQ(contents.result, BinaryUploadService::Result::UNKNOWN);
ASSERT_EQ(contents.size, 0u);
ASSERT_TRUE(contents.data.contents.empty());
ASSERT_TRUE(contents.sha256.empty());
// Directories should not be used as paths passed to GetFileSHA256Blocking, so
// they should return UNKNOWN and have no information set.
contents = DeepScanningDialogDelegate::GetFileContentsSHA256Blocking(
temp_dir.GetPath());
ASSERT_EQ(contents.result, BinaryUploadService::Result::UNKNOWN);
ASSERT_EQ(contents.size, 0u);
ASSERT_TRUE(contents.data.contents.empty());
ASSERT_TRUE(contents.sha256.empty());
}
TEST_F(DeepScanningFileContentsTest, NormalFiles) {
std::string file_contents = "Normal file contents";
// printf "Normal file contents" | sha256sum
std::string expected_sha256 = {
0x29, 0x64, 0x4c, 0x10, 0xbd, 0x03, 0x68, 0x66, 0xfc, 0xfd, 0x2b,
0xda, 0xcf, 0xf3, 0x40, 0xdb, 0x5d, 0xe4, 0x7a, 0x90, 0x00, 0x2d,
0x6a, 0xb0, 0xc4, 0x2d, 0xe6, 0xa2, 0x2c, 0x26, 0x15, 0x8b};
TestFile(file_contents, expected_sha256,
BinaryUploadService::Result::SUCCESS);
std::string almost_large_file_contents(kLargeFileThreshold, 'a');
// python3 -c "print('a' * (50 * 1024 * 1024), end='')" | sha256sum
std::string almost_large_expected_sha256 = {
0x4f, 0x0e, 0x9c, 0x6a, 0x1a, 0x9a, 0x90, 0xf3, 0x5b, 0x88, 0x4d,
0x0f, 0x0e, 0x73, 0x43, 0x45, 0x9c, 0x21, 0x06, 0x0e, 0xef, 0xec,
0x6c, 0x0f, 0x2f, 0xa9, 0xdc, 0x11, 0x18, 0xdb, 0xe5, 0xbe};
TestFile(almost_large_file_contents, almost_large_expected_sha256,
BinaryUploadService::Result::SUCCESS);
}
TEST_F(DeepScanningFileContentsTest, LargeFiles) {
std::string large_file_contents(kLargeFileThreshold + 1, 'a');
// python3 -c "print('a' * (50 * 1024 * 1024 + 1), end='')" | sha256sum
std::string large_expected_sha256 = {
0x9e, 0xb5, 0x6d, 0xb3, 0x0c, 0x49, 0xe1, 0x31, 0x45, 0x9f, 0xe7,
0x35, 0xba, 0x6b, 0x9d, 0x38, 0x32, 0x73, 0x76, 0x22, 0x4e, 0xc8,
0xd5, 0xa1, 0x23, 0x3f, 0x43, 0xa5, 0xb4, 0xa2, 0x59, 0x42};
TestFile(large_file_contents, large_expected_sha256,
BinaryUploadService::Result::FILE_TOO_LARGE);
std::string very_large_file_contents(2 * kLargeFileThreshold, 'a');
// python3 -c "print('a' * (100 * 1024 * 1024), end='')" | sha256sum
std::string very_large_expected_sha256 = {
0xce, 0xe4, 0x1e, 0x98, 0xd0, 0xa6, 0xad, 0x65, 0xcc, 0x0e, 0xc7,
0x7a, 0x2b, 0xa5, 0x0b, 0xf2, 0x6d, 0x64, 0xdc, 0x90, 0x07, 0xf7,
0xf1, 0xc7, 0xd7, 0xdf, 0x68, 0xb8, 0xb7, 0x12, 0x91, 0xa6};
TestFile(very_large_file_contents, very_large_expected_sha256,
BinaryUploadService::Result::FILE_TOO_LARGE);
}
TEST(DeepScanningFileSourceRequestTest, PopulatesDigest) {
base::test::TaskEnvironment task_environment;
std::string file_contents = "Normal file contents";
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
base::FilePath file_path = temp_dir.GetPath().AppendASCII("foo.doc");
// Create the file.
base::File file(file_path, base::File::FLAG_CREATE | base::File::FLAG_WRITE);
file.WriteAtCurrentPos(file_contents.data(), file_contents.size());
DeepScanningDialogDelegate::FileSourceRequest request(nullptr, file_path,
base::DoNothing());
base::RunLoop run_loop;
request.GetRequestData(base::BindLambdaForTesting(
[&run_loop](BinaryUploadService::Result result,
const BinaryUploadService::Request::Data& data) {
run_loop.Quit();
}));
run_loop.Run();
// printf "Normal file contents" | sha256sum | tr '[:lower:]' '[:upper:]'
EXPECT_EQ(request.deep_scanning_request().digest(),
"29644C10BD036866FCFD2BDACFF340DB5DE47A90002D6AB0C42DE6A22C26158B");
}
TEST(DeepScanningFileSourceRequestTest, PopulatesFilename) {
base::test::TaskEnvironment task_environment;
std::string file_contents = "contents";
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
base::FilePath file_path = temp_dir.GetPath().AppendASCII("foo.doc");
// Create the file.
base::File file(file_path, base::File::FLAG_CREATE | base::File::FLAG_WRITE);
file.WriteAtCurrentPos(file_contents.data(), file_contents.size());
DeepScanningDialogDelegate::FileSourceRequest request(nullptr, file_path,
base::DoNothing());
base::RunLoop run_loop;
request.GetRequestData(base::BindLambdaForTesting(
[&run_loop](BinaryUploadService::Result result,
const BinaryUploadService::Request::Data& data) {
run_loop.Quit();
}));
run_loop.Run();
EXPECT_EQ(request.deep_scanning_request().filename(), "foo.doc");
}
} // namespace safe_browsing
// Copyright (c) 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 "chrome/browser/safe_browsing/cloud_content_scanning/file_source_request.h"
#include "base/strings/string_number_conversions.h"
#include "base/task/post_task.h"
#include "chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h"
#include "crypto/secure_hash.h"
#include "crypto/sha2.h"
namespace safe_browsing {
namespace {
std::pair<BinaryUploadService::Result, BinaryUploadService::Request::Data>
GetFileContentsForLargeFile(const base::FilePath& path, base::File* file) {
size_t file_size = file->GetLength();
BinaryUploadService::Request::Data file_data;
file_data.size = file_size;
// Only read 50MB at a time to avoid having very large files in memory.
std::unique_ptr<crypto::SecureHash> secure_hash =
crypto::SecureHash::Create(crypto::SecureHash::SHA256);
size_t bytes_read = 0;
std::string buf;
buf.reserve(BinaryUploadService::kMaxUploadSizeBytes);
while (bytes_read < file_size) {
int64_t bytes_currently_read = file->ReadAtCurrentPos(
&buf[0], BinaryUploadService::kMaxUploadSizeBytes);
if (bytes_currently_read == -1) {
return std::make_pair(BinaryUploadService::Result::UNKNOWN,
BinaryUploadService::Request::Data());
}
secure_hash->Update(buf.data(), bytes_currently_read);
bytes_read += bytes_currently_read;
}
file_data.hash.resize(crypto::kSHA256Length);
secure_hash->Finish(base::data(file_data.hash), crypto::kSHA256Length);
file_data.hash =
base::HexEncode(base::as_bytes(base::make_span(file_data.hash)));
return std::make_pair(BinaryUploadService::Result::FILE_TOO_LARGE, file_data);
}
std::pair<BinaryUploadService::Result, BinaryUploadService::Request::Data>
GetFileContentsForNormalFile(const base::FilePath& path, base::File* file) {
size_t file_size = file->GetLength();
BinaryUploadService::Request::Data file_data;
file_data.size = file_size;
file_data.contents.resize(file_size);
int64_t bytes_currently_read =
file->ReadAtCurrentPos(&file_data.contents[0], file_size);
if (bytes_currently_read == -1) {
return std::make_pair(BinaryUploadService::Result::UNKNOWN,
BinaryUploadService::Request::Data());
}
DCHECK_EQ(static_cast<size_t>(bytes_currently_read), file_size);
file_data.hash = crypto::SHA256HashString(file_data.contents);
file_data.hash =
base::HexEncode(base::as_bytes(base::make_span(file_data.hash)));
return std::make_pair(BinaryUploadService::Result::SUCCESS, file_data);
}
std::pair<BinaryUploadService::Result, BinaryUploadService::Request::Data>
GetFileDataBlocking(const base::FilePath& path) {
base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ);
if (!file.IsValid()) {
return std::make_pair(BinaryUploadService::Result::UNKNOWN,
BinaryUploadService::Request::Data());
}
return static_cast<size_t>(file.GetLength()) >
BinaryUploadService::kMaxUploadSizeBytes
? GetFileContentsForLargeFile(path, &file)
: GetFileContentsForNormalFile(path, &file);
}
} // namespace
FileSourceRequest::FileSourceRequest(base::FilePath path,
BinaryUploadService::Callback callback)
: Request(std::move(callback)),
has_cached_result_(false),
path_(std::move(path)) {
set_filename(path_.BaseName().AsUTF8Unsafe());
}
FileSourceRequest::~FileSourceRequest() = default;
void FileSourceRequest::GetRequestData(DataCallback callback) {
if (has_cached_result_) {
std::move(callback).Run(cached_result_, cached_data_);
return;
}
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, {base::TaskPriority::USER_VISIBLE, base::MayBlock()},
base::BindOnce(&GetFileDataBlocking, path_),
base::BindOnce(&FileSourceRequest::OnGotFileData,
weakptr_factory_.GetWeakPtr(), std::move(callback)));
}
void FileSourceRequest::OnGotFileData(
DataCallback callback,
std::pair<BinaryUploadService::Result, Data> result_and_data) {
set_digest(result_and_data.second.hash);
has_cached_result_ = true;
cached_result_ = result_and_data.first;
cached_data_ = result_and_data.second;
std::move(callback).Run(result_and_data.first, result_and_data.second);
}
} // namespace safe_browsing
// Copyright (c) 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.
#ifndef CHROME_BROWSER_SAFE_BROWSING_CLOUD_CONTENT_SCANNING_FILE_SOURCE_REQUEST_H_
#define CHROME_BROWSER_SAFE_BROWSING_CLOUD_CONTENT_SCANNING_FILE_SOURCE_REQUEST_H_
#include "chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h"
namespace safe_browsing {
// A BinaryUploadService::Request implementation that gets the data to scan
// from the contents of a file. It caches the results so that future calls to
// GetRequestData will return quickly.
class FileSourceRequest : public BinaryUploadService::Request {
public:
FileSourceRequest(base::FilePath path,
BinaryUploadService::Callback callback);
FileSourceRequest(const FileSourceRequest&) = delete;
FileSourceRequest& operator=(const FileSourceRequest&) = delete;
~FileSourceRequest() override;
// BinaryUploadService::Request implementation.
void GetRequestData(DataCallback callback) override;
private:
void OnGotFileData(
DataCallback callback,
std::pair<BinaryUploadService::Result, Data> result_and_data);
bool has_cached_result_;
BinaryUploadService::Result cached_result_;
Data cached_data_;
base::FilePath path_;
base::WeakPtrFactory<FileSourceRequest> weakptr_factory_{this};
};
} // namespace safe_browsing
#endif // CHROME_BROWSER_SAFE_BROWSING_CLOUD_CONTENT_SCANNING_FILE_SOURCE_REQUEST_H_
// Copyright (c) 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 "chrome/browser/safe_browsing/cloud_content_scanning/file_source_request.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/run_loop.h"
#include "base/test/bind_test_util.h"
#include "base/test/task_environment.h"
#include "chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h"
#include "chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace safe_browsing {
namespace {
void GetResultsForFileContents(const std::string& file_contents,
BinaryUploadService::Result* out_result,
BinaryUploadService::Request::Data* out_data) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
base::FilePath file_path = temp_dir.GetPath().AppendASCII("normal.doc");
base::WriteFile(file_path, file_contents.data(), file_contents.size());
FileSourceRequest request(file_path, base::DoNothing());
bool called = false;
base::RunLoop run_loop;
request.GetRequestData(base::BindLambdaForTesting(
[&run_loop, &called, &out_result, &out_data](
BinaryUploadService::Result result,
const BinaryUploadService::Request::Data& data) {
called = true;
run_loop.Quit();
*out_result = result;
*out_data = data;
}));
run_loop.Run();
EXPECT_TRUE(called);
}
} // namespace
TEST(FileSourceRequestTest, InvalidFiles) {
base::test::TaskEnvironment task_environment;
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
{
// Non-existent files should return UNKNOWN and have no information set.
FileSourceRequest request(temp_dir.GetPath().AppendASCII("not_a_real.doc"),
base::DoNothing());
bool called = false;
base::RunLoop run_loop;
request.GetRequestData(base::BindLambdaForTesting(
[&run_loop, &called](BinaryUploadService::Result result,
const BinaryUploadService::Request::Data& data) {
called = true;
run_loop.Quit();
EXPECT_EQ(result, BinaryUploadService::Result::UNKNOWN);
EXPECT_EQ(data.size, 0u);
EXPECT_TRUE(data.contents.empty());
EXPECT_TRUE(data.hash.empty());
}));
run_loop.Run();
EXPECT_TRUE(called);
}
{
// Directories should not be used as paths passed to GetFileSHA256Blocking,
// so they should return UNKNOWN and have no information set.
FileSourceRequest request(temp_dir.GetPath(), base::DoNothing());
bool called = false;
base::RunLoop run_loop;
request.GetRequestData(base::BindLambdaForTesting(
[&run_loop, &called](BinaryUploadService::Result result,
const BinaryUploadService::Request::Data& data) {
called = true;
run_loop.Quit();
EXPECT_EQ(result, BinaryUploadService::Result::UNKNOWN);
EXPECT_EQ(data.size, 0u);
EXPECT_TRUE(data.contents.empty());
EXPECT_TRUE(data.hash.empty());
}));
run_loop.Run();
EXPECT_TRUE(called);
}
}
TEST(FileSourceRequestTest, NormalFiles) {
base::test::TaskEnvironment task_environment;
BinaryUploadService::Result result;
BinaryUploadService::Request::Data data;
std::string normal_contents = "Normal file contents";
GetResultsForFileContents(normal_contents, &result, &data);
EXPECT_EQ(result, BinaryUploadService::Result::SUCCESS);
EXPECT_EQ(data.size, normal_contents.size());
EXPECT_EQ(data.contents, normal_contents);
// printf "Normal file contents" | sha256sum | tr '[:lower:]' '[:upper:]'
EXPECT_EQ(data.hash,
"29644C10BD036866FCFD2BDACFF340DB5DE47A90002D6AB0C42DE6A22C26158B");
std::string long_contents =
std::string(BinaryUploadService::kMaxUploadSizeBytes, 'a');
GetResultsForFileContents(long_contents, &result, &data);
EXPECT_EQ(result, BinaryUploadService::Result::SUCCESS);
EXPECT_EQ(data.size, long_contents.size());
EXPECT_EQ(data.contents, long_contents);
// printf "Normal file contents" | sha256sum | tr '[:lower:]' '[:upper:]'
EXPECT_EQ(data.hash,
"4F0E9C6A1A9A90F35B884D0F0E7343459C21060EEFEC6C0F2FA9DC1118DBE5BE");
}
TEST(FileSourceRequest, LargeFiles) {
base::test::TaskEnvironment task_environment;
BinaryUploadService::Result result;
BinaryUploadService::Request::Data data;
std::string large_file_contents(BinaryUploadService::kMaxUploadSizeBytes + 1,
'a');
GetResultsForFileContents(large_file_contents, &result, &data);
EXPECT_EQ(result, BinaryUploadService::Result::FILE_TOO_LARGE);
EXPECT_EQ(data.size, large_file_contents.size());
EXPECT_TRUE(data.contents.empty());
// python3 -c "print('a' * (50 * 1024 * 1024 + 1), end='')" | sha256sum | tr
// '[:lower:]' '[:upper:]'
EXPECT_EQ(data.hash,
"9EB56DB30C49E131459FE735BA6B9D38327376224EC8D5A1233F43A5B4A25942");
std::string very_large_file_contents(
2 * BinaryUploadService::kMaxUploadSizeBytes, 'a');
GetResultsForFileContents(very_large_file_contents, &result, &data);
EXPECT_EQ(result, BinaryUploadService::Result::FILE_TOO_LARGE);
EXPECT_EQ(data.size, very_large_file_contents.size());
EXPECT_TRUE(data.contents.empty());
// python3 -c "print('a' * (100 * 1024 * 1024), end='')" | sha256sum | tr
// '[:lower:]' '[:upper:]'
EXPECT_EQ(data.hash,
"CEE41E98D0A6AD65CC0EC77A2BA50BF26D64DC9007F7F1C7D7DF68B8B71291A6");
}
TEST(FileSourceRequestTest, PopulatesDigest) {
base::test::TaskEnvironment task_environment;
std::string file_contents = "Normal file contents";
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
base::FilePath file_path = temp_dir.GetPath().AppendASCII("foo.doc");
// Create the file.
base::File file(file_path, base::File::FLAG_CREATE | base::File::FLAG_WRITE);
file.WriteAtCurrentPos(file_contents.data(), file_contents.size());
FileSourceRequest request(file_path, base::DoNothing());
base::RunLoop run_loop;
request.GetRequestData(base::BindLambdaForTesting(
[&run_loop](BinaryUploadService::Result result,
const BinaryUploadService::Request::Data& data) {
run_loop.Quit();
}));
run_loop.Run();
// printf "Normal file contents" | sha256sum | tr '[:lower:]' '[:upper:]'
EXPECT_EQ(request.deep_scanning_request().digest(),
"29644C10BD036866FCFD2BDACFF340DB5DE47A90002D6AB0C42DE6A22C26158B");
}
TEST(FileSourceRequestTest, PopulatesFilename) {
base::test::TaskEnvironment task_environment;
std::string file_contents = "contents";
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
base::FilePath file_path = temp_dir.GetPath().AppendASCII("foo.doc");
// Create the file.
base::File file(file_path, base::File::FLAG_CREATE | base::File::FLAG_WRITE);
file.WriteAtCurrentPos(file_contents.data(), file_contents.size());
FileSourceRequest request(file_path, base::DoNothing());
base::RunLoop run_loop;
request.GetRequestData(base::BindLambdaForTesting(
[&run_loop](BinaryUploadService::Result result,
const BinaryUploadService::Request::Data& data) {
run_loop.Quit();
}));
run_loop.Run();
EXPECT_EQ(request.deep_scanning_request().filename(), "foo.doc");
}
TEST(FileSourceRequestTest, CachesResults) {
base::test::TaskEnvironment task_environment;
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
std::string normal_contents = "Normal file contents";
base::FilePath file_path = temp_dir.GetPath().AppendASCII("normal.doc");
base::WriteFile(file_path, normal_contents.data(), normal_contents.size());
BinaryUploadService::Result async_result;
BinaryUploadService::Request::Data async_data;
FileSourceRequest request(file_path, base::DoNothing());
bool called = false;
base::RunLoop run_loop;
request.GetRequestData(base::BindLambdaForTesting(
[&run_loop, &called, &async_result, &async_data](
BinaryUploadService::Result result,
const BinaryUploadService::Request::Data& data) {
called = true;
run_loop.Quit();
async_result = result;
async_data = data;
}));
run_loop.Run();
ASSERT_TRUE(called);
BinaryUploadService::Result sync_result;
BinaryUploadService::Request::Data sync_data;
request.GetRequestData(base::BindLambdaForTesting(
[&run_loop, &called, &sync_result, &sync_data](
BinaryUploadService::Result result,
const BinaryUploadService::Request::Data& data) {
called = true;
run_loop.Quit();
sync_result = result;
sync_data = data;
}));
EXPECT_EQ(sync_result, async_result);
EXPECT_EQ(sync_data.contents, async_data.contents);
EXPECT_EQ(sync_data.size, async_data.size);
EXPECT_EQ(sync_data.hash, async_data.hash);
}
} // namespace safe_browsing
......@@ -4970,6 +4970,7 @@ test("unit_tests") {
"../browser/safe_browsing/cloud_content_scanning/binary_upload_service_unittest.cc",
"../browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate_unittest.cc",
"../browser/safe_browsing/cloud_content_scanning/deep_scanning_utils_unittest.cc",
"../browser/safe_browsing/cloud_content_scanning/file_source_request_unittest.cc",
"../browser/safe_browsing/cloud_content_scanning/multipart_uploader_unittest.cc",
"../browser/safe_browsing/download_protection/deep_scanning_request_unittest.cc",
"../browser/safe_browsing/download_protection/download_feedback_service_unittest.cc",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment