blob: fb44d7cb7c4d86e33e9bea8d468825612ef698a0 [file] [log] [blame] [edit]
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "secagentd/message_sender.h"
#include <memory>
#include <optional>
#include <string>
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/memory/scoped_refptr.h"
#include "base/test/task_environment.h"
#include "base/test/test_future.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "missive/client/mock_report_queue.h"
#include "missive/client/mock_report_queue_provider.h"
#include "missive/client/report_queue_provider_test_helper.h"
#include "missive/proto/record_constants.pb.h"
#include "missive/util/status.h"
#include "missive/util/statusor.h"
#include "secagentd/proto/security_xdr_events.pb.h"
namespace secagentd::testing {
namespace pb = cros_xdr::reporting;
using ::testing::_;
using ::testing::Eq;
using ::testing::Invoke;
using ::testing::Mock;
using ::testing::NiceMock;
using ::testing::StrictMock;
using ::testing::WithArg;
using ::testing::WithArgs;
class MessageSenderTestFixture : public ::testing::Test {
protected:
MessageSenderTestFixture()
: task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
void SetUp() override {
ASSERT_TRUE(fake_root_.CreateUniqueTempDir());
const base::FilePath timezone_dir =
fake_root_.GetPath().Append("var/lib/timezone");
ASSERT_TRUE(base::CreateDirectory(timezone_dir));
timezone_symlink_ = timezone_dir.Append("localtime");
zoneinfo_dir_ = fake_root_.GetPath().Append("usr/share/zoneinfo");
ASSERT_TRUE(base::CreateDirectory(zoneinfo_dir_));
message_sender_ = MessageSender::CreateForTesting(fake_root_.GetPath());
provider_ =
std::make_unique<NiceMock<reporting::MockReportQueueProvider>>();
reporting::report_queue_provider_test_helper::SetForTesting(
provider_.get());
provider_->ExpectCreateNewSpeculativeQueueAndReturnNewMockQueue(4);
EXPECT_EQ(message_sender_->InitializeQueues(), absl::OkStatus());
for (auto destination : kDestinations) {
auto it = message_sender_->queue_map_.find(destination);
EXPECT_NE(it, message_sender_->queue_map_.end());
mock_queue_map_.insert(std::make_pair(
destination,
google::protobuf::internal::DownCast<reporting::MockReportQueue*>(
it->second.get())));
}
}
pb::CommonEventDataFields* GetCommon() { return &message_sender_->common_; }
void CallInitializeDeviceBtime() { message_sender_->InitializeDeviceBtime(); }
void CallUpdateDeviceTz() {
message_sender_->UpdateDeviceTz(timezone_symlink_, false);
}
base::test::TaskEnvironment task_environment_;
base::ScopedTempDir fake_root_;
scoped_refptr<MessageSender> message_sender_;
base::FilePath timezone_symlink_;
base::FilePath zoneinfo_dir_;
std::unique_ptr<NiceMock<reporting::MockReportQueueProvider>> provider_;
std::unordered_map<reporting::Destination, reporting::MockReportQueue*>
mock_queue_map_;
static const reporting::Priority kPriority_ = reporting::SLOW_BATCH;
constexpr static const reporting::Destination kDestinations[4] = {
reporting::Destination::CROS_SECURITY_NETWORK,
reporting::CROS_SECURITY_PROCESS, reporting::CROS_SECURITY_AGENT,
reporting::CROS_SECURITY_USER};
};
TEST_F(MessageSenderTestFixture, TestInitializeBtime) {
const std::string kStatContents =
"cpu 331574 58430 92503 1962802 6568 24763 7752 0 0 0\n"
"cpu0 18478 11108 17247 350739 777 8197 4561 0 0 0\n"
"cpu1 22345 8002 13230 364796 1006 3470 961 0 0 0\n"
"cpu2 23079 8248 12590 365637 1163 2955 737 0 0 0\n"
"cpu3 23019 8297 12074 366703 1085 2756 630 0 0 0\n"
"cpu4 108517 11661 18315 272063 1037 3519 442 0 0 0\n"
"cpu5 136133 11112 19045 242863 1498 3863 419 0 0 0\n"
"intr 17153789 0 1877556 2940893 0 0 22514 424451 0 0 0 0 0 0 0 0 0 0 0 "
"0 0 0 0 0 9546173 0 756967 263 1557 1 0 0 0 288285 62 0 158 0 0 12282 "
"128 56 82 44 15 22533 0 192916 1 17569 519 6 0 0 0 0 0 0 0 221447 0 977 "
"0 0 0 0 10765 0 0 0 214680 14 263403 0 0 0 0 0 1 1 0 0 0 284203 14 2 1 "
"51429 0 2 0 0 0 0 1819\n"
"ctxt 15507989\n"
"btime 1667427768\n"
"processes 20013\n"
"procs_running 1\n"
"procs_blocked 0\n"
"softirq 5429921 130273 509093 53702 235430 109885 0 433061 1603480 2368 "
"2352629";
const base::FilePath proc_dir = fake_root_.GetPath().Append("proc");
ASSERT_TRUE(base::CreateDirectory(proc_dir));
ASSERT_TRUE(base::WriteFile(proc_dir.Append("stat"), kStatContents));
CallInitializeDeviceBtime();
EXPECT_EQ(1667427768, GetCommon()->device_boot_time());
}
TEST_F(MessageSenderTestFixture, TestTzUpdateWithPrefix) {
const base::FilePath us_dir = zoneinfo_dir_.Append("US");
ASSERT_TRUE(base::CreateDirectory(us_dir));
const base::FilePath pacific = us_dir.Append("Pacific");
ASSERT_TRUE(base::WriteFile(pacific, ""));
ASSERT_TRUE(base::CreateSymbolicLink(pacific, timezone_symlink_));
CallUpdateDeviceTz();
EXPECT_EQ("US/Pacific", GetCommon()->local_timezone());
}
TEST_F(MessageSenderTestFixture, TestTzUpdateWithoutPrefix) {
// Zulu doesn't have a prefix. Probably will never happen but supported
// nonetheless.
const base::FilePath zulu = zoneinfo_dir_.Append("Zulu");
ASSERT_TRUE(base::WriteFile(zulu, ""));
ASSERT_TRUE(base::CreateSymbolicLink(zulu, timezone_symlink_));
CallUpdateDeviceTz();
EXPECT_EQ("Zulu", GetCommon()->local_timezone());
}
TEST_F(MessageSenderTestFixture, TestTzUpdateNotInZoneInfo) {
const base::FilePath bad = fake_root_.GetPath().Append("IAmError");
ASSERT_TRUE(base::WriteFile(bad, ""));
ASSERT_TRUE(base::CreateSymbolicLink(bad, timezone_symlink_));
CallUpdateDeviceTz();
// Timezone isn't updated.
EXPECT_EQ("", GetCommon()->local_timezone());
}
TEST_F(MessageSenderTestFixture, TestSendMessageValidDestination) {
auto common = GetCommon();
common->set_device_boot_time(100);
common->set_local_timezone("US/Pacific");
std::string proto_string;
// Process Event.
EXPECT_CALL(*(mock_queue_map_.find(reporting::CROS_SECURITY_PROCESS)->second),
AddProducedRecord(_, kPriority_, _))
.WillOnce(WithArgs<0, 2>(Invoke(
[&proto_string](
base::OnceCallback<reporting::StatusOr<std::string>()> record_cb,
base::OnceCallback<void(reporting::Status)> status_cb) {
auto serialized = std::move(record_cb).Run();
proto_string = serialized.value();
std::move(status_cb).Run(reporting::Status::StatusOK());
})));
auto process_message =
std::make_unique<cros_xdr::reporting::XdrProcessEvent>();
auto mutable_common = process_message->mutable_common();
reporting::Destination destination =
reporting::Destination::CROS_SECURITY_PROCESS;
message_sender_->SendMessage(destination, mutable_common,
std::move(process_message), std::nullopt);
auto process_deserialized =
std::make_unique<cros_xdr::reporting::XdrProcessEvent>();
process_deserialized->ParseFromString(proto_string);
EXPECT_EQ(common->device_boot_time(),
process_deserialized->common().device_boot_time());
EXPECT_EQ(common->local_timezone(),
process_deserialized->common().local_timezone());
// Agent Event.
EXPECT_CALL(*(mock_queue_map_.find(reporting::CROS_SECURITY_AGENT)->second),
AddProducedRecord(_, kPriority_, _))
.WillOnce(WithArgs<0, 2>(Invoke(
[&proto_string](
base::OnceCallback<reporting::StatusOr<std::string>()> record_cb,
base::OnceCallback<void(reporting::Status)> status_cb) {
auto serialized = std::move(record_cb).Run();
proto_string = serialized.value();
std::move(status_cb).Run(reporting::Status::StatusOK());
})));
auto agent_message = std::make_unique<cros_xdr::reporting::XdrAgentEvent>();
mutable_common = agent_message->mutable_common();
destination = reporting::Destination::CROS_SECURITY_AGENT;
message_sender_->SendMessage(destination, mutable_common,
std::move(agent_message), std::nullopt);
auto agent_deserialized =
std::make_unique<cros_xdr::reporting::XdrAgentEvent>();
agent_deserialized->ParseFromString(proto_string);
EXPECT_EQ(common->device_boot_time(),
agent_deserialized->common().device_boot_time());
EXPECT_EQ(common->local_timezone(),
agent_deserialized->common().local_timezone());
}
TEST_F(MessageSenderTestFixture, TestSendMessageInvalidDestination) {
auto message = std::make_unique<cros_xdr::reporting::XdrProcessEvent>();
auto mutable_common = message->mutable_common();
const reporting::Destination destination = reporting::Destination(-1);
EXPECT_DEATH(
{
message_sender_->SendMessage(destination, mutable_common,
std::move(message), std::nullopt);
},
".*FATAL secagentd_testrunner:.*Check failed: it != queue_map_\\.end.*");
}
TEST_F(MessageSenderTestFixture, TestSendMessageWithCallback) {
auto message = std::make_unique<cros_xdr::reporting::XdrProcessEvent>();
auto mutable_common = message->mutable_common();
const reporting::Destination destination =
reporting::Destination::CROS_SECURITY_PROCESS;
EXPECT_CALL(*(mock_queue_map_.find(reporting::CROS_SECURITY_PROCESS)->second),
AddProducedRecord(_, kPriority_, _))
.WillOnce(WithArg<2>(
Invoke([](base::OnceCallback<void(reporting::Status)> status_cb) {
std::move(status_cb).Run(reporting::Status::StatusOK());
})));
base::test::TestFuture<reporting::Status> future;
message_sender_->SendMessage(destination, mutable_common, std::move(message),
future.GetCallback());
EXPECT_TRUE(future.Get().ok());
}
} // namespace secagentd::testing