| // Copyright 2021 The ChromiumOS Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "dlp/dlp_adaptor.h" | |
| #include <map> | |
| #include <memory> | |
| #include <poll.h> | |
| #include <string> | |
| #include <utility> | |
| #include <base/containers/contains.h> | |
| #include <base/files/file_util.h> | |
| #include <base/files/scoped_file.h> | |
| #include <base/files/scoped_temp_dir.h> | |
| #include <base/functional/callback.h> | |
| #include <base/memory/scoped_refptr.h> | |
| #include <base/process/process_handle.h> | |
| #include <base/run_loop.h> | |
| #include "base/time/time.h" | |
| #include <brillo/dbus/mock_dbus_method_response.h> | |
| #include <brillo/files/file_util.h> | |
| #include <gtest/gtest.h> | |
| #include "dlp/dlp_adaptor_test_helper.h" | |
| #include "dlp/file_id.h" | |
| using testing::_; | |
| using testing::ElementsAre; | |
| using testing::Invoke; | |
| using testing::Return; | |
| namespace dlp { | |
| namespace { | |
| // Some arbitrary D-Bus message serial number. Required for mocking D-Bus calls. | |
| constexpr int kDBusSerial = 123; | |
| constexpr int kPid = 1234; | |
| constexpr int kIoTaskId = 12345; | |
| class FileOpenRequestResultWaiter { | |
| public: | |
| FileOpenRequestResultWaiter() = default; | |
| ~FileOpenRequestResultWaiter() = default; | |
| FileOpenRequestResultWaiter(const FileOpenRequestResultWaiter&) = delete; | |
| FileOpenRequestResultWaiter& operator=(const FileOpenRequestResultWaiter&) = | |
| delete; | |
| // Waits until the result is available and returns it. | |
| bool GetResult() { | |
| run_loop_.Run(); | |
| return result_; | |
| } | |
| // Returns the callback which should be passed to | |
| // DlpAdaptor::ProcessFileOpenRequest. | |
| base::OnceCallback<void(bool)> GetCallback() { | |
| return base::BindOnce(&FileOpenRequestResultWaiter::OnResult, | |
| base::Unretained(this)); | |
| } | |
| private: | |
| // Invoked when a result is available. | |
| void OnResult(bool result) { | |
| result_ = result; | |
| run_loop_.Quit(); | |
| } | |
| base::RunLoop run_loop_; | |
| // Not initialized before run loop is quit. | |
| bool result_; | |
| }; | |
| bool IsFdClosed(int fd) { | |
| struct pollfd pfd = { | |
| .fd = fd, | |
| .events = POLLERR, | |
| }; | |
| if (poll(&pfd, 1, 1) < 0) | |
| return false; | |
| return pfd.revents & POLLERR; | |
| } | |
| // Parses a response message from a byte array. | |
| template <typename TResponse> | |
| TResponse ParseResponse(const std::vector<uint8_t>& response_blob) { | |
| TResponse response; | |
| EXPECT_TRUE( | |
| response.ParseFromArray(response_blob.data(), response_blob.size())); | |
| return response; | |
| } | |
| // Pseudo-random proto made of determined 10 bytes. | |
| std::vector<uint8_t> RandomProtoBlob() { | |
| const size_t random_proto_size = 10; | |
| std::vector<uint8_t> proto_blob(random_proto_size); | |
| for (uint8_t i = 0; i < random_proto_size; ++i) { | |
| proto_blob.push_back(i); | |
| } | |
| return proto_blob; | |
| } | |
| } // namespace | |
| class DlpAdaptorTest : public ::testing::Test { | |
| public: | |
| DlpAdaptorTest() { | |
| // By passing true to SetFanotifyWatcherStartedForTesting, | |
| // DlpAdaptor won't try to start Fanotify. And given that these tests are | |
| // meant to test DlpAdaptor and don't depend on Fanotify, so Fanotify | |
| // initialisation isn't needed anyway. | |
| GetDlpAdaptor()->SetFanotifyWatcherStartedForTesting(true); | |
| } | |
| ~DlpAdaptorTest() override = default; | |
| DlpAdaptorTest(const DlpAdaptorTest&) = delete; | |
| DlpAdaptorTest& operator=(const DlpAdaptorTest&) = delete; | |
| DlpAdaptor* GetDlpAdaptor() { return helper_.adaptor(); } | |
| scoped_refptr<dbus::MockObjectProxy> GetMockDlpFilesPolicyServiceProxy() { | |
| return helper_.mock_dlp_files_policy_service_proxy(); | |
| } | |
| AddFileRequest CreateAddFileRequest(const base::FilePath& path, | |
| const std::string& source, | |
| const std::string& referrer) { | |
| AddFileRequest request; | |
| request.set_file_path(path.value()); | |
| request.set_source_url(source); | |
| request.set_referrer_url(referrer); | |
| return request; | |
| } | |
| std::vector<uint8_t> CreateSerializedAddFilesRequest( | |
| std::vector<AddFileRequest> add_file_requests) { | |
| AddFilesRequest request; | |
| *request.mutable_add_file_requests() = {add_file_requests.begin(), | |
| add_file_requests.end()}; | |
| std::vector<uint8_t> proto_blob(request.ByteSizeLong()); | |
| request.SerializeToArray(proto_blob.data(), proto_blob.size()); | |
| return proto_blob; | |
| } | |
| std::vector<uint8_t> CreateSerializedRequestFileAccessRequest( | |
| std::vector<std::string> files_paths, | |
| int pid, | |
| const std::string& destination) { | |
| RequestFileAccessRequest request; | |
| *request.mutable_files_paths() = {files_paths.begin(), files_paths.end()}; | |
| request.set_process_id(pid); | |
| request.set_destination_url(destination); | |
| std::vector<uint8_t> proto_blob(request.ByteSizeLong()); | |
| request.SerializeToArray(proto_blob.data(), proto_blob.size()); | |
| return proto_blob; | |
| } | |
| std::vector<uint8_t> CreateSerializedRequestFileAccessRequest( | |
| std::vector<std::string> files_paths, int pid, DlpComponent component) { | |
| RequestFileAccessRequest request; | |
| *request.mutable_files_paths() = {files_paths.begin(), files_paths.end()}; | |
| request.set_process_id(pid); | |
| request.set_destination_component(component); | |
| std::vector<uint8_t> proto_blob(request.ByteSizeLong()); | |
| request.SerializeToArray(proto_blob.data(), proto_blob.size()); | |
| return proto_blob; | |
| } | |
| std::vector<uint8_t> CreateSerializedCheckFilesTransferRequest( | |
| std::vector<std::string> files_paths, const std::string& destination) { | |
| CheckFilesTransferRequest request; | |
| *request.mutable_files_paths() = {files_paths.begin(), files_paths.end()}; | |
| request.set_destination_url(destination); | |
| request.set_file_action(FileAction::TRANSFER); | |
| request.set_io_task_id(kIoTaskId); | |
| std::vector<uint8_t> proto_blob(request.ByteSizeLong()); | |
| request.SerializeToArray(proto_blob.data(), proto_blob.size()); | |
| return proto_blob; | |
| } | |
| std::vector<uint8_t> CreateSerializedCheckFilesTransferRequest( | |
| std::vector<std::string> files_paths, DlpComponent destination) { | |
| CheckFilesTransferRequest request; | |
| *request.mutable_files_paths() = {files_paths.begin(), files_paths.end()}; | |
| request.set_destination_component(destination); | |
| request.set_file_action(FileAction::TRANSFER); | |
| request.set_io_task_id(kIoTaskId); | |
| std::vector<uint8_t> proto_blob(request.ByteSizeLong()); | |
| request.SerializeToArray(proto_blob.data(), proto_blob.size()); | |
| return proto_blob; | |
| } | |
| std::vector<uint8_t> CreateSerializedGetFilesSourcesRequest( | |
| std::vector<std::string> paths) { | |
| GetFilesSourcesRequest request; | |
| *request.mutable_files_paths() = {paths.begin(), paths.end()}; | |
| std::vector<uint8_t> proto_blob(request.ByteSizeLong()); | |
| request.SerializeToArray(proto_blob.data(), proto_blob.size()); | |
| return proto_blob; | |
| } | |
| void StubIsDlpPolicyMatched( | |
| dbus::MethodCall* method_call, | |
| int /* timeout_ms */, | |
| dbus::MockObjectProxy::ResponseCallback* response_callback, | |
| dbus::MockObjectProxy::ErrorCallback* error_callback) { | |
| method_call->SetSerial(kDBusSerial); | |
| auto response = dbus::Response::FromMethodCall(method_call); | |
| dbus::MessageWriter writer(response.get()); | |
| IsDlpPolicyMatchedResponse response_proto; | |
| response_proto.set_restricted(is_file_policy_restricted_); | |
| writer.AppendProtoAsArrayOfBytes(response_proto); | |
| std::move(*response_callback).Run(response.get()); | |
| } | |
| void StubReplyWithError( | |
| dbus::MethodCall* method_call, | |
| int /* timeout_ms */, | |
| dbus::MockObjectProxy::ResponseCallback* response_callback, | |
| dbus::MockObjectProxy::ErrorCallback* error_callback) { | |
| method_call->SetSerial(kDBusSerial); | |
| auto error_response = dbus::ErrorResponse::FromMethodCall( | |
| method_call, "dlp.Error", "error message"); | |
| std::move(*error_callback).Run(error_response.get()); | |
| } | |
| void StubReplyBadProto( | |
| dbus::MethodCall* method_call, | |
| int /* timeout_ms */, | |
| dbus::MockObjectProxy::ResponseCallback* response_callback, | |
| dbus::MockObjectProxy::ErrorCallback* error_callback) { | |
| method_call->SetSerial(kDBusSerial); | |
| auto response = dbus::Response::FromMethodCall(method_call); | |
| dbus::MessageWriter writer(response.get()); | |
| writer.AppendArrayOfBytes(RandomProtoBlob()); | |
| std::move(*response_callback).Run(response.get()); | |
| } | |
| void StubIsFilesTransferRestricted( | |
| dbus::MethodCall* method_call, | |
| int /* timeout_ms */, | |
| dbus::MockObjectProxy::ResponseCallback* response_callback, | |
| dbus::MockObjectProxy::ErrorCallback* error_callback) { | |
| dbus::MessageReader reader(method_call); | |
| IsFilesTransferRestrictedRequest request; | |
| EXPECT_TRUE(reader.PopArrayOfBytesAsProto(&request)); | |
| method_call->SetSerial(kDBusSerial); | |
| auto response = dbus::Response::FromMethodCall(method_call); | |
| dbus::MessageWriter writer(response.get()); | |
| IsFilesTransferRestrictedResponse response_proto; | |
| for (const auto& file : request.transferred_files()) { | |
| for (const auto& [file_metadata, restriction_level] : | |
| files_restrictions_) { | |
| if (file.inode() == file_metadata.inode()) { | |
| FileRestriction* file_restriction = | |
| response_proto.add_files_restrictions(); | |
| *file_restriction->mutable_file_metadata() = file_metadata; | |
| file_restriction->set_restriction_level(restriction_level); | |
| break; | |
| } | |
| } | |
| } | |
| writer.AppendProtoAsArrayOfBytes(response_proto); | |
| std::move(*response_callback).Run(response.get()); | |
| } | |
| void AddFilesAndCheck(const std::vector<AddFileRequest>& add_file_requests, | |
| bool expected_result) { | |
| bool success; | |
| std::unique_ptr< | |
| brillo::dbus_utils::MockDBusMethodResponse<std::vector<uint8_t>>> | |
| response = std::make_unique< | |
| brillo::dbus_utils::MockDBusMethodResponse<std::vector<uint8_t>>>( | |
| nullptr); | |
| base::RunLoop run_loop; | |
| response->set_return_callback(base::BindOnce( | |
| [](bool* success, base::RunLoop* run_loop, | |
| const std::vector<uint8_t>& proto_blob) { | |
| AddFilesResponse response = | |
| ParseResponse<AddFilesResponse>(proto_blob); | |
| *success = response.error_message().empty(); | |
| run_loop->Quit(); | |
| }, | |
| &success, &run_loop)); | |
| GetDlpAdaptor()->AddFiles( | |
| std::move(response), | |
| CreateSerializedAddFilesRequest(add_file_requests)); | |
| run_loop.Run(); | |
| EXPECT_EQ(expected_result, success); | |
| } | |
| GetFilesSourcesResponse GetFilesSources(std::vector<std::string> paths) { | |
| GetFilesSourcesResponse result; | |
| std::unique_ptr< | |
| brillo::dbus_utils::MockDBusMethodResponse<std::vector<uint8_t>>> | |
| response = std::make_unique< | |
| brillo::dbus_utils::MockDBusMethodResponse<std::vector<uint8_t>>>( | |
| nullptr); | |
| base::RunLoop run_loop; | |
| response->set_return_callback(base::BindOnce( | |
| [](GetFilesSourcesResponse* result, base::RunLoop* run_loop, | |
| const std::vector<uint8_t>& proto_blob) { | |
| *result = ParseResponse<GetFilesSourcesResponse>(proto_blob); | |
| run_loop->Quit(); | |
| }, | |
| &result, &run_loop)); | |
| GetDlpAdaptor()->GetFilesSources( | |
| std::move(response), CreateSerializedGetFilesSourcesRequest(paths)); | |
| run_loop.Run(); | |
| return result; | |
| } | |
| GetDatabaseEntriesResponse GetDatabaseEntries() { | |
| GetDatabaseEntriesResponse result; | |
| std::unique_ptr< | |
| brillo::dbus_utils::MockDBusMethodResponse<std::vector<uint8_t>>> | |
| response = std::make_unique< | |
| brillo::dbus_utils::MockDBusMethodResponse<std::vector<uint8_t>>>( | |
| nullptr); | |
| base::RunLoop run_loop; | |
| response->set_return_callback(base::BindOnce( | |
| [](GetDatabaseEntriesResponse* result, base::RunLoop* run_loop, | |
| const std::vector<uint8_t>& proto_blob) { | |
| *result = ParseResponse<GetDatabaseEntriesResponse>(proto_blob); | |
| run_loop->Quit(); | |
| }, | |
| &result, &run_loop)); | |
| GetDlpAdaptor()->GetDatabaseEntries(std::move(response)); | |
| run_loop.Run(); | |
| return result; | |
| } | |
| void InitDatabase() { | |
| database_directory_ = std::make_unique<base::ScopedTempDir>(); | |
| ASSERT_TRUE(database_directory_->CreateUniqueTempDir()); | |
| base::RunLoop run_loop; | |
| GetDlpAdaptor()->InitDatabase(database_directory_->GetPath(), | |
| run_loop.QuitClosure()); | |
| run_loop.Run(); | |
| } | |
| void SetRulesAndInitFanotify() { | |
| GetDlpAdaptor()->SetFanotifyWatcherStartedForTesting(false); | |
| SetDlpFilesPolicyRequest request; | |
| ::dlp::DlpFilesRule* rule = request.add_rules(); | |
| rule->add_source_urls("example.com"); | |
| rule->add_destination_urls("*"); | |
| std::vector<uint8_t> proto_blob(request.ByteSizeLong()); | |
| request.SerializeToArray(proto_blob.data(), proto_blob.size()); | |
| std::vector<uint8_t> response_blob = | |
| GetDlpAdaptor()->SetDlpFilesPolicy(proto_blob); | |
| SetDlpFilesPolicyResponse response = | |
| ParseResponse<SetDlpFilesPolicyResponse>(response_blob); | |
| EXPECT_FALSE(response.has_error_message()); | |
| EXPECT_TRUE(helper_.IsFanotifyWatcherActive()); | |
| } | |
| protected: | |
| bool is_file_policy_restricted_; | |
| std::vector<std::pair<FileMetadata, RestrictionLevel>> files_restrictions_; | |
| std::unique_ptr<base::ScopedTempDir> database_directory_; | |
| DlpAdaptorTestHelper helper_; | |
| }; | |
| TEST_F(DlpAdaptorTest, AllowedWithoutDatabase) { | |
| FileOpenRequestResultWaiter waiter; | |
| helper_.ProcessFileOpenRequest({/*inode=*/1, /*crtime=*/0}, kPid, | |
| waiter.GetCallback()); | |
| EXPECT_TRUE(waiter.GetResult()); | |
| EXPECT_THAT( | |
| helper_.GetMetrics(kDlpAdaptorErrorHistogram), | |
| ElementsAre(static_cast<int>(AdaptorError::kDatabaseNotReadyError))); | |
| } | |
| TEST_F(DlpAdaptorTest, AllowedWithDatabase) { | |
| InitDatabase(); | |
| FileOpenRequestResultWaiter waiter; | |
| helper_.ProcessFileOpenRequest({/*inode=*/1, /*crtime=*/0}, kPid, | |
| waiter.GetCallback()); | |
| EXPECT_TRUE(waiter.GetResult()); | |
| EXPECT_THAT(helper_.GetMetrics(kDlpAdaptorErrorHistogram), ElementsAre()); | |
| } | |
| TEST_F(DlpAdaptorTest, NotRestrictedFileAddedAndAllowed) { | |
| InitDatabase(); | |
| base::FilePath file_path; | |
| base::CreateTemporaryFileInDir(helper_.home_path(), &file_path); | |
| AddFilesAndCheck({CreateAddFileRequest(file_path, "source", "referrer")}, | |
| /*expected_result=*/true); | |
| FileId id = GetFileId(file_path.value()); | |
| is_file_policy_restricted_ = false; | |
| EXPECT_CALL(*GetMockDlpFilesPolicyServiceProxy(), | |
| DoCallMethodWithErrorCallback(_, _, _, _)) | |
| .WillOnce(Invoke(this, &DlpAdaptorTest::StubIsDlpPolicyMatched)); | |
| FileOpenRequestResultWaiter waiter; | |
| helper_.ProcessFileOpenRequest(id, kPid, waiter.GetCallback()); | |
| EXPECT_TRUE(waiter.GetResult()); | |
| EXPECT_THAT(helper_.GetMetrics(kDlpAdaptorErrorHistogram), ElementsAre()); | |
| } | |
| TEST_F(DlpAdaptorTest, NotRestrictedFileAddedAndDlpPolicyMatched_BadProto) { | |
| InitDatabase(); | |
| base::FilePath file_path; | |
| base::CreateTemporaryFileInDir(helper_.home_path(), &file_path); | |
| AddFilesAndCheck({CreateAddFileRequest(file_path, "source", "referrer")}, | |
| /*expected_result=*/true); | |
| FileId id = GetFileId(file_path.value()); | |
| is_file_policy_restricted_ = false; | |
| EXPECT_CALL(*GetMockDlpFilesPolicyServiceProxy(), | |
| DoCallMethodWithErrorCallback(_, _, _, _)) | |
| .WillOnce(Invoke(this, &DlpAdaptorTest::StubReplyBadProto)); | |
| FileOpenRequestResultWaiter waiter; | |
| helper_.ProcessFileOpenRequest(id, kPid, waiter.GetCallback()); | |
| EXPECT_FALSE(waiter.GetResult()); | |
| EXPECT_THAT(helper_.GetMetrics(kDlpAdaptorErrorHistogram), | |
| ElementsAre(static_cast<int>(AdaptorError::kInvalidProtoError))); | |
| } | |
| TEST_F(DlpAdaptorTest, | |
| NotRestrictedFileAddedAndDlpPolicyMatched_ResponseError) { | |
| InitDatabase(); | |
| base::FilePath file_path; | |
| base::CreateTemporaryFileInDir(helper_.home_path(), &file_path); | |
| AddFilesAndCheck({CreateAddFileRequest(file_path, "source", "referrer")}, | |
| /*expected_result=*/true); | |
| FileId id = GetFileId(file_path.value()); | |
| is_file_policy_restricted_ = false; | |
| EXPECT_CALL(*GetMockDlpFilesPolicyServiceProxy(), | |
| DoCallMethodWithErrorCallback(_, _, _, _)) | |
| .WillOnce(Invoke(this, &DlpAdaptorTest::StubReplyWithError)); | |
| FileOpenRequestResultWaiter waiter; | |
| helper_.ProcessFileOpenRequest(id, kPid, waiter.GetCallback()); | |
| EXPECT_FALSE(waiter.GetResult()); | |
| EXPECT_THAT( | |
| helper_.GetMetrics(kDlpAdaptorErrorHistogram), | |
| ElementsAre(static_cast<int>(AdaptorError::kRestrictionDetectionError))); | |
| } | |
| TEST_F(DlpAdaptorTest, RestrictedFileAddedAndNotAllowed) { | |
| InitDatabase(); | |
| base::FilePath file_path; | |
| base::CreateTemporaryFileInDir(helper_.home_path(), &file_path); | |
| AddFilesAndCheck({CreateAddFileRequest(file_path, "source", "referrer")}, | |
| /*expected_result=*/true); | |
| FileId id = GetFileId(file_path.value()); | |
| is_file_policy_restricted_ = true; | |
| EXPECT_CALL(*GetMockDlpFilesPolicyServiceProxy(), | |
| DoCallMethodWithErrorCallback(_, _, _, _)) | |
| .WillOnce(Invoke(this, &DlpAdaptorTest::StubIsDlpPolicyMatched)); | |
| FileOpenRequestResultWaiter waiter; | |
| helper_.ProcessFileOpenRequest(id, kPid, waiter.GetCallback()); | |
| EXPECT_FALSE(waiter.GetResult()); | |
| EXPECT_THAT(helper_.GetMetrics(kDlpAdaptorErrorHistogram), ElementsAre()); | |
| } | |
| TEST_F(DlpAdaptorTest, RestrictedFileAllowedForItself) { | |
| InitDatabase(); | |
| base::FilePath file_path; | |
| base::CreateTemporaryFileInDir(helper_.home_path(), &file_path); | |
| AddFilesAndCheck({CreateAddFileRequest(file_path, "source", "referrer")}, | |
| /*expected_result=*/true); | |
| FileId id = GetFileId(file_path.value()); | |
| FileOpenRequestResultWaiter waiter; | |
| helper_.ProcessFileOpenRequest(id, base::GetCurrentProcId(), | |
| waiter.GetCallback()); | |
| EXPECT_TRUE(waiter.GetResult()); | |
| EXPECT_THAT(helper_.GetMetrics(kDlpAdaptorErrorHistogram), ElementsAre()); | |
| } | |
| // | |
| TEST_F(DlpAdaptorTest, RestrictedFileAddedAndRequestedAllowed) { | |
| // Create database. | |
| base::ScopedTempDir database_directory; | |
| ASSERT_TRUE(database_directory.CreateUniqueTempDir()); | |
| base::RunLoop run_loop; | |
| GetDlpAdaptor()->InitDatabase(database_directory.GetPath(), | |
| run_loop.QuitClosure()); | |
| run_loop.Run(); | |
| // Create files to request access by ids. | |
| base::FilePath file_path1; | |
| base::CreateTemporaryFileInDir(helper_.home_path(), &file_path1); | |
| const FileId id1 = GetFileId(file_path1.value()); | |
| base::FilePath file_path2; | |
| base::CreateTemporaryFileInDir(helper_.home_path(), &file_path2); | |
| const FileId id2 = GetFileId(file_path2.value()); | |
| // Add the files to the database. | |
| AddFilesAndCheck({CreateAddFileRequest(file_path1, "source", "referrer"), | |
| CreateAddFileRequest(file_path2, "source", "referrer")}, | |
| /*expected_result=*/true); | |
| // Setup callback for DlpFilesPolicyService::IsFilesTransferRestricted() | |
| EXPECT_CALL(*GetMockDlpFilesPolicyServiceProxy(), | |
| DoCallMethodWithErrorCallback(_, _, _, _)) | |
| .WillOnce(Invoke(this, &DlpAdaptorTest::StubIsFilesTransferRestricted)); | |
| // Request access to the file. | |
| auto response = std::make_unique<brillo::dbus_utils::MockDBusMethodResponse< | |
| std::vector<uint8_t>, base::ScopedFD>>(nullptr); | |
| bool allowed; | |
| base::ScopedFD lifeline_fd; | |
| base::RunLoop request_file_access_run_loop; | |
| response->set_return_callback(base::BindOnce( | |
| [](bool* allowed, base::ScopedFD* lifeline_fd, base::RunLoop* run_loop, | |
| const std::vector<uint8_t>& proto_blob, const base::ScopedFD& fd) { | |
| RequestFileAccessResponse response = | |
| ParseResponse<RequestFileAccessResponse>(proto_blob); | |
| *allowed = response.allowed(); | |
| lifeline_fd->reset(dup(fd.get())); | |
| run_loop->Quit(); | |
| }, | |
| &allowed, &lifeline_fd, &request_file_access_run_loop)); | |
| GetDlpAdaptor()->RequestFileAccess( | |
| std::move(response), | |
| CreateSerializedRequestFileAccessRequest( | |
| {file_path1.value(), file_path2.value()}, kPid, "destination")); | |
| request_file_access_run_loop.Run(); | |
| EXPECT_TRUE(allowed); | |
| EXPECT_FALSE(IsFdClosed(lifeline_fd.get())); | |
| // Access the first file. | |
| FileOpenRequestResultWaiter waiter; | |
| helper_.ProcessFileOpenRequest(id1, kPid, waiter.GetCallback()); | |
| EXPECT_TRUE(waiter.GetResult()); | |
| // Second request still allowed. | |
| FileOpenRequestResultWaiter waiter2; | |
| helper_.ProcessFileOpenRequest(id1, kPid, waiter2.GetCallback()); | |
| EXPECT_TRUE(waiter2.GetResult()); | |
| // Access the second file. | |
| FileOpenRequestResultWaiter waiter3; | |
| helper_.ProcessFileOpenRequest(id2, kPid, waiter3.GetCallback()); | |
| EXPECT_TRUE(waiter3.GetResult()); | |
| EXPECT_THAT(helper_.GetMetrics(kDlpAdaptorErrorHistogram), ElementsAre()); | |
| } | |
| // Cached allow response had no access grant attached to its ScopedFD. | |
| // This test makes sure this doesn't happen anymore. | |
| // http://b/281497666 | |
| TEST_F(DlpAdaptorTest, RestrictedFileAddedAndRequestedCachedAllowed) { | |
| InitDatabase(); | |
| // Create files to request access by ids. | |
| base::FilePath file_path; | |
| base::CreateTemporaryFileInDir(helper_.home_path(), &file_path); | |
| const FileId id = GetFileId(file_path.value()); | |
| // Add the files to the database. | |
| AddFilesAndCheck({CreateAddFileRequest(file_path, "source", "referrer")}, | |
| /*expected_result=*/true); | |
| // Setup callback for DlpFilesPolicyService::IsFilesTransferRestricted() | |
| FileMetadata file_metadata; | |
| file_metadata.set_inode(id.first); | |
| file_metadata.set_crtime(id.second); | |
| file_metadata.set_path(file_path.value()); | |
| files_restrictions_.push_back( | |
| {std::move(file_metadata), RestrictionLevel::LEVEL_ALLOW}); | |
| ON_CALL(*GetMockDlpFilesPolicyServiceProxy(), | |
| DoCallMethodWithErrorCallback(_, _, _, _)) | |
| .WillByDefault( | |
| Invoke(this, &DlpAdaptorTest::StubIsFilesTransferRestricted)); | |
| // Called for the first RequestFileAccess and both ProcessFileOpen after the | |
| // closed ScopedFD. The second RequestFileAccess is cached. | |
| EXPECT_CALL(*GetMockDlpFilesPolicyServiceProxy(), | |
| DoCallMethodWithErrorCallback(_, _, _, _)) | |
| .Times(3); | |
| // Second loop run with cached results | |
| for (int i = 0; i < 2; ++i) { | |
| // Request access to the file. | |
| auto response = std::make_unique<brillo::dbus_utils::MockDBusMethodResponse< | |
| std::vector<uint8_t>, base::ScopedFD>>(nullptr); | |
| bool allowed; | |
| base::ScopedFD lifeline_fd; | |
| base::RunLoop request_file_access_run_loop; | |
| response->set_return_callback(base::BindOnce( | |
| [](bool* allowed, base::ScopedFD* lifeline_fd, base::RunLoop* run_loop, | |
| const std::vector<uint8_t>& proto_blob, const base::ScopedFD& fd) { | |
| RequestFileAccessResponse response = | |
| ParseResponse<RequestFileAccessResponse>(proto_blob); | |
| *allowed = response.allowed(); | |
| lifeline_fd->reset(dup(fd.get())); | |
| run_loop->Quit(); | |
| }, | |
| &allowed, &lifeline_fd, &request_file_access_run_loop)); | |
| GetDlpAdaptor()->RequestFileAccess( | |
| std::move(response), CreateSerializedRequestFileAccessRequest( | |
| {file_path.value()}, kPid, DlpComponent::USB)); | |
| request_file_access_run_loop.Run(); | |
| EXPECT_TRUE(allowed); | |
| EXPECT_FALSE(IsFdClosed(lifeline_fd.get())); | |
| // Access the file. | |
| FileOpenRequestResultWaiter waiter; | |
| helper_.ProcessFileOpenRequest(id, kPid, waiter.GetCallback()); | |
| EXPECT_TRUE(waiter.GetResult()); | |
| // Cancel access to the file. | |
| lifeline_fd.reset(); | |
| // Let DlpAdaptor process that lifeline_fd is closed. | |
| base::RunLoop().RunUntilIdle(); | |
| // Second request: still allowed | |
| FileOpenRequestResultWaiter waiter2; | |
| helper_.ProcessFileOpenRequest(id, kPid, waiter2.GetCallback()); | |
| EXPECT_TRUE(waiter2.GetResult()); | |
| } | |
| EXPECT_THAT(helper_.GetMetrics(kDlpAdaptorErrorHistogram), ElementsAre()); | |
| } | |
| TEST_F(DlpAdaptorTest, RestrictedFileSystemRequestedAllowed) { | |
| InitDatabase(); | |
| // Create files to request access by ids. | |
| base::FilePath file_path; | |
| base::CreateTemporaryFileInDir(helper_.home_path(), &file_path); | |
| const FileId id = GetFileId(file_path.value()); | |
| // Add the files to the database. | |
| AddFilesAndCheck({CreateAddFileRequest(file_path, "source", "referrer")}, | |
| /*expected_result=*/true); | |
| // Setup callback for DlpFilesPolicyService::IsFilesTransferRestricted() | |
| FileMetadata file_metadata; | |
| file_metadata.set_inode(id.first); | |
| file_metadata.set_crtime(id.second); | |
| file_metadata.set_path(file_path.value()); | |
| files_restrictions_.push_back( | |
| {std::move(file_metadata), RestrictionLevel::LEVEL_BLOCK}); | |
| ON_CALL(*GetMockDlpFilesPolicyServiceProxy(), DoCallMethodWithErrorCallback) | |
| .WillByDefault( | |
| Invoke(this, &DlpAdaptorTest::StubIsFilesTransferRestricted)); | |
| // Called for both ProcessFileOpen after the closed ScopedFD. | |
| // RequestFileAccess is allowed with only checking component. | |
| EXPECT_CALL(*GetMockDlpFilesPolicyServiceProxy(), | |
| DoCallMethodWithErrorCallback) | |
| .Times(2); | |
| // Both runs should be answered without getting the cache involved. | |
| for (int i = 0; i < 2; ++i) { | |
| // Request access to the file. | |
| auto response = std::make_unique<brillo::dbus_utils::MockDBusMethodResponse< | |
| std::vector<uint8_t>, base::ScopedFD>>(nullptr); | |
| bool allowed; | |
| base::ScopedFD lifeline_fd; | |
| base::RunLoop request_file_access_run_loop; | |
| response->set_return_callback(base::BindOnce( | |
| [](bool* allowed, base::ScopedFD* lifeline_fd, base::RunLoop* run_loop, | |
| const std::vector<uint8_t>& proto_blob, const base::ScopedFD& fd) { | |
| RequestFileAccessResponse response = | |
| ParseResponse<RequestFileAccessResponse>(proto_blob); | |
| *allowed = response.allowed(); | |
| lifeline_fd->reset(dup(fd.get())); | |
| run_loop->Quit(); | |
| }, | |
| &allowed, &lifeline_fd, &request_file_access_run_loop)); | |
| GetDlpAdaptor()->RequestFileAccess( | |
| std::move(response), | |
| CreateSerializedRequestFileAccessRequest({file_path.value()}, kPid, | |
| DlpComponent::SYSTEM)); | |
| request_file_access_run_loop.Run(); | |
| EXPECT_TRUE(allowed); | |
| EXPECT_FALSE(IsFdClosed(lifeline_fd.get())); | |
| // Access the file. | |
| FileOpenRequestResultWaiter waiter; | |
| helper_.ProcessFileOpenRequest(id, kPid, waiter.GetCallback()); | |
| EXPECT_TRUE(waiter.GetResult()); | |
| // Cancel access to the file. | |
| lifeline_fd.reset(); | |
| // Let DlpAdaptor process that lifeline_fd is closed. | |
| base::RunLoop().RunUntilIdle(); | |
| // Second request: still allowed | |
| FileOpenRequestResultWaiter waiter2; | |
| helper_.ProcessFileOpenRequest(id, kPid, waiter2.GetCallback()); | |
| EXPECT_TRUE(waiter2.GetResult()); | |
| } | |
| EXPECT_THAT(helper_.GetMetrics(kDlpAdaptorErrorHistogram), ElementsAre()); | |
| } | |
| TEST_F(DlpAdaptorTest, RestrictedFileAddedAndRequestedButBadProto) { | |
| InitDatabase(); | |
| // Create file to request access by ids. | |
| base::FilePath file_path; | |
| base::CreateTemporaryFileInDir(helper_.home_path(), &file_path); | |
| // Add the file to the database. | |
| AddFilesAndCheck({CreateAddFileRequest(file_path, "source", "referrer")}, | |
| /*expected_result=*/true); | |
| // Setup callback for DlpFilesPolicyService::IsFilesTransferRestricted() | |
| EXPECT_CALL(*GetMockDlpFilesPolicyServiceProxy(), | |
| DoCallMethodWithErrorCallback(_, _, _, _)) | |
| .WillOnce(Invoke(this, &DlpAdaptorTest::StubReplyBadProto)); | |
| // Request access to the file. | |
| auto response = std::make_unique<brillo::dbus_utils::MockDBusMethodResponse< | |
| std::vector<uint8_t>, base::ScopedFD>>(nullptr); | |
| bool allowed; | |
| base::ScopedFD lifeline_fd; | |
| base::RunLoop request_file_access_run_loop; | |
| response->set_return_callback(base::BindOnce( | |
| [](bool* allowed, base::ScopedFD* lifeline_fd, base::RunLoop* run_loop, | |
| const std::vector<uint8_t>& proto_blob, const base::ScopedFD& fd) { | |
| RequestFileAccessResponse response = | |
| ParseResponse<RequestFileAccessResponse>(proto_blob); | |
| *allowed = response.allowed(); | |
| lifeline_fd->reset(dup(fd.get())); | |
| run_loop->Quit(); | |
| }, | |
| &allowed, &lifeline_fd, &request_file_access_run_loop)); | |
| GetDlpAdaptor()->RequestFileAccess( | |
| std::move(response), CreateSerializedRequestFileAccessRequest( | |
| {file_path.value()}, kPid, DlpComponent::USB)); | |
| request_file_access_run_loop.Run(); | |
| EXPECT_FALSE(allowed); | |
| EXPECT_TRUE(IsFdClosed(lifeline_fd.get())); | |
| EXPECT_THAT(helper_.GetMetrics(kDlpAdaptorErrorHistogram), | |
| ElementsAre(static_cast<int>(AdaptorError::kInvalidProtoError))); | |
| } | |
| TEST_F(DlpAdaptorTest, RestrictedFileAddedAndRequestedButErrorResponse) { | |
| InitDatabase(); | |
| // Create file to request access by inodes. | |
| base::FilePath file_path; | |
| base::CreateTemporaryFileInDir(helper_.home_path(), &file_path); | |
| // Add the file to the database. | |
| AddFilesAndCheck({CreateAddFileRequest(file_path, "source", "referrer")}, | |
| /*expected_result=*/true); | |
| // Setup callback for DlpFilesPolicyService::IsFilesTransferRestricted() | |
| EXPECT_CALL(*GetMockDlpFilesPolicyServiceProxy(), | |
| DoCallMethodWithErrorCallback(_, _, _, _)) | |
| .WillOnce(Invoke(this, &DlpAdaptorTest::StubReplyWithError)); | |
| // Request access to the file. | |
| auto response = std::make_unique<brillo::dbus_utils::MockDBusMethodResponse< | |
| std::vector<uint8_t>, base::ScopedFD>>(nullptr); | |
| bool allowed; | |
| base::ScopedFD lifeline_fd; | |
| base::RunLoop request_file_access_run_loop; | |
| response->set_return_callback(base::BindOnce( | |
| [](bool* allowed, base::ScopedFD* lifeline_fd, base::RunLoop* run_loop, | |
| const std::vector<uint8_t>& proto_blob, const base::ScopedFD& fd) { | |
| RequestFileAccessResponse response = | |
| ParseResponse<RequestFileAccessResponse>(proto_blob); | |
| *allowed = response.allowed(); | |
| lifeline_fd->reset(dup(fd.get())); | |
| run_loop->Quit(); | |
| }, | |
| &allowed, &lifeline_fd, &request_file_access_run_loop)); | |
| GetDlpAdaptor()->RequestFileAccess( | |
| std::move(response), CreateSerializedRequestFileAccessRequest( | |
| {file_path.value()}, kPid, DlpComponent::USB)); | |
| request_file_access_run_loop.Run(); | |
| EXPECT_FALSE(allowed); | |
| EXPECT_TRUE(IsFdClosed(lifeline_fd.get())); | |
| EXPECT_THAT( | |
| helper_.GetMetrics(kDlpAdaptorErrorHistogram), | |
| ElementsAre(static_cast<int>(AdaptorError::kRestrictionDetectionError))); | |
| } | |
| TEST_F(DlpAdaptorTest, RestrictedFileAddedAndRequestedCachedNotAllowed) { | |
| InitDatabase(); | |
| // Create files to request access by ids. | |
| base::FilePath file_path; | |
| base::CreateTemporaryFileInDir(helper_.home_path(), &file_path); | |
| const FileId id = GetFileId(file_path.value()); | |
| // Add the files to the database. | |
| AddFilesAndCheck({CreateAddFileRequest(file_path, "source", "referrer")}, | |
| /*expected_result=*/true); | |
| // Setup callback for DlpFilesPolicyService::IsFilesTransferRestricted() | |
| FileMetadata file_metadata; | |
| file_metadata.set_inode(id.first); | |
| file_metadata.set_crtime(id.second); | |
| file_metadata.set_path(file_path.value()); | |
| files_restrictions_.push_back( | |
| {std::move(file_metadata), RestrictionLevel::LEVEL_BLOCK}); | |
| // Second loop run with cached results, third with another destination. | |
| for (int i = 0; i < 3; ++i) { | |
| // Request access to the file. | |
| auto response = std::make_unique<brillo::dbus_utils::MockDBusMethodResponse< | |
| std::vector<uint8_t>, base::ScopedFD>>(nullptr); | |
| bool allowed; | |
| base::ScopedFD lifeline_fd; | |
| base::RunLoop request_file_access_run_loop; | |
| response->set_return_callback(base::BindOnce( | |
| [](bool* allowed, base::ScopedFD* lifeline_fd, base::RunLoop* run_loop, | |
| const std::vector<uint8_t>& proto_blob, const base::ScopedFD& fd) { | |
| RequestFileAccessResponse response = | |
| ParseResponse<RequestFileAccessResponse>(proto_blob); | |
| *allowed = response.allowed(); | |
| lifeline_fd->reset(dup(fd.get())); | |
| run_loop->Quit(); | |
| }, | |
| &allowed, &lifeline_fd, &request_file_access_run_loop)); | |
| // The first and third call needs to query the proxy - the second call is | |
| // answered from the cache | |
| if (i == 0 || i == 2) { | |
| EXPECT_CALL(*GetMockDlpFilesPolicyServiceProxy(), | |
| DoCallMethodWithErrorCallback(_, _, _, _)) | |
| .WillOnce( | |
| Invoke(this, &DlpAdaptorTest::StubIsFilesTransferRestricted)); | |
| } | |
| const std::string destination = i < 2 ? "destination" : "destination2"; | |
| GetDlpAdaptor()->RequestFileAccess( | |
| std::move(response), CreateSerializedRequestFileAccessRequest( | |
| {file_path.value()}, kPid, destination)); | |
| request_file_access_run_loop.Run(); | |
| EXPECT_FALSE(allowed); | |
| EXPECT_TRUE(IsFdClosed(lifeline_fd.get())); | |
| is_file_policy_restricted_ = true; | |
| EXPECT_CALL(*GetMockDlpFilesPolicyServiceProxy(), | |
| DoCallMethodWithErrorCallback(_, _, _, _)) | |
| .WillOnce(Invoke(this, &DlpAdaptorTest::StubIsDlpPolicyMatched)); | |
| // Access the file. | |
| FileOpenRequestResultWaiter waiter; | |
| helper_.ProcessFileOpenRequest(id, kPid, waiter.GetCallback()); | |
| EXPECT_FALSE(waiter.GetResult()); | |
| } | |
| EXPECT_THAT(helper_.GetMetrics(kDlpAdaptorErrorHistogram), ElementsAre()); | |
| } | |
| TEST_F(DlpAdaptorTest, RestrictedFilesNotAddedAndRequestedAllowed) { | |
| InitDatabase(); | |
| // Create files to request access by ids. | |
| base::FilePath file_path1; | |
| base::CreateTemporaryFileInDir(helper_.home_path(), &file_path1); | |
| const FileId id1 = GetFileId(file_path1.value()); | |
| base::FilePath file_path2; | |
| base::CreateTemporaryFileInDir(helper_.home_path(), &file_path2); | |
| const FileId id2 = GetFileId(file_path2.value()); | |
| // Add only first file to the database. | |
| AddFilesAndCheck({CreateAddFileRequest(file_path1, "source", "referrer")}, | |
| /*expected_result=*/true); | |
| // Setup callback for DlpFilesPolicyService::IsFilesTransferRestricted() | |
| EXPECT_CALL(*GetMockDlpFilesPolicyServiceProxy(), | |
| DoCallMethodWithErrorCallback(_, _, _, _)) | |
| .WillOnce(Invoke(this, &DlpAdaptorTest::StubIsFilesTransferRestricted)); | |
| // Request access to the file. | |
| auto response = std::make_unique<brillo::dbus_utils::MockDBusMethodResponse< | |
| std::vector<uint8_t>, base::ScopedFD>>(nullptr); | |
| bool allowed; | |
| base::ScopedFD lifeline_fd; | |
| base::RunLoop request_file_access_run_loop; | |
| response->set_return_callback(base::BindOnce( | |
| [](bool* allowed, base::ScopedFD* lifeline_fd, base::RunLoop* run_loop, | |
| const std::vector<uint8_t>& proto_blob, const base::ScopedFD& fd) { | |
| RequestFileAccessResponse response = | |
| ParseResponse<RequestFileAccessResponse>(proto_blob); | |
| *allowed = response.allowed(); | |
| lifeline_fd->reset(dup(fd.get())); | |
| run_loop->Quit(); | |
| }, | |
| &allowed, &lifeline_fd, &request_file_access_run_loop)); | |
| GetDlpAdaptor()->RequestFileAccess( | |
| std::move(response), | |
| CreateSerializedRequestFileAccessRequest( | |
| {file_path1.value(), file_path2.value()}, kPid, "destination")); | |
| request_file_access_run_loop.Run(); | |
| EXPECT_TRUE(allowed); | |
| EXPECT_FALSE(IsFdClosed(lifeline_fd.get())); | |
| // Access the first file. | |
| FileOpenRequestResultWaiter waiter; | |
| helper_.ProcessFileOpenRequest(id1, kPid, waiter.GetCallback()); | |
| EXPECT_TRUE(waiter.GetResult()); | |
| // Access the second file. | |
| FileOpenRequestResultWaiter waiter2; | |
| helper_.ProcessFileOpenRequest(id2, kPid, waiter2.GetCallback()); | |
| EXPECT_TRUE(waiter2.GetResult()); | |
| EXPECT_THAT(helper_.GetMetrics(kDlpAdaptorErrorHistogram), ElementsAre()); | |
| } | |
| TEST_F(DlpAdaptorTest, RestrictedFileNotAddedAndImmediatelyAllowed) { | |
| InitDatabase(); | |
| // Create files to request access by ids. | |
| base::FilePath file_path; | |
| base::CreateTemporaryFileInDir(helper_.home_path(), &file_path); | |
| const FileId id = GetFileId(file_path.value()); | |
| // Access already allowed. | |
| FileOpenRequestResultWaiter waiter; | |
| helper_.ProcessFileOpenRequest(id, kPid, waiter.GetCallback()); | |
| EXPECT_TRUE(waiter.GetResult()); | |
| // Setup callback for DlpFilesPolicyService::IsFilesTransferRestricted() | |
| EXPECT_CALL(*GetMockDlpFilesPolicyServiceProxy(), | |
| DoCallMethodWithErrorCallback(_, _, _, _)) | |
| .Times(0); | |
| // Request access to the file. | |
| auto response = std::make_unique<brillo::dbus_utils::MockDBusMethodResponse< | |
| std::vector<uint8_t>, base::ScopedFD>>(nullptr); | |
| bool allowed; | |
| base::ScopedFD lifeline_fd; | |
| base::RunLoop request_file_access_run_loop; | |
| response->set_return_callback(base::BindOnce( | |
| [](bool* allowed, base::ScopedFD* lifeline_fd, base::RunLoop* run_loop, | |
| const std::vector<uint8_t>& proto_blob, const base::ScopedFD& fd) { | |
| RequestFileAccessResponse response = | |
| ParseResponse<RequestFileAccessResponse>(proto_blob); | |
| *allowed = response.allowed(); | |
| lifeline_fd->reset(dup(fd.get())); | |
| run_loop->Quit(); | |
| }, | |
| &allowed, &lifeline_fd, &request_file_access_run_loop)); | |
| GetDlpAdaptor()->RequestFileAccess( | |
| std::move(response), CreateSerializedRequestFileAccessRequest( | |
| {file_path.value()}, kPid, "destination")); | |
| request_file_access_run_loop.Run(); | |
| EXPECT_TRUE(allowed); | |
| // Access still allowed. | |
| FileOpenRequestResultWaiter waiter2; | |
| helper_.ProcessFileOpenRequest(id, kPid, waiter2.GetCallback()); | |
| EXPECT_TRUE(waiter2.GetResult()); | |
| EXPECT_THAT(helper_.GetMetrics(kDlpAdaptorErrorHistogram), ElementsAre()); | |
| } | |
| TEST_F(DlpAdaptorTest, RestrictedFileAddedAndRequestedNotAllowed) { | |
| InitDatabase(); | |
| // Create file to request access by id. | |
| base::FilePath file_path; | |
| base::CreateTemporaryFileInDir(helper_.home_path(), &file_path); | |
| const FileId id = GetFileId(file_path.value()); | |
| // Add the file to the database. | |
| AddFilesAndCheck({CreateAddFileRequest(file_path, "source", "referrer")}, | |
| /*expected_result=*/true); | |
| // Setup callback for DlpFilesPolicyService::IsFilesTransferRestricted() | |
| FileMetadata file_metadata; | |
| file_metadata.set_path(file_path.value()); | |
| file_metadata.set_inode(id.first); | |
| file_metadata.set_crtime(id.second); | |
| files_restrictions_.push_back( | |
| {std::move(file_metadata), RestrictionLevel::LEVEL_BLOCK}); | |
| EXPECT_CALL(*GetMockDlpFilesPolicyServiceProxy(), | |
| DoCallMethodWithErrorCallback(_, _, _, _)) | |
| .WillOnce(Invoke(this, &DlpAdaptorTest::StubIsFilesTransferRestricted)); | |
| // Request access to the file. | |
| auto response = std::make_unique<brillo::dbus_utils::MockDBusMethodResponse< | |
| std::vector<uint8_t>, base::ScopedFD>>(nullptr); | |
| bool allowed; | |
| base::ScopedFD lifeline_fd; | |
| base::RunLoop request_file_access_run_loop; | |
| response->set_return_callback(base::BindOnce( | |
| [](bool* allowed, base::ScopedFD* lifeline_fd, base::RunLoop* run_loop, | |
| const std::vector<uint8_t>& proto_blob, const base::ScopedFD& fd) { | |
| RequestFileAccessResponse response = | |
| ParseResponse<RequestFileAccessResponse>(proto_blob); | |
| *allowed = response.allowed(); | |
| lifeline_fd->reset(dup(fd.get())); | |
| run_loop->Quit(); | |
| }, | |
| &allowed, &lifeline_fd, &request_file_access_run_loop)); | |
| GetDlpAdaptor()->RequestFileAccess( | |
| std::move(response), CreateSerializedRequestFileAccessRequest( | |
| {file_path.value()}, kPid, "destination")); | |
| request_file_access_run_loop.Run(); | |
| EXPECT_FALSE(allowed); | |
| EXPECT_TRUE(IsFdClosed(lifeline_fd.get())); | |
| // Setup callback for DlpFilesPolicyService::IsDlpPolicyMatched() | |
| is_file_policy_restricted_ = true; | |
| EXPECT_CALL(*GetMockDlpFilesPolicyServiceProxy(), | |
| DoCallMethodWithErrorCallback(_, _, _, _)) | |
| .WillOnce(Invoke(this, &DlpAdaptorTest::StubIsDlpPolicyMatched)); | |
| // Request access to the file. | |
| FileOpenRequestResultWaiter waiter; | |
| helper_.ProcessFileOpenRequest(id, kPid, waiter.GetCallback()); | |
| EXPECT_FALSE(waiter.GetResult()); | |
| EXPECT_THAT(helper_.GetMetrics(kDlpAdaptorErrorHistogram), ElementsAre()); | |
| } | |
| TEST_F(DlpAdaptorTest, RestrictedFileAddedRequestedAndCancelledNotAllowed) { | |
| InitDatabase(); | |
| // Create file to request access by id. | |
| base::FilePath file_path; | |
| base::CreateTemporaryFileInDir(helper_.home_path(), &file_path); | |
| const FileId id = GetFileId(file_path.value()); | |
| // Add the file to the database. | |
| AddFilesAndCheck({CreateAddFileRequest(file_path, "source", "referrer")}, | |
| /*expected_result=*/true); | |
| // Setup callback for DlpFilesPolicyService::IsFilesTransferRestricted() | |
| EXPECT_CALL(*GetMockDlpFilesPolicyServiceProxy(), | |
| DoCallMethodWithErrorCallback(_, _, _, _)) | |
| .WillOnce(Invoke(this, &DlpAdaptorTest::StubIsFilesTransferRestricted)); | |
| // Request access to the file. | |
| auto response = std::make_unique<brillo::dbus_utils::MockDBusMethodResponse< | |
| std::vector<uint8_t>, base::ScopedFD>>(nullptr); | |
| bool allowed; | |
| base::ScopedFD lifeline_fd; | |
| base::RunLoop request_file_access_run_loop; | |
| response->set_return_callback(base::BindOnce( | |
| [](bool* allowed, base::ScopedFD* lifeline_fd, base::RunLoop* run_loop, | |
| const std::vector<uint8_t>& proto_blob, const base::ScopedFD& fd) { | |
| RequestFileAccessResponse response = | |
| ParseResponse<RequestFileAccessResponse>(proto_blob); | |
| *allowed = response.allowed(); | |
| lifeline_fd->reset(dup((fd.get()))); | |
| run_loop->Quit(); | |
| }, | |
| &allowed, &lifeline_fd, &request_file_access_run_loop)); | |
| GetDlpAdaptor()->RequestFileAccess( | |
| std::move(response), CreateSerializedRequestFileAccessRequest( | |
| {file_path.value()}, kPid, "destination")); | |
| request_file_access_run_loop.Run(); | |
| EXPECT_TRUE(allowed); | |
| EXPECT_FALSE(IsFdClosed(lifeline_fd.get())); | |
| // Cancel access to the file. | |
| lifeline_fd.reset(); | |
| // Let DlpAdaptor process that lifeline_fd is closed. | |
| base::RunLoop().RunUntilIdle(); | |
| // Setup callback for DlpFilesPolicyService::IsDlpPolicyMatched() | |
| is_file_policy_restricted_ = true; | |
| EXPECT_CALL(*GetMockDlpFilesPolicyServiceProxy(), | |
| DoCallMethodWithErrorCallback(_, _, _, _)) | |
| .WillOnce(Invoke(this, &DlpAdaptorTest::StubIsDlpPolicyMatched)); | |
| // Request access to the file. | |
| FileOpenRequestResultWaiter waiter; | |
| helper_.ProcessFileOpenRequest(id, kPid, waiter.GetCallback()); | |
| EXPECT_FALSE(waiter.GetResult()); | |
| EXPECT_THAT(helper_.GetMetrics(kDlpAdaptorErrorHistogram), ElementsAre()); | |
| } | |
| // DlpAdaptor::RequestFileAccess crashes if file access is requested while the | |
| // database isn't created yet. This test makes sure this doesn't happen anymore. | |
| // https://crbug.com/1267295. | |
| TEST_F(DlpAdaptorTest, RequestAllowedWithoutDatabase) { | |
| // Create file to request access by id. | |
| base::FilePath file_path; | |
| base::CreateTemporaryFileInDir(helper_.home_path(), &file_path); | |
| // Request access to the file. | |
| auto response = std::make_unique<brillo::dbus_utils::MockDBusMethodResponse< | |
| std::vector<uint8_t>, base::ScopedFD>>(nullptr); | |
| bool allowed; | |
| base::RunLoop request_file_access_run_loop; | |
| response->set_return_callback(base::BindOnce( | |
| [](bool* allowed, base::RunLoop* run_loop, | |
| const std::vector<uint8_t>& proto_blob, const base::ScopedFD& fd) { | |
| RequestFileAccessResponse response = | |
| ParseResponse<RequestFileAccessResponse>(proto_blob); | |
| *allowed = response.allowed(); | |
| run_loop->Quit(); | |
| }, | |
| &allowed, &request_file_access_run_loop)); | |
| GetDlpAdaptor()->RequestFileAccess( | |
| std::move(response), CreateSerializedRequestFileAccessRequest( | |
| {file_path.value()}, kPid, "destination")); | |
| request_file_access_run_loop.Run(); | |
| EXPECT_TRUE(allowed); | |
| EXPECT_THAT(helper_.GetMetrics(kDlpAdaptorErrorHistogram), ElementsAre()); | |
| } | |
| TEST_F(DlpAdaptorTest, GetFilesSources) { | |
| InitDatabase(); | |
| // Create files to request sources. | |
| base::FilePath file_path1; | |
| ASSERT_TRUE(base::CreateTemporaryFileInDir(helper_.home_path(), &file_path1)); | |
| const FileId id1 = GetFileId(file_path1.value()); | |
| base::FilePath file_path2; | |
| ASSERT_TRUE(base::CreateTemporaryFileInDir(helper_.home_path(), &file_path2)); | |
| const std::string source1 = "source1"; | |
| const std::string referrer1 = "referrer1"; | |
| // Add one of the files to the database. | |
| AddFilesAndCheck({CreateAddFileRequest(file_path1, source1, referrer1)}, | |
| /*expected_result=*/true); | |
| GetFilesSourcesResponse response = | |
| GetFilesSources({file_path1.value(), file_path2.value(), "/bad/path"}); | |
| ASSERT_EQ(response.files_metadata_size(), 1u); | |
| FileMetadata file_metadata1 = response.files_metadata()[0]; | |
| EXPECT_EQ(file_metadata1.inode(), id1.first); | |
| EXPECT_EQ(file_metadata1.crtime(), id1.second); | |
| EXPECT_EQ(file_metadata1.path(), file_path1.value()); | |
| EXPECT_EQ(file_metadata1.source_url(), source1); | |
| EXPECT_EQ(file_metadata1.referrer_url(), referrer1); | |
| } | |
| TEST_F(DlpAdaptorTest, GetFilesSourcesDatabaseMigrated) { | |
| // Opening the database with the new table. | |
| InitDatabase(); | |
| // Create files to request sources by ids. | |
| base::FilePath file_path1; | |
| ASSERT_TRUE(base::CreateTemporaryFileInDir(helper_.home_path(), &file_path1)); | |
| const FileId id1 = GetFileId(file_path1.value()); | |
| base::FilePath file_path2; | |
| ASSERT_TRUE(base::CreateTemporaryFileInDir(helper_.home_path(), &file_path2)); | |
| const FileId id2 = GetFileId(file_path2.value()); | |
| const std::string source1 = "source1"; | |
| const std::string source2 = "source2"; | |
| const std::string referrer1 = "referrer1"; | |
| const std::string referrer2 = "referrer2"; | |
| // Add first file to the legacy database. | |
| helper_.AddFileToLegacyDb(id1, source1, referrer1); | |
| // Add the second file to the new database. | |
| AddFilesAndCheck({CreateAddFileRequest(file_path2, source2, referrer2)}, | |
| /*expected_result=*/true); | |
| GetFilesSourcesResponse response = | |
| GetFilesSources({file_path1.value(), file_path2.value(), "/bad/path"}); | |
| // Only the second file is expected as database was not migrated. | |
| ASSERT_EQ(response.files_metadata_size(), 1u); | |
| FileMetadata file_metadata = response.files_metadata()[0]; | |
| EXPECT_EQ(file_metadata.inode(), id2.first); | |
| EXPECT_EQ(file_metadata.crtime(), id2.second); | |
| EXPECT_EQ(file_metadata.source_url(), source2); | |
| EXPECT_EQ(file_metadata.referrer_url(), referrer2); | |
| // Reinitialize database to run migration. Do it twice to ensure that second | |
| // migration is not kicked out or doesn't change anything. | |
| for (int i = 0; i < 2; i++) { | |
| GetDlpAdaptor()->CloseDatabaseForTesting(); | |
| base::RunLoop run_loop; | |
| GetDlpAdaptor()->InitDatabase(database_directory_->GetPath(), | |
| run_loop.QuitClosure()); | |
| run_loop.Run(); | |
| response = | |
| GetFilesSources({file_path1.value(), file_path2.value(), "/bad/path"}); | |
| ASSERT_EQ(response.files_metadata_size(), 2u); | |
| FileMetadata file_metadata1 = response.files_metadata()[0]; | |
| EXPECT_EQ(file_metadata1.inode(), id1.first); | |
| EXPECT_EQ(file_metadata1.crtime(), id1.second); | |
| EXPECT_EQ(file_metadata1.source_url(), source1); | |
| EXPECT_EQ(file_metadata1.referrer_url(), referrer1); | |
| FileMetadata file_metadata2 = response.files_metadata()[1]; | |
| EXPECT_EQ(file_metadata2.inode(), id2.first); | |
| EXPECT_EQ(file_metadata2.crtime(), id2.second); | |
| EXPECT_EQ(file_metadata2.source_url(), source2); | |
| EXPECT_EQ(file_metadata2.referrer_url(), referrer2); | |
| } | |
| // There are 4 histogram entries - on the first run when tables were created | |
| // (false), after data to migrate was added (true), after it was migrated | |
| // (false) and on the next init indicating that no migration is needed anymore | |
| // (false). | |
| EXPECT_THAT(helper_.GetMetrics(kDlpDatabaseMigrationNeededHistogram), | |
| ElementsAre(static_cast<int>(false), static_cast<int>(true), | |
| static_cast<int>(false), static_cast<int>(false))); | |
| EXPECT_THAT(helper_.GetMetrics(kDlpAdaptorErrorHistogram), ElementsAre()); | |
| } | |
| TEST_F(DlpAdaptorTest, GetFilesSourcesWithoutDatabase) { | |
| // Create files to request sources by ids. | |
| base::FilePath file_path1; | |
| ASSERT_TRUE(base::CreateTemporaryFileInDir(helper_.home_path(), &file_path1)); | |
| const FileId id1 = GetFileId(file_path1.value()); | |
| base::FilePath file_path2; | |
| ASSERT_TRUE(base::CreateTemporaryFileInDir(helper_.home_path(), &file_path2)); | |
| const FileId id2 = GetFileId(file_path2.value()); | |
| const std::string source1 = "source1"; | |
| const std::string source2 = "source2"; | |
| const std::string referrer1 = "referrer1"; | |
| const std::string referrer2 = "referrer2"; | |
| // Add the files to the database. The addition will be pending, so success | |
| // is returned. | |
| AddFilesAndCheck({CreateAddFileRequest(file_path1, source1, referrer1), | |
| CreateAddFileRequest(file_path2, source2, referrer2)}, | |
| /*expected_result=*/true); | |
| GetFilesSourcesResponse response = | |
| GetFilesSources({file_path1.value(), file_path2.value()}); | |
| EXPECT_EQ(response.files_metadata_size(), 0u); | |
| // Create database and add pending files. | |
| InitDatabase(); | |
| // Check that the pending entries were added. | |
| response = GetFilesSources({file_path1.value(), file_path2.value()}); | |
| ASSERT_EQ(response.files_metadata_size(), 2u); | |
| FileMetadata file_metadata1 = response.files_metadata()[0]; | |
| EXPECT_EQ(file_metadata1.inode(), id1.first); | |
| EXPECT_EQ(file_metadata1.crtime(), id1.second); | |
| EXPECT_EQ(file_metadata1.source_url(), source1); | |
| EXPECT_EQ(file_metadata1.referrer_url(), referrer1); | |
| FileMetadata file_metadata2 = response.files_metadata()[1]; | |
| EXPECT_EQ(file_metadata2.inode(), id2.first); | |
| EXPECT_EQ(file_metadata2.crtime(), id2.second); | |
| EXPECT_EQ(file_metadata2.source_url(), source2); | |
| EXPECT_EQ(file_metadata2.referrer_url(), referrer2); | |
| EXPECT_THAT( | |
| helper_.GetMetrics(kDlpAdaptorErrorHistogram), | |
| ElementsAre(static_cast<int>(AdaptorError::kDatabaseNotReadyError), | |
| static_cast<int>(AdaptorError::kDatabaseNotReadyError))); | |
| } | |
| // TODO(b/290389988): Flaky test | |
| TEST_F(DlpAdaptorTest, DISABLED_GetFilesSourcesWithoutDatabaseNotAdded) { | |
| // Create files to request sources by ids. | |
| base::FilePath file_path1; | |
| ASSERT_TRUE(base::CreateTemporaryFileInDir(helper_.home_path(), &file_path1)); | |
| base::FilePath file_path2; | |
| ASSERT_TRUE(base::CreateTemporaryFileInDir(helper_.home_path(), &file_path2)); | |
| const std::string source1 = "source1"; | |
| const std::string source2 = "source2"; | |
| // Add the files to the database. The addition will be pending, so success | |
| // is returned. | |
| AddFilesAndCheck({CreateAddFileRequest(file_path1, source1, "referrer1"), | |
| CreateAddFileRequest(file_path2, source2, "referrer2")}, | |
| /*expected_result=*/true); | |
| GetFilesSourcesResponse response = | |
| GetFilesSources({file_path1.value(), file_path2.value()}); | |
| EXPECT_EQ(response.files_metadata_size(), 0u); | |
| helper_.ReCreateAdaptor(); | |
| // Create database and add pending files. | |
| InitDatabase(); | |
| // Check that the pending entries were not added. | |
| response = GetFilesSources({file_path1.value(), file_path2.value()}); | |
| EXPECT_EQ(response.files_metadata_size(), 0u); | |
| } | |
| TEST_F(DlpAdaptorTest, GetFilesSourcesFileDeletedDBReopenedWithCleanup) { | |
| // Enable feature. | |
| helper_.SetDatabaseCleanupFeatureEnabled(true); | |
| // Create database. | |
| InitDatabase(); | |
| // Create files to request sources by ids. | |
| base::FilePath file_path1; | |
| ASSERT_TRUE(base::CreateTemporaryFileInDir(helper_.home_path(), &file_path1)); | |
| const FileId id1 = GetFileId(file_path1.value()); | |
| base::FilePath file_path2; | |
| ASSERT_TRUE(base::CreateTemporaryFileInDir(helper_.home_path(), &file_path2)); | |
| const std::string source1 = "source1"; | |
| const std::string source2 = "source2"; | |
| const std::string referrer1 = "referrer1"; | |
| const std::string referrer2 = "referrer2"; | |
| // Add the files to the database. | |
| AddFilesAndCheck({CreateAddFileRequest(file_path1, source1, referrer1), | |
| CreateAddFileRequest(file_path2, source2, referrer2)}, | |
| /*expected_result=*/true); | |
| // Delete one of the files. | |
| brillo::DeleteFile(file_path2); | |
| // Reinitialize database. | |
| GetDlpAdaptor()->CloseDatabaseForTesting(); | |
| base::RunLoop run_loop2; | |
| GetDlpAdaptor()->InitDatabase(database_directory_->GetPath(), | |
| run_loop2.QuitClosure()); | |
| run_loop2.Run(); | |
| GetFilesSourcesResponse response = | |
| GetFilesSources({file_path1.value(), file_path2.value()}); | |
| ASSERT_EQ(response.files_metadata_size(), 1u); | |
| FileMetadata file_metadata1 = response.files_metadata()[0]; | |
| EXPECT_EQ(file_metadata1.inode(), id1.first); | |
| EXPECT_EQ(file_metadata1.crtime(), id1.second); | |
| EXPECT_EQ(file_metadata1.source_url(), source1); | |
| EXPECT_EQ(file_metadata1.referrer_url(), referrer1); | |
| EXPECT_THAT(helper_.GetMetrics(kDlpAdaptorErrorHistogram), ElementsAre()); | |
| } | |
| TEST_F(DlpAdaptorTest, GetFilesSourcesFileDeletedDBReopenedWithoutCleanup) { | |
| // Disabled feature. | |
| helper_.SetDatabaseCleanupFeatureEnabled(false); | |
| // Create database. | |
| InitDatabase(); | |
| // Create files to request sources by ids. | |
| base::FilePath file_path1; | |
| ASSERT_TRUE(base::CreateTemporaryFileInDir(helper_.home_path(), &file_path1)); | |
| const FileId id1 = GetFileId(file_path1.value()); | |
| base::FilePath file_path2; | |
| ASSERT_TRUE(base::CreateTemporaryFileInDir(helper_.home_path(), &file_path2)); | |
| const std::string source1 = "source1"; | |
| const std::string source2 = "source2"; | |
| const std::string referrer1 = "referrer1"; | |
| const std::string referrer2 = "referrer2"; | |
| // Add the files to the database. | |
| AddFilesAndCheck({CreateAddFileRequest(file_path1, source1, referrer1), | |
| CreateAddFileRequest(file_path2, source2, referrer2)}, | |
| /*expected_result=*/true); | |
| // Delete one of the files. | |
| brillo::DeleteFile(file_path2); | |
| // Reinitialize database. | |
| GetDlpAdaptor()->CloseDatabaseForTesting(); | |
| base::RunLoop run_loop2; | |
| GetDlpAdaptor()->InitDatabase(database_directory_->GetPath(), | |
| run_loop2.QuitClosure()); | |
| run_loop2.Run(); | |
| GetFilesSourcesResponse response = | |
| GetFilesSources({file_path1.value(), file_path2.value()}); | |
| ASSERT_EQ(response.files_metadata_size(), 1u); | |
| FileMetadata file_metadata1 = response.files_metadata()[0]; | |
| EXPECT_EQ(file_metadata1.inode(), id1.first); | |
| EXPECT_EQ(file_metadata1.crtime(), id1.second); | |
| EXPECT_EQ(file_metadata1.source_url(), source1); | |
| EXPECT_EQ(file_metadata1.referrer_url(), referrer1); | |
| EXPECT_THAT(helper_.GetMetrics(kDlpAdaptorErrorHistogram), ElementsAre()); | |
| } | |
| TEST_F(DlpAdaptorTest, GetFilesSourcesFileDeletedInFlight) { | |
| SetRulesAndInitFanotify(); | |
| // Create database. | |
| InitDatabase(); | |
| // Create files to request sources by ids. | |
| base::FilePath file_path1; | |
| ASSERT_TRUE(base::CreateTemporaryFileInDir(helper_.home_path(), &file_path1)); | |
| const FileId id1 = GetFileId(file_path1.value()); | |
| base::FilePath file_path2; | |
| ASSERT_TRUE(base::CreateTemporaryFileInDir(helper_.home_path(), &file_path2)); | |
| const FileId id2 = GetFileId(file_path2.value()); | |
| const std::string source1 = "source1"; | |
| const std::string source2 = "source2"; | |
| const std::string referrer1 = "referrer1"; | |
| const std::string referrer2 = "referrer2"; | |
| // Add the files to the database. | |
| AddFilesAndCheck({CreateAddFileRequest(file_path1, source1, referrer1), | |
| CreateAddFileRequest(file_path2, source2, referrer2)}, | |
| /*expected_result=*/true); | |
| // Delete one of the files. | |
| brillo::DeleteFile(file_path2); | |
| // Notify that file was deleted. | |
| helper_.OnFileDeleted(id2); | |
| GetFilesSourcesResponse response = | |
| GetFilesSources({file_path1.value(), file_path2.value()}); | |
| ASSERT_EQ(response.files_metadata_size(), 1u); | |
| FileMetadata file_metadata1 = response.files_metadata()[0]; | |
| EXPECT_EQ(file_metadata1.inode(), id1.first); | |
| EXPECT_EQ(file_metadata1.crtime(), id1.second); | |
| EXPECT_EQ(file_metadata1.source_url(), source1); | |
| EXPECT_EQ(file_metadata1.referrer_url(), referrer1); | |
| // Reinitialize database. | |
| GetDlpAdaptor()->CloseDatabaseForTesting(); | |
| InitDatabase(); | |
| // Delete the other file. | |
| brillo::DeleteFile(file_path1); | |
| // Notify that file was deleted. | |
| helper_.OnFileDeleted(id1); | |
| response = GetFilesSources({file_path1.value(), file_path2.value()}); | |
| ASSERT_EQ(response.files_metadata_size(), 0u); | |
| EXPECT_THAT(helper_.GetMetrics(kDlpAdaptorErrorHistogram), ElementsAre()); | |
| } | |
| TEST_F(DlpAdaptorTest, GetFilesSourcesOverwrite) { | |
| // Create database. | |
| InitDatabase(); | |
| // Create files to request sources by ids. | |
| base::FilePath file_path1; | |
| ASSERT_TRUE(base::CreateTemporaryFileInDir(helper_.home_path(), &file_path1)); | |
| const FileId id1 = GetFileId(file_path1.value()); | |
| base::FilePath file_path2; | |
| ASSERT_TRUE(base::CreateTemporaryFileInDir(helper_.home_path(), &file_path2)); | |
| const FileId id2 = GetFileId(file_path2.value()); | |
| const std::string source1 = "source1"; | |
| const std::string source2 = "source2"; | |
| const std::string referrer1 = "referrer1"; | |
| const std::string referrer2 = "referrer2"; | |
| // Add the files to the database. | |
| AddFilesAndCheck({CreateAddFileRequest(file_path1, source2, referrer2)}, | |
| /*expected_result=*/true); | |
| AddFilesAndCheck({CreateAddFileRequest(file_path1, source1, referrer1), | |
| CreateAddFileRequest(file_path2, source2, referrer2)}, | |
| /*expected_result=*/true); | |
| GetFilesSourcesResponse response = | |
| GetFilesSources({file_path1.value(), file_path2.value()}); | |
| ASSERT_EQ(response.files_metadata_size(), 2u); | |
| FileMetadata file_metadata1 = response.files_metadata()[0]; | |
| EXPECT_EQ(file_metadata1.inode(), id1.first); | |
| EXPECT_EQ(file_metadata1.crtime(), id1.second); | |
| EXPECT_EQ(file_metadata1.source_url(), source1); | |
| EXPECT_EQ(file_metadata1.referrer_url(), referrer1); | |
| FileMetadata file_metadata2 = response.files_metadata()[1]; | |
| EXPECT_EQ(file_metadata2.inode(), id2.first); | |
| EXPECT_EQ(file_metadata2.crtime(), id2.second); | |
| EXPECT_EQ(file_metadata2.source_url(), source2); | |
| EXPECT_EQ(file_metadata2.referrer_url(), referrer2); | |
| EXPECT_THAT(helper_.GetMetrics(kDlpAdaptorErrorHistogram), ElementsAre()); | |
| } | |
| TEST_F(DlpAdaptorTest, SetDlpFilesPolicy_EmptyProto) { | |
| SetDlpFilesPolicyRequest request; | |
| request.add_rules(); | |
| std::vector<uint8_t> proto_blob(request.ByteSizeLong()); | |
| request.SerializeToArray(proto_blob.data(), proto_blob.size()); | |
| std::vector<uint8_t> response_blob = | |
| GetDlpAdaptor()->SetDlpFilesPolicy(proto_blob); | |
| SetDlpFilesPolicyResponse response = | |
| ParseResponse<SetDlpFilesPolicyResponse>(response_blob); | |
| EXPECT_FALSE(response.has_error_message()); | |
| EXPECT_FALSE(helper_.IsFanotifyWatcherActive()); | |
| EXPECT_THAT(helper_.GetMetrics(kDlpAdaptorErrorHistogram), ElementsAre()); | |
| } | |
| TEST_F(DlpAdaptorTest, SetDlpFilesPolicy_BadProto) { | |
| std::vector<uint8_t> response_blob = | |
| GetDlpAdaptor()->SetDlpFilesPolicy(RandomProtoBlob()); | |
| SetDlpFilesPolicyResponse response = | |
| ParseResponse<SetDlpFilesPolicyResponse>(response_blob); | |
| EXPECT_TRUE(response.has_error_message()); | |
| EXPECT_THAT(helper_.GetMetrics(kDlpAdaptorErrorHistogram), | |
| ElementsAre(static_cast<int>(AdaptorError::kInvalidProtoError))); | |
| } | |
| TEST_F(DlpAdaptorTest, SetDlpFilesPolicy_EnableDisable) { | |
| // Testing fanotify watcher start for real. | |
| GetDlpAdaptor()->SetFanotifyWatcherStartedForTesting(false); | |
| // Send non-empty policy -> enabled. | |
| { | |
| SetDlpFilesPolicyRequest request; | |
| ::dlp::DlpFilesRule* rule = request.add_rules(); | |
| rule->add_source_urls("example.com"); | |
| rule->add_destination_urls("*"); | |
| std::vector<uint8_t> proto_blob(request.ByteSizeLong()); | |
| request.SerializeToArray(proto_blob.data(), proto_blob.size()); | |
| std::vector<uint8_t> response_blob = | |
| GetDlpAdaptor()->SetDlpFilesPolicy(proto_blob); | |
| SetDlpFilesPolicyResponse response = | |
| ParseResponse<SetDlpFilesPolicyResponse>(response_blob); | |
| EXPECT_FALSE(response.has_error_message()); | |
| EXPECT_TRUE(helper_.IsFanotifyWatcherActive()); | |
| } | |
| // Send another non-empty policy -> enabled. | |
| { | |
| SetDlpFilesPolicyRequest request; | |
| ::dlp::DlpFilesRule* rule = request.add_rules(); | |
| rule->add_source_urls("example.com"); | |
| rule->add_destination_urls("google.com"); | |
| std::vector<uint8_t> proto_blob(request.ByteSizeLong()); | |
| request.SerializeToArray(proto_blob.data(), proto_blob.size()); | |
| std::vector<uint8_t> response_blob = | |
| GetDlpAdaptor()->SetDlpFilesPolicy(proto_blob); | |
| SetDlpFilesPolicyResponse response = | |
| ParseResponse<SetDlpFilesPolicyResponse>(response_blob); | |
| EXPECT_FALSE(response.has_error_message()); | |
| EXPECT_TRUE(helper_.IsFanotifyWatcherActive()); | |
| } | |
| // Send same non-empty policy -> enabled. | |
| { | |
| SetDlpFilesPolicyRequest request; | |
| ::dlp::DlpFilesRule* rule = request.add_rules(); | |
| rule->add_source_urls("example.com"); | |
| rule->add_destination_urls("google.com"); | |
| std::vector<uint8_t> proto_blob(request.ByteSizeLong()); | |
| request.SerializeToArray(proto_blob.data(), proto_blob.size()); | |
| std::vector<uint8_t> response_blob = | |
| GetDlpAdaptor()->SetDlpFilesPolicy(proto_blob); | |
| SetDlpFilesPolicyResponse response = | |
| ParseResponse<SetDlpFilesPolicyResponse>(response_blob); | |
| EXPECT_FALSE(response.has_error_message()); | |
| EXPECT_TRUE(helper_.IsFanotifyWatcherActive()); | |
| } | |
| // Send empty policy -> disabled. | |
| { | |
| SetDlpFilesPolicyRequest request; | |
| std::vector<uint8_t> proto_blob(request.ByteSizeLong()); | |
| request.SerializeToArray(proto_blob.data(), proto_blob.size()); | |
| std::vector<uint8_t> response_blob = | |
| GetDlpAdaptor()->SetDlpFilesPolicy(proto_blob); | |
| SetDlpFilesPolicyResponse response = | |
| ParseResponse<SetDlpFilesPolicyResponse>(response_blob); | |
| EXPECT_FALSE(response.has_error_message()); | |
| EXPECT_FALSE(helper_.IsFanotifyWatcherActive()); | |
| } | |
| EXPECT_THAT(helper_.GetMetrics(kDlpAdaptorErrorHistogram), ElementsAre()); | |
| } | |
| TEST_F(DlpAdaptorTest, AddZeroFilesToTheDaemon) { | |
| AddFilesAndCheck({}, /*expected_result=*/true); | |
| EXPECT_THAT(helper_.GetMetrics(kDlpAdaptorErrorHistogram), ElementsAre()); | |
| } | |
| TEST_F(DlpAdaptorTest, AddFiles_BadProto) { | |
| bool success; | |
| std::unique_ptr< | |
| brillo::dbus_utils::MockDBusMethodResponse<std::vector<uint8_t>>> | |
| response = std::make_unique< | |
| brillo::dbus_utils::MockDBusMethodResponse<std::vector<uint8_t>>>( | |
| nullptr); | |
| base::RunLoop run_loop; | |
| response->set_return_callback(base::BindOnce( | |
| [](bool* success, base::RunLoop* run_loop, | |
| const std::vector<uint8_t>& proto_blob) { | |
| AddFilesResponse response = ParseResponse<AddFilesResponse>(proto_blob); | |
| *success = response.error_message().empty(); | |
| run_loop->Quit(); | |
| }, | |
| &success, &run_loop)); | |
| GetDlpAdaptor()->AddFiles(std::move(response), RandomProtoBlob()); | |
| run_loop.Run(); | |
| EXPECT_FALSE(success); | |
| EXPECT_THAT(helper_.GetMetrics(kDlpAdaptorErrorHistogram), | |
| ElementsAre(static_cast<int>(AdaptorError::kAddFileError), | |
| static_cast<int>(AdaptorError::kInvalidProtoError))); | |
| } | |
| TEST_F(DlpAdaptorTest, AddFiles_NonExistentFile) { | |
| AddFilesAndCheck( | |
| {CreateAddFileRequest(base::FilePath("/tmp/non-existent-file"), "source", | |
| "referrer")}, | |
| /*expected_result=*/false); | |
| EXPECT_THAT( | |
| helper_.GetMetrics(kDlpAdaptorErrorHistogram), | |
| ElementsAre(static_cast<int>(AdaptorError::kAddFileError), | |
| static_cast<int>(AdaptorError::kInodeRetrievalError))); | |
| } | |
| // TODO(b/304574852): Using TaskEnvironment causes DlpAdaptorTest to hang. | |
| // We need to find another way to fake time or rewrite the test. | |
| TEST_F(DlpAdaptorTest, DISABLED_AddFiles_OldFile) { | |
| base::FilePath file_path; | |
| base::CreateTemporaryFileInDir(helper_.home_path(), &file_path); | |
| // const FileId id = GetFileId(file_path.value()); | |
| //// Set to file creation time. | |
| // task_environment_.FastForwardBy(base::Time::FromTimeT(id.second) - | |
| // base::Time::Now()); | |
| //// Advance by a minute. | |
| // task_environment_.FastForwardBy(base::Minutes(1)); | |
| AddFilesAndCheck({CreateAddFileRequest(file_path, "source", "referrer")}, | |
| /*expected_result=*/false); | |
| EXPECT_THAT(helper_.GetMetrics(kDlpAdaptorErrorHistogram), | |
| ElementsAre(static_cast<int>(AdaptorError::kAddFileError), | |
| static_cast<int>(AdaptorError::kAddedFileIsTooOld))); | |
| } | |
| TEST_F(DlpAdaptorTest, AddFiles_Symlink) { | |
| base::FilePath file_path; | |
| base::CreateTemporaryFile(&file_path); | |
| base::FilePath symlink_path; | |
| base::CreateTemporaryFileInDir(helper_.home_path(), &symlink_path); | |
| brillo::DeleteFile(symlink_path); | |
| base::CreateSymbolicLink(file_path, symlink_path); | |
| AddFilesAndCheck({CreateAddFileRequest(symlink_path, "source", "referrer")}, | |
| /*expected_result=*/false); | |
| EXPECT_THAT( | |
| helper_.GetMetrics(kDlpAdaptorErrorHistogram), | |
| ElementsAre(static_cast<int>(AdaptorError::kAddFileError), | |
| static_cast<int>(AdaptorError::kAddedFileIsNotOnUserHome))); | |
| } | |
| TEST_F(DlpAdaptorTest, RequestFileAccess_BadProto) { | |
| auto response = std::make_unique<brillo::dbus_utils::MockDBusMethodResponse< | |
| std::vector<uint8_t>, base::ScopedFD>>(nullptr); | |
| base::ScopedFD lifeline_fd; | |
| base::RunLoop request_file_access_run_loop; | |
| RequestFileAccessResponse request_file_response; | |
| response->set_return_callback(base::BindOnce( | |
| [](RequestFileAccessResponse* response, base::ScopedFD* lifeline_fd, | |
| base::RunLoop* run_loop, const std::vector<uint8_t>& proto_blob, | |
| const base::ScopedFD& fd) { | |
| *response = ParseResponse<RequestFileAccessResponse>(proto_blob); | |
| lifeline_fd->reset(dup(fd.get())); | |
| run_loop->Quit(); | |
| }, | |
| &request_file_response, &lifeline_fd, &request_file_access_run_loop)); | |
| GetDlpAdaptor()->RequestFileAccess(std::move(response), RandomProtoBlob()); | |
| request_file_access_run_loop.Run(); | |
| EXPECT_FALSE(request_file_response.allowed()); | |
| EXPECT_FALSE(request_file_response.error_message().empty()); | |
| EXPECT_TRUE(IsFdClosed(lifeline_fd.get())); | |
| EXPECT_THAT(helper_.GetMetrics(kDlpAdaptorErrorHistogram), | |
| ElementsAre(static_cast<int>(AdaptorError::kInvalidProtoError))); | |
| } | |
| TEST_F(DlpAdaptorTest, RequestFileAccess_NonExistentFile) { | |
| InitDatabase(); | |
| auto response = std::make_unique<brillo::dbus_utils::MockDBusMethodResponse< | |
| std::vector<uint8_t>, base::ScopedFD>>(nullptr); | |
| base::ScopedFD lifeline_fd; | |
| base::RunLoop request_file_access_run_loop; | |
| RequestFileAccessResponse request_file_response; | |
| response->set_return_callback(base::BindOnce( | |
| [](RequestFileAccessResponse* response, base::ScopedFD* lifeline_fd, | |
| base::RunLoop* run_loop, const std::vector<uint8_t>& proto_blob, | |
| const base::ScopedFD& fd) { | |
| *response = ParseResponse<RequestFileAccessResponse>(proto_blob); | |
| lifeline_fd->reset(dup(fd.get())); | |
| run_loop->Quit(); | |
| }, | |
| &request_file_response, &lifeline_fd, &request_file_access_run_loop)); | |
| GetDlpAdaptor()->RequestFileAccess( | |
| std::move(response), | |
| CreateSerializedRequestFileAccessRequest({"/tmp/non-existent-file"}, kPid, | |
| "destination")); | |
| request_file_access_run_loop.Run(); | |
| EXPECT_TRUE(request_file_response.allowed()); | |
| EXPECT_TRUE(request_file_response.error_message().empty()); | |
| EXPECT_TRUE(IsFdClosed(lifeline_fd.get())); | |
| EXPECT_THAT(helper_.GetMetrics(kDlpAdaptorErrorHistogram), ElementsAre()); | |
| } | |
| TEST_F(DlpAdaptorTest, GetFilesSources_BadProto) { | |
| GetFilesSourcesResponse result; | |
| std::unique_ptr< | |
| brillo::dbus_utils::MockDBusMethodResponse<std::vector<uint8_t>>> | |
| response = std::make_unique< | |
| brillo::dbus_utils::MockDBusMethodResponse<std::vector<uint8_t>>>( | |
| nullptr); | |
| base::RunLoop run_loop; | |
| response->set_return_callback(base::BindOnce( | |
| [](GetFilesSourcesResponse* result, base::RunLoop* run_loop, | |
| const std::vector<uint8_t>& proto_blob) { | |
| *result = ParseResponse<GetFilesSourcesResponse>(proto_blob); | |
| run_loop->Quit(); | |
| }, | |
| &result, &run_loop)); | |
| GetDlpAdaptor()->GetFilesSources(std::move(response), RandomProtoBlob()); | |
| run_loop.Run(); | |
| EXPECT_FALSE(result.error_message().empty()); | |
| EXPECT_THAT(helper_.GetMetrics(kDlpAdaptorErrorHistogram), | |
| ElementsAre(static_cast<int>(AdaptorError::kInvalidProtoError))); | |
| } | |
| TEST_F(DlpAdaptorTest, CheckFilesTransfer_BadProto) { | |
| InitDatabase(); | |
| CheckFilesTransferResponse result; | |
| base::RunLoop run_loop; | |
| auto response = std::make_unique< | |
| brillo::dbus_utils::MockDBusMethodResponse<std::vector<uint8_t>>>( | |
| nullptr); | |
| response->set_return_callback(base::BindOnce( | |
| [](CheckFilesTransferResponse* result, base::RunLoop* run_loop, | |
| const std::vector<uint8_t>& proto_blob) { | |
| *result = ParseResponse<CheckFilesTransferResponse>(proto_blob); | |
| run_loop->Quit(); | |
| }, | |
| &result, &run_loop)); | |
| GetDlpAdaptor()->CheckFilesTransfer(std::move(response), RandomProtoBlob()); | |
| run_loop.Run(); | |
| EXPECT_FALSE(result.error_message().empty()); | |
| EXPECT_TRUE(result.files_paths().empty()); | |
| EXPECT_THAT(helper_.GetMetrics(kDlpAdaptorErrorHistogram), | |
| ElementsAre(static_cast<int>(AdaptorError::kInvalidProtoError))); | |
| } | |
| TEST_F(DlpAdaptorTest, CheckFilesTransfer_DbNotInitialized) { | |
| CheckFilesTransferResponse result; | |
| base::RunLoop run_loop; | |
| // Create files. | |
| base::FilePath file_path1; | |
| ASSERT_TRUE(base::CreateTemporaryFileInDir(helper_.home_path(), &file_path1)); | |
| base::FilePath file_path2; | |
| ASSERT_TRUE(base::CreateTemporaryFileInDir(helper_.home_path(), &file_path2)); | |
| const std::string source1 = "source1"; | |
| const std::string source2 = "source2"; | |
| // Add the files to the database. | |
| AddFilesAndCheck({CreateAddFileRequest(file_path1, source1, "referrer1"), | |
| CreateAddFileRequest(file_path2, source2, "referrer2")}, | |
| /*expected_result=*/true); | |
| auto response = std::make_unique< | |
| brillo::dbus_utils::MockDBusMethodResponse<std::vector<uint8_t>>>( | |
| nullptr); | |
| response->set_return_callback(base::BindOnce( | |
| [](CheckFilesTransferResponse* result, base::RunLoop* run_loop, | |
| const std::vector<uint8_t>& proto_blob) { | |
| *result = ParseResponse<CheckFilesTransferResponse>(proto_blob); | |
| run_loop->Quit(); | |
| }, | |
| &result, &run_loop)); | |
| GetDlpAdaptor()->CheckFilesTransfer( | |
| std::move(response), | |
| CreateSerializedCheckFilesTransferRequest( | |
| {file_path1.value(), file_path2.value()}, DlpComponent::USB)); | |
| run_loop.Run(); | |
| EXPECT_FALSE(result.error_message().empty()); | |
| EXPECT_TRUE(result.files_paths().empty()); | |
| EXPECT_THAT( | |
| helper_.GetMetrics(kDlpAdaptorErrorHistogram), | |
| ElementsAre(static_cast<int>(AdaptorError::kDatabaseNotReadyError), | |
| static_cast<int>(AdaptorError::kDatabaseNotReadyError))); | |
| } | |
| TEST_F(DlpAdaptorTest, CheckFilesTransfer_IsFilesTransferRestrictedBadProto) { | |
| // Create database. | |
| InitDatabase(); | |
| CheckFilesTransferResponse result; | |
| base::RunLoop run_loop; | |
| // Create file. | |
| base::FilePath file_path1; | |
| ASSERT_TRUE(base::CreateTemporaryFileInDir(helper_.home_path(), &file_path1)); | |
| const std::string source1 = "source1"; | |
| // Add the file to the database. | |
| AddFilesAndCheck({CreateAddFileRequest(file_path1, source1, "referrer1")}, | |
| /*expected_result=*/true); | |
| EXPECT_CALL(*GetMockDlpFilesPolicyServiceProxy(), | |
| DoCallMethodWithErrorCallback(_, _, _, _)) | |
| .WillOnce(Invoke(this, &DlpAdaptorTest::StubReplyBadProto)); | |
| auto response = std::make_unique< | |
| brillo::dbus_utils::MockDBusMethodResponse<std::vector<uint8_t>>>( | |
| nullptr); | |
| response->set_return_callback(base::BindOnce( | |
| [](CheckFilesTransferResponse* result, base::RunLoop* run_loop, | |
| const std::vector<uint8_t>& proto_blob) { | |
| *result = ParseResponse<CheckFilesTransferResponse>(proto_blob); | |
| run_loop->Quit(); | |
| }, | |
| &result, &run_loop)); | |
| GetDlpAdaptor()->CheckFilesTransfer( | |
| std::move(response), CreateSerializedCheckFilesTransferRequest( | |
| {file_path1.value()}, DlpComponent::USB)); | |
| run_loop.Run(); | |
| EXPECT_FALSE(result.error_message().empty()); | |
| EXPECT_TRUE(result.files_paths().empty()); | |
| EXPECT_THAT(helper_.GetMetrics(kDlpAdaptorErrorHistogram), | |
| ElementsAre(static_cast<int>(AdaptorError::kInvalidProtoError))); | |
| } | |
| TEST_F(DlpAdaptorTest, | |
| CheckFilesTransfer_IsFilesTransferRestrictedResponseError) { | |
| // Create database. | |
| InitDatabase(); | |
| CheckFilesTransferResponse result; | |
| base::RunLoop run_loop; | |
| // Create file. | |
| base::FilePath file_path1; | |
| ASSERT_TRUE(base::CreateTemporaryFileInDir(helper_.home_path(), &file_path1)); | |
| const std::string source1 = "source1"; | |
| // Add the file to the database. | |
| AddFilesAndCheck({CreateAddFileRequest(file_path1, source1, "referrer1")}, | |
| /*expected_result=*/true); | |
| EXPECT_CALL(*GetMockDlpFilesPolicyServiceProxy(), | |
| DoCallMethodWithErrorCallback(_, _, _, _)) | |
| .WillOnce(Invoke(this, &DlpAdaptorTest::StubReplyWithError)); | |
| auto response = std::make_unique< | |
| brillo::dbus_utils::MockDBusMethodResponse<std::vector<uint8_t>>>( | |
| nullptr); | |
| response->set_return_callback(base::BindOnce( | |
| [](CheckFilesTransferResponse* result, base::RunLoop* run_loop, | |
| const std::vector<uint8_t>& proto_blob) { | |
| *result = ParseResponse<CheckFilesTransferResponse>(proto_blob); | |
| run_loop->Quit(); | |
| }, | |
| &result, &run_loop)); | |
| GetDlpAdaptor()->CheckFilesTransfer( | |
| std::move(response), CreateSerializedCheckFilesTransferRequest( | |
| {file_path1.value()}, DlpComponent::USB)); | |
| run_loop.Run(); | |
| EXPECT_FALSE(result.error_message().empty()); | |
| EXPECT_TRUE(result.files_paths().empty()); | |
| EXPECT_THAT( | |
| helper_.GetMetrics(kDlpAdaptorErrorHistogram), | |
| ElementsAre(static_cast<int>(AdaptorError::kRestrictionDetectionError))); | |
| } | |
| TEST_F(DlpAdaptorTest, GetDatabaseEntries) { | |
| InitDatabase(); | |
| // Create files. | |
| base::FilePath file_path1; | |
| ASSERT_TRUE(base::CreateTemporaryFileInDir(helper_.home_path(), &file_path1)); | |
| const FileId id1 = GetFileId(file_path1.value()); | |
| base::FilePath file_path2; | |
| ASSERT_TRUE(base::CreateTemporaryFileInDir(helper_.home_path(), &file_path2)); | |
| const FileId id2 = GetFileId(file_path2.value()); | |
| const std::string source1 = "source1"; | |
| const std::string source2 = "source2"; | |
| const std::string referrer1 = "referrer1"; | |
| const std::string referrer2 = "referrer2"; | |
| // Add two of the files to the database. | |
| AddFilesAndCheck({CreateAddFileRequest(file_path1, source1, referrer1), | |
| CreateAddFileRequest(file_path2, source2, referrer2)}, | |
| /*expected_result=*/true); | |
| GetDatabaseEntriesResponse response = GetDatabaseEntries(); | |
| ASSERT_EQ(response.files_entries_size(), 2u); | |
| std::map<FileId, FileMetadata> files_entries_map; | |
| for (const auto& file_entry : response.files_entries()) { | |
| files_entries_map[FileId(file_entry.inode(), file_entry.crtime())] = | |
| file_entry; | |
| } | |
| ASSERT_TRUE(base::Contains(files_entries_map, id1)); | |
| EXPECT_EQ(files_entries_map.at(id1).source_url(), source1); | |
| EXPECT_EQ(files_entries_map.at(id1).referrer_url(), referrer1); | |
| ASSERT_TRUE(base::Contains(files_entries_map, id2)); | |
| EXPECT_EQ(files_entries_map.at(id2).source_url(), source2); | |
| EXPECT_EQ(files_entries_map.at(id2).referrer_url(), referrer2); | |
| } | |
| class DlpAdaptorCheckFilesTransferTest | |
| : public DlpAdaptorTest, | |
| public ::testing::WithParamInterface<RestrictionLevel> { | |
| public: | |
| DlpAdaptorCheckFilesTransferTest(const DlpAdaptorCheckFilesTransferTest&) = | |
| delete; | |
| DlpAdaptorCheckFilesTransferTest& operator=( | |
| const DlpAdaptorCheckFilesTransferTest&) = delete; | |
| protected: | |
| DlpAdaptorCheckFilesTransferTest() = default; | |
| ~DlpAdaptorCheckFilesTransferTest() override = default; | |
| }; | |
| INSTANTIATE_TEST_SUITE_P(DlpAdaptor, | |
| DlpAdaptorCheckFilesTransferTest, | |
| ::testing::Values(RestrictionLevel::LEVEL_UNSPECIFIED, | |
| RestrictionLevel::LEVEL_ALLOW, | |
| RestrictionLevel::LEVEL_REPORT, | |
| RestrictionLevel::LEVEL_WARN_PROCEED, | |
| RestrictionLevel::LEVEL_WARN_CANCEL, | |
| RestrictionLevel::LEVEL_BLOCK)); | |
| TEST_P(DlpAdaptorCheckFilesTransferTest, Run) { | |
| // Create database. | |
| InitDatabase(); | |
| // Create files. | |
| base::FilePath file_path1; | |
| ASSERT_TRUE(base::CreateTemporaryFileInDir(helper_.home_path(), &file_path1)); | |
| const FileId id1 = GetFileId(file_path1.value()); | |
| base::FilePath file_path2; | |
| ASSERT_TRUE(base::CreateTemporaryFileInDir(helper_.home_path(), &file_path2)); | |
| const FileId id2 = GetFileId(file_path2.value()); | |
| base::FilePath file_path3; | |
| ASSERT_TRUE(base::CreateTemporaryFileInDir(helper_.home_path(), &file_path3)); | |
| const std::string source1 = "source1"; | |
| const std::string source2 = "source2"; | |
| const std::string referrer1 = "referrer1"; | |
| const std::string referrer2 = "referrer2"; | |
| // Add two of the files to the database. | |
| AddFilesAndCheck({CreateAddFileRequest(file_path1, source1, referrer1), | |
| CreateAddFileRequest(file_path2, source2, referrer2)}, | |
| /*expected_result=*/true); | |
| // Setup callback for DlpFilesPolicyService::IsFilesTransferRestricted() | |
| files_restrictions_.clear(); | |
| FileMetadata file1_metadata; | |
| file1_metadata.set_inode(id1.first); | |
| file1_metadata.set_crtime(id1.second); | |
| file1_metadata.set_path(file_path1.value()); | |
| FileMetadata file2_metadata; | |
| file2_metadata.set_path(file_path2.value()); | |
| file2_metadata.set_inode(id2.first); | |
| file2_metadata.set_crtime(id2.second); | |
| files_restrictions_.push_back( | |
| {std::move(file1_metadata), RestrictionLevel::LEVEL_BLOCK}); | |
| files_restrictions_.push_back({std::move(file2_metadata), GetParam()}); | |
| const int is_files_transfer_restricted_calls = | |
| (GetParam() == LEVEL_UNSPECIFIED || GetParam() == LEVEL_WARN_CANCEL) ? 2 | |
| : 1; | |
| EXPECT_CALL(*GetMockDlpFilesPolicyServiceProxy(), | |
| DoCallMethodWithErrorCallback(_, _, _, _)) | |
| .Times(is_files_transfer_restricted_calls) | |
| .WillRepeatedly( | |
| Invoke(this, &DlpAdaptorTest::StubIsFilesTransferRestricted)); | |
| // Do 2 times for cache. | |
| for (int i = 0; i < 2; i++) { | |
| // Request access to the file. | |
| auto response = std::make_unique< | |
| brillo::dbus_utils::MockDBusMethodResponse<std::vector<uint8_t>>>( | |
| nullptr); | |
| std::vector<std::string> restricted_files_paths; | |
| base::RunLoop check_files_transfer_run_loop; | |
| response->set_return_callback(base::BindOnce( | |
| [](std::vector<std::string>* restricted_files_paths, | |
| base::RunLoop* run_loop, const std::vector<uint8_t>& proto_blob) { | |
| CheckFilesTransferResponse response = | |
| ParseResponse<CheckFilesTransferResponse>(proto_blob); | |
| restricted_files_paths->insert(restricted_files_paths->begin(), | |
| response.files_paths().begin(), | |
| response.files_paths().end()); | |
| run_loop->Quit(); | |
| }, | |
| &restricted_files_paths, &check_files_transfer_run_loop)); | |
| GetDlpAdaptor()->CheckFilesTransfer( | |
| std::move(response), | |
| CreateSerializedCheckFilesTransferRequest( | |
| {file_path1.value(), file_path2.value(), file_path3.value()}, | |
| "destination")); | |
| check_files_transfer_run_loop.Run(); | |
| if (GetParam() == RestrictionLevel::LEVEL_BLOCK || | |
| GetParam() == RestrictionLevel::LEVEL_WARN_CANCEL) { | |
| EXPECT_EQ(restricted_files_paths.size(), 2u); | |
| } else { | |
| EXPECT_EQ(restricted_files_paths.size(), 1u); | |
| EXPECT_EQ(restricted_files_paths[0], file_path1.value()); | |
| } | |
| } | |
| EXPECT_THAT(helper_.GetMetrics(kDlpAdaptorErrorHistogram), ElementsAre()); | |
| } | |
| } // namespace dlp |