From 2fb92bb57714cb66efd3c25fb1d54f53c0f44975 Mon Sep 17 00:00:00 2001 From: Lucho Donda <95248059+LucioDonda@users.noreply.github.com> Date: Tue, 24 Dec 2024 12:19:21 -0800 Subject: [PATCH 01/21] feat: Initial changes for windoes event reader and fix file test for windows --- etc/config/wazuh-agent.yml | 9 ++++ src/cmake/config.cmake | 4 ++ src/common/config/include/config.h.in | 2 + src/modules/logcollector/README.md | 6 ++- .../logcollector/include/logcollector.hpp | 5 ++ src/modules/logcollector/src/logcollector.cpp | 26 ++++++++++ src/modules/logcollector/src/reader.hpp | 1 + .../logcollector/src/we_reader_win.cpp | 39 ++++++++++++++ .../logcollector/src/we_reader_win.hpp | 52 +++++++++++++++++++ .../tests/unit/file_reader_test.cpp | 39 ++++++++++---- .../tests/unit/logcollector_mock.hpp | 4 ++ .../tests/unit/logcollector_test.cpp | 28 ++++++++++ .../logcollector/tests/unit/tempfile.hpp | 37 +++++++++++-- .../tests/unit/wec_reader_test.cpp | 36 +++++++++++++ 14 files changed, 271 insertions(+), 17 deletions(-) create mode 100644 src/modules/logcollector/src/we_reader_win.cpp create mode 100644 src/modules/logcollector/src/we_reader_win.hpp create mode 100644 src/modules/logcollector/tests/unit/wec_reader_test.cpp diff --git a/etc/config/wazuh-agent.yml b/etc/config/wazuh-agent.yml index 32bd261c2c..c295bc328e 100644 --- a/etc/config/wazuh-agent.yml +++ b/etc/config/wazuh-agent.yml @@ -24,3 +24,12 @@ logcollector: - /var/log/auth.log reload_interval: 1m file_wait: 500ms + windows: + - channel: Application + query: Event[System/EventID = 4624] + use-bookmark: true + reconnect-time: 5s + - channel: System + query: Event[System/EventID = 7040] + use-bookmark: false + reconnect-time: 5s diff --git a/src/cmake/config.cmake b/src/cmake/config.cmake index d378a389d9..44acde4c2c 100644 --- a/src/cmake/config.cmake +++ b/src/cmake/config.cmake @@ -34,6 +34,10 @@ set(DEFAULT_FILE_WAIT 500 CACHE STRING "Default Logcollector file reading interv set(DEFAULT_RELOAD_INTERVAL 60000 CACHE STRING "Default Logcollector reload interval (1m)") +set(DEFAULT_RECONNECT_TIME 5000 CACHE STRING "Default Logcollector reconnect time (5000ms)") + +set(DEFAULT_USE_BOOKMARK false CACHE BOOL "Default Logcollector windows bookmark enabled (false)") + set(DEFAULT_INVENTORY_ENABLED true CACHE BOOL "Default inventory enabled") set(DEFAULT_INTERVAL 3600000 CACHE STRING "Default inventory interval (1h)") diff --git a/src/common/config/include/config.h.in b/src/common/config/include/config.h.in index 2ae00498c1..67c70d2379 100644 --- a/src/common/config/include/config.h.in +++ b/src/common/config/include/config.h.in @@ -32,6 +32,8 @@ namespace config constexpr auto DEFAULT_FILE_WAIT = @DEFAULT_FILE_WAIT@; constexpr auto DEFAULT_RELOAD_INTERVAL = @DEFAULT_RELOAD_INTERVAL@; constexpr auto DEFAULT_LOCALFILES = "/var/log/auth.log"; + constexpr auto DEFAULT_RECONNECT_TIME = @DEFAULT_RECONNECT_TIME@; + constexpr auto DEFAULT_USE_BOOKMARK = @DEFAULT_USE_BOOKMARK@; } namespace inventory diff --git a/src/modules/logcollector/README.md b/src/modules/logcollector/README.md index e6d371686a..5518f382a5 100644 --- a/src/modules/logcollector/README.md +++ b/src/modules/logcollector/README.md @@ -19,10 +19,12 @@ logcollector: delim-regex: "\n" use-bookmark: true windows: + reconnect-time: 5s + use-bookmark: true - channel: Application query: Event[System/EventID = 4624] - use-bookmark: true - reconnect-time: 5s + - channel: System + query: Event[System/EventID = 7040] journald: - filter: - field: "_SYSTEMD_UNIT" diff --git a/src/modules/logcollector/include/logcollector.hpp b/src/modules/logcollector/include/logcollector.hpp index 680a098ab5..90495f09a5 100644 --- a/src/modules/logcollector/include/logcollector.hpp +++ b/src/modules/logcollector/include/logcollector.hpp @@ -84,6 +84,11 @@ class Logcollector { /// @brief Clean all readers void CleanAllReaders(); +#ifdef _WIN32 + /// @brief Sets up the Windows Event Channel reader + /// @param configurationParser Configuration parser + void SetupWEReader(const std::shared_ptr configurationParser); +#endif private: /// @brief Module name diff --git a/src/modules/logcollector/src/logcollector.cpp b/src/modules/logcollector/src/logcollector.cpp index 2b370113d2..76787ebe39 100644 --- a/src/modules/logcollector/src/logcollector.cpp +++ b/src/modules/logcollector/src/logcollector.cpp @@ -9,10 +9,14 @@ #include #include +#include #include #include "file_reader.hpp" +#ifdef _WIN32 +#include "we_reader_win.hpp" +#endif using namespace logcollector; namespace logcollector { @@ -79,6 +83,28 @@ void Logcollector::SetupFileReader(const std::shared_ptr configurationParser) { + const auto reconnectTime = configurationParser->GetConfig("logcollector", "reconnect-time").value_or(config::logcollector::DEFAULT_RECONNECT_TIME); + + const auto bookmarkEnabled = configurationParser->GetConfig("logcollector", "use-bookmark").value_or(config::logcollector::DEFAULT_USE_BOOKMARK); + + const auto windowsConfig = configurationParser->GetConfig>>("logcollector", "windows").value_or( + std::vector> {}); + + std::list channelsList; + std::list queriesList; + for (auto& entry : windowsConfig) + { + auto channel = entry.at("channel"); + channelsList.emplace_back(channel); + auto query = entry.at("query"); + queriesList.emplace_back(query); + } + AddReader(std::make_shared(*this, channelsList, queriesList, reconnectTime, bookmarkEnabled)); +} +#endif + void Logcollector::Stop() { CleanAllReaders(); m_ioContext.stop(); diff --git a/src/modules/logcollector/src/reader.hpp b/src/modules/logcollector/src/reader.hpp index d4eb60c6d9..29857ec002 100644 --- a/src/modules/logcollector/src/reader.hpp +++ b/src/modules/logcollector/src/reader.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include namespace logcollector { diff --git a/src/modules/logcollector/src/we_reader_win.cpp b/src/modules/logcollector/src/we_reader_win.cpp new file mode 100644 index 0000000000..b4aaeb309c --- /dev/null +++ b/src/modules/logcollector/src/we_reader_win.cpp @@ -0,0 +1,39 @@ +#include "we_reader_win.hpp" + +#include + +using namespace logcollector; + +WindowsEventTracerReader::WindowsEventTracerReader(Logcollector &logcollector, + const std::list channels, + const std::list queries, + const std::time_t reloadInterval, + bool bookmarkEnabled) : + IReader(logcollector), + m_channelsList(channels), + m_queriesList(queries), + m_ReaderReloadInterval(reloadInterval), + m_bookmarkEnabled(bookmarkEnabled) { } + +Awaitable WindowsEventTracerReader::Run() +{ + // TODO: add lambda for loop break + while (true) { + m_logcollector.EnqueueTask(ReadEventChannel()); + + co_await m_logcollector.Wait( + std::chrono::milliseconds(m_ReaderReloadInterval)); + } +} + +Awaitable WindowsEventTracerReader::ReadEventChannel() +{ + for (auto eventChannel : m_channelsList) + { + const std::string log {}; + m_logcollector.SendMessage(eventChannel, log, m_collectorType); + + co_await m_logcollector.Wait(std::chrono::milliseconds(m_ReaderReloadInterval)); + } +} + diff --git a/src/modules/logcollector/src/we_reader_win.hpp b/src/modules/logcollector/src/we_reader_win.hpp new file mode 100644 index 0000000000..56793e5ac3 --- /dev/null +++ b/src/modules/logcollector/src/we_reader_win.hpp @@ -0,0 +1,52 @@ +#ifdef _WIN32 +#pragma once + +#include +#include +#include + +#include "reader.hpp" + +namespace logcollector { + +/// @brief Windows Event Tracer Reader class +/// TODO: doc +class WindowsEventTracerReader : public IReader +{ +public: + /// @brief Constructor for the Windows Event Tracer Reader + /// @param logcollector Log collector instance + /// @param channels List of channel names + /// @param queries List of queries + /// @param reloadInterval + /// @param bookmarkEnabled + WindowsEventTracerReader(Logcollector &logcollector, + const std::list channels, + const std::list queries, + const std::time_t reloadInterval, + bool bookmarkEnabled); + + /// @brief Runs the file reader + /// @return Awaitable result + Awaitable Run() override; + + // TODO: doc + Awaitable ReadEventChannel(); + +private: + /// @brief + std::time_t m_ReaderReloadInterval; + + std::list m_channelsList; + + std::list m_queriesList; + + bool m_bookmarkEnabled; + + int m_availableEvents = 0; + + const std::string m_collectorType = "eventchannel"; +}; + +} // namespace logcollector +#endif diff --git a/src/modules/logcollector/tests/unit/file_reader_test.cpp b/src/modules/logcollector/tests/unit/file_reader_test.cpp index b22d9c36fe..bf3c98a0d3 100644 --- a/src/modules/logcollector/tests/unit/file_reader_test.cpp +++ b/src/modules/logcollector/tests/unit/file_reader_test.cpp @@ -10,6 +10,18 @@ using namespace logcollector; +#ifdef _WIN32 +static constexpr auto TMP_FILE_DIR = "C:\\Temp\\"; +#else +static constexpr auto TMP_FILE_DIR = "/tmp/" +#endif + +inline std::string getFullFileName(const std::string& filename) { + //TODO: move to setup stage of test only for windows + std::filesystem::create_directories(TMP_FILE_DIR); + return TMP_FILE_DIR + filename; +} + class MockCallback { public: MOCK_METHOD(void, Call, (const std::string &), ()); @@ -51,8 +63,8 @@ TEST(Localfile, OpenError) TEST(Localfile, Rotated) { - auto fileA = TempFile("/tmp/A.log", "Hello World"); - auto lf = Localfile("/tmp/A.log"); + auto fileA = TempFile(getFullFileName("A.log"), "Hello World"); + auto lf = Localfile(getFullFileName("A.log")); lf.SeekEnd(); ASSERT_FALSE(lf.Rotated()); @@ -63,6 +75,10 @@ TEST(Localfile, Rotated) TEST(Localfile, Deleted) { +#ifdef _WIN32 + //FIXME: The process cannot access the file because it is being used by another process. + GTEST_SKIP(); +#endif auto fileA = std::make_unique("/tmp/A.log", "Hello World"); auto lf = Localfile("/tmp/A.log"); @@ -80,18 +96,19 @@ TEST(FileReader, Reload) { spdlog::default_logger()->sinks().clear(); MockCallback mockCallback; - EXPECT_CALL(mockCallback, Call("/tmp/A.log")).Times(1); - EXPECT_CALL(mockCallback, Call("/tmp/B.log")).Times(1); - EXPECT_CALL(mockCallback, Call("/tmp/C.log")).Times(1); - EXPECT_CALL(mockCallback, Call("/tmp/D.log")).Times(1); + EXPECT_CALL(mockCallback, Call(getFullFileName("A.log"))).Times(1); + EXPECT_CALL(mockCallback, Call(getFullFileName("B.log"))).Times(1); + EXPECT_CALL(mockCallback, Call(getFullFileName("C.log"))).Times(1); + EXPECT_CALL(mockCallback, Call(getFullFileName("D.log"))).Times(1); - auto a = TempFile("/tmp/A.log"); - auto b = TempFile("/tmp/B.log"); - auto c = TempFile("/tmp/C.log"); + auto a = TempFile(getFullFileName("A.log")); + auto b = TempFile(getFullFileName("B.log")); + auto c = TempFile(getFullFileName("C.log")); - FileReader reader(Logcollector::Instance(), "/tmp/*.log", 500, 60000); //NOLINT + auto regex = TMP_FILE_DIR + std::string("*.log"); + FileReader reader(Logcollector::Instance(), regex, 500, 60000); //NOLINT reader.Reload([&](Localfile& lf) { mockCallback.Call(lf.Filename()); }); - auto d = TempFile("/tmp/D.log"); + auto d = TempFile(getFullFileName("D.log")); reader.Reload([&](Localfile& lf) { mockCallback.Call(lf.Filename()); }); } diff --git a/src/modules/logcollector/tests/unit/logcollector_mock.hpp b/src/modules/logcollector/tests/unit/logcollector_mock.hpp index 66723c2cd7..fc35b05729 100644 --- a/src/modules/logcollector/tests/unit/logcollector_mock.hpp +++ b/src/modules/logcollector/tests/unit/logcollector_mock.hpp @@ -20,6 +20,10 @@ class LogcollectorMock : public Logcollector { Logcollector::SetupFileReader(configurationParser); } + void SetupWEReader(const std::shared_ptr configurationParser) { + Logcollector::SetupWEReader(configurationParser); + } + MOCK_METHOD(void, AddReader, (std::shared_ptr reader), (override)); MOCK_METHOD(void, EnqueueTask, (Awaitable task), (override)); }; diff --git a/src/modules/logcollector/tests/unit/logcollector_test.cpp b/src/modules/logcollector/tests/unit/logcollector_test.cpp index 1b334f4145..8c8b6fda9a 100644 --- a/src/modules/logcollector/tests/unit/logcollector_test.cpp +++ b/src/modules/logcollector/tests/unit/logcollector_test.cpp @@ -79,3 +79,31 @@ TEST(Logcollector, SendMessage) { ASSERT_EQ(capturedMessage.data["event"]["provider"], PROVIDER); ASSERT_EQ(capturedMessage.metaData, METADATA); } + +TEST(Logcollector, SetupWECReader) { + auto constexpr CONFIG_RAW = R"( + logcollector: + windows: + - channel: Application + query: Event[System/EventID = 4624] + - channel: System + query: Event[System/EventID = 7040] + )"; + + std::shared_ptr capturedReader1; + auto logcollector = LogcollectorMock(); + auto config = std::make_shared(std::string(CONFIG_RAW)); + + EXPECT_CALL(logcollector, AddReader(::testing::_)).Times(1) + .WillOnce(::testing::SaveArg<0>(&capturedReader1)); + + logcollector.SetupWEReader(config); + + ASSERT_NE(capturedReader1, nullptr); +} + +int main(int argc, char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/src/modules/logcollector/tests/unit/tempfile.hpp b/src/modules/logcollector/tests/unit/tempfile.hpp index 0e4bc2986d..5dde2c3cdb 100644 --- a/src/modules/logcollector/tests/unit/tempfile.hpp +++ b/src/modules/logcollector/tests/unit/tempfile.hpp @@ -1,13 +1,16 @@ #pragma once #include +#include +#include class TempFile { public: TempFile(std::string path, const std::string& str = "") : - m_path(std::move(path)), - m_stream(m_path) + m_path(std::move(path)) { + m_stream.open(m_path, std::ios::out | std::ios::binary); + if (!str.empty()) { Write(str); } @@ -19,13 +22,39 @@ class TempFile { } void Truncate() { + // Close and reopen the file to ensure Windows allows the resize + m_stream.close(); std::filesystem::resize_file(m_path, 0); - m_stream.seekp(0); + m_stream.open(m_path, std::ios::out | std::ios::binary | std::ios::trunc); } ~TempFile() { + m_stream.close(); + std::error_code ec; - std::filesystem::remove(m_path, ec); + if (!std::filesystem::remove(m_path, ec)) + { + auto error_msg = std::system_category().message(ec.value()); + + // Alternative approach using Windows API directly + #ifdef _WIN32 + std::wstring wpath(m_path.begin(), m_path.end()); + if (DeleteFileW(wpath.c_str()) == 0) { + DWORD win_error = GetLastError(); + char win_error_msg[256]; + FormatMessageA( + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + win_error, + 0, + win_error_msg, + sizeof(win_error_msg), + NULL + ); + // win_error_msg (from Windows API) + } + #endif + } } const std::string& Path() const { diff --git a/src/modules/logcollector/tests/unit/wec_reader_test.cpp b/src/modules/logcollector/tests/unit/wec_reader_test.cpp new file mode 100644 index 0000000000..ae66a48cbb --- /dev/null +++ b/src/modules/logcollector/tests/unit/wec_reader_test.cpp @@ -0,0 +1,36 @@ +#include +#include +#include + +#include +#include +#include "queue_mock.hpp" +#include "logcollector_mock.hpp" + +using namespace logcollector; + + +TEST(WindowsEventChannel, Constructor) +{ + GTEST_SKIP(); +} + +TEST(WindowsEventChannel, NoneEvent) +{ + GTEST_SKIP(); +} + +TEST(WindowsEventChannel, SingleEvent) +{ + GTEST_SKIP(); +} + +TEST(WindowsEventChannel, Several) +{ + GTEST_SKIP(); +} + +TEST(WindowsEventChannel, BookmarkUsage) +{ + GTEST_SKIP(); +} From a09b8e54ccb123265403561ce7f178fb00c55425 Mon Sep 17 00:00:00 2001 From: Lucho Donda <95248059+LucioDonda@users.noreply.github.com> Date: Tue, 24 Dec 2024 15:27:45 -0800 Subject: [PATCH 02/21] feat: base functionality for windows events monitoring --- src/modules/logcollector/src/logcollector.cpp | 4 +- .../logcollector/src/we_reader_win.cpp | 135 +++++++++++++++++- .../logcollector/src/we_reader_win.hpp | 37 ++++- 3 files changed, 162 insertions(+), 14 deletions(-) diff --git a/src/modules/logcollector/src/logcollector.cpp b/src/modules/logcollector/src/logcollector.cpp index 76787ebe39..a65e4f84b4 100644 --- a/src/modules/logcollector/src/logcollector.cpp +++ b/src/modules/logcollector/src/logcollector.cpp @@ -92,8 +92,8 @@ void Logcollector::SetupWEReader(const std::shared_ptrGetConfig>>("logcollector", "windows").value_or( std::vector> {}); - std::list channelsList; - std::list queriesList; + std::vector channelsList; + std::vector queriesList; for (auto& entry : windowsConfig) { auto channel = entry.at("channel"); diff --git a/src/modules/logcollector/src/we_reader_win.cpp b/src/modules/logcollector/src/we_reader_win.cpp index b4aaeb309c..c0fd612186 100644 --- a/src/modules/logcollector/src/we_reader_win.cpp +++ b/src/modules/logcollector/src/we_reader_win.cpp @@ -5,8 +5,8 @@ using namespace logcollector; WindowsEventTracerReader::WindowsEventTracerReader(Logcollector &logcollector, - const std::list channels, - const std::list queries, + const std::vector channels, + const std::vector queries, const std::time_t reloadInterval, bool bookmarkEnabled) : IReader(logcollector), @@ -28,12 +28,133 @@ Awaitable WindowsEventTracerReader::Run() Awaitable WindowsEventTracerReader::ReadEventChannel() { - for (auto eventChannel : m_channelsList) - { - const std::string log {}; - m_logcollector.SendMessage(eventChannel, log, m_collectorType); - + for (size_t i = 0; i < m_channelsList.size(); i++) + { + QueryEvents(m_channelsList.at(i), m_queriesList.at(i)); co_await m_logcollector.Wait(std::chrono::milliseconds(m_ReaderReloadInterval)); } } +void WindowsEventTracerReader::QueryEvents(const std::string channel, const std::string query) +{ + // Load bookmark if exists + EVT_HANDLE bookmarkHandle = LoadBookmark(); + + //TODO: rework this casting + std::wstring wide_string_channel = std::wstring(channel.begin(), channel.end()); + std::wstring wide_string_query = std::wstring(query.begin(), query.end()); + EVT_HANDLE eventQueryHandle = EvtQuery( + NULL,// Local machine + wide_string_channel.c_str(), + wide_string_query.c_str(), + EvtQueryChannelPath | EvtQueryReverseDirection); + + if (eventQueryHandle == NULL) + { + // TODO: Logging fix + // LogError("Failed to query event log: {}", GetLastError()); + std::cerr << "Failed to query event log: " << GetLastError() << std::endl; + return; + } + + EVT_HANDLE events[10]; + DWORD eventCount = 0; + + while (EvtNext(eventQueryHandle, 10, events, INFINITE, 0, &eventCount)) + { + for (DWORD i = 0; i < eventCount; ++i) + { + ProcessEvent(events[i], channel); + + if(m_bookmarkEnabled) + { + // Save the last event as a bookmark + bookmarkHandle = CreateBookmark(events[i], bookmarkHandle); + SaveBookmark(bookmarkHandle); + } + EvtClose(events[i]); + } + } + + EvtClose(eventQueryHandle); + if (bookmarkHandle) + { + EvtClose(bookmarkHandle); + } +} + +void WindowsEventTracerReader::ProcessEvent(EVT_HANDLE event, const std::string channel) +{ + DWORD bufferUsed = 0; + DWORD propertyCount = 0; + + EvtRender( + NULL, + event, + EvtRenderEventXml, + 0, + NULL, + &bufferUsed, + &propertyCount); + + if (bufferUsed > 0) + { + std::vector buffer(bufferUsed); + if (EvtRender(NULL, event, EvtRenderEventXml, bufferUsed, buffer.data(), &bufferUsed, &propertyCount)) + { + // TODO: Logging fix + // LogTrace("Event: {}",buffer.data()); + std::wcout << L"Event: " << buffer.data() << std::endl; + // const std::string log {}; + // m_logcollector.SendMessage(channel, log, m_collectorType); + } + } +} + +EVT_HANDLE WindowsEventTracerReader::CreateBookmark(EVT_HANDLE event, EVT_HANDLE existingBookmark) +{ + EVT_HANDLE bookmark = existingBookmark ? existingBookmark : EvtCreateBookmark(NULL); + if (!EvtUpdateBookmark(bookmark, event)) + { + // TODO: Logging fix + // LogError("Failed to update bookmark: {}", GetLastError()); + std::cerr << "Failed to update bookmark: " << GetLastError() << std::endl; + } + return bookmark; +} + +void WindowsEventTracerReader::SaveBookmark(EVT_HANDLE bookmarkHandle) +{ + DWORD bufferUsed = 0; + + EvtRender(NULL, bookmarkHandle, EvtRenderBookmark, 0, NULL, &bufferUsed, NULL); + std::vector buffer(bufferUsed); + if (EvtRender(NULL, bookmarkHandle, EvtRenderBookmark, bufferUsed, buffer.data(), &bufferUsed, NULL)) + { + std::wofstream file(bookmarkFile_); + if (file.is_open()) + { + file.write(buffer.data(), bufferUsed / sizeof(wchar_t)); + file.close(); + } + } +} + +EVT_HANDLE WindowsEventTracerReader::LoadBookmark() +{ + if(m_bookmarkEnabled) + { + std::wifstream file(bookmarkFile_); + if (file.is_open()) + { + std::wstringstream buffer; + buffer << file.rdbuf(); + std::wstring bookmarkXML = buffer.str(); + file.close(); + return EvtCreateBookmark(bookmarkXML.c_str()); + } + } + + return NULL; +} + diff --git a/src/modules/logcollector/src/we_reader_win.hpp b/src/modules/logcollector/src/we_reader_win.hpp index 56793e5ac3..a60a4396bc 100644 --- a/src/modules/logcollector/src/we_reader_win.hpp +++ b/src/modules/logcollector/src/we_reader_win.hpp @@ -2,11 +2,21 @@ #pragma once #include -#include +#include +#include +#include +#include #include +#include + +#include +#include +#include #include "reader.hpp" +#pragma comment(lib, "wevtapi.lib") + namespace logcollector { /// @brief Windows Event Tracer Reader class @@ -21,8 +31,8 @@ class WindowsEventTracerReader : public IReader /// @param reloadInterval /// @param bookmarkEnabled WindowsEventTracerReader(Logcollector &logcollector, - const std::list channels, - const std::list queries, + const std::vector channels, + const std::vector queries, const std::time_t reloadInterval, bool bookmarkEnabled); @@ -33,13 +43,30 @@ class WindowsEventTracerReader : public IReader // TODO: doc Awaitable ReadEventChannel(); + // Main function to execute the event query with bookmarks and filters + void QueryEvents(const std::string channel, const std::string query); + private: + // Process an individual event and print its XML representation + void ProcessEvent(EVT_HANDLE event, const std::string channel); + + // Create a bookmark for the current event + EVT_HANDLE CreateBookmark(EVT_HANDLE event, EVT_HANDLE existingBookmark = NULL); + + // Save bookmark to file + void SaveBookmark(EVT_HANDLE bookmarkHandle); + + // Load bookmark from file (if it exists) + EVT_HANDLE LoadBookmark(); + + std::wstring bookmarkFile_; + /// @brief std::time_t m_ReaderReloadInterval; - std::list m_channelsList; + std::vector m_channelsList; - std::list m_queriesList; + std::vector m_queriesList; bool m_bookmarkEnabled; From 5551e7900a0eb45d20374f5c89ebc9c07361bfec Mon Sep 17 00:00:00 2001 From: Lucho Donda <95248059+LucioDonda@users.noreply.github.com> Date: Fri, 27 Dec 2024 08:33:13 -0800 Subject: [PATCH 03/21] feat: additional iteration without tests --- .../logcollector/include/logcollector.hpp | 2 +- src/modules/logcollector/src/logcollector.cpp | 22 ++++++--- .../logcollector/src/we_reader_win.cpp | 48 +++++++------------ .../logcollector/src/we_reader_win.hpp | 23 +++++---- .../tests/unit/file_reader_test.cpp | 2 +- .../tests/unit/logcollector_mock.hpp | 6 ++- .../tests/unit/logcollector_test.cpp | 12 +++-- 7 files changed, 59 insertions(+), 56 deletions(-) diff --git a/src/modules/logcollector/include/logcollector.hpp b/src/modules/logcollector/include/logcollector.hpp index 90495f09a5..786c5c151d 100644 --- a/src/modules/logcollector/include/logcollector.hpp +++ b/src/modules/logcollector/include/logcollector.hpp @@ -48,7 +48,7 @@ class Logcollector { /// @param log Message to send /// @param collectorType type of logcollector /// @pre The message queue must be set with SetMessageQueue - virtual void SendMessage(const std::string& location, const std::string& log, const std::string& collectorType); + void SendMessage(const std::string& location, const std::string& log, const std::string& collectorType); /// @brief Enqueues an ASIO task (coroutine) /// @param task Task to enqueue diff --git a/src/modules/logcollector/src/logcollector.cpp b/src/modules/logcollector/src/logcollector.cpp index a65e4f84b4..61b84a4c40 100644 --- a/src/modules/logcollector/src/logcollector.cpp +++ b/src/modules/logcollector/src/logcollector.cpp @@ -23,7 +23,8 @@ namespace logcollector { constexpr int ACTIVE_READERS_WAIT_MS = 10; } -void Logcollector::Start() { +void Logcollector::Start() +{ if (!m_enabled) { LogInfo("Logcollector module is disabled."); return; @@ -54,7 +55,8 @@ void Logcollector::EnqueueTask(boost::asio::awaitable task) { // NOLINTEND(cppcoreguidelines-avoid-capturing-lambda-coroutines) } -void Logcollector::Setup(std::shared_ptr configurationParser) { +void Logcollector::Setup(std::shared_ptr configurationParser) +{ if (!configurationParser) { LogError("Invalid Configuration Parser passed to setup, module set to disabled."); m_enabled = false; @@ -71,7 +73,8 @@ void Logcollector::Setup(std::shared_ptr configurationParser) { +void Logcollector::SetupFileReader(const std::shared_ptr configurationParser) +{ auto fileWait = configurationParser->GetConfig("logcollector", "file_wait").value_or(config::logcollector::DEFAULT_FILE_WAIT); auto reloadInterval = configurationParser->GetConfig("logcollector", "reload_interval").value_or(config::logcollector::DEFAULT_RELOAD_INTERVAL); @@ -84,7 +87,8 @@ void Logcollector::SetupFileReader(const std::shared_ptr configurationParser) { +void Logcollector::SetupWEReader(const std::shared_ptr configurationParser) +{ const auto reconnectTime = configurationParser->GetConfig("logcollector", "reconnect-time").value_or(config::logcollector::DEFAULT_RECONNECT_TIME); const auto bookmarkEnabled = configurationParser->GetConfig("logcollector", "use-bookmark").value_or(config::logcollector::DEFAULT_USE_BOOKMARK); @@ -113,7 +117,8 @@ void Logcollector::Stop() { // NOLINTBEGIN(performance-unnecessary-value-param) Co_CommandExecutionResult Logcollector::ExecuteCommand(const std::string command, - [[maybe_unused]] const nlohmann::json parameters) { + [[maybe_unused]] const nlohmann::json parameters) + { LogInfo("Logcollector command: ", command); co_return module_command::CommandExecutionResult{module_command::Status::SUCCESS, "OK"}; } @@ -123,7 +128,8 @@ void Logcollector::SetPushMessageFunction(const std::function& pus m_pushMessage = pushMessage; } -void Logcollector::SendMessage(const std::string& location, const std::string& log, const std::string& collectorType) { +void Logcollector::SendMessage(const std::string& location, const std::string& log, const std::string& collectorType) +{ auto metadata = nlohmann::json::object(); auto data = nlohmann::json::object(); @@ -143,7 +149,9 @@ void Logcollector::SendMessage(const std::string& location, const std::string& l LogTrace("Message pushed: '{}':'{}'", location, log); } -void Logcollector::AddReader(std::shared_ptr reader) { +void Logcollector::AddReader(std::shared_ptr reader) +{ + //TODO: do we need m_readers ? m_readers.push_back(reader); EnqueueTask(reader->Run()); } diff --git a/src/modules/logcollector/src/we_reader_win.cpp b/src/modules/logcollector/src/we_reader_win.cpp index c0fd612186..00fd60531a 100644 --- a/src/modules/logcollector/src/we_reader_win.cpp +++ b/src/modules/logcollector/src/we_reader_win.cpp @@ -7,35 +7,29 @@ using namespace logcollector; WindowsEventTracerReader::WindowsEventTracerReader(Logcollector &logcollector, const std::vector channels, const std::vector queries, - const std::time_t reloadInterval, + const std::time_t channelRefreshInterval, bool bookmarkEnabled) : IReader(logcollector), m_channelsList(channels), m_queriesList(queries), - m_ReaderReloadInterval(reloadInterval), + m_ChannelsRefreshInterval(channelRefreshInterval), m_bookmarkEnabled(bookmarkEnabled) { } Awaitable WindowsEventTracerReader::Run() { // TODO: add lambda for loop break - while (true) { - m_logcollector.EnqueueTask(ReadEventChannel()); - - co_await m_logcollector.Wait( - std::chrono::milliseconds(m_ReaderReloadInterval)); - } -} - -Awaitable WindowsEventTracerReader::ReadEventChannel() -{ - for (size_t i = 0; i < m_channelsList.size(); i++) + while (true) { - QueryEvents(m_channelsList.at(i), m_queriesList.at(i)); - co_await m_logcollector.Wait(std::chrono::milliseconds(m_ReaderReloadInterval)); + for (size_t i = 0; i < m_channelsList.size(); i++) + { + QueryEvents(m_channelsList.at(i), m_queriesList.at(i)); + } + + co_await m_logcollector.Wait(std::chrono::milliseconds(m_ChannelsRefreshInterval)); } } -void WindowsEventTracerReader::QueryEvents(const std::string channel, const std::string query) +Awaitable WindowsEventTracerReader::QueryEvents(const std::string channel, const std::string query) { // Load bookmark if exists EVT_HANDLE bookmarkHandle = LoadBookmark(); @@ -43,10 +37,7 @@ void WindowsEventTracerReader::QueryEvents(const std::string channel, const std: //TODO: rework this casting std::wstring wide_string_channel = std::wstring(channel.begin(), channel.end()); std::wstring wide_string_query = std::wstring(query.begin(), query.end()); - EVT_HANDLE eventQueryHandle = EvtQuery( - NULL,// Local machine - wide_string_channel.c_str(), - wide_string_query.c_str(), + EVT_HANDLE eventQueryHandle = EvtQuery( NULL, wide_string_channel.c_str(), wide_string_query.c_str(), EvtQueryChannelPath | EvtQueryReverseDirection); if (eventQueryHandle == NULL) @@ -54,7 +45,7 @@ void WindowsEventTracerReader::QueryEvents(const std::string channel, const std: // TODO: Logging fix // LogError("Failed to query event log: {}", GetLastError()); std::cerr << "Failed to query event log: " << GetLastError() << std::endl; - return; + co_return; } EVT_HANDLE events[10]; @@ -74,6 +65,8 @@ void WindowsEventTracerReader::QueryEvents(const std::string channel, const std: } EvtClose(events[i]); } + //TODO: check using logcollec + co_await m_logcollector.Wait(std::chrono::milliseconds(m_eventsProcessingInterval)); } EvtClose(eventQueryHandle); @@ -88,14 +81,7 @@ void WindowsEventTracerReader::ProcessEvent(EVT_HANDLE event, const std::string DWORD bufferUsed = 0; DWORD propertyCount = 0; - EvtRender( - NULL, - event, - EvtRenderEventXml, - 0, - NULL, - &bufferUsed, - &propertyCount); + EvtRender(NULL, event, EvtRenderEventXml, 0, NULL, &bufferUsed, &propertyCount); if (bufferUsed > 0) { @@ -105,7 +91,7 @@ void WindowsEventTracerReader::ProcessEvent(EVT_HANDLE event, const std::string // TODO: Logging fix // LogTrace("Event: {}",buffer.data()); std::wcout << L"Event: " << buffer.data() << std::endl; - // const std::string log {}; + const std::string log {}; // m_logcollector.SendMessage(channel, log, m_collectorType); } } @@ -141,7 +127,7 @@ void WindowsEventTracerReader::SaveBookmark(EVT_HANDLE bookmarkHandle) } EVT_HANDLE WindowsEventTracerReader::LoadBookmark() -{ +{ if(m_bookmarkEnabled) { std::wifstream file(bookmarkFile_); diff --git a/src/modules/logcollector/src/we_reader_win.hpp b/src/modules/logcollector/src/we_reader_win.hpp index a60a4396bc..36984830ab 100644 --- a/src/modules/logcollector/src/we_reader_win.hpp +++ b/src/modules/logcollector/src/we_reader_win.hpp @@ -21,7 +21,7 @@ namespace logcollector { /// @brief Windows Event Tracer Reader class /// TODO: doc -class WindowsEventTracerReader : public IReader +class WindowsEventTracerReader : public IReader { public: /// @brief Constructor for the Windows Event Tracer Reader @@ -40,11 +40,8 @@ class WindowsEventTracerReader : public IReader /// @return Awaitable result Awaitable Run() override; - // TODO: doc - Awaitable ReadEventChannel(); - // Main function to execute the event query with bookmarks and filters - void QueryEvents(const std::string channel, const std::string query); + Awaitable QueryEvents(const std::string channel, const std::string query); private: // Process an individual event and print its XML representation @@ -59,14 +56,21 @@ class WindowsEventTracerReader : public IReader // Load bookmark from file (if it exists) EVT_HANDLE LoadBookmark(); + std::vector m_channelsList; + + std::vector m_queriesList; + std::wstring bookmarkFile_; + std::string m_channel; + + std::string m_query; + /// @brief - std::time_t m_ReaderReloadInterval; + std::time_t m_ChannelsRefreshInterval; - std::vector m_channelsList; - - std::vector m_queriesList; + /// @brief + std::time_t m_eventsProcessingInterval; bool m_bookmarkEnabled; @@ -74,6 +78,5 @@ class WindowsEventTracerReader : public IReader const std::string m_collectorType = "eventchannel"; }; - } // namespace logcollector #endif diff --git a/src/modules/logcollector/tests/unit/file_reader_test.cpp b/src/modules/logcollector/tests/unit/file_reader_test.cpp index bf3c98a0d3..026a65c9bc 100644 --- a/src/modules/logcollector/tests/unit/file_reader_test.cpp +++ b/src/modules/logcollector/tests/unit/file_reader_test.cpp @@ -13,7 +13,7 @@ using namespace logcollector; #ifdef _WIN32 static constexpr auto TMP_FILE_DIR = "C:\\Temp\\"; #else -static constexpr auto TMP_FILE_DIR = "/tmp/" +static constexpr auto TMP_FILE_DIR = "/tmp/"; #endif inline std::string getFullFileName(const std::string& filename) { diff --git a/src/modules/logcollector/tests/unit/logcollector_mock.hpp b/src/modules/logcollector/tests/unit/logcollector_mock.hpp index fc35b05729..d3690fd19c 100644 --- a/src/modules/logcollector/tests/unit/logcollector_mock.hpp +++ b/src/modules/logcollector/tests/unit/logcollector_mock.hpp @@ -16,11 +16,13 @@ class LogcollectorMock : public Logcollector { ); } - void SetupFileReader(std::shared_ptr configurationParser) { + void SetupFileReader(std::shared_ptr configurationParser) + { Logcollector::SetupFileReader(configurationParser); } - void SetupWEReader(const std::shared_ptr configurationParser) { + void SetupWEReader(const std::shared_ptr configurationParser) + { Logcollector::SetupWEReader(configurationParser); } diff --git a/src/modules/logcollector/tests/unit/logcollector_test.cpp b/src/modules/logcollector/tests/unit/logcollector_test.cpp index 8c8b6fda9a..29ec391d26 100644 --- a/src/modules/logcollector/tests/unit/logcollector_test.cpp +++ b/src/modules/logcollector/tests/unit/logcollector_test.cpp @@ -15,7 +15,8 @@ static bool IsISO8601(const std::string& datetime) { return std::regex_match(datetime, iso8601Regex); } -TEST(Logcollector, AddReader) { +TEST(Logcollector, AddReader) +{ auto logcollector = LogcollectorMock(); auto a = TempFile("/tmp/A.log"); auto fileReader = std::make_shared(logcollector, "/tmp/*.log", 500, 60000); //NOLINT @@ -26,7 +27,8 @@ TEST(Logcollector, AddReader) { logcollector.AddReader(fileReader); } -TEST(Logcollector, SetupFileReader) { +TEST(Logcollector, SetupFileReader) +{ auto constexpr CONFIG_RAW = R"( logcollector: localfiles: @@ -51,7 +53,8 @@ TEST(Logcollector, SetupFileReader) { ASSERT_NE(capturedReader2, nullptr); } -TEST(Logcollector, SendMessage) { +TEST(Logcollector, SendMessage) +{ PushMessageMock mock; LogcollectorMock logcollector; @@ -80,7 +83,8 @@ TEST(Logcollector, SendMessage) { ASSERT_EQ(capturedMessage.metaData, METADATA); } -TEST(Logcollector, SetupWECReader) { +TEST(Logcollector, SetupWECReader) +{ auto constexpr CONFIG_RAW = R"( logcollector: windows: From 400021c9f963b1f3c6930c9bd92bf72675c8dd65 Mon Sep 17 00:00:00 2001 From: Lucho Donda <95248059+LucioDonda@users.noreply.github.com> Date: Fri, 3 Jan 2025 14:16:37 -0800 Subject: [PATCH 04/21] feat: receiving old windows events, pending live WIP --- src/cmake/config.cmake | 4 +- src/common/config/include/config.h.in | 2 +- src/modules/logcollector/src/logcollector.cpp | 14 ++- .../logcollector/src/we_reader_win.cpp | 117 ++++++++++++------ .../logcollector/src/we_reader_win.hpp | 20 ++- 5 files changed, 98 insertions(+), 59 deletions(-) diff --git a/src/cmake/config.cmake b/src/cmake/config.cmake index 44acde4c2c..a194ce7077 100644 --- a/src/cmake/config.cmake +++ b/src/cmake/config.cmake @@ -34,9 +34,9 @@ set(DEFAULT_FILE_WAIT 500 CACHE STRING "Default Logcollector file reading interv set(DEFAULT_RELOAD_INTERVAL 60000 CACHE STRING "Default Logcollector reload interval (1m)") -set(DEFAULT_RECONNECT_TIME 5000 CACHE STRING "Default Logcollector reconnect time (5000ms)") +set(CHANNEL_REFRESH_INTERVAL 5000 CACHE STRING "Default Logcollector reconnect time (5000ms)") -set(DEFAULT_USE_BOOKMARK false CACHE BOOL "Default Logcollector windows bookmark enabled (false)") +set(DEFAULT_USE_BOOKMARK true CACHE BOOL "Default Logcollector windows bookmark enabled (false)") set(DEFAULT_INVENTORY_ENABLED true CACHE BOOL "Default inventory enabled") diff --git a/src/common/config/include/config.h.in b/src/common/config/include/config.h.in index 67c70d2379..78a2baaf0a 100644 --- a/src/common/config/include/config.h.in +++ b/src/common/config/include/config.h.in @@ -32,7 +32,7 @@ namespace config constexpr auto DEFAULT_FILE_WAIT = @DEFAULT_FILE_WAIT@; constexpr auto DEFAULT_RELOAD_INTERVAL = @DEFAULT_RELOAD_INTERVAL@; constexpr auto DEFAULT_LOCALFILES = "/var/log/auth.log"; - constexpr auto DEFAULT_RECONNECT_TIME = @DEFAULT_RECONNECT_TIME@; + constexpr auto CHANNEL_REFRESH_INTERVAL = @CHANNEL_REFRESH_INTERVAL@; constexpr auto DEFAULT_USE_BOOKMARK = @DEFAULT_USE_BOOKMARK@; } diff --git a/src/modules/logcollector/src/logcollector.cpp b/src/modules/logcollector/src/logcollector.cpp index 61b84a4c40..5e28fdb7c9 100644 --- a/src/modules/logcollector/src/logcollector.cpp +++ b/src/modules/logcollector/src/logcollector.cpp @@ -71,6 +71,9 @@ void Logcollector::Setup(std::shared_ptr configurationParser) @@ -89,9 +92,9 @@ void Logcollector::SetupFileReader(const std::shared_ptr configurationParser) { - const auto reconnectTime = configurationParser->GetConfig("logcollector", "reconnect-time").value_or(config::logcollector::DEFAULT_RECONNECT_TIME); + const auto refreshInterval = configurationParser->GetConfig("logcollector", "channel_refresh").value_or(config::logcollector::CHANNEL_REFRESH_INTERVAL); - const auto bookmarkEnabled = configurationParser->GetConfig("logcollector", "use-bookmark").value_or(config::logcollector::DEFAULT_USE_BOOKMARK); + const auto bookmarkEnabled = configurationParser->GetConfig("logcollector", "use_bookmark").value_or(config::logcollector::DEFAULT_USE_BOOKMARK); const auto windowsConfig = configurationParser->GetConfig>>("logcollector", "windows").value_or( std::vector> {}); @@ -101,11 +104,11 @@ void Logcollector::SetupWEReader(const std::shared_ptr(*this, channelsList, queriesList, reconnectTime, bookmarkEnabled)); + AddReader(std::make_shared(*this, channelsList, queriesList, refreshInterval, bookmarkEnabled)); } #endif @@ -146,7 +149,8 @@ void Logcollector::SendMessage(const std::string& location, const std::string& l auto message = Message(MessageType::STATELESS, data, m_moduleName, collectorType, metadata.dump()); m_pushMessage(message); - LogTrace("Message pushed: '{}':'{}'", location, log); + //TODO: undo + LogInfo("Message pushed: '{}':'{}'", location, log); } void Logcollector::AddReader(std::shared_ptr reader) diff --git a/src/modules/logcollector/src/we_reader_win.cpp b/src/modules/logcollector/src/we_reader_win.cpp index 00fd60531a..ce329c2caa 100644 --- a/src/modules/logcollector/src/we_reader_win.cpp +++ b/src/modules/logcollector/src/we_reader_win.cpp @@ -17,63 +17,80 @@ WindowsEventTracerReader::WindowsEventTracerReader(Logcollector &logcollector, Awaitable WindowsEventTracerReader::Run() { - // TODO: add lambda for loop break - while (true) - { - for (size_t i = 0; i < m_channelsList.size(); i++) - { - QueryEvents(m_channelsList.at(i), m_queriesList.at(i)); - } + //TODO: find a clearer way of connecting this with outside modules + bool keepRunning = true; + std::function shouldContinue = [&keepRunning]() { return keepRunning; }; - co_await m_logcollector.Wait(std::chrono::milliseconds(m_ChannelsRefreshInterval)); + for (size_t i = 0; i < m_channelsList.size(); i++) + { + m_logcollector.EnqueueTask(QueryEvents(m_channelsList.at(i), m_queriesList.at(i), shouldContinue)); } + co_return; } -Awaitable WindowsEventTracerReader::QueryEvents(const std::string channel, const std::string query) +Awaitable WindowsEventTracerReader::QueryEvents(const std::string channel, const std::string query, std::function shouldContinue) { - // Load bookmark if exists EVT_HANDLE bookmarkHandle = LoadBookmark(); //TODO: rework this casting - std::wstring wide_string_channel = std::wstring(channel.begin(), channel.end()); - std::wstring wide_string_query = std::wstring(query.begin(), query.end()); - EVT_HANDLE eventQueryHandle = EvtQuery( NULL, wide_string_channel.c_str(), wide_string_query.c_str(), - EvtQueryChannelPath | EvtQueryReverseDirection); + std::wstring wideStringChannel = std::wstring(channel.begin(), channel.end()); + std::wstring wideStringQuery = std::wstring(query.begin(), query.end()); + + EVT_HANDLE eventQueryHandle = EvtQuery( NULL, wideStringChannel.c_str(), wideStringQuery.c_str(), + EvtQueryChannelPath | EvtQueryForwardDirection); if (eventQueryHandle == NULL) { - // TODO: Logging fix - // LogError("Failed to query event log: {}", GetLastError()); - std::cerr << "Failed to query event log: " << GetLastError() << std::endl; + LogError("Failed to query event log: {}", std::to_string(GetLastError())); co_return; } + LogInfo("Querying events for {} channel with query: '{}'", channel, query); + EVT_HANDLE events[10]; DWORD eventCount = 0; - while (EvtNext(eventQueryHandle, 10, events, INFINITE, 0, &eventCount)) + while (shouldContinue()) { - for (DWORD i = 0; i < eventCount; ++i) + if (EvtNext(eventQueryHandle, 10, events, 1000, 0, &eventCount)) { - ProcessEvent(events[i], channel); - - if(m_bookmarkEnabled) + for (DWORD i = 0; i < eventCount; ++i) + { + ProcessEvent(events[i], channel); + if(m_bookmarkEnabled) + { + bookmarkHandle = CreateBookmark(events[i], bookmarkHandle); + SaveBookmark(bookmarkHandle); + } + EvtClose(events[i]); + } + } + else + { + DWORD error = GetLastError(); + if (error == ERROR_NO_MORE_ITEMS) + { + //TODO: delete or make it trace + LogInfo("No more events. Waiting for new events..."); + co_await m_logcollector.Wait(std::chrono::milliseconds(m_ChannelsRefreshInterval)); + } + else { - // Save the last event as a bookmark - bookmarkHandle = CreateBookmark(events[i], bookmarkHandle); - SaveBookmark(bookmarkHandle); + LogError("EvtNext failed with error: {}" , std::to_string(error)); + break; } - EvtClose(events[i]); } - //TODO: check using logcollec - co_await m_logcollector.Wait(std::chrono::milliseconds(m_eventsProcessingInterval)); } - EvtClose(eventQueryHandle); if (bookmarkHandle) { EvtClose(bookmarkHandle); } + + if (eventQueryHandle) + { + EvtClose(eventQueryHandle); + } } void WindowsEventTracerReader::ProcessEvent(EVT_HANDLE event, const std::string channel) @@ -88,11 +105,17 @@ void WindowsEventTracerReader::ProcessEvent(EVT_HANDLE event, const std::string std::vector buffer(bufferUsed); if (EvtRender(NULL, event, EvtRenderEventXml, bufferUsed, buffer.data(), &bufferUsed, &propertyCount)) { - // TODO: Logging fix - // LogTrace("Event: {}",buffer.data()); - std::wcout << L"Event: " << buffer.data() << std::endl; - const std::string log {}; - // m_logcollector.SendMessage(channel, log, m_collectorType); + std::string logString; + try + { + logString = WcharVecToString(buffer); + } + catch(const std::exception& e) + { + LogError("Cannot convert utf16 string: {}", e.what()); + return; + } + m_logcollector.SendMessage(channel, logString, m_collectorType); } } } @@ -102,9 +125,7 @@ EVT_HANDLE WindowsEventTracerReader::CreateBookmark(EVT_HANDLE event, EVT_HANDLE EVT_HANDLE bookmark = existingBookmark ? existingBookmark : EvtCreateBookmark(NULL); if (!EvtUpdateBookmark(bookmark, event)) { - // TODO: Logging fix - // LogError("Failed to update bookmark: {}", GetLastError()); - std::cerr << "Failed to update bookmark: " << GetLastError() << std::endl; + LogError("Failed to update bookmark: {}", std::to_string(GetLastError())); } return bookmark; } @@ -117,7 +138,7 @@ void WindowsEventTracerReader::SaveBookmark(EVT_HANDLE bookmarkHandle) std::vector buffer(bufferUsed); if (EvtRender(NULL, bookmarkHandle, EvtRenderBookmark, bufferUsed, buffer.data(), &bufferUsed, NULL)) { - std::wofstream file(bookmarkFile_); + std::wofstream file(m_bookmarkFile); if (file.is_open()) { file.write(buffer.data(), bufferUsed / sizeof(wchar_t)); @@ -130,17 +151,35 @@ EVT_HANDLE WindowsEventTracerReader::LoadBookmark() { if(m_bookmarkEnabled) { - std::wifstream file(bookmarkFile_); + std::wifstream file(m_bookmarkFile); if (file.is_open()) { std::wstringstream buffer; buffer << file.rdbuf(); std::wstring bookmarkXML = buffer.str(); file.close(); + //TODO: delete later + LogInfo("Creating bookark"); return EvtCreateBookmark(bookmarkXML.c_str()); } + else + { + LogError("Couldn't open bookmark file: {}", m_bookmarkFile); + } } return NULL; } +std::string WindowsEventTracerReader::WcharVecToString(std::vector& buffer) +{ + buffer.erase(std::remove(buffer.begin(), buffer.end(), L'\0'), buffer.end()); + std::wstring wstr(buffer.begin(), buffer.end()); + std::string result; + result.reserve(wstr.size() * sizeof(wchar_t)); + std::transform(wstr.begin(), wstr.end(), std::back_inserter(result), + [](wchar_t wc) -> char { + return static_cast(wc); + }); + return result; +} diff --git a/src/modules/logcollector/src/we_reader_win.hpp b/src/modules/logcollector/src/we_reader_win.hpp index 36984830ab..caa79b28c8 100644 --- a/src/modules/logcollector/src/we_reader_win.hpp +++ b/src/modules/logcollector/src/we_reader_win.hpp @@ -2,6 +2,7 @@ #pragma once #include +#include #include #include #include @@ -33,7 +34,7 @@ class WindowsEventTracerReader : public IReader WindowsEventTracerReader(Logcollector &logcollector, const std::vector channels, const std::vector queries, - const std::time_t reloadInterval, + const std::time_t channelRefreshInterval, bool bookmarkEnabled); /// @brief Runs the file reader @@ -41,7 +42,7 @@ class WindowsEventTracerReader : public IReader Awaitable Run() override; // Main function to execute the event query with bookmarks and filters - Awaitable QueryEvents(const std::string channel, const std::string query); + Awaitable QueryEvents(const std::string channel, const std::string query, std::function shouldContinue); private: // Process an individual event and print its XML representation @@ -56,26 +57,21 @@ class WindowsEventTracerReader : public IReader // Load bookmark from file (if it exists) EVT_HANDLE LoadBookmark(); + //TODO: doc + std::string WcharVecToString(std::vector& buffer); + std::vector m_channelsList; std::vector m_queriesList; - std::wstring bookmarkFile_; - - std::string m_channel; - - std::string m_query; + //TODO: change to configurable + std::string m_bookmarkFile = "bookmark.xml"; /// @brief std::time_t m_ChannelsRefreshInterval; - /// @brief - std::time_t m_eventsProcessingInterval; - bool m_bookmarkEnabled; - int m_availableEvents = 0; - const std::string m_collectorType = "eventchannel"; }; } // namespace logcollector From ed71e78e288fe06a148865d1f13f1cb4de071c6a Mon Sep 17 00:00:00 2001 From: Lucho Donda <95248059+LucioDonda@users.noreply.github.com> Date: Tue, 24 Dec 2024 12:19:21 -0800 Subject: [PATCH 05/21] feat: Initial changes for windoes event reader and fix file test for windows --- src/cmake/config.cmake | 4 +- src/common/config/include/config.h.in | 2 +- .../logcollector/include/logcollector.hpp | 6 + src/modules/logcollector/src/logcollector.cpp | 15 +- .../logcollector/src/we_reader_win.cpp | 176 ++---------------- .../logcollector/src/we_reader_win.hpp | 56 ++---- .../tests/unit/file_reader_test.cpp | 2 +- .../tests/unit/logcollector_mock.hpp | 3 +- .../tests/unit/logcollector_test.cpp | 3 +- 9 files changed, 49 insertions(+), 218 deletions(-) diff --git a/src/cmake/config.cmake b/src/cmake/config.cmake index a194ce7077..44acde4c2c 100644 --- a/src/cmake/config.cmake +++ b/src/cmake/config.cmake @@ -34,9 +34,9 @@ set(DEFAULT_FILE_WAIT 500 CACHE STRING "Default Logcollector file reading interv set(DEFAULT_RELOAD_INTERVAL 60000 CACHE STRING "Default Logcollector reload interval (1m)") -set(CHANNEL_REFRESH_INTERVAL 5000 CACHE STRING "Default Logcollector reconnect time (5000ms)") +set(DEFAULT_RECONNECT_TIME 5000 CACHE STRING "Default Logcollector reconnect time (5000ms)") -set(DEFAULT_USE_BOOKMARK true CACHE BOOL "Default Logcollector windows bookmark enabled (false)") +set(DEFAULT_USE_BOOKMARK false CACHE BOOL "Default Logcollector windows bookmark enabled (false)") set(DEFAULT_INVENTORY_ENABLED true CACHE BOOL "Default inventory enabled") diff --git a/src/common/config/include/config.h.in b/src/common/config/include/config.h.in index 78a2baaf0a..67c70d2379 100644 --- a/src/common/config/include/config.h.in +++ b/src/common/config/include/config.h.in @@ -32,7 +32,7 @@ namespace config constexpr auto DEFAULT_FILE_WAIT = @DEFAULT_FILE_WAIT@; constexpr auto DEFAULT_RELOAD_INTERVAL = @DEFAULT_RELOAD_INTERVAL@; constexpr auto DEFAULT_LOCALFILES = "/var/log/auth.log"; - constexpr auto CHANNEL_REFRESH_INTERVAL = @CHANNEL_REFRESH_INTERVAL@; + constexpr auto DEFAULT_RECONNECT_TIME = @DEFAULT_RECONNECT_TIME@; constexpr auto DEFAULT_USE_BOOKMARK = @DEFAULT_USE_BOOKMARK@; } diff --git a/src/modules/logcollector/include/logcollector.hpp b/src/modules/logcollector/include/logcollector.hpp index 786c5c151d..d162d8c09a 100644 --- a/src/modules/logcollector/include/logcollector.hpp +++ b/src/modules/logcollector/include/logcollector.hpp @@ -90,6 +90,12 @@ class Logcollector { void SetupWEReader(const std::shared_ptr configurationParser); #endif +#ifdef _WIN32 + /// @brief Sets up the Windows Event Channel reader + /// @param configurationParser Configuration parser + void SetupWEReader(const std::shared_ptr configurationParser); +#endif + private: /// @brief Module name const std::string m_moduleName = "logcollector"; diff --git a/src/modules/logcollector/src/logcollector.cpp b/src/modules/logcollector/src/logcollector.cpp index 5e28fdb7c9..3e6387a993 100644 --- a/src/modules/logcollector/src/logcollector.cpp +++ b/src/modules/logcollector/src/logcollector.cpp @@ -90,25 +90,24 @@ void Logcollector::SetupFileReader(const std::shared_ptr configurationParser) -{ - const auto refreshInterval = configurationParser->GetConfig("logcollector", "channel_refresh").value_or(config::logcollector::CHANNEL_REFRESH_INTERVAL); +void Logcollector::SetupWEReader(const std::shared_ptr configurationParser) { + const auto reconnectTime = configurationParser->GetConfig("logcollector", "reconnect-time").value_or(config::logcollector::DEFAULT_RECONNECT_TIME); - const auto bookmarkEnabled = configurationParser->GetConfig("logcollector", "use_bookmark").value_or(config::logcollector::DEFAULT_USE_BOOKMARK); + const auto bookmarkEnabled = configurationParser->GetConfig("logcollector", "use-bookmark").value_or(config::logcollector::DEFAULT_USE_BOOKMARK); const auto windowsConfig = configurationParser->GetConfig>>("logcollector", "windows").value_or( std::vector> {}); - std::vector channelsList; - std::vector queriesList; + std::list channelsList; + std::list queriesList; for (auto& entry : windowsConfig) { auto channel = entry.at("channel"); - auto query = entry.at("query"); channelsList.emplace_back(channel); + auto query = entry.at("query"); queriesList.emplace_back(query); } - AddReader(std::make_shared(*this, channelsList, queriesList, refreshInterval, bookmarkEnabled)); + AddReader(std::make_shared(*this, channelsList, queriesList, reconnectTime, bookmarkEnabled)); } #endif diff --git a/src/modules/logcollector/src/we_reader_win.cpp b/src/modules/logcollector/src/we_reader_win.cpp index ce329c2caa..b4aaeb309c 100644 --- a/src/modules/logcollector/src/we_reader_win.cpp +++ b/src/modules/logcollector/src/we_reader_win.cpp @@ -5,181 +5,35 @@ using namespace logcollector; WindowsEventTracerReader::WindowsEventTracerReader(Logcollector &logcollector, - const std::vector channels, - const std::vector queries, - const std::time_t channelRefreshInterval, + const std::list channels, + const std::list queries, + const std::time_t reloadInterval, bool bookmarkEnabled) : IReader(logcollector), m_channelsList(channels), m_queriesList(queries), - m_ChannelsRefreshInterval(channelRefreshInterval), + m_ReaderReloadInterval(reloadInterval), m_bookmarkEnabled(bookmarkEnabled) { } Awaitable WindowsEventTracerReader::Run() { - //TODO: find a clearer way of connecting this with outside modules - bool keepRunning = true; - std::function shouldContinue = [&keepRunning]() { return keepRunning; }; + // TODO: add lambda for loop break + while (true) { + m_logcollector.EnqueueTask(ReadEventChannel()); - for (size_t i = 0; i < m_channelsList.size(); i++) - { - m_logcollector.EnqueueTask(QueryEvents(m_channelsList.at(i), m_queriesList.at(i), shouldContinue)); + co_await m_logcollector.Wait( + std::chrono::milliseconds(m_ReaderReloadInterval)); } - co_return; } -Awaitable WindowsEventTracerReader::QueryEvents(const std::string channel, const std::string query, std::function shouldContinue) +Awaitable WindowsEventTracerReader::ReadEventChannel() { - EVT_HANDLE bookmarkHandle = LoadBookmark(); + for (auto eventChannel : m_channelsList) + { + const std::string log {}; + m_logcollector.SendMessage(eventChannel, log, m_collectorType); - //TODO: rework this casting - std::wstring wideStringChannel = std::wstring(channel.begin(), channel.end()); - std::wstring wideStringQuery = std::wstring(query.begin(), query.end()); - - EVT_HANDLE eventQueryHandle = EvtQuery( NULL, wideStringChannel.c_str(), wideStringQuery.c_str(), - EvtQueryChannelPath | EvtQueryForwardDirection); - - if (eventQueryHandle == NULL) - { - LogError("Failed to query event log: {}", std::to_string(GetLastError())); - co_return; - } - - LogInfo("Querying events for {} channel with query: '{}'", channel, query); - - EVT_HANDLE events[10]; - DWORD eventCount = 0; - - while (shouldContinue()) - { - if (EvtNext(eventQueryHandle, 10, events, 1000, 0, &eventCount)) - { - for (DWORD i = 0; i < eventCount; ++i) - { - ProcessEvent(events[i], channel); - if(m_bookmarkEnabled) - { - bookmarkHandle = CreateBookmark(events[i], bookmarkHandle); - SaveBookmark(bookmarkHandle); - } - EvtClose(events[i]); - } - } - else - { - DWORD error = GetLastError(); - if (error == ERROR_NO_MORE_ITEMS) - { - //TODO: delete or make it trace - LogInfo("No more events. Waiting for new events..."); - co_await m_logcollector.Wait(std::chrono::milliseconds(m_ChannelsRefreshInterval)); - } - else - { - LogError("EvtNext failed with error: {}" , std::to_string(error)); - break; - } - } - } - - if (bookmarkHandle) - { - EvtClose(bookmarkHandle); - } - - if (eventQueryHandle) - { - EvtClose(eventQueryHandle); - } -} - -void WindowsEventTracerReader::ProcessEvent(EVT_HANDLE event, const std::string channel) -{ - DWORD bufferUsed = 0; - DWORD propertyCount = 0; - - EvtRender(NULL, event, EvtRenderEventXml, 0, NULL, &bufferUsed, &propertyCount); - - if (bufferUsed > 0) - { - std::vector buffer(bufferUsed); - if (EvtRender(NULL, event, EvtRenderEventXml, bufferUsed, buffer.data(), &bufferUsed, &propertyCount)) - { - std::string logString; - try - { - logString = WcharVecToString(buffer); - } - catch(const std::exception& e) - { - LogError("Cannot convert utf16 string: {}", e.what()); - return; - } - m_logcollector.SendMessage(channel, logString, m_collectorType); - } + co_await m_logcollector.Wait(std::chrono::milliseconds(m_ReaderReloadInterval)); } } -EVT_HANDLE WindowsEventTracerReader::CreateBookmark(EVT_HANDLE event, EVT_HANDLE existingBookmark) -{ - EVT_HANDLE bookmark = existingBookmark ? existingBookmark : EvtCreateBookmark(NULL); - if (!EvtUpdateBookmark(bookmark, event)) - { - LogError("Failed to update bookmark: {}", std::to_string(GetLastError())); - } - return bookmark; -} - -void WindowsEventTracerReader::SaveBookmark(EVT_HANDLE bookmarkHandle) -{ - DWORD bufferUsed = 0; - - EvtRender(NULL, bookmarkHandle, EvtRenderBookmark, 0, NULL, &bufferUsed, NULL); - std::vector buffer(bufferUsed); - if (EvtRender(NULL, bookmarkHandle, EvtRenderBookmark, bufferUsed, buffer.data(), &bufferUsed, NULL)) - { - std::wofstream file(m_bookmarkFile); - if (file.is_open()) - { - file.write(buffer.data(), bufferUsed / sizeof(wchar_t)); - file.close(); - } - } -} - -EVT_HANDLE WindowsEventTracerReader::LoadBookmark() -{ - if(m_bookmarkEnabled) - { - std::wifstream file(m_bookmarkFile); - if (file.is_open()) - { - std::wstringstream buffer; - buffer << file.rdbuf(); - std::wstring bookmarkXML = buffer.str(); - file.close(); - //TODO: delete later - LogInfo("Creating bookark"); - return EvtCreateBookmark(bookmarkXML.c_str()); - } - else - { - LogError("Couldn't open bookmark file: {}", m_bookmarkFile); - } - } - - return NULL; -} - -std::string WindowsEventTracerReader::WcharVecToString(std::vector& buffer) -{ - buffer.erase(std::remove(buffer.begin(), buffer.end(), L'\0'), buffer.end()); - std::wstring wstr(buffer.begin(), buffer.end()); - std::string result; - result.reserve(wstr.size() * sizeof(wchar_t)); - std::transform(wstr.begin(), wstr.end(), std::back_inserter(result), - [](wchar_t wc) -> char { - return static_cast(wc); - }); - return result; -} diff --git a/src/modules/logcollector/src/we_reader_win.hpp b/src/modules/logcollector/src/we_reader_win.hpp index caa79b28c8..56793e5ac3 100644 --- a/src/modules/logcollector/src/we_reader_win.hpp +++ b/src/modules/logcollector/src/we_reader_win.hpp @@ -2,27 +2,16 @@ #pragma once #include -#include -#include -#include -#include -#include +#include #include -#include - -#include -#include -#include #include "reader.hpp" -#pragma comment(lib, "wevtapi.lib") - namespace logcollector { /// @brief Windows Event Tracer Reader class /// TODO: doc -class WindowsEventTracerReader : public IReader +class WindowsEventTracerReader : public IReader { public: /// @brief Constructor for the Windows Event Tracer Reader @@ -32,47 +21,32 @@ class WindowsEventTracerReader : public IReader /// @param reloadInterval /// @param bookmarkEnabled WindowsEventTracerReader(Logcollector &logcollector, - const std::vector channels, - const std::vector queries, - const std::time_t channelRefreshInterval, + const std::list channels, + const std::list queries, + const std::time_t reloadInterval, bool bookmarkEnabled); /// @brief Runs the file reader /// @return Awaitable result Awaitable Run() override; - // Main function to execute the event query with bookmarks and filters - Awaitable QueryEvents(const std::string channel, const std::string query, std::function shouldContinue); + // TODO: doc + Awaitable ReadEventChannel(); private: - // Process an individual event and print its XML representation - void ProcessEvent(EVT_HANDLE event, const std::string channel); - - // Create a bookmark for the current event - EVT_HANDLE CreateBookmark(EVT_HANDLE event, EVT_HANDLE existingBookmark = NULL); - - // Save bookmark to file - void SaveBookmark(EVT_HANDLE bookmarkHandle); - - // Load bookmark from file (if it exists) - EVT_HANDLE LoadBookmark(); - - //TODO: doc - std::string WcharVecToString(std::vector& buffer); - - std::vector m_channelsList; - - std::vector m_queriesList; - - //TODO: change to configurable - std::string m_bookmarkFile = "bookmark.xml"; - /// @brief - std::time_t m_ChannelsRefreshInterval; + std::time_t m_ReaderReloadInterval; + + std::list m_channelsList; + + std::list m_queriesList; bool m_bookmarkEnabled; + int m_availableEvents = 0; + const std::string m_collectorType = "eventchannel"; }; + } // namespace logcollector #endif diff --git a/src/modules/logcollector/tests/unit/file_reader_test.cpp b/src/modules/logcollector/tests/unit/file_reader_test.cpp index 026a65c9bc..bf3c98a0d3 100644 --- a/src/modules/logcollector/tests/unit/file_reader_test.cpp +++ b/src/modules/logcollector/tests/unit/file_reader_test.cpp @@ -13,7 +13,7 @@ using namespace logcollector; #ifdef _WIN32 static constexpr auto TMP_FILE_DIR = "C:\\Temp\\"; #else -static constexpr auto TMP_FILE_DIR = "/tmp/"; +static constexpr auto TMP_FILE_DIR = "/tmp/" #endif inline std::string getFullFileName(const std::string& filename) { diff --git a/src/modules/logcollector/tests/unit/logcollector_mock.hpp b/src/modules/logcollector/tests/unit/logcollector_mock.hpp index d3690fd19c..25b7192ee4 100644 --- a/src/modules/logcollector/tests/unit/logcollector_mock.hpp +++ b/src/modules/logcollector/tests/unit/logcollector_mock.hpp @@ -21,8 +21,7 @@ class LogcollectorMock : public Logcollector { Logcollector::SetupFileReader(configurationParser); } - void SetupWEReader(const std::shared_ptr configurationParser) - { + void SetupWEReader(const std::shared_ptr configurationParser) { Logcollector::SetupWEReader(configurationParser); } diff --git a/src/modules/logcollector/tests/unit/logcollector_test.cpp b/src/modules/logcollector/tests/unit/logcollector_test.cpp index 29ec391d26..8c5d1b4173 100644 --- a/src/modules/logcollector/tests/unit/logcollector_test.cpp +++ b/src/modules/logcollector/tests/unit/logcollector_test.cpp @@ -83,8 +83,7 @@ TEST(Logcollector, SendMessage) ASSERT_EQ(capturedMessage.metaData, METADATA); } -TEST(Logcollector, SetupWECReader) -{ +TEST(Logcollector, SetupWECReader) { auto constexpr CONFIG_RAW = R"( logcollector: windows: From 4b5e94789950c6bb3cbf02ebbc10e683638bf47f Mon Sep 17 00:00:00 2001 From: Lucho Donda <95248059+LucioDonda@users.noreply.github.com> Date: Tue, 24 Dec 2024 15:27:45 -0800 Subject: [PATCH 06/21] feat: base functionality for windows events monitoring --- src/modules/logcollector/src/logcollector.cpp | 4 +- .../logcollector/src/we_reader_win.cpp | 135 +++++++++++++++++- .../logcollector/src/we_reader_win.hpp | 37 ++++- 3 files changed, 162 insertions(+), 14 deletions(-) diff --git a/src/modules/logcollector/src/logcollector.cpp b/src/modules/logcollector/src/logcollector.cpp index 3e6387a993..d07ce691a1 100644 --- a/src/modules/logcollector/src/logcollector.cpp +++ b/src/modules/logcollector/src/logcollector.cpp @@ -98,8 +98,8 @@ void Logcollector::SetupWEReader(const std::shared_ptrGetConfig>>("logcollector", "windows").value_or( std::vector> {}); - std::list channelsList; - std::list queriesList; + std::vector channelsList; + std::vector queriesList; for (auto& entry : windowsConfig) { auto channel = entry.at("channel"); diff --git a/src/modules/logcollector/src/we_reader_win.cpp b/src/modules/logcollector/src/we_reader_win.cpp index b4aaeb309c..c0fd612186 100644 --- a/src/modules/logcollector/src/we_reader_win.cpp +++ b/src/modules/logcollector/src/we_reader_win.cpp @@ -5,8 +5,8 @@ using namespace logcollector; WindowsEventTracerReader::WindowsEventTracerReader(Logcollector &logcollector, - const std::list channels, - const std::list queries, + const std::vector channels, + const std::vector queries, const std::time_t reloadInterval, bool bookmarkEnabled) : IReader(logcollector), @@ -28,12 +28,133 @@ Awaitable WindowsEventTracerReader::Run() Awaitable WindowsEventTracerReader::ReadEventChannel() { - for (auto eventChannel : m_channelsList) - { - const std::string log {}; - m_logcollector.SendMessage(eventChannel, log, m_collectorType); - + for (size_t i = 0; i < m_channelsList.size(); i++) + { + QueryEvents(m_channelsList.at(i), m_queriesList.at(i)); co_await m_logcollector.Wait(std::chrono::milliseconds(m_ReaderReloadInterval)); } } +void WindowsEventTracerReader::QueryEvents(const std::string channel, const std::string query) +{ + // Load bookmark if exists + EVT_HANDLE bookmarkHandle = LoadBookmark(); + + //TODO: rework this casting + std::wstring wide_string_channel = std::wstring(channel.begin(), channel.end()); + std::wstring wide_string_query = std::wstring(query.begin(), query.end()); + EVT_HANDLE eventQueryHandle = EvtQuery( + NULL,// Local machine + wide_string_channel.c_str(), + wide_string_query.c_str(), + EvtQueryChannelPath | EvtQueryReverseDirection); + + if (eventQueryHandle == NULL) + { + // TODO: Logging fix + // LogError("Failed to query event log: {}", GetLastError()); + std::cerr << "Failed to query event log: " << GetLastError() << std::endl; + return; + } + + EVT_HANDLE events[10]; + DWORD eventCount = 0; + + while (EvtNext(eventQueryHandle, 10, events, INFINITE, 0, &eventCount)) + { + for (DWORD i = 0; i < eventCount; ++i) + { + ProcessEvent(events[i], channel); + + if(m_bookmarkEnabled) + { + // Save the last event as a bookmark + bookmarkHandle = CreateBookmark(events[i], bookmarkHandle); + SaveBookmark(bookmarkHandle); + } + EvtClose(events[i]); + } + } + + EvtClose(eventQueryHandle); + if (bookmarkHandle) + { + EvtClose(bookmarkHandle); + } +} + +void WindowsEventTracerReader::ProcessEvent(EVT_HANDLE event, const std::string channel) +{ + DWORD bufferUsed = 0; + DWORD propertyCount = 0; + + EvtRender( + NULL, + event, + EvtRenderEventXml, + 0, + NULL, + &bufferUsed, + &propertyCount); + + if (bufferUsed > 0) + { + std::vector buffer(bufferUsed); + if (EvtRender(NULL, event, EvtRenderEventXml, bufferUsed, buffer.data(), &bufferUsed, &propertyCount)) + { + // TODO: Logging fix + // LogTrace("Event: {}",buffer.data()); + std::wcout << L"Event: " << buffer.data() << std::endl; + // const std::string log {}; + // m_logcollector.SendMessage(channel, log, m_collectorType); + } + } +} + +EVT_HANDLE WindowsEventTracerReader::CreateBookmark(EVT_HANDLE event, EVT_HANDLE existingBookmark) +{ + EVT_HANDLE bookmark = existingBookmark ? existingBookmark : EvtCreateBookmark(NULL); + if (!EvtUpdateBookmark(bookmark, event)) + { + // TODO: Logging fix + // LogError("Failed to update bookmark: {}", GetLastError()); + std::cerr << "Failed to update bookmark: " << GetLastError() << std::endl; + } + return bookmark; +} + +void WindowsEventTracerReader::SaveBookmark(EVT_HANDLE bookmarkHandle) +{ + DWORD bufferUsed = 0; + + EvtRender(NULL, bookmarkHandle, EvtRenderBookmark, 0, NULL, &bufferUsed, NULL); + std::vector buffer(bufferUsed); + if (EvtRender(NULL, bookmarkHandle, EvtRenderBookmark, bufferUsed, buffer.data(), &bufferUsed, NULL)) + { + std::wofstream file(bookmarkFile_); + if (file.is_open()) + { + file.write(buffer.data(), bufferUsed / sizeof(wchar_t)); + file.close(); + } + } +} + +EVT_HANDLE WindowsEventTracerReader::LoadBookmark() +{ + if(m_bookmarkEnabled) + { + std::wifstream file(bookmarkFile_); + if (file.is_open()) + { + std::wstringstream buffer; + buffer << file.rdbuf(); + std::wstring bookmarkXML = buffer.str(); + file.close(); + return EvtCreateBookmark(bookmarkXML.c_str()); + } + } + + return NULL; +} + diff --git a/src/modules/logcollector/src/we_reader_win.hpp b/src/modules/logcollector/src/we_reader_win.hpp index 56793e5ac3..a60a4396bc 100644 --- a/src/modules/logcollector/src/we_reader_win.hpp +++ b/src/modules/logcollector/src/we_reader_win.hpp @@ -2,11 +2,21 @@ #pragma once #include -#include +#include +#include +#include +#include #include +#include + +#include +#include +#include #include "reader.hpp" +#pragma comment(lib, "wevtapi.lib") + namespace logcollector { /// @brief Windows Event Tracer Reader class @@ -21,8 +31,8 @@ class WindowsEventTracerReader : public IReader /// @param reloadInterval /// @param bookmarkEnabled WindowsEventTracerReader(Logcollector &logcollector, - const std::list channels, - const std::list queries, + const std::vector channels, + const std::vector queries, const std::time_t reloadInterval, bool bookmarkEnabled); @@ -33,13 +43,30 @@ class WindowsEventTracerReader : public IReader // TODO: doc Awaitable ReadEventChannel(); + // Main function to execute the event query with bookmarks and filters + void QueryEvents(const std::string channel, const std::string query); + private: + // Process an individual event and print its XML representation + void ProcessEvent(EVT_HANDLE event, const std::string channel); + + // Create a bookmark for the current event + EVT_HANDLE CreateBookmark(EVT_HANDLE event, EVT_HANDLE existingBookmark = NULL); + + // Save bookmark to file + void SaveBookmark(EVT_HANDLE bookmarkHandle); + + // Load bookmark from file (if it exists) + EVT_HANDLE LoadBookmark(); + + std::wstring bookmarkFile_; + /// @brief std::time_t m_ReaderReloadInterval; - std::list m_channelsList; + std::vector m_channelsList; - std::list m_queriesList; + std::vector m_queriesList; bool m_bookmarkEnabled; From 5ecc3daea6b900f51dd8987c8fd91c0e654817bc Mon Sep 17 00:00:00 2001 From: Lucho Donda <95248059+LucioDonda@users.noreply.github.com> Date: Fri, 27 Dec 2024 08:33:13 -0800 Subject: [PATCH 07/21] feat: additional iteration without tests --- src/modules/logcollector/src/logcollector.cpp | 3 +- .../logcollector/src/we_reader_win.cpp | 48 +++++++------------ .../logcollector/src/we_reader_win.hpp | 23 +++++---- .../tests/unit/file_reader_test.cpp | 2 +- .../tests/unit/logcollector_mock.hpp | 3 +- .../tests/unit/logcollector_test.cpp | 3 +- 6 files changed, 37 insertions(+), 45 deletions(-) diff --git a/src/modules/logcollector/src/logcollector.cpp b/src/modules/logcollector/src/logcollector.cpp index d07ce691a1..e8446150b7 100644 --- a/src/modules/logcollector/src/logcollector.cpp +++ b/src/modules/logcollector/src/logcollector.cpp @@ -90,7 +90,8 @@ void Logcollector::SetupFileReader(const std::shared_ptr configurationParser) { +void Logcollector::SetupWEReader(const std::shared_ptr configurationParser) +{ const auto reconnectTime = configurationParser->GetConfig("logcollector", "reconnect-time").value_or(config::logcollector::DEFAULT_RECONNECT_TIME); const auto bookmarkEnabled = configurationParser->GetConfig("logcollector", "use-bookmark").value_or(config::logcollector::DEFAULT_USE_BOOKMARK); diff --git a/src/modules/logcollector/src/we_reader_win.cpp b/src/modules/logcollector/src/we_reader_win.cpp index c0fd612186..00fd60531a 100644 --- a/src/modules/logcollector/src/we_reader_win.cpp +++ b/src/modules/logcollector/src/we_reader_win.cpp @@ -7,35 +7,29 @@ using namespace logcollector; WindowsEventTracerReader::WindowsEventTracerReader(Logcollector &logcollector, const std::vector channels, const std::vector queries, - const std::time_t reloadInterval, + const std::time_t channelRefreshInterval, bool bookmarkEnabled) : IReader(logcollector), m_channelsList(channels), m_queriesList(queries), - m_ReaderReloadInterval(reloadInterval), + m_ChannelsRefreshInterval(channelRefreshInterval), m_bookmarkEnabled(bookmarkEnabled) { } Awaitable WindowsEventTracerReader::Run() { // TODO: add lambda for loop break - while (true) { - m_logcollector.EnqueueTask(ReadEventChannel()); - - co_await m_logcollector.Wait( - std::chrono::milliseconds(m_ReaderReloadInterval)); - } -} - -Awaitable WindowsEventTracerReader::ReadEventChannel() -{ - for (size_t i = 0; i < m_channelsList.size(); i++) + while (true) { - QueryEvents(m_channelsList.at(i), m_queriesList.at(i)); - co_await m_logcollector.Wait(std::chrono::milliseconds(m_ReaderReloadInterval)); + for (size_t i = 0; i < m_channelsList.size(); i++) + { + QueryEvents(m_channelsList.at(i), m_queriesList.at(i)); + } + + co_await m_logcollector.Wait(std::chrono::milliseconds(m_ChannelsRefreshInterval)); } } -void WindowsEventTracerReader::QueryEvents(const std::string channel, const std::string query) +Awaitable WindowsEventTracerReader::QueryEvents(const std::string channel, const std::string query) { // Load bookmark if exists EVT_HANDLE bookmarkHandle = LoadBookmark(); @@ -43,10 +37,7 @@ void WindowsEventTracerReader::QueryEvents(const std::string channel, const std: //TODO: rework this casting std::wstring wide_string_channel = std::wstring(channel.begin(), channel.end()); std::wstring wide_string_query = std::wstring(query.begin(), query.end()); - EVT_HANDLE eventQueryHandle = EvtQuery( - NULL,// Local machine - wide_string_channel.c_str(), - wide_string_query.c_str(), + EVT_HANDLE eventQueryHandle = EvtQuery( NULL, wide_string_channel.c_str(), wide_string_query.c_str(), EvtQueryChannelPath | EvtQueryReverseDirection); if (eventQueryHandle == NULL) @@ -54,7 +45,7 @@ void WindowsEventTracerReader::QueryEvents(const std::string channel, const std: // TODO: Logging fix // LogError("Failed to query event log: {}", GetLastError()); std::cerr << "Failed to query event log: " << GetLastError() << std::endl; - return; + co_return; } EVT_HANDLE events[10]; @@ -74,6 +65,8 @@ void WindowsEventTracerReader::QueryEvents(const std::string channel, const std: } EvtClose(events[i]); } + //TODO: check using logcollec + co_await m_logcollector.Wait(std::chrono::milliseconds(m_eventsProcessingInterval)); } EvtClose(eventQueryHandle); @@ -88,14 +81,7 @@ void WindowsEventTracerReader::ProcessEvent(EVT_HANDLE event, const std::string DWORD bufferUsed = 0; DWORD propertyCount = 0; - EvtRender( - NULL, - event, - EvtRenderEventXml, - 0, - NULL, - &bufferUsed, - &propertyCount); + EvtRender(NULL, event, EvtRenderEventXml, 0, NULL, &bufferUsed, &propertyCount); if (bufferUsed > 0) { @@ -105,7 +91,7 @@ void WindowsEventTracerReader::ProcessEvent(EVT_HANDLE event, const std::string // TODO: Logging fix // LogTrace("Event: {}",buffer.data()); std::wcout << L"Event: " << buffer.data() << std::endl; - // const std::string log {}; + const std::string log {}; // m_logcollector.SendMessage(channel, log, m_collectorType); } } @@ -141,7 +127,7 @@ void WindowsEventTracerReader::SaveBookmark(EVT_HANDLE bookmarkHandle) } EVT_HANDLE WindowsEventTracerReader::LoadBookmark() -{ +{ if(m_bookmarkEnabled) { std::wifstream file(bookmarkFile_); diff --git a/src/modules/logcollector/src/we_reader_win.hpp b/src/modules/logcollector/src/we_reader_win.hpp index a60a4396bc..36984830ab 100644 --- a/src/modules/logcollector/src/we_reader_win.hpp +++ b/src/modules/logcollector/src/we_reader_win.hpp @@ -21,7 +21,7 @@ namespace logcollector { /// @brief Windows Event Tracer Reader class /// TODO: doc -class WindowsEventTracerReader : public IReader +class WindowsEventTracerReader : public IReader { public: /// @brief Constructor for the Windows Event Tracer Reader @@ -40,11 +40,8 @@ class WindowsEventTracerReader : public IReader /// @return Awaitable result Awaitable Run() override; - // TODO: doc - Awaitable ReadEventChannel(); - // Main function to execute the event query with bookmarks and filters - void QueryEvents(const std::string channel, const std::string query); + Awaitable QueryEvents(const std::string channel, const std::string query); private: // Process an individual event and print its XML representation @@ -59,14 +56,21 @@ class WindowsEventTracerReader : public IReader // Load bookmark from file (if it exists) EVT_HANDLE LoadBookmark(); + std::vector m_channelsList; + + std::vector m_queriesList; + std::wstring bookmarkFile_; + std::string m_channel; + + std::string m_query; + /// @brief - std::time_t m_ReaderReloadInterval; + std::time_t m_ChannelsRefreshInterval; - std::vector m_channelsList; - - std::vector m_queriesList; + /// @brief + std::time_t m_eventsProcessingInterval; bool m_bookmarkEnabled; @@ -74,6 +78,5 @@ class WindowsEventTracerReader : public IReader const std::string m_collectorType = "eventchannel"; }; - } // namespace logcollector #endif diff --git a/src/modules/logcollector/tests/unit/file_reader_test.cpp b/src/modules/logcollector/tests/unit/file_reader_test.cpp index bf3c98a0d3..026a65c9bc 100644 --- a/src/modules/logcollector/tests/unit/file_reader_test.cpp +++ b/src/modules/logcollector/tests/unit/file_reader_test.cpp @@ -13,7 +13,7 @@ using namespace logcollector; #ifdef _WIN32 static constexpr auto TMP_FILE_DIR = "C:\\Temp\\"; #else -static constexpr auto TMP_FILE_DIR = "/tmp/" +static constexpr auto TMP_FILE_DIR = "/tmp/"; #endif inline std::string getFullFileName(const std::string& filename) { diff --git a/src/modules/logcollector/tests/unit/logcollector_mock.hpp b/src/modules/logcollector/tests/unit/logcollector_mock.hpp index 25b7192ee4..d3690fd19c 100644 --- a/src/modules/logcollector/tests/unit/logcollector_mock.hpp +++ b/src/modules/logcollector/tests/unit/logcollector_mock.hpp @@ -21,7 +21,8 @@ class LogcollectorMock : public Logcollector { Logcollector::SetupFileReader(configurationParser); } - void SetupWEReader(const std::shared_ptr configurationParser) { + void SetupWEReader(const std::shared_ptr configurationParser) + { Logcollector::SetupWEReader(configurationParser); } diff --git a/src/modules/logcollector/tests/unit/logcollector_test.cpp b/src/modules/logcollector/tests/unit/logcollector_test.cpp index 8c5d1b4173..29ec391d26 100644 --- a/src/modules/logcollector/tests/unit/logcollector_test.cpp +++ b/src/modules/logcollector/tests/unit/logcollector_test.cpp @@ -83,7 +83,8 @@ TEST(Logcollector, SendMessage) ASSERT_EQ(capturedMessage.metaData, METADATA); } -TEST(Logcollector, SetupWECReader) { +TEST(Logcollector, SetupWECReader) +{ auto constexpr CONFIG_RAW = R"( logcollector: windows: From 9e2cf96c3e89f7a697ad54f687ac549efea7fda1 Mon Sep 17 00:00:00 2001 From: Lucho Donda <95248059+LucioDonda@users.noreply.github.com> Date: Fri, 3 Jan 2025 14:16:37 -0800 Subject: [PATCH 08/21] feat: receiving old windows events, pending live WIP --- src/cmake/config.cmake | 4 +- src/common/config/include/config.h.in | 2 +- src/modules/logcollector/src/logcollector.cpp | 8 +- .../logcollector/src/we_reader_win.cpp | 117 ++++++++++++------ .../logcollector/src/we_reader_win.hpp | 20 ++- 5 files changed, 93 insertions(+), 58 deletions(-) diff --git a/src/cmake/config.cmake b/src/cmake/config.cmake index 44acde4c2c..a194ce7077 100644 --- a/src/cmake/config.cmake +++ b/src/cmake/config.cmake @@ -34,9 +34,9 @@ set(DEFAULT_FILE_WAIT 500 CACHE STRING "Default Logcollector file reading interv set(DEFAULT_RELOAD_INTERVAL 60000 CACHE STRING "Default Logcollector reload interval (1m)") -set(DEFAULT_RECONNECT_TIME 5000 CACHE STRING "Default Logcollector reconnect time (5000ms)") +set(CHANNEL_REFRESH_INTERVAL 5000 CACHE STRING "Default Logcollector reconnect time (5000ms)") -set(DEFAULT_USE_BOOKMARK false CACHE BOOL "Default Logcollector windows bookmark enabled (false)") +set(DEFAULT_USE_BOOKMARK true CACHE BOOL "Default Logcollector windows bookmark enabled (false)") set(DEFAULT_INVENTORY_ENABLED true CACHE BOOL "Default inventory enabled") diff --git a/src/common/config/include/config.h.in b/src/common/config/include/config.h.in index 67c70d2379..78a2baaf0a 100644 --- a/src/common/config/include/config.h.in +++ b/src/common/config/include/config.h.in @@ -32,7 +32,7 @@ namespace config constexpr auto DEFAULT_FILE_WAIT = @DEFAULT_FILE_WAIT@; constexpr auto DEFAULT_RELOAD_INTERVAL = @DEFAULT_RELOAD_INTERVAL@; constexpr auto DEFAULT_LOCALFILES = "/var/log/auth.log"; - constexpr auto DEFAULT_RECONNECT_TIME = @DEFAULT_RECONNECT_TIME@; + constexpr auto CHANNEL_REFRESH_INTERVAL = @CHANNEL_REFRESH_INTERVAL@; constexpr auto DEFAULT_USE_BOOKMARK = @DEFAULT_USE_BOOKMARK@; } diff --git a/src/modules/logcollector/src/logcollector.cpp b/src/modules/logcollector/src/logcollector.cpp index e8446150b7..5e28fdb7c9 100644 --- a/src/modules/logcollector/src/logcollector.cpp +++ b/src/modules/logcollector/src/logcollector.cpp @@ -92,9 +92,9 @@ void Logcollector::SetupFileReader(const std::shared_ptr configurationParser) { - const auto reconnectTime = configurationParser->GetConfig("logcollector", "reconnect-time").value_or(config::logcollector::DEFAULT_RECONNECT_TIME); + const auto refreshInterval = configurationParser->GetConfig("logcollector", "channel_refresh").value_or(config::logcollector::CHANNEL_REFRESH_INTERVAL); - const auto bookmarkEnabled = configurationParser->GetConfig("logcollector", "use-bookmark").value_or(config::logcollector::DEFAULT_USE_BOOKMARK); + const auto bookmarkEnabled = configurationParser->GetConfig("logcollector", "use_bookmark").value_or(config::logcollector::DEFAULT_USE_BOOKMARK); const auto windowsConfig = configurationParser->GetConfig>>("logcollector", "windows").value_or( std::vector> {}); @@ -104,11 +104,11 @@ void Logcollector::SetupWEReader(const std::shared_ptr(*this, channelsList, queriesList, reconnectTime, bookmarkEnabled)); + AddReader(std::make_shared(*this, channelsList, queriesList, refreshInterval, bookmarkEnabled)); } #endif diff --git a/src/modules/logcollector/src/we_reader_win.cpp b/src/modules/logcollector/src/we_reader_win.cpp index 00fd60531a..ce329c2caa 100644 --- a/src/modules/logcollector/src/we_reader_win.cpp +++ b/src/modules/logcollector/src/we_reader_win.cpp @@ -17,63 +17,80 @@ WindowsEventTracerReader::WindowsEventTracerReader(Logcollector &logcollector, Awaitable WindowsEventTracerReader::Run() { - // TODO: add lambda for loop break - while (true) - { - for (size_t i = 0; i < m_channelsList.size(); i++) - { - QueryEvents(m_channelsList.at(i), m_queriesList.at(i)); - } + //TODO: find a clearer way of connecting this with outside modules + bool keepRunning = true; + std::function shouldContinue = [&keepRunning]() { return keepRunning; }; - co_await m_logcollector.Wait(std::chrono::milliseconds(m_ChannelsRefreshInterval)); + for (size_t i = 0; i < m_channelsList.size(); i++) + { + m_logcollector.EnqueueTask(QueryEvents(m_channelsList.at(i), m_queriesList.at(i), shouldContinue)); } + co_return; } -Awaitable WindowsEventTracerReader::QueryEvents(const std::string channel, const std::string query) +Awaitable WindowsEventTracerReader::QueryEvents(const std::string channel, const std::string query, std::function shouldContinue) { - // Load bookmark if exists EVT_HANDLE bookmarkHandle = LoadBookmark(); //TODO: rework this casting - std::wstring wide_string_channel = std::wstring(channel.begin(), channel.end()); - std::wstring wide_string_query = std::wstring(query.begin(), query.end()); - EVT_HANDLE eventQueryHandle = EvtQuery( NULL, wide_string_channel.c_str(), wide_string_query.c_str(), - EvtQueryChannelPath | EvtQueryReverseDirection); + std::wstring wideStringChannel = std::wstring(channel.begin(), channel.end()); + std::wstring wideStringQuery = std::wstring(query.begin(), query.end()); + + EVT_HANDLE eventQueryHandle = EvtQuery( NULL, wideStringChannel.c_str(), wideStringQuery.c_str(), + EvtQueryChannelPath | EvtQueryForwardDirection); if (eventQueryHandle == NULL) { - // TODO: Logging fix - // LogError("Failed to query event log: {}", GetLastError()); - std::cerr << "Failed to query event log: " << GetLastError() << std::endl; + LogError("Failed to query event log: {}", std::to_string(GetLastError())); co_return; } + LogInfo("Querying events for {} channel with query: '{}'", channel, query); + EVT_HANDLE events[10]; DWORD eventCount = 0; - while (EvtNext(eventQueryHandle, 10, events, INFINITE, 0, &eventCount)) + while (shouldContinue()) { - for (DWORD i = 0; i < eventCount; ++i) + if (EvtNext(eventQueryHandle, 10, events, 1000, 0, &eventCount)) { - ProcessEvent(events[i], channel); - - if(m_bookmarkEnabled) + for (DWORD i = 0; i < eventCount; ++i) + { + ProcessEvent(events[i], channel); + if(m_bookmarkEnabled) + { + bookmarkHandle = CreateBookmark(events[i], bookmarkHandle); + SaveBookmark(bookmarkHandle); + } + EvtClose(events[i]); + } + } + else + { + DWORD error = GetLastError(); + if (error == ERROR_NO_MORE_ITEMS) + { + //TODO: delete or make it trace + LogInfo("No more events. Waiting for new events..."); + co_await m_logcollector.Wait(std::chrono::milliseconds(m_ChannelsRefreshInterval)); + } + else { - // Save the last event as a bookmark - bookmarkHandle = CreateBookmark(events[i], bookmarkHandle); - SaveBookmark(bookmarkHandle); + LogError("EvtNext failed with error: {}" , std::to_string(error)); + break; } - EvtClose(events[i]); } - //TODO: check using logcollec - co_await m_logcollector.Wait(std::chrono::milliseconds(m_eventsProcessingInterval)); } - EvtClose(eventQueryHandle); if (bookmarkHandle) { EvtClose(bookmarkHandle); } + + if (eventQueryHandle) + { + EvtClose(eventQueryHandle); + } } void WindowsEventTracerReader::ProcessEvent(EVT_HANDLE event, const std::string channel) @@ -88,11 +105,17 @@ void WindowsEventTracerReader::ProcessEvent(EVT_HANDLE event, const std::string std::vector buffer(bufferUsed); if (EvtRender(NULL, event, EvtRenderEventXml, bufferUsed, buffer.data(), &bufferUsed, &propertyCount)) { - // TODO: Logging fix - // LogTrace("Event: {}",buffer.data()); - std::wcout << L"Event: " << buffer.data() << std::endl; - const std::string log {}; - // m_logcollector.SendMessage(channel, log, m_collectorType); + std::string logString; + try + { + logString = WcharVecToString(buffer); + } + catch(const std::exception& e) + { + LogError("Cannot convert utf16 string: {}", e.what()); + return; + } + m_logcollector.SendMessage(channel, logString, m_collectorType); } } } @@ -102,9 +125,7 @@ EVT_HANDLE WindowsEventTracerReader::CreateBookmark(EVT_HANDLE event, EVT_HANDLE EVT_HANDLE bookmark = existingBookmark ? existingBookmark : EvtCreateBookmark(NULL); if (!EvtUpdateBookmark(bookmark, event)) { - // TODO: Logging fix - // LogError("Failed to update bookmark: {}", GetLastError()); - std::cerr << "Failed to update bookmark: " << GetLastError() << std::endl; + LogError("Failed to update bookmark: {}", std::to_string(GetLastError())); } return bookmark; } @@ -117,7 +138,7 @@ void WindowsEventTracerReader::SaveBookmark(EVT_HANDLE bookmarkHandle) std::vector buffer(bufferUsed); if (EvtRender(NULL, bookmarkHandle, EvtRenderBookmark, bufferUsed, buffer.data(), &bufferUsed, NULL)) { - std::wofstream file(bookmarkFile_); + std::wofstream file(m_bookmarkFile); if (file.is_open()) { file.write(buffer.data(), bufferUsed / sizeof(wchar_t)); @@ -130,17 +151,35 @@ EVT_HANDLE WindowsEventTracerReader::LoadBookmark() { if(m_bookmarkEnabled) { - std::wifstream file(bookmarkFile_); + std::wifstream file(m_bookmarkFile); if (file.is_open()) { std::wstringstream buffer; buffer << file.rdbuf(); std::wstring bookmarkXML = buffer.str(); file.close(); + //TODO: delete later + LogInfo("Creating bookark"); return EvtCreateBookmark(bookmarkXML.c_str()); } + else + { + LogError("Couldn't open bookmark file: {}", m_bookmarkFile); + } } return NULL; } +std::string WindowsEventTracerReader::WcharVecToString(std::vector& buffer) +{ + buffer.erase(std::remove(buffer.begin(), buffer.end(), L'\0'), buffer.end()); + std::wstring wstr(buffer.begin(), buffer.end()); + std::string result; + result.reserve(wstr.size() * sizeof(wchar_t)); + std::transform(wstr.begin(), wstr.end(), std::back_inserter(result), + [](wchar_t wc) -> char { + return static_cast(wc); + }); + return result; +} diff --git a/src/modules/logcollector/src/we_reader_win.hpp b/src/modules/logcollector/src/we_reader_win.hpp index 36984830ab..caa79b28c8 100644 --- a/src/modules/logcollector/src/we_reader_win.hpp +++ b/src/modules/logcollector/src/we_reader_win.hpp @@ -2,6 +2,7 @@ #pragma once #include +#include #include #include #include @@ -33,7 +34,7 @@ class WindowsEventTracerReader : public IReader WindowsEventTracerReader(Logcollector &logcollector, const std::vector channels, const std::vector queries, - const std::time_t reloadInterval, + const std::time_t channelRefreshInterval, bool bookmarkEnabled); /// @brief Runs the file reader @@ -41,7 +42,7 @@ class WindowsEventTracerReader : public IReader Awaitable Run() override; // Main function to execute the event query with bookmarks and filters - Awaitable QueryEvents(const std::string channel, const std::string query); + Awaitable QueryEvents(const std::string channel, const std::string query, std::function shouldContinue); private: // Process an individual event and print its XML representation @@ -56,26 +57,21 @@ class WindowsEventTracerReader : public IReader // Load bookmark from file (if it exists) EVT_HANDLE LoadBookmark(); + //TODO: doc + std::string WcharVecToString(std::vector& buffer); + std::vector m_channelsList; std::vector m_queriesList; - std::wstring bookmarkFile_; - - std::string m_channel; - - std::string m_query; + //TODO: change to configurable + std::string m_bookmarkFile = "bookmark.xml"; /// @brief std::time_t m_ChannelsRefreshInterval; - /// @brief - std::time_t m_eventsProcessingInterval; - bool m_bookmarkEnabled; - int m_availableEvents = 0; - const std::string m_collectorType = "eventchannel"; }; } // namespace logcollector From 27140453a8a2af9b5628a0aba7b5cd259ee36424 Mon Sep 17 00:00:00 2001 From: LucioDonda Date: Mon, 6 Jan 2025 11:55:19 +0000 Subject: [PATCH 09/21] fix: rebased and windows compile errors --- .../tests/unit/file_reader_test.cpp | 22 +++++++++---------- .../tests/unit/logcollector_mock.hpp | 4 ++-- .../tests/unit/logcollector_test.cpp | 2 ++ .../tests/unit/wec_reader_test.cpp | 1 - 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/modules/logcollector/tests/unit/file_reader_test.cpp b/src/modules/logcollector/tests/unit/file_reader_test.cpp index 026a65c9bc..de78ab72cb 100644 --- a/src/modules/logcollector/tests/unit/file_reader_test.cpp +++ b/src/modules/logcollector/tests/unit/file_reader_test.cpp @@ -16,7 +16,7 @@ static constexpr auto TMP_FILE_DIR = "C:\\Temp\\"; static constexpr auto TMP_FILE_DIR = "/tmp/"; #endif -inline std::string getFullFileName(const std::string& filename) { +inline std::string GetFullFileName(const std::string& filename) { //TODO: move to setup stage of test only for windows std::filesystem::create_directories(TMP_FILE_DIR); return TMP_FILE_DIR + filename; @@ -63,8 +63,8 @@ TEST(Localfile, OpenError) TEST(Localfile, Rotated) { - auto fileA = TempFile(getFullFileName("A.log"), "Hello World"); - auto lf = Localfile(getFullFileName("A.log")); + auto fileA = TempFile(GetFullFileName("A.log"), "Hello World"); + auto lf = Localfile(GetFullFileName("A.log")); lf.SeekEnd(); ASSERT_FALSE(lf.Rotated()); @@ -96,19 +96,19 @@ TEST(FileReader, Reload) { spdlog::default_logger()->sinks().clear(); MockCallback mockCallback; - EXPECT_CALL(mockCallback, Call(getFullFileName("A.log"))).Times(1); - EXPECT_CALL(mockCallback, Call(getFullFileName("B.log"))).Times(1); - EXPECT_CALL(mockCallback, Call(getFullFileName("C.log"))).Times(1); - EXPECT_CALL(mockCallback, Call(getFullFileName("D.log"))).Times(1); + EXPECT_CALL(mockCallback, Call(GetFullFileName("A.log"))).Times(1); + EXPECT_CALL(mockCallback, Call(GetFullFileName("B.log"))).Times(1); + EXPECT_CALL(mockCallback, Call(GetFullFileName("C.log"))).Times(1); + EXPECT_CALL(mockCallback, Call(GetFullFileName("D.log"))).Times(1); - auto a = TempFile(getFullFileName("A.log")); - auto b = TempFile(getFullFileName("B.log")); - auto c = TempFile(getFullFileName("C.log")); + auto a = TempFile(GetFullFileName("A.log")); + auto b = TempFile(GetFullFileName("B.log")); + auto c = TempFile(GetFullFileName("C.log")); auto regex = TMP_FILE_DIR + std::string("*.log"); FileReader reader(Logcollector::Instance(), regex, 500, 60000); //NOLINT reader.Reload([&](Localfile& lf) { mockCallback.Call(lf.Filename()); }); - auto d = TempFile(getFullFileName("D.log")); + auto d = TempFile(GetFullFileName("D.log")); reader.Reload([&](Localfile& lf) { mockCallback.Call(lf.Filename()); }); } diff --git a/src/modules/logcollector/tests/unit/logcollector_mock.hpp b/src/modules/logcollector/tests/unit/logcollector_mock.hpp index d3690fd19c..c4d0a4df7c 100644 --- a/src/modules/logcollector/tests/unit/logcollector_mock.hpp +++ b/src/modules/logcollector/tests/unit/logcollector_mock.hpp @@ -20,12 +20,12 @@ class LogcollectorMock : public Logcollector { { Logcollector::SetupFileReader(configurationParser); } - +#ifdef _WIN32 void SetupWEReader(const std::shared_ptr configurationParser) { Logcollector::SetupWEReader(configurationParser); } - +#endif MOCK_METHOD(void, AddReader, (std::shared_ptr reader), (override)); MOCK_METHOD(void, EnqueueTask, (Awaitable task), (override)); }; diff --git a/src/modules/logcollector/tests/unit/logcollector_test.cpp b/src/modules/logcollector/tests/unit/logcollector_test.cpp index 29ec391d26..2a1f7f6c97 100644 --- a/src/modules/logcollector/tests/unit/logcollector_test.cpp +++ b/src/modules/logcollector/tests/unit/logcollector_test.cpp @@ -83,6 +83,7 @@ TEST(Logcollector, SendMessage) ASSERT_EQ(capturedMessage.metaData, METADATA); } +#ifdef _WIN32 TEST(Logcollector, SetupWECReader) { auto constexpr CONFIG_RAW = R"( @@ -105,6 +106,7 @@ TEST(Logcollector, SetupWECReader) ASSERT_NE(capturedReader1, nullptr); } +#endif int main(int argc, char** argv) { diff --git a/src/modules/logcollector/tests/unit/wec_reader_test.cpp b/src/modules/logcollector/tests/unit/wec_reader_test.cpp index ae66a48cbb..567b8b8f14 100644 --- a/src/modules/logcollector/tests/unit/wec_reader_test.cpp +++ b/src/modules/logcollector/tests/unit/wec_reader_test.cpp @@ -4,7 +4,6 @@ #include #include -#include "queue_mock.hpp" #include "logcollector_mock.hpp" using namespace logcollector; From b2be165e1e1dfb8851110977d89d8300d6c85fec Mon Sep 17 00:00:00 2001 From: Lucho Donda <95248059+LucioDonda@users.noreply.github.com> Date: Tue, 7 Jan 2025 09:01:45 -0800 Subject: [PATCH 10/21] feat: past and live events received accordingly --- src/modules/logcollector/src/logcollector.cpp | 413 +++++++++--------- .../logcollector/src/we_reader_win.cpp | 137 +++--- .../logcollector/src/we_reader_win.hpp | 23 +- 3 files changed, 301 insertions(+), 272 deletions(-) diff --git a/src/modules/logcollector/src/logcollector.cpp b/src/modules/logcollector/src/logcollector.cpp index 5e28fdb7c9..07e71dd153 100644 --- a/src/modules/logcollector/src/logcollector.cpp +++ b/src/modules/logcollector/src/logcollector.cpp @@ -1,209 +1,204 @@ -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "file_reader.hpp" - -#ifdef _WIN32 -#include "we_reader_win.hpp" -#endif -using namespace logcollector; - -namespace logcollector { - constexpr int ACTIVE_READERS_WAIT_MS = 10; -} - -void Logcollector::Start() -{ - if (!m_enabled) { - LogInfo("Logcollector module is disabled."); - return; - } - - LogInfo("Logcollector module started."); - m_ioContext.run(); -} - -void Logcollector::EnqueueTask(boost::asio::awaitable task) { - // NOLINTBEGIN(cppcoreguidelines-avoid-capturing-lambda-coroutines) - boost::asio::co_spawn( - m_ioContext, - [task = std::move(task), this]() mutable -> boost::asio::awaitable - { - try - { - m_activeReaders++; - co_await std::move(task); - } - catch (const std::exception& e) - { - LogError("Logcollector coroutine task exited with an exception: {}", e.what()); - } - m_activeReaders--; - }, - boost::asio::detached); - // NOLINTEND(cppcoreguidelines-avoid-capturing-lambda-coroutines) -} - -void Logcollector::Setup(std::shared_ptr configurationParser) -{ - if (!configurationParser) { - LogError("Invalid Configuration Parser passed to setup, module set to disabled."); - m_enabled = false; - return; - } - - m_enabled = configurationParser->GetConfig("logcollector", "enabled").value_or(config::logcollector::DEFAULT_ENABLED); - - if (m_ioContext.stopped()) - { - m_ioContext.restart(); - } - - SetupFileReader(configurationParser); -#ifdef _WIN32 - SetupWEReader(configurationParser); -#endif -} - -void Logcollector::SetupFileReader(const std::shared_ptr configurationParser) -{ - auto fileWait = configurationParser->GetConfig("logcollector", "file_wait").value_or(config::logcollector::DEFAULT_FILE_WAIT); - - auto reloadInterval = configurationParser->GetConfig("logcollector", "reload_interval").value_or(config::logcollector::DEFAULT_RELOAD_INTERVAL); - - auto localfiles = configurationParser->GetConfig>("logcollector", "localfiles").value_or(std::vector({config::logcollector::DEFAULT_LOCALFILES})); - - for (auto& lf : localfiles) { - AddReader(std::make_shared(*this, lf, fileWait, reloadInterval)); - } -} - -#ifdef _WIN32 -void Logcollector::SetupWEReader(const std::shared_ptr configurationParser) -{ - const auto refreshInterval = configurationParser->GetConfig("logcollector", "channel_refresh").value_or(config::logcollector::CHANNEL_REFRESH_INTERVAL); - - const auto bookmarkEnabled = configurationParser->GetConfig("logcollector", "use_bookmark").value_or(config::logcollector::DEFAULT_USE_BOOKMARK); - - const auto windowsConfig = configurationParser->GetConfig>>("logcollector", "windows").value_or( - std::vector> {}); - - std::vector channelsList; - std::vector queriesList; - for (auto& entry : windowsConfig) - { - auto channel = entry.at("channel"); - auto query = entry.at("query"); - channelsList.emplace_back(channel); - queriesList.emplace_back(query); - } - AddReader(std::make_shared(*this, channelsList, queriesList, refreshInterval, bookmarkEnabled)); -} -#endif - -void Logcollector::Stop() { - CleanAllReaders(); - m_ioContext.stop(); - LogInfo("Logcollector module stopped."); -} - -// NOLINTBEGIN(performance-unnecessary-value-param) -Co_CommandExecutionResult Logcollector::ExecuteCommand(const std::string command, - [[maybe_unused]] const nlohmann::json parameters) - { - LogInfo("Logcollector command: ", command); - co_return module_command::CommandExecutionResult{module_command::Status::SUCCESS, "OK"}; -} -// NOLINTEND(performance-unnecessary-value-param) - -void Logcollector::SetPushMessageFunction(const std::function& pushMessage) { - m_pushMessage = pushMessage; -} - -void Logcollector::SendMessage(const std::string& location, const std::string& log, const std::string& collectorType) -{ - auto metadata = nlohmann::json::object(); - auto data = nlohmann::json::object(); - - metadata["module"] = m_moduleName; - metadata["type"] = collectorType; - - data["log"]["file"]["path"] = location; - data["tags"] = nlohmann::json::array({"mvp"}); - data["event"]["original"] = log; - data["event"]["created"] = Utils::getCurrentISO8601(); - data["event"]["module"] = m_moduleName; - data["event"]["provider"] = "syslog"; - - auto message = Message(MessageType::STATELESS, data, m_moduleName, collectorType, metadata.dump()); - m_pushMessage(message); - - //TODO: undo - LogInfo("Message pushed: '{}':'{}'", location, log); -} - -void Logcollector::AddReader(std::shared_ptr reader) -{ - //TODO: do we need m_readers ? - m_readers.push_back(reader); - EnqueueTask(reader->Run()); -} - -void Logcollector::CleanAllReaders() { - for (const auto &reader : m_readers) { - reader->Stop(); - } - - { - std::lock_guard lock(m_timersMutex); - for (const auto &timer : m_timers) { - timer->cancel(); - } - } - - while (m_activeReaders) { - std::this_thread::sleep_for(std::chrono::milliseconds(ACTIVE_READERS_WAIT_MS)); - } - m_readers.clear(); -} - -Awaitable Logcollector::Wait(std::chrono::milliseconds ms) { - if (!m_ioContext.stopped()) { - auto timer = boost::asio::steady_timer(m_ioContext, ms); - { - std::lock_guard lock(m_timersMutex); - m_timers.push_back(&timer); - } - - boost::system::error_code ec; - co_await timer.async_wait(boost::asio::redirect_error(boost::asio::use_awaitable, ec)); - - if (ec) - { - if (ec == boost::asio::error::operation_aborted) - { - LogDebug("Logcollector coroutine timer was canceled."); - } - else - { - LogDebug("Logcollector coroutine timer wait failed: {}.", ec.message()); - } - } - - { - std::lock_guard lock(m_timersMutex); - m_timers.remove(&timer); - } - } -} +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "file_reader.hpp" + +#ifdef _WIN32 +#include "we_reader_win.hpp" +#endif +using namespace logcollector; + +namespace logcollector { + constexpr int ACTIVE_READERS_WAIT_MS = 10; +} + +void Logcollector::Start() +{ + if (!m_enabled) { + LogInfo("Logcollector module is disabled."); + return; + } + + LogInfo("Logcollector module started."); + m_ioContext.run(); +} + +void Logcollector::EnqueueTask(boost::asio::awaitable task) { + // NOLINTBEGIN(cppcoreguidelines-avoid-capturing-lambda-coroutines) + boost::asio::co_spawn( + m_ioContext, + [task = std::move(task), this]() mutable -> boost::asio::awaitable + { + try + { + m_activeReaders++; + co_await std::move(task); + } + catch (const std::exception& e) + { + LogError("Logcollector coroutine task exited with an exception: {}", e.what()); + } + m_activeReaders--; + }, + boost::asio::detached); + // NOLINTEND(cppcoreguidelines-avoid-capturing-lambda-coroutines) +} + +void Logcollector::Setup(std::shared_ptr configurationParser) +{ + if (!configurationParser) { + LogError("Invalid Configuration Parser passed to setup, module set to disabled."); + m_enabled = false; + return; + } + + m_enabled = configurationParser->GetConfig("logcollector", "enabled").value_or(config::logcollector::DEFAULT_ENABLED); + + if (m_ioContext.stopped()) + { + m_ioContext.restart(); + } + + SetupFileReader(configurationParser); +#ifdef _WIN32 + SetupWEReader(configurationParser); +#endif +} + +void Logcollector::SetupFileReader(const std::shared_ptr configurationParser) +{ + auto fileWait = configurationParser->GetConfig("logcollector", "file_wait").value_or(config::logcollector::DEFAULT_FILE_WAIT); + + auto reloadInterval = configurationParser->GetConfig("logcollector", "reload_interval").value_or(config::logcollector::DEFAULT_RELOAD_INTERVAL); + + auto localfiles = configurationParser->GetConfig>("logcollector", "localfiles").value_or(std::vector({config::logcollector::DEFAULT_LOCALFILES})); + + for (auto& lf : localfiles) { + AddReader(std::make_shared(*this, lf, fileWait, reloadInterval)); + } +} + +#ifdef _WIN32 +void Logcollector::SetupWEReader(const std::shared_ptr configurationParser) +{ + const auto refreshInterval = configurationParser->GetConfig("logcollector", "channel_refresh").value_or(config::logcollector::CHANNEL_REFRESH_INTERVAL); + + const auto bookmarkEnabled = configurationParser->GetConfig("logcollector", "use_bookmark").value_or(config::logcollector::DEFAULT_USE_BOOKMARK); + + const auto windowsConfig = configurationParser->GetConfig>>("logcollector", "windows").value_or( + std::vector> {}); + + for (auto& entry : windowsConfig) + { + auto channel = entry.at("channel"); + auto query = entry.at("query"); + AddReader(std::make_shared(*this, channel, query, refreshInterval, bookmarkEnabled)); + } +} +#endif + +void Logcollector::Stop() { + CleanAllReaders(); + m_ioContext.stop(); + LogInfo("Logcollector module stopped."); +} + +// NOLINTBEGIN(performance-unnecessary-value-param) +Co_CommandExecutionResult Logcollector::ExecuteCommand(const std::string command, + [[maybe_unused]] const nlohmann::json parameters) + { + LogInfo("Logcollector command: ", command); + co_return module_command::CommandExecutionResult{module_command::Status::SUCCESS, "OK"}; +} +// NOLINTEND(performance-unnecessary-value-param) + +void Logcollector::SetPushMessageFunction(const std::function& pushMessage) { + m_pushMessage = pushMessage; +} + +void Logcollector::SendMessage(const std::string& location, const std::string& log, const std::string& collectorType) +{ + auto metadata = nlohmann::json::object(); + auto data = nlohmann::json::object(); + + metadata["module"] = m_moduleName; + metadata["type"] = collectorType; + + data["log"]["file"]["path"] = location; + data["tags"] = nlohmann::json::array({"mvp"}); + data["event"]["original"] = log; + data["event"]["created"] = Utils::getCurrentISO8601(); + data["event"]["module"] = m_moduleName; + data["event"]["provider"] = "syslog"; + + auto message = Message(MessageType::STATELESS, data, m_moduleName, collectorType, metadata.dump()); + m_pushMessage(message); + + //TODO: undo + LogInfo("Message pushed: '{}':'{}'", location, log); +} + +void Logcollector::AddReader(std::shared_ptr reader) +{ + m_readers.push_back(reader); + EnqueueTask(reader->Run()); +} + +void Logcollector::CleanAllReaders() { + for (const auto &reader : m_readers) { + reader->Stop(); + } + + { + std::lock_guard lock(m_timersMutex); + for (const auto &timer : m_timers) { + timer->cancel(); + } + } + + while (m_activeReaders) { + std::this_thread::sleep_for(std::chrono::milliseconds(ACTIVE_READERS_WAIT_MS)); + } + m_readers.clear(); +} + +Awaitable Logcollector::Wait(std::chrono::milliseconds ms) { + if (!m_ioContext.stopped()) { + auto timer = boost::asio::steady_timer(m_ioContext, ms); + { + std::lock_guard lock(m_timersMutex); + m_timers.push_back(&timer); + } + + boost::system::error_code ec; + co_await timer.async_wait(boost::asio::redirect_error(boost::asio::use_awaitable, ec)); + + if (ec) + { + if (ec == boost::asio::error::operation_aborted) + { + LogDebug("Logcollector coroutine timer was canceled."); + } + else + { + LogDebug("Logcollector coroutine timer wait failed: {}.", ec.message()); + } + } + + { + std::lock_guard lock(m_timersMutex); + m_timers.remove(&timer); + } + } +} diff --git a/src/modules/logcollector/src/we_reader_win.cpp b/src/modules/logcollector/src/we_reader_win.cpp index ce329c2caa..ae9da2101a 100644 --- a/src/modules/logcollector/src/we_reader_win.cpp +++ b/src/modules/logcollector/src/we_reader_win.cpp @@ -5,91 +5,108 @@ using namespace logcollector; WindowsEventTracerReader::WindowsEventTracerReader(Logcollector &logcollector, - const std::vector channels, - const std::vector queries, + const std::string channel, + const std::string query, const std::time_t channelRefreshInterval, bool bookmarkEnabled) : IReader(logcollector), - m_channelsList(channels), - m_queriesList(queries), + m_channel(channel), + m_query(query), m_ChannelsRefreshInterval(channelRefreshInterval), - m_bookmarkEnabled(bookmarkEnabled) { } + m_bookmarkEnabled(bookmarkEnabled) + { + if(m_bookmarkEnabled) + { + std::string fileName {"\\bookmark.xml"}; + std::string filePath = config::DEFAULT_DATA_PATH; + m_bookmarkFile = filePath + fileName; + } + } Awaitable WindowsEventTracerReader::Run() { - //TODO: find a clearer way of connecting this with outside modules - bool keepRunning = true; - std::function shouldContinue = [&keepRunning]() { return keepRunning; }; - - for (size_t i = 0; i < m_channelsList.size(); i++) - { - m_logcollector.EnqueueTask(QueryEvents(m_channelsList.at(i), m_queriesList.at(i), shouldContinue)); - } + m_logcollector.EnqueueTask(QueryEvents(m_channel, m_query)); co_return; } -Awaitable WindowsEventTracerReader::QueryEvents(const std::string channel, const std::string query, std::function shouldContinue) +void WindowsEventTracerReader::Stop() +{ + //TODO + m_keepRunning = false; +} + +Awaitable WindowsEventTracerReader::QueryEvents(const std::string channel, const std::string query) { EVT_HANDLE bookmarkHandle = LoadBookmark(); - //TODO: rework this casting std::wstring wideStringChannel = std::wstring(channel.begin(), channel.end()); std::wstring wideStringQuery = std::wstring(query.begin(), query.end()); - EVT_HANDLE eventQueryHandle = EvtQuery( NULL, wideStringChannel.c_str(), wideStringQuery.c_str(), - EvtQueryChannelPath | EvtQueryForwardDirection); + auto subscriptionCallback = [](EVT_SUBSCRIBE_NOTIFY_ACTION action, PVOID userContext, + EVT_HANDLE event) -> DWORD + { + auto* reader = static_cast(userContext); + + switch (action) + { + case EvtSubscribeActionDeliver: + reader->ProcessEvent(event, reader->GetChannel()); + if (reader->IsBookmarkEnabled()) + { + EVT_HANDLE bookmarkHandle = reader->CreateBookmark(event, nullptr); + reader->SaveBookmark(bookmarkHandle); + EvtClose(bookmarkHandle); + } + break; + + case EvtSubscribeActionError: + //TODO: Handle this error + break; + + default: + break; + } + + return ERROR_SUCCESS; + }; - if (eventQueryHandle == NULL) + EVT_HANDLE subscriptionHandle = EvtSubscribe( + nullptr, + nullptr, + wideStringChannel.c_str(), + wideStringQuery.empty() ? nullptr : wideStringQuery.c_str(), + bookmarkHandle, + this, + subscriptionCallback, + m_bookmarkEnabled && bookmarkHandle? EvtSubscribeStartAfterBookmark : EvtSubscribeStartAtOldestRecord); + + if (!subscriptionHandle) { - LogError("Failed to query event log: {}", std::to_string(GetLastError())); + LogError("Failed to subscribe to event log: {}", std::to_string(GetLastError())); co_return; } - LogInfo("Querying events for {} channel with query: '{}'", channel, query); - - EVT_HANDLE events[10]; - DWORD eventCount = 0; + LogInfo("Subscribed to events for {} channel with query: '{}'", channel, query); - while (shouldContinue()) + while (true) { - if (EvtNext(eventQueryHandle, 10, events, 1000, 0, &eventCount)) + if (!m_keepRunning) { - for (DWORD i = 0; i < eventCount; ++i) - { - ProcessEvent(events[i], channel); - if(m_bookmarkEnabled) - { - bookmarkHandle = CreateBookmark(events[i], bookmarkHandle); - SaveBookmark(bookmarkHandle); - } - EvtClose(events[i]); - } - } - else - { - DWORD error = GetLastError(); - if (error == ERROR_NO_MORE_ITEMS) - { - //TODO: delete or make it trace - LogInfo("No more events. Waiting for new events..."); - co_await m_logcollector.Wait(std::chrono::milliseconds(m_ChannelsRefreshInterval)); - } - else - { - LogError("EvtNext failed with error: {}" , std::to_string(error)); - break; - } + LogInfo("Stopping subscription based on external signal..."); + break; } + + co_await m_logcollector.Wait(std::chrono::milliseconds(m_ChannelsRefreshInterval)); } + LogInfo("Unsubscribing to channel '{}'.", channel); if (bookmarkHandle) { EvtClose(bookmarkHandle); } - - if (eventQueryHandle) + if (subscriptionHandle) { - EvtClose(eventQueryHandle); + EvtClose(subscriptionHandle); } } @@ -158,13 +175,19 @@ EVT_HANDLE WindowsEventTracerReader::LoadBookmark() buffer << file.rdbuf(); std::wstring bookmarkXML = buffer.str(); file.close(); - //TODO: delete later - LogInfo("Creating bookark"); - return EvtCreateBookmark(bookmarkXML.c_str()); + if(bookmarkXML.empty()) + { + LogTrace("Empty bookark file, exiting"); + } + else + { + LogInfo("Creating bookark"); + return EvtCreateBookmark(bookmarkXML.c_str()); + } } else { - LogError("Couldn't open bookmark file: {}", m_bookmarkFile); + LogWarn("Couldn't open bookmark file."); } } diff --git a/src/modules/logcollector/src/we_reader_win.hpp b/src/modules/logcollector/src/we_reader_win.hpp index caa79b28c8..0f4c6585f4 100644 --- a/src/modules/logcollector/src/we_reader_win.hpp +++ b/src/modules/logcollector/src/we_reader_win.hpp @@ -32,8 +32,8 @@ class WindowsEventTracerReader : public IReader /// @param reloadInterval /// @param bookmarkEnabled WindowsEventTracerReader(Logcollector &logcollector, - const std::vector channels, - const std::vector queries, + const std::string channel, + const std::string query, const std::time_t channelRefreshInterval, bool bookmarkEnabled); @@ -41,8 +41,17 @@ class WindowsEventTracerReader : public IReader /// @return Awaitable result Awaitable Run() override; + //TODO: doc + void Stop(); + // Main function to execute the event query with bookmarks and filters - Awaitable QueryEvents(const std::string channel, const std::string query, std::function shouldContinue); + Awaitable QueryEvents(const std::string channel, const std::string query); + + // TODO: doc + bool IsBookmarkEnabled() { return m_bookmarkEnabled; }; + + //TODO: doc + std::string GetChannel() { return m_channel; }; private: // Process an individual event and print its XML representation @@ -60,18 +69,20 @@ class WindowsEventTracerReader : public IReader //TODO: doc std::string WcharVecToString(std::vector& buffer); - std::vector m_channelsList; + std::string m_channel; - std::vector m_queriesList; + std::string m_query; //TODO: change to configurable - std::string m_bookmarkFile = "bookmark.xml"; + std::string m_bookmarkFile; /// @brief std::time_t m_ChannelsRefreshInterval; bool m_bookmarkEnabled; + bool m_keepRunning {true}; + const std::string m_collectorType = "eventchannel"; }; } // namespace logcollector From 33dc9a1a62dfe30dfa03d31f9a9abfe66cefd63b Mon Sep 17 00:00:00 2001 From: Lucho Donda <95248059+LucioDonda@users.noreply.github.com> Date: Tue, 7 Jan 2025 13:59:15 -0800 Subject: [PATCH 11/21] feat: several channels with bookmarking --- etc/config/wazuh-agent.yml | 68 +++++++++---------- src/modules/logcollector/README.md | 4 +- .../logcollector/src/we_reader_win.cpp | 58 ++++++++++++---- .../logcollector/src/we_reader_win.hpp | 15 ++-- .../tests/unit/logcollector_test.cpp | 9 ++- 5 files changed, 96 insertions(+), 58 deletions(-) diff --git a/etc/config/wazuh-agent.yml b/etc/config/wazuh-agent.yml index c295bc328e..22c1985648 100644 --- a/etc/config/wazuh-agent.yml +++ b/etc/config/wazuh-agent.yml @@ -1,35 +1,33 @@ -agent: - thread_count: 4 - server_url: https://localhost:27000 - retry_interval: 30s - verification_mode: none # TODO: change this setting to full -events: - batch_interval: 10s - batch_size: 1MB -inventory: - enabled: true - interval: 1h - scan_on_start: true - hardware: true - system: true - networks: true - packages: true - ports: true - ports_all: true - processes: true - hotfixes: true -logcollector: - enabled: true - localfiles: - - /var/log/auth.log - reload_interval: 1m - file_wait: 500ms - windows: - - channel: Application - query: Event[System/EventID = 4624] - use-bookmark: true - reconnect-time: 5s - - channel: System - query: Event[System/EventID = 7040] - use-bookmark: false - reconnect-time: 5s +agent: + thread_count: 4 + server_url: https://localhost:27000 + retry_interval: 30s + verification_mode: none # TODO: change this setting to full +events: + batch_interval: 10s + batch_size: 1MB +inventory: + enabled: true + interval: 1h + scan_on_start: true + hardware: true + system: true + networks: true + packages: true + ports: true + ports_all: true + processes: true + hotfixes: true +logcollector: + enabled: true + localfiles: + - /var/log/auth.log + reload_interval: 1m + file_wait: 500ms + use_bookmark: true + channel_refresh: 5s + windows: + - channel: Application + query: Event[System/EventID = 4624] + - channel: System + query: Event[System/EventID = 7040] diff --git a/src/modules/logcollector/README.md b/src/modules/logcollector/README.md index 5518f382a5..5a4db8b257 100644 --- a/src/modules/logcollector/README.md +++ b/src/modules/logcollector/README.md @@ -13,14 +13,14 @@ system API. ```yaml logcollector: enabled: true + use_bookmark: true + channel_refresh: 5s file: - location: /var/log/*.log age: 1d delim-regex: "\n" use-bookmark: true windows: - reconnect-time: 5s - use-bookmark: true - channel: Application query: Event[System/EventID = 4624] - channel: System diff --git a/src/modules/logcollector/src/we_reader_win.cpp b/src/modules/logcollector/src/we_reader_win.cpp index ae9da2101a..317e9c44db 100644 --- a/src/modules/logcollector/src/we_reader_win.cpp +++ b/src/modules/logcollector/src/we_reader_win.cpp @@ -1,5 +1,8 @@ #include "we_reader_win.hpp" +#include +#include + #include using namespace logcollector; @@ -17,9 +20,11 @@ WindowsEventTracerReader::WindowsEventTracerReader(Logcollector &logcollector, { if(m_bookmarkEnabled) { - std::string fileName {"\\bookmark.xml"}; + // Creates a single file per instance std::string filePath = config::DEFAULT_DATA_PATH; - m_bookmarkFile = filePath + fileName; + std::string sanitizedChannel = channel; + std::replace(sanitizedChannel.begin(), sanitizedChannel.end(), '\\', '_'); + m_bookmarkFile = filePath + "\\" + sanitizedChannel + Base64Encode(query) + ".bmk";; } } @@ -31,8 +36,7 @@ Awaitable WindowsEventTracerReader::Run() void WindowsEventTracerReader::Stop() { - //TODO - m_keepRunning = false; + m_keepRunning.store(false); } Awaitable WindowsEventTracerReader::QueryEvents(const std::string channel, const std::string query) @@ -83,19 +87,17 @@ Awaitable WindowsEventTracerReader::QueryEvents(const std::string channel, const if (!subscriptionHandle) { LogError("Failed to subscribe to event log: {}", std::to_string(GetLastError())); + if (bookmarkHandle) + { + EvtClose(bookmarkHandle); + } co_return; } LogInfo("Subscribed to events for {} channel with query: '{}'", channel, query); - while (true) + while (m_keepRunning.load()) { - if (!m_keepRunning) - { - LogInfo("Stopping subscription based on external signal..."); - break; - } - co_await m_logcollector.Wait(std::chrono::milliseconds(m_ChannelsRefreshInterval)); } @@ -177,7 +179,7 @@ EVT_HANDLE WindowsEventTracerReader::LoadBookmark() file.close(); if(bookmarkXML.empty()) { - LogTrace("Empty bookark file, exiting"); + LogTrace("Empty bookmark file"); } else { @@ -187,7 +189,7 @@ EVT_HANDLE WindowsEventTracerReader::LoadBookmark() } else { - LogWarn("Couldn't open bookmark file."); + LogWarn("Couldn't open bookmark file '{}'.",m_bookmarkFile); } } @@ -206,3 +208,33 @@ std::string WindowsEventTracerReader::WcharVecToString(std::vector& buf }); return result; } + +std::string WindowsEventTracerReader::Base64Encode(const std::string& input) +{ + static const char* chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + std::string result; + unsigned int val = 0; + int valb = -6; + for (unsigned char c : input) + { + val = (val << 8) + c; + valb += 8; + while (valb >= 0) + { + result.push_back(chars[(val >> valb) & 0x3F]); + valb -= 6; + } + } + + if (valb > -6) + { + result.push_back(chars[((val << 8) >> (valb + 8)) & 0x3F]); + } + + while (result.size() % 4) + { + result.push_back('='); + } + + return result; +} diff --git a/src/modules/logcollector/src/we_reader_win.hpp b/src/modules/logcollector/src/we_reader_win.hpp index 0f4c6585f4..736cb5532d 100644 --- a/src/modules/logcollector/src/we_reader_win.hpp +++ b/src/modules/logcollector/src/we_reader_win.hpp @@ -42,7 +42,7 @@ class WindowsEventTracerReader : public IReader Awaitable Run() override; //TODO: doc - void Stop(); + void Stop() override; // Main function to execute the event query with bookmarks and filters Awaitable QueryEvents(const std::string channel, const std::string query); @@ -63,26 +63,31 @@ class WindowsEventTracerReader : public IReader // Save bookmark to file void SaveBookmark(EVT_HANDLE bookmarkHandle); - // Load bookmark from file (if it exists) + // Load bookmark from file EVT_HANDLE LoadBookmark(); //TODO: doc std::string WcharVecToString(std::vector& buffer); + /// @brief + std::string Base64Encode(const std::string& input); + + /// @brief std::string m_channel; + /// @brief std::string m_query; - //TODO: change to configurable + /// @brief std::string m_bookmarkFile; /// @brief std::time_t m_ChannelsRefreshInterval; + /// @brief true if bookmark is used bool m_bookmarkEnabled; - bool m_keepRunning {true}; - + /// @brief collector type const std::string m_collectorType = "eventchannel"; }; } // namespace logcollector diff --git a/src/modules/logcollector/tests/unit/logcollector_test.cpp b/src/modules/logcollector/tests/unit/logcollector_test.cpp index 2a1f7f6c97..358a7799d2 100644 --- a/src/modules/logcollector/tests/unit/logcollector_test.cpp +++ b/src/modules/logcollector/tests/unit/logcollector_test.cpp @@ -84,7 +84,7 @@ TEST(Logcollector, SendMessage) } #ifdef _WIN32 -TEST(Logcollector, SetupWECReader) +TEST(Logcollector, SetupWEReader) { auto constexpr CONFIG_RAW = R"( logcollector: @@ -96,15 +96,18 @@ TEST(Logcollector, SetupWECReader) )"; std::shared_ptr capturedReader1; + std::shared_ptr capturedReader2; auto logcollector = LogcollectorMock(); auto config = std::make_shared(std::string(CONFIG_RAW)); - EXPECT_CALL(logcollector, AddReader(::testing::_)).Times(1) - .WillOnce(::testing::SaveArg<0>(&capturedReader1)); + EXPECT_CALL(logcollector, AddReader(::testing::_)).Times(2) + .WillOnce(::testing::SaveArg<0>(&capturedReader1)) + .WillOnce(::testing::SaveArg<0>(&capturedReader2)); logcollector.SetupWEReader(config); ASSERT_NE(capturedReader1, nullptr); + ASSERT_NE(capturedReader2, nullptr); } #endif From 4b5f0d767a307c461003785f57f56b59cdc9f350 Mon Sep 17 00:00:00 2001 From: LucioDonda Date: Wed, 8 Jan 2025 20:04:11 +0000 Subject: [PATCH 12/21] feat: avoiding ifdef, renaming and line ending fix --- etc/config/wazuh-agent.yml | 66 +-- .../logcollector/include/logcollector.hpp | 9 +- .../logcollector/src/another_reader_unix.cpp | 14 + ...we_reader_win.cpp => event_reader_win.cpp} | 25 +- ...we_reader_win.hpp => event_reader_win.hpp} | 0 src/modules/logcollector/src/logcollector.cpp | 398 +++++++++--------- .../tests/unit/logcollector_mock.hpp | 6 - .../tests/unit/logcollector_test.cpp | 28 -- .../tests/unit/wec_reader_test.cpp | 2 +- 9 files changed, 268 insertions(+), 280 deletions(-) create mode 100644 src/modules/logcollector/src/another_reader_unix.cpp rename src/modules/logcollector/src/{we_reader_win.cpp => event_reader_win.cpp} (86%) rename src/modules/logcollector/src/{we_reader_win.hpp => event_reader_win.hpp} (100%) diff --git a/etc/config/wazuh-agent.yml b/etc/config/wazuh-agent.yml index 22c1985648..b0b638cb00 100644 --- a/etc/config/wazuh-agent.yml +++ b/etc/config/wazuh-agent.yml @@ -1,33 +1,33 @@ -agent: - thread_count: 4 - server_url: https://localhost:27000 - retry_interval: 30s - verification_mode: none # TODO: change this setting to full -events: - batch_interval: 10s - batch_size: 1MB -inventory: - enabled: true - interval: 1h - scan_on_start: true - hardware: true - system: true - networks: true - packages: true - ports: true - ports_all: true - processes: true - hotfixes: true -logcollector: - enabled: true - localfiles: - - /var/log/auth.log - reload_interval: 1m - file_wait: 500ms - use_bookmark: true - channel_refresh: 5s - windows: - - channel: Application - query: Event[System/EventID = 4624] - - channel: System - query: Event[System/EventID = 7040] +agent: + thread_count: 4 + server_url: https://localhost:27000 + retry_interval: 30s + verification_mode: none # TODO: change this setting to full +events: + batch_interval: 10s + batch_size: 1MB +inventory: + enabled: true + interval: 1h + scan_on_start: true + hardware: true + system: true + networks: true + packages: true + ports: true + ports_all: true + processes: true + hotfixes: true +logcollector: + enabled: true + localfiles: + - /var/log/auth.log + reload_interval: 1m + file_wait: 500ms + use_bookmark: true + channel_refresh: 5s + windows: + - channel: Application + query: Event[System/EventID = 4624] + - channel: System + query: Event[System/EventID = 7040] diff --git a/src/modules/logcollector/include/logcollector.hpp b/src/modules/logcollector/include/logcollector.hpp index d162d8c09a..ee37f1f555 100644 --- a/src/modules/logcollector/include/logcollector.hpp +++ b/src/modules/logcollector/include/logcollector.hpp @@ -90,12 +90,6 @@ class Logcollector { void SetupWEReader(const std::shared_ptr configurationParser); #endif -#ifdef _WIN32 - /// @brief Sets up the Windows Event Channel reader - /// @param configurationParser Configuration parser - void SetupWEReader(const std::shared_ptr configurationParser); -#endif - private: /// @brief Module name const std::string m_moduleName = "logcollector"; @@ -122,4 +116,7 @@ class Logcollector { std::list m_timers; }; +//TODO:check +void AddPlatformSpecificReader(std::shared_ptr configurationParser, Logcollector &logcollector); + } diff --git a/src/modules/logcollector/src/another_reader_unix.cpp b/src/modules/logcollector/src/another_reader_unix.cpp new file mode 100644 index 0000000000..3c784e3200 --- /dev/null +++ b/src/modules/logcollector/src/another_reader_unix.cpp @@ -0,0 +1,14 @@ +#include +#include + +#include +#include + +namespace logcollector { + +void AddPlatformSpecificReader([[maybe_unused]] std::shared_ptr configurationParser, [[maybe_unused]] Logcollector &logcollector) +{ + +} + +} \ No newline at end of file diff --git a/src/modules/logcollector/src/we_reader_win.cpp b/src/modules/logcollector/src/event_reader_win.cpp similarity index 86% rename from src/modules/logcollector/src/we_reader_win.cpp rename to src/modules/logcollector/src/event_reader_win.cpp index 317e9c44db..1cc0bf700b 100644 --- a/src/modules/logcollector/src/we_reader_win.cpp +++ b/src/modules/logcollector/src/event_reader_win.cpp @@ -1,11 +1,30 @@ -#include "we_reader_win.hpp" +#include "event_reader_win.hpp" #include #include #include +#include -using namespace logcollector; +namespace logcollector +{ + +void AddPlatformSpecificReader(std::shared_ptr configurationParser, Logcollector &logcollector) +{ + const auto refreshInterval = configurationParser->GetConfig("logcollector", "channel_refresh").value_or(config::logcollector::CHANNEL_REFRESH_INTERVAL); + + const auto bookmarkEnabled = configurationParser->GetConfig("logcollector", "use_bookmark").value_or(config::logcollector::DEFAULT_USE_BOOKMARK); + + const auto windowsConfig = configurationParser->GetConfig>>("logcollector", "windows").value_or( + std::vector> {}); + + for (auto& entry : windowsConfig) + { + auto channel = entry.at("channel"); + auto query = entry.at("query"); + logcollector.AddReader(std::make_shared(logcollector, channel, query, refreshInterval, bookmarkEnabled)); + } +} WindowsEventTracerReader::WindowsEventTracerReader(Logcollector &logcollector, const std::string channel, @@ -238,3 +257,5 @@ std::string WindowsEventTracerReader::Base64Encode(const std::string& input) return result; } + +} diff --git a/src/modules/logcollector/src/we_reader_win.hpp b/src/modules/logcollector/src/event_reader_win.hpp similarity index 100% rename from src/modules/logcollector/src/we_reader_win.hpp rename to src/modules/logcollector/src/event_reader_win.hpp diff --git a/src/modules/logcollector/src/logcollector.cpp b/src/modules/logcollector/src/logcollector.cpp index 07e71dd153..18996000a3 100644 --- a/src/modules/logcollector/src/logcollector.cpp +++ b/src/modules/logcollector/src/logcollector.cpp @@ -1,204 +1,194 @@ -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "file_reader.hpp" - -#ifdef _WIN32 -#include "we_reader_win.hpp" -#endif -using namespace logcollector; - -namespace logcollector { - constexpr int ACTIVE_READERS_WAIT_MS = 10; -} - -void Logcollector::Start() -{ - if (!m_enabled) { - LogInfo("Logcollector module is disabled."); - return; - } - - LogInfo("Logcollector module started."); - m_ioContext.run(); -} - -void Logcollector::EnqueueTask(boost::asio::awaitable task) { - // NOLINTBEGIN(cppcoreguidelines-avoid-capturing-lambda-coroutines) - boost::asio::co_spawn( - m_ioContext, - [task = std::move(task), this]() mutable -> boost::asio::awaitable - { - try - { - m_activeReaders++; - co_await std::move(task); - } - catch (const std::exception& e) - { - LogError("Logcollector coroutine task exited with an exception: {}", e.what()); - } - m_activeReaders--; - }, - boost::asio::detached); - // NOLINTEND(cppcoreguidelines-avoid-capturing-lambda-coroutines) -} - -void Logcollector::Setup(std::shared_ptr configurationParser) -{ - if (!configurationParser) { - LogError("Invalid Configuration Parser passed to setup, module set to disabled."); - m_enabled = false; - return; - } - - m_enabled = configurationParser->GetConfig("logcollector", "enabled").value_or(config::logcollector::DEFAULT_ENABLED); - - if (m_ioContext.stopped()) - { - m_ioContext.restart(); - } - - SetupFileReader(configurationParser); -#ifdef _WIN32 - SetupWEReader(configurationParser); -#endif -} - -void Logcollector::SetupFileReader(const std::shared_ptr configurationParser) -{ - auto fileWait = configurationParser->GetConfig("logcollector", "file_wait").value_or(config::logcollector::DEFAULT_FILE_WAIT); - - auto reloadInterval = configurationParser->GetConfig("logcollector", "reload_interval").value_or(config::logcollector::DEFAULT_RELOAD_INTERVAL); - - auto localfiles = configurationParser->GetConfig>("logcollector", "localfiles").value_or(std::vector({config::logcollector::DEFAULT_LOCALFILES})); - - for (auto& lf : localfiles) { - AddReader(std::make_shared(*this, lf, fileWait, reloadInterval)); - } -} - -#ifdef _WIN32 -void Logcollector::SetupWEReader(const std::shared_ptr configurationParser) -{ - const auto refreshInterval = configurationParser->GetConfig("logcollector", "channel_refresh").value_or(config::logcollector::CHANNEL_REFRESH_INTERVAL); - - const auto bookmarkEnabled = configurationParser->GetConfig("logcollector", "use_bookmark").value_or(config::logcollector::DEFAULT_USE_BOOKMARK); - - const auto windowsConfig = configurationParser->GetConfig>>("logcollector", "windows").value_or( - std::vector> {}); - - for (auto& entry : windowsConfig) - { - auto channel = entry.at("channel"); - auto query = entry.at("query"); - AddReader(std::make_shared(*this, channel, query, refreshInterval, bookmarkEnabled)); - } -} -#endif - -void Logcollector::Stop() { - CleanAllReaders(); - m_ioContext.stop(); - LogInfo("Logcollector module stopped."); -} - -// NOLINTBEGIN(performance-unnecessary-value-param) -Co_CommandExecutionResult Logcollector::ExecuteCommand(const std::string command, - [[maybe_unused]] const nlohmann::json parameters) - { - LogInfo("Logcollector command: ", command); - co_return module_command::CommandExecutionResult{module_command::Status::SUCCESS, "OK"}; -} -// NOLINTEND(performance-unnecessary-value-param) - -void Logcollector::SetPushMessageFunction(const std::function& pushMessage) { - m_pushMessage = pushMessage; -} - -void Logcollector::SendMessage(const std::string& location, const std::string& log, const std::string& collectorType) -{ - auto metadata = nlohmann::json::object(); - auto data = nlohmann::json::object(); - - metadata["module"] = m_moduleName; - metadata["type"] = collectorType; - - data["log"]["file"]["path"] = location; - data["tags"] = nlohmann::json::array({"mvp"}); - data["event"]["original"] = log; - data["event"]["created"] = Utils::getCurrentISO8601(); - data["event"]["module"] = m_moduleName; - data["event"]["provider"] = "syslog"; - - auto message = Message(MessageType::STATELESS, data, m_moduleName, collectorType, metadata.dump()); - m_pushMessage(message); - - //TODO: undo - LogInfo("Message pushed: '{}':'{}'", location, log); -} - -void Logcollector::AddReader(std::shared_ptr reader) -{ - m_readers.push_back(reader); - EnqueueTask(reader->Run()); -} - -void Logcollector::CleanAllReaders() { - for (const auto &reader : m_readers) { - reader->Stop(); - } - - { - std::lock_guard lock(m_timersMutex); - for (const auto &timer : m_timers) { - timer->cancel(); - } - } - - while (m_activeReaders) { - std::this_thread::sleep_for(std::chrono::milliseconds(ACTIVE_READERS_WAIT_MS)); - } - m_readers.clear(); -} - -Awaitable Logcollector::Wait(std::chrono::milliseconds ms) { - if (!m_ioContext.stopped()) { - auto timer = boost::asio::steady_timer(m_ioContext, ms); - { - std::lock_guard lock(m_timersMutex); - m_timers.push_back(&timer); - } - - boost::system::error_code ec; - co_await timer.async_wait(boost::asio::redirect_error(boost::asio::use_awaitable, ec)); - - if (ec) - { - if (ec == boost::asio::error::operation_aborted) - { - LogDebug("Logcollector coroutine timer was canceled."); - } - else - { - LogDebug("Logcollector coroutine timer wait failed: {}.", ec.message()); - } - } - - { - std::lock_guard lock(m_timersMutex); - m_timers.remove(&timer); - } - } -} +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "file_reader.hpp" + +using namespace logcollector; + +namespace logcollector +{ + constexpr int ACTIVE_READERS_WAIT_MS = 10; +} + +void Logcollector::Start() +{ + if (!m_enabled) + { + LogInfo("Logcollector module is disabled."); + return; + } + + LogInfo("Logcollector module started."); + m_ioContext.run(); +} + +void Logcollector::EnqueueTask(boost::asio::awaitable task) +{ + // NOLINTBEGIN(cppcoreguidelines-avoid-capturing-lambda-coroutines) + boost::asio::co_spawn( + m_ioContext, + [task = std::move(task), this]() mutable -> boost::asio::awaitable + { + try + { + m_activeReaders++; + co_await std::move(task); + } + catch (const std::exception& e) + { + LogError("Logcollector coroutine task exited with an exception: {}", e.what()); + } + m_activeReaders--; + }, + boost::asio::detached); + // NOLINTEND(cppcoreguidelines-avoid-capturing-lambda-coroutines) +} + +void Logcollector::Setup(std::shared_ptr configurationParser) +{ + if (!configurationParser) + { + LogError("Invalid Configuration Parser passed to setup, module set to disabled."); + m_enabled = false; + return; + } + + m_enabled = configurationParser->GetConfig("logcollector", "enabled").value_or(config::logcollector::DEFAULT_ENABLED); + + if (m_ioContext.stopped()) + { + m_ioContext.restart(); + } + + SetupFileReader(configurationParser); + + AddPlatformSpecificReader(configurationParser, *this); +} + +void Logcollector::SetupFileReader(const std::shared_ptr configurationParser) +{ + auto fileWait = configurationParser->GetConfig("logcollector", "file_wait").value_or(config::logcollector::DEFAULT_FILE_WAIT); + + auto reloadInterval = configurationParser->GetConfig("logcollector", "reload_interval").value_or(config::logcollector::DEFAULT_RELOAD_INTERVAL); + + auto localfiles = configurationParser->GetConfig>("logcollector", "localfiles").value_or(std::vector({config::logcollector::DEFAULT_LOCALFILES})); + + for (auto& lf : localfiles) + { + AddReader(std::make_shared(*this, lf, fileWait, reloadInterval)); + } +} + +void Logcollector::Stop() +{ + CleanAllReaders(); + m_ioContext.stop(); + LogInfo("Logcollector module stopped."); +} + +// NOLINTBEGIN(performance-unnecessary-value-param) +Co_CommandExecutionResult Logcollector::ExecuteCommand(const std::string command, + [[maybe_unused]] const nlohmann::json parameters) + { + LogInfo("Logcollector command: ", command); + co_return module_command::CommandExecutionResult{module_command::Status::SUCCESS, "OK"}; +} +// NOLINTEND(performance-unnecessary-value-param) + +void Logcollector::SetPushMessageFunction(const std::function& pushMessage) +{ + m_pushMessage = pushMessage; +} + +void Logcollector::SendMessage(const std::string& location, const std::string& log, const std::string& collectorType) +{ + auto metadata = nlohmann::json::object(); + auto data = nlohmann::json::object(); + + metadata["module"] = m_moduleName; + metadata["type"] = collectorType; + + data["log"]["file"]["path"] = location; + data["tags"] = nlohmann::json::array({"mvp"}); + data["event"]["original"] = log; + data["event"]["created"] = Utils::getCurrentISO8601(); + data["event"]["module"] = m_moduleName; + data["event"]["provider"] = "syslog"; + + auto message = Message(MessageType::STATELESS, data, m_moduleName, collectorType, metadata.dump()); + m_pushMessage(message); + + //TODO: undo + LogInfo("Message pushed: '{}':'{}'", location, log); +} + +void Logcollector::AddReader(std::shared_ptr reader) +{ + m_readers.push_back(reader); + EnqueueTask(reader->Run()); +} + +void Logcollector::CleanAllReaders() +{ + for (const auto &reader : m_readers) + { + reader->Stop(); + } + + { + std::lock_guard lock(m_timersMutex); + for (const auto &timer : m_timers) + { + timer->cancel(); + } + } + + while (m_activeReaders) + { + std::this_thread::sleep_for(std::chrono::milliseconds(ACTIVE_READERS_WAIT_MS)); + } + m_readers.clear(); +} + +Awaitable Logcollector::Wait(std::chrono::milliseconds ms) +{ + if (!m_ioContext.stopped()) + { + auto timer = boost::asio::steady_timer(m_ioContext, ms); + { + std::lock_guard lock(m_timersMutex); + m_timers.push_back(&timer); + } + + boost::system::error_code ec; + co_await timer.async_wait(boost::asio::redirect_error(boost::asio::use_awaitable, ec)); + + if (ec) + { + if (ec == boost::asio::error::operation_aborted) + { + LogDebug("Logcollector coroutine timer was canceled."); + } + else + { + LogDebug("Logcollector coroutine timer wait failed: {}.", ec.message()); + } + } + + { + std::lock_guard lock(m_timersMutex); + m_timers.remove(&timer); + } + } +} diff --git a/src/modules/logcollector/tests/unit/logcollector_mock.hpp b/src/modules/logcollector/tests/unit/logcollector_mock.hpp index c4d0a4df7c..524b40212a 100644 --- a/src/modules/logcollector/tests/unit/logcollector_mock.hpp +++ b/src/modules/logcollector/tests/unit/logcollector_mock.hpp @@ -20,12 +20,6 @@ class LogcollectorMock : public Logcollector { { Logcollector::SetupFileReader(configurationParser); } -#ifdef _WIN32 - void SetupWEReader(const std::shared_ptr configurationParser) - { - Logcollector::SetupWEReader(configurationParser); - } -#endif MOCK_METHOD(void, AddReader, (std::shared_ptr reader), (override)); MOCK_METHOD(void, EnqueueTask, (Awaitable task), (override)); }; diff --git a/src/modules/logcollector/tests/unit/logcollector_test.cpp b/src/modules/logcollector/tests/unit/logcollector_test.cpp index 358a7799d2..da8bf7d6a8 100644 --- a/src/modules/logcollector/tests/unit/logcollector_test.cpp +++ b/src/modules/logcollector/tests/unit/logcollector_test.cpp @@ -83,34 +83,6 @@ TEST(Logcollector, SendMessage) ASSERT_EQ(capturedMessage.metaData, METADATA); } -#ifdef _WIN32 -TEST(Logcollector, SetupWEReader) -{ - auto constexpr CONFIG_RAW = R"( - logcollector: - windows: - - channel: Application - query: Event[System/EventID = 4624] - - channel: System - query: Event[System/EventID = 7040] - )"; - - std::shared_ptr capturedReader1; - std::shared_ptr capturedReader2; - auto logcollector = LogcollectorMock(); - auto config = std::make_shared(std::string(CONFIG_RAW)); - - EXPECT_CALL(logcollector, AddReader(::testing::_)).Times(2) - .WillOnce(::testing::SaveArg<0>(&capturedReader1)) - .WillOnce(::testing::SaveArg<0>(&capturedReader2)); - - logcollector.SetupWEReader(config); - - ASSERT_NE(capturedReader1, nullptr); - ASSERT_NE(capturedReader2, nullptr); -} -#endif - int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); diff --git a/src/modules/logcollector/tests/unit/wec_reader_test.cpp b/src/modules/logcollector/tests/unit/wec_reader_test.cpp index 567b8b8f14..89a8df2eae 100644 --- a/src/modules/logcollector/tests/unit/wec_reader_test.cpp +++ b/src/modules/logcollector/tests/unit/wec_reader_test.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include "event_reader_win.hpp" #include "logcollector_mock.hpp" using namespace logcollector; From 4d89780099b27536fae07b96e0efe1e30e4e4072 Mon Sep 17 00:00:00 2001 From: LucioDonda Date: Thu, 9 Jan 2025 16:04:05 +0000 Subject: [PATCH 13/21] feat: separating tests according to arch --- .../logcollector/src/event_reader_win.hpp | 2 - .../logcollector/tests/unit/CMakeLists.txt | 8 ++ ...der_test.cpp => event_reader_win_test.cpp} | 0 .../tests/unit/file_reader_unix_test.cpp | 98 +++++++++++++++++++ ...ader_test.cpp => file_reader_win_test.cpp} | 6 -- .../logcollector/tests/unit/tempfile.hpp | 19 ---- 6 files changed, 106 insertions(+), 27 deletions(-) rename src/modules/logcollector/tests/unit/{wec_reader_test.cpp => event_reader_win_test.cpp} (100%) create mode 100644 src/modules/logcollector/tests/unit/file_reader_unix_test.cpp rename src/modules/logcollector/tests/unit/{file_reader_test.cpp => file_reader_win_test.cpp} (96%) diff --git a/src/modules/logcollector/src/event_reader_win.hpp b/src/modules/logcollector/src/event_reader_win.hpp index 736cb5532d..9ff045b63b 100644 --- a/src/modules/logcollector/src/event_reader_win.hpp +++ b/src/modules/logcollector/src/event_reader_win.hpp @@ -1,4 +1,3 @@ -#ifdef _WIN32 #pragma once #include @@ -91,4 +90,3 @@ class WindowsEventTracerReader : public IReader const std::string m_collectorType = "eventchannel"; }; } // namespace logcollector -#endif diff --git a/src/modules/logcollector/tests/unit/CMakeLists.txt b/src/modules/logcollector/tests/unit/CMakeLists.txt index 5f1b910762..a60a7b07f7 100644 --- a/src/modules/logcollector/tests/unit/CMakeLists.txt +++ b/src/modules/logcollector/tests/unit/CMakeLists.txt @@ -2,6 +2,14 @@ find_package(GTest CONFIG REQUIRED) FILE(GLOB LOGCOLLECTOR_TEST_SOURCES *_test.cpp) +if(WIN32) + FILE(GLOB EXCLUDED_SOURCES *_unix_test.cpp) +else() + FILE(GLOB EXCLUDED_SOURCES *_win_test.cpp) +endif() + +list(REMOVE_ITEM LOGCOLLECTOR_TEST_SOURCES ${EXCLUDED_SOURCES}) + add_executable(logcollector_unit_tests ${LOGCOLLECTOR_TEST_SOURCES}) configure_target(logcollector_unit_tests) diff --git a/src/modules/logcollector/tests/unit/wec_reader_test.cpp b/src/modules/logcollector/tests/unit/event_reader_win_test.cpp similarity index 100% rename from src/modules/logcollector/tests/unit/wec_reader_test.cpp rename to src/modules/logcollector/tests/unit/event_reader_win_test.cpp diff --git a/src/modules/logcollector/tests/unit/file_reader_unix_test.cpp b/src/modules/logcollector/tests/unit/file_reader_unix_test.cpp new file mode 100644 index 0000000000..293937569e --- /dev/null +++ b/src/modules/logcollector/tests/unit/file_reader_unix_test.cpp @@ -0,0 +1,98 @@ +#include +#include +#include +#include + +#include +#include +#include "tempfile.hpp" +#include "logcollector_mock.hpp" + +using namespace logcollector; + +class MockCallback { +public: + MOCK_METHOD(void, Call, (const std::string &), ()); +}; + +TEST(Localfile, FullLine) +{ + auto stream = std::make_shared(); + auto lf = Localfile(stream); + + *stream << "Hello World\n"; + auto answer = lf.NextLog(); + ASSERT_EQ(answer, "Hello World"); +} + +TEST(Localfile, PartialLine) +{ + auto stream = std::make_shared(); + auto lf = Localfile(stream); + + *stream << "Hello World"; + auto answer = lf.NextLog(); + ASSERT_EQ(answer, ""); + + *stream << "\n"; + answer = lf.NextLog(); + ASSERT_EQ(answer, "Hello World"); +} + +TEST(Localfile, OpenError) +{ + try { + auto lf = Localfile("unexisting.file"); + FAIL() << "Expected OpenError"; + } catch (OpenError & err) { + ASSERT_STREQ(err.what(), "Cannot open file: unexisting.file"); + } +} + +TEST(Localfile, Rotated) +{ + auto fileA = TempFile("/tmp/A.log", "Hello World"); + auto lf = Localfile("/tmp/A.log"); + + lf.SeekEnd(); + ASSERT_FALSE(lf.Rotated()); + + fileA.Truncate(); + ASSERT_TRUE(lf.Rotated()); +} + +TEST(Localfile, Deleted) +{ + auto fileA = std::make_unique("/tmp/A.log", "Hello World"); + auto lf = Localfile("/tmp/A.log"); + + fileA.reset(); + + try { + lf.Rotated(); + FAIL() << "Expected OpenError"; + } catch (OpenError & err) { + ASSERT_STREQ(err.what(), "Cannot open file: /tmp/A.log"); + } +} + +TEST(FileReader, Reload) { + spdlog::default_logger()->sinks().clear(); + MockCallback mockCallback; + + EXPECT_CALL(mockCallback, Call("/tmp/fileA.log")).Times(1); + EXPECT_CALL(mockCallback, Call("/tmp/fileB.log")).Times(1); + EXPECT_CALL(mockCallback, Call("/tmp/fileC.log")).Times(1); + EXPECT_CALL(mockCallback, Call("/tmp/fileD.log")).Times(1); + + auto a = TempFile("/tmp/fileA.log"); + auto b = TempFile("/tmp/fileB.log"); + auto c = TempFile("/tmp/fileC.log"); + + auto regex = "/tmp/file*.log"; + FileReader reader(Logcollector::Instance(), regex, 500, 60000); //NOLINT + reader.Reload([&](Localfile& lf) { mockCallback.Call(lf.Filename()); }); + + auto d = TempFile("/tmp/fileD.log"); + reader.Reload([&](Localfile& lf) { mockCallback.Call(lf.Filename()); }); +} diff --git a/src/modules/logcollector/tests/unit/file_reader_test.cpp b/src/modules/logcollector/tests/unit/file_reader_win_test.cpp similarity index 96% rename from src/modules/logcollector/tests/unit/file_reader_test.cpp rename to src/modules/logcollector/tests/unit/file_reader_win_test.cpp index de78ab72cb..4adf806834 100644 --- a/src/modules/logcollector/tests/unit/file_reader_test.cpp +++ b/src/modules/logcollector/tests/unit/file_reader_win_test.cpp @@ -10,11 +10,7 @@ using namespace logcollector; -#ifdef _WIN32 static constexpr auto TMP_FILE_DIR = "C:\\Temp\\"; -#else -static constexpr auto TMP_FILE_DIR = "/tmp/"; -#endif inline std::string GetFullFileName(const std::string& filename) { //TODO: move to setup stage of test only for windows @@ -75,10 +71,8 @@ TEST(Localfile, Rotated) TEST(Localfile, Deleted) { -#ifdef _WIN32 //FIXME: The process cannot access the file because it is being used by another process. GTEST_SKIP(); -#endif auto fileA = std::make_unique("/tmp/A.log", "Hello World"); auto lf = Localfile("/tmp/A.log"); diff --git a/src/modules/logcollector/tests/unit/tempfile.hpp b/src/modules/logcollector/tests/unit/tempfile.hpp index 5dde2c3cdb..7cac7221b8 100644 --- a/src/modules/logcollector/tests/unit/tempfile.hpp +++ b/src/modules/logcollector/tests/unit/tempfile.hpp @@ -35,25 +35,6 @@ class TempFile { if (!std::filesystem::remove(m_path, ec)) { auto error_msg = std::system_category().message(ec.value()); - - // Alternative approach using Windows API directly - #ifdef _WIN32 - std::wstring wpath(m_path.begin(), m_path.end()); - if (DeleteFileW(wpath.c_str()) == 0) { - DWORD win_error = GetLastError(); - char win_error_msg[256]; - FormatMessageA( - FORMAT_MESSAGE_FROM_SYSTEM, - NULL, - win_error, - 0, - win_error_msg, - sizeof(win_error_msg), - NULL - ); - // win_error_msg (from Windows API) - } - #endif } } From 1a2ecf6c9d7265d134fb4914dc9b43219d3ef4f3 Mon Sep 17 00:00:00 2001 From: LucioDonda Date: Thu, 9 Jan 2025 19:32:53 +0000 Subject: [PATCH 14/21] feat: removing bookmark feature --- etc/config/wazuh-agent.yml | 1 - src/cmake/config.cmake | 2 - src/common/config/include/config.h.in | 1 - .../logcollector/src/event_reader_win.cpp | 129 +----------------- .../logcollector/src/event_reader_win.hpp | 27 +--- .../tests/unit/event_reader_win_test.cpp | 5 - 6 files changed, 8 insertions(+), 157 deletions(-) diff --git a/etc/config/wazuh-agent.yml b/etc/config/wazuh-agent.yml index b0b638cb00..843ee854d3 100644 --- a/etc/config/wazuh-agent.yml +++ b/etc/config/wazuh-agent.yml @@ -24,7 +24,6 @@ logcollector: - /var/log/auth.log reload_interval: 1m file_wait: 500ms - use_bookmark: true channel_refresh: 5s windows: - channel: Application diff --git a/src/cmake/config.cmake b/src/cmake/config.cmake index a194ce7077..9cd5ba2e6d 100644 --- a/src/cmake/config.cmake +++ b/src/cmake/config.cmake @@ -36,8 +36,6 @@ set(DEFAULT_RELOAD_INTERVAL 60000 CACHE STRING "Default Logcollector reload inte set(CHANNEL_REFRESH_INTERVAL 5000 CACHE STRING "Default Logcollector reconnect time (5000ms)") -set(DEFAULT_USE_BOOKMARK true CACHE BOOL "Default Logcollector windows bookmark enabled (false)") - set(DEFAULT_INVENTORY_ENABLED true CACHE BOOL "Default inventory enabled") set(DEFAULT_INTERVAL 3600000 CACHE STRING "Default inventory interval (1h)") diff --git a/src/common/config/include/config.h.in b/src/common/config/include/config.h.in index 78a2baaf0a..5a0b674095 100644 --- a/src/common/config/include/config.h.in +++ b/src/common/config/include/config.h.in @@ -33,7 +33,6 @@ namespace config constexpr auto DEFAULT_RELOAD_INTERVAL = @DEFAULT_RELOAD_INTERVAL@; constexpr auto DEFAULT_LOCALFILES = "/var/log/auth.log"; constexpr auto CHANNEL_REFRESH_INTERVAL = @CHANNEL_REFRESH_INTERVAL@; - constexpr auto DEFAULT_USE_BOOKMARK = @DEFAULT_USE_BOOKMARK@; } namespace inventory diff --git a/src/modules/logcollector/src/event_reader_win.cpp b/src/modules/logcollector/src/event_reader_win.cpp index 1cc0bf700b..41641efafc 100644 --- a/src/modules/logcollector/src/event_reader_win.cpp +++ b/src/modules/logcollector/src/event_reader_win.cpp @@ -13,8 +13,6 @@ void AddPlatformSpecificReader(std::shared_ptrGetConfig("logcollector", "channel_refresh").value_or(config::logcollector::CHANNEL_REFRESH_INTERVAL); - const auto bookmarkEnabled = configurationParser->GetConfig("logcollector", "use_bookmark").value_or(config::logcollector::DEFAULT_USE_BOOKMARK); - const auto windowsConfig = configurationParser->GetConfig>>("logcollector", "windows").value_or( std::vector> {}); @@ -22,30 +20,18 @@ void AddPlatformSpecificReader(std::shared_ptr(logcollector, channel, query, refreshInterval, bookmarkEnabled)); + logcollector.AddReader(std::make_shared(logcollector, channel, query, refreshInterval)); } } WindowsEventTracerReader::WindowsEventTracerReader(Logcollector &logcollector, const std::string channel, const std::string query, - const std::time_t channelRefreshInterval, - bool bookmarkEnabled) : + const std::time_t channelRefreshInterval) : IReader(logcollector), m_channel(channel), m_query(query), - m_ChannelsRefreshInterval(channelRefreshInterval), - m_bookmarkEnabled(bookmarkEnabled) - { - if(m_bookmarkEnabled) - { - // Creates a single file per instance - std::string filePath = config::DEFAULT_DATA_PATH; - std::string sanitizedChannel = channel; - std::replace(sanitizedChannel.begin(), sanitizedChannel.end(), '\\', '_'); - m_bookmarkFile = filePath + "\\" + sanitizedChannel + Base64Encode(query) + ".bmk";; - } - } + m_ChannelsRefreshInterval(channelRefreshInterval) { } Awaitable WindowsEventTracerReader::Run() { @@ -60,8 +46,6 @@ void WindowsEventTracerReader::Stop() Awaitable WindowsEventTracerReader::QueryEvents(const std::string channel, const std::string query) { - EVT_HANDLE bookmarkHandle = LoadBookmark(); - std::wstring wideStringChannel = std::wstring(channel.begin(), channel.end()); std::wstring wideStringQuery = std::wstring(query.begin(), query.end()); @@ -74,12 +58,6 @@ Awaitable WindowsEventTracerReader::QueryEvents(const std::string channel, const { case EvtSubscribeActionDeliver: reader->ProcessEvent(event, reader->GetChannel()); - if (reader->IsBookmarkEnabled()) - { - EVT_HANDLE bookmarkHandle = reader->CreateBookmark(event, nullptr); - reader->SaveBookmark(bookmarkHandle); - EvtClose(bookmarkHandle); - } break; case EvtSubscribeActionError: @@ -97,19 +75,15 @@ Awaitable WindowsEventTracerReader::QueryEvents(const std::string channel, const nullptr, nullptr, wideStringChannel.c_str(), - wideStringQuery.empty() ? nullptr : wideStringQuery.c_str(), - bookmarkHandle, + wideStringQuery.c_str(), + NULL, this, subscriptionCallback, - m_bookmarkEnabled && bookmarkHandle? EvtSubscribeStartAfterBookmark : EvtSubscribeStartAtOldestRecord); + EvtSubscribeToFutureEvents); // EvtSubscribeStartAtOldestRecord if (!subscriptionHandle) { LogError("Failed to subscribe to event log: {}", std::to_string(GetLastError())); - if (bookmarkHandle) - { - EvtClose(bookmarkHandle); - } co_return; } @@ -121,10 +95,6 @@ Awaitable WindowsEventTracerReader::QueryEvents(const std::string channel, const } LogInfo("Unsubscribing to channel '{}'.", channel); - if (bookmarkHandle) - { - EvtClose(bookmarkHandle); - } if (subscriptionHandle) { EvtClose(subscriptionHandle); @@ -158,63 +128,6 @@ void WindowsEventTracerReader::ProcessEvent(EVT_HANDLE event, const std::string } } -EVT_HANDLE WindowsEventTracerReader::CreateBookmark(EVT_HANDLE event, EVT_HANDLE existingBookmark) -{ - EVT_HANDLE bookmark = existingBookmark ? existingBookmark : EvtCreateBookmark(NULL); - if (!EvtUpdateBookmark(bookmark, event)) - { - LogError("Failed to update bookmark: {}", std::to_string(GetLastError())); - } - return bookmark; -} - -void WindowsEventTracerReader::SaveBookmark(EVT_HANDLE bookmarkHandle) -{ - DWORD bufferUsed = 0; - - EvtRender(NULL, bookmarkHandle, EvtRenderBookmark, 0, NULL, &bufferUsed, NULL); - std::vector buffer(bufferUsed); - if (EvtRender(NULL, bookmarkHandle, EvtRenderBookmark, bufferUsed, buffer.data(), &bufferUsed, NULL)) - { - std::wofstream file(m_bookmarkFile); - if (file.is_open()) - { - file.write(buffer.data(), bufferUsed / sizeof(wchar_t)); - file.close(); - } - } -} - -EVT_HANDLE WindowsEventTracerReader::LoadBookmark() -{ - if(m_bookmarkEnabled) - { - std::wifstream file(m_bookmarkFile); - if (file.is_open()) - { - std::wstringstream buffer; - buffer << file.rdbuf(); - std::wstring bookmarkXML = buffer.str(); - file.close(); - if(bookmarkXML.empty()) - { - LogTrace("Empty bookmark file"); - } - else - { - LogInfo("Creating bookark"); - return EvtCreateBookmark(bookmarkXML.c_str()); - } - } - else - { - LogWarn("Couldn't open bookmark file '{}'.",m_bookmarkFile); - } - } - - return NULL; -} - std::string WindowsEventTracerReader::WcharVecToString(std::vector& buffer) { buffer.erase(std::remove(buffer.begin(), buffer.end(), L'\0'), buffer.end()); @@ -228,34 +141,4 @@ std::string WindowsEventTracerReader::WcharVecToString(std::vector& buf return result; } -std::string WindowsEventTracerReader::Base64Encode(const std::string& input) -{ - static const char* chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - std::string result; - unsigned int val = 0; - int valb = -6; - for (unsigned char c : input) - { - val = (val << 8) + c; - valb += 8; - while (valb >= 0) - { - result.push_back(chars[(val >> valb) & 0x3F]); - valb -= 6; - } - } - - if (valb > -6) - { - result.push_back(chars[((val << 8) >> (valb + 8)) & 0x3F]); - } - - while (result.size() % 4) - { - result.push_back('='); - } - - return result; -} - } diff --git a/src/modules/logcollector/src/event_reader_win.hpp b/src/modules/logcollector/src/event_reader_win.hpp index 9ff045b63b..53503a0cc5 100644 --- a/src/modules/logcollector/src/event_reader_win.hpp +++ b/src/modules/logcollector/src/event_reader_win.hpp @@ -29,12 +29,10 @@ class WindowsEventTracerReader : public IReader /// @param channels List of channel names /// @param queries List of queries /// @param reloadInterval - /// @param bookmarkEnabled WindowsEventTracerReader(Logcollector &logcollector, const std::string channel, const std::string query, - const std::time_t channelRefreshInterval, - bool bookmarkEnabled); + const std::time_t channelRefreshInterval); /// @brief Runs the file reader /// @return Awaitable result @@ -43,12 +41,9 @@ class WindowsEventTracerReader : public IReader //TODO: doc void Stop() override; - // Main function to execute the event query with bookmarks and filters + // Main function to execute the event query Awaitable QueryEvents(const std::string channel, const std::string query); - // TODO: doc - bool IsBookmarkEnabled() { return m_bookmarkEnabled; }; - //TODO: doc std::string GetChannel() { return m_channel; }; @@ -56,36 +51,18 @@ class WindowsEventTracerReader : public IReader // Process an individual event and print its XML representation void ProcessEvent(EVT_HANDLE event, const std::string channel); - // Create a bookmark for the current event - EVT_HANDLE CreateBookmark(EVT_HANDLE event, EVT_HANDLE existingBookmark = NULL); - - // Save bookmark to file - void SaveBookmark(EVT_HANDLE bookmarkHandle); - - // Load bookmark from file - EVT_HANDLE LoadBookmark(); - //TODO: doc std::string WcharVecToString(std::vector& buffer); - /// @brief - std::string Base64Encode(const std::string& input); - /// @brief std::string m_channel; /// @brief std::string m_query; - /// @brief - std::string m_bookmarkFile; - /// @brief std::time_t m_ChannelsRefreshInterval; - /// @brief true if bookmark is used - bool m_bookmarkEnabled; - /// @brief collector type const std::string m_collectorType = "eventchannel"; }; diff --git a/src/modules/logcollector/tests/unit/event_reader_win_test.cpp b/src/modules/logcollector/tests/unit/event_reader_win_test.cpp index 89a8df2eae..8a53602294 100644 --- a/src/modules/logcollector/tests/unit/event_reader_win_test.cpp +++ b/src/modules/logcollector/tests/unit/event_reader_win_test.cpp @@ -28,8 +28,3 @@ TEST(WindowsEventChannel, Several) { GTEST_SKIP(); } - -TEST(WindowsEventChannel, BookmarkUsage) -{ - GTEST_SKIP(); -} From 18656140aaa619f50d718c8938f0bedd985b1ac2 Mon Sep 17 00:00:00 2001 From: LucioDonda Date: Fri, 10 Jan 2025 22:00:44 +0000 Subject: [PATCH 15/21] feat: Adding windows specific tests WIP --- src/modules/logcollector/README.md | 1 - .../logcollector/src/event_reader_win.cpp | 12 +-- .../logcollector/src/event_reader_win.hpp | 2 +- .../tests/unit/event_reader_win_test.cpp | 93 +++++++++++++++++-- 4 files changed, 90 insertions(+), 18 deletions(-) diff --git a/src/modules/logcollector/README.md b/src/modules/logcollector/README.md index 5a4db8b257..c0c9416219 100644 --- a/src/modules/logcollector/README.md +++ b/src/modules/logcollector/README.md @@ -13,7 +13,6 @@ system API. ```yaml logcollector: enabled: true - use_bookmark: true channel_refresh: 5s file: - location: /var/log/*.log diff --git a/src/modules/logcollector/src/event_reader_win.cpp b/src/modules/logcollector/src/event_reader_win.cpp index 41641efafc..265a1fa25e 100644 --- a/src/modules/logcollector/src/event_reader_win.cpp +++ b/src/modules/logcollector/src/event_reader_win.cpp @@ -35,7 +35,7 @@ WindowsEventTracerReader::WindowsEventTracerReader(Logcollector &logcollector, Awaitable WindowsEventTracerReader::Run() { - m_logcollector.EnqueueTask(QueryEvents(m_channel, m_query)); + m_logcollector.EnqueueTask(QueryEvents()); co_return; } @@ -44,10 +44,10 @@ void WindowsEventTracerReader::Stop() m_keepRunning.store(false); } -Awaitable WindowsEventTracerReader::QueryEvents(const std::string channel, const std::string query) +Awaitable WindowsEventTracerReader::QueryEvents() { - std::wstring wideStringChannel = std::wstring(channel.begin(), channel.end()); - std::wstring wideStringQuery = std::wstring(query.begin(), query.end()); + std::wstring wideStringChannel = std::wstring(m_channel.begin(), m_channel.end()); + std::wstring wideStringQuery = std::wstring(m_query.begin(), m_query.end()); auto subscriptionCallback = [](EVT_SUBSCRIBE_NOTIFY_ACTION action, PVOID userContext, EVT_HANDLE event) -> DWORD @@ -87,14 +87,14 @@ Awaitable WindowsEventTracerReader::QueryEvents(const std::string channel, const co_return; } - LogInfo("Subscribed to events for {} channel with query: '{}'", channel, query); + LogInfo("Subscribed to events for {} channel with query: '{}'", m_channel, m_query); while (m_keepRunning.load()) { co_await m_logcollector.Wait(std::chrono::milliseconds(m_ChannelsRefreshInterval)); } - LogInfo("Unsubscribing to channel '{}'.", channel); + LogInfo("Unsubscribing to channel '{}'.", m_channel); if (subscriptionHandle) { EvtClose(subscriptionHandle); diff --git a/src/modules/logcollector/src/event_reader_win.hpp b/src/modules/logcollector/src/event_reader_win.hpp index 53503a0cc5..38b2a15acb 100644 --- a/src/modules/logcollector/src/event_reader_win.hpp +++ b/src/modules/logcollector/src/event_reader_win.hpp @@ -42,7 +42,7 @@ class WindowsEventTracerReader : public IReader void Stop() override; // Main function to execute the event query - Awaitable QueryEvents(const std::string channel, const std::string query); + Awaitable QueryEvents(); //TODO: doc std::string GetChannel() { return m_channel; }; diff --git a/src/modules/logcollector/tests/unit/event_reader_win_test.cpp b/src/modules/logcollector/tests/unit/event_reader_win_test.cpp index 8a53602294..0b0c39cb9a 100644 --- a/src/modules/logcollector/tests/unit/event_reader_win_test.cpp +++ b/src/modules/logcollector/tests/unit/event_reader_win_test.cpp @@ -1,30 +1,103 @@ +#define UNICODE #include -#include -#include -#include +#include #include "event_reader_win.hpp" +#include #include "logcollector_mock.hpp" -using namespace logcollector; +#include +#include + +class WindowsEventChannel : public ::testing::Test +{ +protected: + void SetUp() override { }; + + void GenerateTestEvent() + { + HANDLE eventLog = RegisterEventSource(nullptr, L"MyTestSource"); + + const wchar_t* message = L"Test Event Content."; + auto res = ReportEvent( + eventLog, + EVENTLOG_INFORMATION_TYPE, + 0, + 1001, + nullptr, + 1, + 0, + &message, + nullptr); + + res = 0; + DeregisterEventSource(eventLog); + }; -TEST(WindowsEventChannel, Constructor) + const std::string channelName {"Application"}; + const std::string query {"TestQuery"}; + +}; + +TEST_F(WindowsEventChannel, Constructor) { - GTEST_SKIP(); + auto logcollector = LogcollectorMock(); + EXPECT_NO_THROW(WindowsEventTracerReader(logcollector,"","", (time_t) 0 )); } -TEST(WindowsEventChannel, NoneEvent) +TEST_F(WindowsEventChannel, RunMethodEnqueueTask) { - GTEST_SKIP(); + auto mockedLogcollector = LogcollectorMock(); + auto reader = std::make_shared(mockedLogcollector, channelName, query, 5000); + EXPECT_EQ(reader->GetChannel(), channelName); + + EXPECT_CALL(mockedLogcollector, EnqueueTask(::testing::_)).Times(1); + EXPECT_CALL(mockedLogcollector, AddReader(::testing::_)); + + mockedLogcollector.AddReader(reader); } -TEST(WindowsEventChannel, SingleEvent) +TEST_F(WindowsEventChannel, AddReader) +{ + auto constexpr CONFIG_RAW = R"( + logcollector: + windows: + - channel: Application + query: Event[System/EventID = 4624] + - channel: System + query: Event[System/EventID = 7040] + )"; + + std::shared_ptr capturedReader1; + std::shared_ptr capturedReader2; + auto mockedLogcollector = LogcollectorMock(); + auto config = std::make_shared(std::string(CONFIG_RAW)); + + EXPECT_CALL(mockedLogcollector, AddReader(::testing::_)).Times(2) + .WillOnce(::testing::SaveArg<0>(&capturedReader1)) + .WillOnce(::testing::SaveArg<0>(&capturedReader2)); + + AddPlatformSpecificReader(config, mockedLogcollector); + + ASSERT_NE(capturedReader1, nullptr); + ASSERT_NE(capturedReader2, nullptr); +} + +TEST_F(WindowsEventChannel, NoneEvent) { GTEST_SKIP(); } -TEST(WindowsEventChannel, Several) +TEST_F(WindowsEventChannel, SingleEvent) +{ + auto mockedLogcollector = LogcollectorMock(); + auto reader = WindowsEventTracerReader(mockedLogcollector, channelName, query, 5000); + GenerateTestEvent(); + reader.QueryEvents(); +} + +TEST_F(WindowsEventChannel, Several) { GTEST_SKIP(); } From 651b49c7f418b3e1dcdc8e565a59fda8e35f3975 Mon Sep 17 00:00:00 2001 From: LucioDonda Date: Mon, 13 Jan 2025 15:06:55 +0000 Subject: [PATCH 16/21] feat: documentation comments and rebase corrections --- .../logcollector/include/logcollector.hpp | 9 ++--- .../logcollector/src/another_reader_unix.cpp | 1 + .../logcollector/src/event_reader_win.cpp | 8 ++-- .../logcollector/src/event_reader_win.hpp | 37 +++++++++---------- src/modules/logcollector/src/logcollector.cpp | 3 +- .../tests/unit/event_reader_win_test.cpp | 1 - .../tests/unit/file_reader_win_test.cpp | 4 +- 7 files changed, 29 insertions(+), 34 deletions(-) diff --git a/src/modules/logcollector/include/logcollector.hpp b/src/modules/logcollector/include/logcollector.hpp index ee37f1f555..6f8b074768 100644 --- a/src/modules/logcollector/include/logcollector.hpp +++ b/src/modules/logcollector/include/logcollector.hpp @@ -84,11 +84,6 @@ class Logcollector { /// @brief Clean all readers void CleanAllReaders(); -#ifdef _WIN32 - /// @brief Sets up the Windows Event Channel reader - /// @param configurationParser Configuration parser - void SetupWEReader(const std::shared_ptr configurationParser); -#endif private: /// @brief Module name @@ -116,7 +111,9 @@ class Logcollector { std::list m_timers; }; -//TODO:check +/// @brief Add platform specific implementation of IReader to logcollector. +/// @param ConfgurationParser where to get parameters. +/// @param logcollector instance. void AddPlatformSpecificReader(std::shared_ptr configurationParser, Logcollector &logcollector); } diff --git a/src/modules/logcollector/src/another_reader_unix.cpp b/src/modules/logcollector/src/another_reader_unix.cpp index 3c784e3200..28e2a69a7e 100644 --- a/src/modules/logcollector/src/another_reader_unix.cpp +++ b/src/modules/logcollector/src/another_reader_unix.cpp @@ -6,6 +6,7 @@ namespace logcollector { +//TODO: Delete once the unix implementation is ready void AddPlatformSpecificReader([[maybe_unused]] std::shared_ptr configurationParser, [[maybe_unused]] Logcollector &logcollector) { diff --git a/src/modules/logcollector/src/event_reader_win.cpp b/src/modules/logcollector/src/event_reader_win.cpp index 265a1fa25e..2b4b4842ac 100644 --- a/src/modules/logcollector/src/event_reader_win.cpp +++ b/src/modules/logcollector/src/event_reader_win.cpp @@ -57,7 +57,7 @@ Awaitable WindowsEventTracerReader::QueryEvents() switch (action) { case EvtSubscribeActionDeliver: - reader->ProcessEvent(event, reader->GetChannel()); + reader->ProcessEvent(event); break; case EvtSubscribeActionError: @@ -79,7 +79,7 @@ Awaitable WindowsEventTracerReader::QueryEvents() NULL, this, subscriptionCallback, - EvtSubscribeToFutureEvents); // EvtSubscribeStartAtOldestRecord + EvtSubscribeToFutureEvents); if (!subscriptionHandle) { @@ -101,7 +101,7 @@ Awaitable WindowsEventTracerReader::QueryEvents() } } -void WindowsEventTracerReader::ProcessEvent(EVT_HANDLE event, const std::string channel) +void WindowsEventTracerReader::ProcessEvent(EVT_HANDLE event) { DWORD bufferUsed = 0; DWORD propertyCount = 0; @@ -123,7 +123,7 @@ void WindowsEventTracerReader::ProcessEvent(EVT_HANDLE event, const std::string LogError("Cannot convert utf16 string: {}", e.what()); return; } - m_logcollector.SendMessage(channel, logString, m_collectorType); + m_logcollector.SendMessage(m_channel, logString, m_collectorType); } } } diff --git a/src/modules/logcollector/src/event_reader_win.hpp b/src/modules/logcollector/src/event_reader_win.hpp index 38b2a15acb..f0aab9e9c2 100644 --- a/src/modules/logcollector/src/event_reader_win.hpp +++ b/src/modules/logcollector/src/event_reader_win.hpp @@ -20,50 +20,49 @@ namespace logcollector { /// @brief Windows Event Tracer Reader class -/// TODO: doc class WindowsEventTracerReader : public IReader { public: /// @brief Constructor for the Windows Event Tracer Reader - /// @param logcollector Log collector instance - /// @param channels List of channel names - /// @param queries List of queries - /// @param reloadInterval + /// @param logcollector Log collector instance. + /// @param channel Channel name. + /// @param query Query. + /// @param channelRefreshInterval channel query refresh interval in millisecconds. WindowsEventTracerReader(Logcollector &logcollector, const std::string channel, const std::string query, const std::time_t channelRefreshInterval); - /// @brief Runs the file reader - /// @return Awaitable result + /// @brief Runs the event reader. + /// @return Awaitable result. Awaitable Run() override; - //TODO: doc + //@brief Stops the event reader. void Stop() override; - // Main function to execute the event query + ///@brief Main function to query events from event channel. Awaitable QueryEvents(); - //TODO: doc - std::string GetChannel() { return m_channel; }; - private: - // Process an individual event and print its XML representation - void ProcessEvent(EVT_HANDLE event, const std::string channel); + ///@brief Process an individual event and print its XML representation. + /// @param event subscription handle event. + void ProcessEvent(EVT_HANDLE event); - //TODO: doc + /// @brief Function for wchar to string convertion. + /// @param buffer wchar vector as input. + /// @return buffer converted to std::string. std::string WcharVecToString(std::vector& buffer); - /// @brief + /// @brief channel name. std::string m_channel; - /// @brief + /// @brief query string. std::string m_query; - /// @brief + /// @brief channel query refresh interval in millisecconds. std::time_t m_ChannelsRefreshInterval; - /// @brief collector type + /// @brief collector type. const std::string m_collectorType = "eventchannel"; }; } // namespace logcollector diff --git a/src/modules/logcollector/src/logcollector.cpp b/src/modules/logcollector/src/logcollector.cpp index 18996000a3..2d5f8361c9 100644 --- a/src/modules/logcollector/src/logcollector.cpp +++ b/src/modules/logcollector/src/logcollector.cpp @@ -129,8 +129,7 @@ void Logcollector::SendMessage(const std::string& location, const std::string& l auto message = Message(MessageType::STATELESS, data, m_moduleName, collectorType, metadata.dump()); m_pushMessage(message); - //TODO: undo - LogInfo("Message pushed: '{}':'{}'", location, log); + LogTrace("Message pushed: '{}':'{}'", location, log); } void Logcollector::AddReader(std::shared_ptr reader) diff --git a/src/modules/logcollector/tests/unit/event_reader_win_test.cpp b/src/modules/logcollector/tests/unit/event_reader_win_test.cpp index 0b0c39cb9a..f4fdf8b2bb 100644 --- a/src/modules/logcollector/tests/unit/event_reader_win_test.cpp +++ b/src/modules/logcollector/tests/unit/event_reader_win_test.cpp @@ -50,7 +50,6 @@ TEST_F(WindowsEventChannel, RunMethodEnqueueTask) { auto mockedLogcollector = LogcollectorMock(); auto reader = std::make_shared(mockedLogcollector, channelName, query, 5000); - EXPECT_EQ(reader->GetChannel(), channelName); EXPECT_CALL(mockedLogcollector, EnqueueTask(::testing::_)).Times(1); EXPECT_CALL(mockedLogcollector, AddReader(::testing::_)); diff --git a/src/modules/logcollector/tests/unit/file_reader_win_test.cpp b/src/modules/logcollector/tests/unit/file_reader_win_test.cpp index 4adf806834..0e080731d4 100644 --- a/src/modules/logcollector/tests/unit/file_reader_win_test.cpp +++ b/src/modules/logcollector/tests/unit/file_reader_win_test.cpp @@ -12,8 +12,8 @@ using namespace logcollector; static constexpr auto TMP_FILE_DIR = "C:\\Temp\\"; -inline std::string GetFullFileName(const std::string& filename) { - //TODO: move to setup stage of test only for windows +inline std::string GetFullFileName(const std::string& filename) +{ std::filesystem::create_directories(TMP_FILE_DIR); return TMP_FILE_DIR + filename; } From 9d8bcd7661415f4953d60ef798f1ab4794fdf1ab Mon Sep 17 00:00:00 2001 From: LucioDonda Date: Wed, 15 Jan 2025 01:55:34 +0000 Subject: [PATCH 17/21] feat: wrapping event widnoes event api for testing --- .../logcollector/src/event_reader_win.cpp | 26 ++-- .../logcollector/src/event_reader_win.hpp | 14 +- .../logcollector/src/winevt_wrapper_win.hpp | 72 ++++++++++ .../tests/unit/event_reader_mocks.hpp | 40 ++++++ .../tests/unit/event_reader_win_test.cpp | 123 +++++++++++++----- .../tests/unit/logcollector_mock.hpp | 10 ++ 6 files changed, 231 insertions(+), 54 deletions(-) create mode 100644 src/modules/logcollector/src/winevt_wrapper_win.hpp create mode 100644 src/modules/logcollector/tests/unit/event_reader_mocks.hpp diff --git a/src/modules/logcollector/src/event_reader_win.cpp b/src/modules/logcollector/src/event_reader_win.cpp index 2b4b4842ac..eb9828dc20 100644 --- a/src/modules/logcollector/src/event_reader_win.cpp +++ b/src/modules/logcollector/src/event_reader_win.cpp @@ -18,8 +18,8 @@ void AddPlatformSpecificReader(std::shared_ptr(logcollector, channel, query, refreshInterval)); } } @@ -27,11 +27,13 @@ void AddPlatformSpecificReader(std::shared_ptr winAPI) : IReader(logcollector), m_channel(channel), m_query(query), - m_ChannelsRefreshInterval(channelRefreshInterval) { } + m_ChannelsRefreshInterval(channelRefreshInterval), + m_winAPI(winAPI ? winAPI : std::make_shared()) { } Awaitable WindowsEventTracerReader::Run() { @@ -46,8 +48,8 @@ void WindowsEventTracerReader::Stop() Awaitable WindowsEventTracerReader::QueryEvents() { - std::wstring wideStringChannel = std::wstring(m_channel.begin(), m_channel.end()); - std::wstring wideStringQuery = std::wstring(m_query.begin(), m_query.end()); + const auto wideStringChannel = std::wstring(m_channel.begin(), m_channel.end()); + const auto wideStringQuery = std::wstring(m_query.begin(), m_query.end()); auto subscriptionCallback = [](EVT_SUBSCRIBE_NOTIFY_ACTION action, PVOID userContext, EVT_HANDLE event) -> DWORD @@ -61,7 +63,7 @@ Awaitable WindowsEventTracerReader::QueryEvents() break; case EvtSubscribeActionError: - //TODO: Handle this error + //Should this error be handled break; default: @@ -71,7 +73,7 @@ Awaitable WindowsEventTracerReader::QueryEvents() return ERROR_SUCCESS; }; - EVT_HANDLE subscriptionHandle = EvtSubscribe( + EVT_HANDLE subscriptionHandle = m_winAPI->EvtSubscribe( nullptr, nullptr, wideStringChannel.c_str(), @@ -87,7 +89,7 @@ Awaitable WindowsEventTracerReader::QueryEvents() co_return; } - LogInfo("Subscribed to events for {} channel with query: '{}'", m_channel, m_query); + LogInfo("Subscribed to events for '{}' channel with query: '{}'", m_channel, m_query); while (m_keepRunning.load()) { @@ -97,7 +99,7 @@ Awaitable WindowsEventTracerReader::QueryEvents() LogInfo("Unsubscribing to channel '{}'.", m_channel); if (subscriptionHandle) { - EvtClose(subscriptionHandle); + m_winAPI->EvtClose(subscriptionHandle); } } @@ -106,12 +108,12 @@ void WindowsEventTracerReader::ProcessEvent(EVT_HANDLE event) DWORD bufferUsed = 0; DWORD propertyCount = 0; - EvtRender(NULL, event, EvtRenderEventXml, 0, NULL, &bufferUsed, &propertyCount); + m_winAPI->EvtRender(NULL, event, EvtRenderEventXml, 0, NULL, &bufferUsed, &propertyCount); if (bufferUsed > 0) { std::vector buffer(bufferUsed); - if (EvtRender(NULL, event, EvtRenderEventXml, bufferUsed, buffer.data(), &bufferUsed, &propertyCount)) + if (m_winAPI->EvtRender(NULL, event, EvtRenderEventXml, bufferUsed, buffer.data(), &bufferUsed, &propertyCount)) { std::string logString; try diff --git a/src/modules/logcollector/src/event_reader_win.hpp b/src/modules/logcollector/src/event_reader_win.hpp index f0aab9e9c2..5c89323198 100644 --- a/src/modules/logcollector/src/event_reader_win.hpp +++ b/src/modules/logcollector/src/event_reader_win.hpp @@ -9,11 +9,8 @@ #include #include -#include -#include -#include - #include "reader.hpp" +#include "winevt_wrapper_win.hpp" #pragma comment(lib, "wevtapi.lib") @@ -28,10 +25,12 @@ class WindowsEventTracerReader : public IReader /// @param channel Channel name. /// @param query Query. /// @param channelRefreshInterval channel query refresh interval in millisecconds. + /// @param . WindowsEventTracerReader(Logcollector &logcollector, const std::string channel, const std::string query, - const std::time_t channelRefreshInterval); + const std::time_t channelRefreshInterval, + std::shared_ptr winAPI = nullptr); /// @brief Runs the event reader. /// @return Awaitable result. @@ -43,10 +42,10 @@ class WindowsEventTracerReader : public IReader ///@brief Main function to query events from event channel. Awaitable QueryEvents(); -private: ///@brief Process an individual event and print its XML representation. /// @param event subscription handle event. void ProcessEvent(EVT_HANDLE event); +private: /// @brief Function for wchar to string convertion. /// @param buffer wchar vector as input. @@ -62,6 +61,9 @@ class WindowsEventTracerReader : public IReader /// @brief channel query refresh interval in millisecconds. std::time_t m_ChannelsRefreshInterval; + /// @brief TODO + std::shared_ptr m_winAPI; + /// @brief collector type. const std::string m_collectorType = "eventchannel"; }; diff --git a/src/modules/logcollector/src/winevt_wrapper_win.hpp b/src/modules/logcollector/src/winevt_wrapper_win.hpp new file mode 100644 index 0000000000..e35c40227e --- /dev/null +++ b/src/modules/logcollector/src/winevt_wrapper_win.hpp @@ -0,0 +1,72 @@ +// winapi_wrapper.hpp +#pragma once + +#include +#include +#include + + +namespace logcollector { + +class IWinAPIWrapper +{ +public: + virtual ~IWinAPIWrapper() = default; + + virtual EVT_HANDLE EvtSubscribe( + HANDLE session, + HANDLE signalEvent, + LPCWSTR channelPath, + LPCWSTR query, + EVT_HANDLE bookmark, + PVOID context, + EVT_SUBSCRIBE_CALLBACK callback, + DWORD flags) = 0; + + virtual BOOL EvtRender( + EVT_HANDLE context, + EVT_HANDLE fragment, + DWORD flags, + DWORD bufferSize, + PVOID buffer, + DWORD* bufferUsed, + DWORD* propertyCount) = 0; + + virtual BOOL EvtClose(EVT_HANDLE object) = 0; +}; + +class DefaultWinAPIWrapper : public IWinAPIWrapper +{ +public: + EVT_HANDLE EvtSubscribe( + HANDLE session, + HANDLE signalEvent, + LPCWSTR channelPath, + LPCWSTR query, + EVT_HANDLE bookmark, + PVOID context, + EVT_SUBSCRIBE_CALLBACK callback, + DWORD flags) override + { + return ::EvtSubscribe(session, signalEvent, channelPath, query, bookmark, context, callback, flags); + } + + BOOL EvtRender( + EVT_HANDLE context, + EVT_HANDLE fragment, + DWORD flags, + DWORD bufferSize, + PVOID buffer, + DWORD* bufferUsed, + DWORD* propertyCount) override + { + return ::EvtRender(context, fragment, flags, bufferSize, buffer, bufferUsed, propertyCount); + } + + BOOL EvtClose(EVT_HANDLE object) override + { + return ::EvtClose(object); + } +}; + +} \ No newline at end of file diff --git a/src/modules/logcollector/tests/unit/event_reader_mocks.hpp b/src/modules/logcollector/tests/unit/event_reader_mocks.hpp new file mode 100644 index 0000000000..8ee6de1265 --- /dev/null +++ b/src/modules/logcollector/tests/unit/event_reader_mocks.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include + +#include "reader.hpp" +#include "winevt_wrapper_win.hpp" +#include "logcollector.hpp" + +using namespace logcollector; + +class MockWinAPIWrapper : public IWinAPIWrapper +{ +public: + MOCK_METHOD(EVT_HANDLE, EvtSubscribe, + (HANDLE session, HANDLE signalEvent, LPCWSTR channelPath, LPCWSTR query, + EVT_HANDLE bookmark, PVOID context, EVT_SUBSCRIBE_CALLBACK callback, DWORD flags), + (override)); + MOCK_METHOD(BOOL, EvtRender, (EVT_HANDLE, EVT_HANDLE, DWORD, DWORD, PVOID, DWORD*, DWORD*), + (override)); + MOCK_METHOD(BOOL, EvtClose, (EVT_HANDLE), (override)); + void TriggerCallback(EVT_SUBSCRIBE_NOTIFY_ACTION action, EVT_HANDLE event) { + if (storedCallback) { + storedCallback(action, storedContext, event); + } + } + EVT_HANDLE StoreCallback(HANDLE, HANDLE, LPCWSTR, LPCWSTR, EVT_HANDLE, PVOID context, + EVT_SUBSCRIBE_CALLBACK callback, DWORD) { + storedContext = context; + storedCallback = callback; + return mockSubscriptionHandle; + } + + EVT_HANDLE mockSubscriptionHandle = reinterpret_cast(1); + +private: + PVOID storedContext = nullptr; + EVT_SUBSCRIBE_CALLBACK storedCallback = nullptr; + +}; + diff --git a/src/modules/logcollector/tests/unit/event_reader_win_test.cpp b/src/modules/logcollector/tests/unit/event_reader_win_test.cpp index f4fdf8b2bb..a629c5dc74 100644 --- a/src/modules/logcollector/tests/unit/event_reader_win_test.cpp +++ b/src/modules/logcollector/tests/unit/event_reader_win_test.cpp @@ -1,43 +1,28 @@ -#define UNICODE #include #include -#include "event_reader_win.hpp" +#include #include +#include "event_reader_mocks.hpp" #include "logcollector_mock.hpp" #include #include +#include +#include +#include + +using namespace testing; class WindowsEventChannel : public ::testing::Test { protected: void SetUp() override { }; - void GenerateTestEvent() - { - HANDLE eventLog = RegisterEventSource(nullptr, L"MyTestSource"); - - const wchar_t* message = L"Test Event Content."; - auto res = ReportEvent( - eventLog, - EVENTLOG_INFORMATION_TYPE, - 0, - 1001, - nullptr, - 1, - 0, - &message, - nullptr); - - res = 0; - - DeregisterEventSource(eventLog); - }; - const std::string channelName {"Application"}; const std::string query {"TestQuery"}; - + const std::string collectorType {"eventchannel"}; + const time_t defaultChannelRefresh {50}; }; TEST_F(WindowsEventChannel, Constructor) @@ -51,8 +36,8 @@ TEST_F(WindowsEventChannel, RunMethodEnqueueTask) auto mockedLogcollector = LogcollectorMock(); auto reader = std::make_shared(mockedLogcollector, channelName, query, 5000); - EXPECT_CALL(mockedLogcollector, EnqueueTask(::testing::_)).Times(1); - EXPECT_CALL(mockedLogcollector, AddReader(::testing::_)); + EXPECT_CALL(mockedLogcollector, EnqueueTask(_)).Times(1); + EXPECT_CALL(mockedLogcollector, AddReader(_)); mockedLogcollector.AddReader(reader); } @@ -73,9 +58,9 @@ TEST_F(WindowsEventChannel, AddReader) auto mockedLogcollector = LogcollectorMock(); auto config = std::make_shared(std::string(CONFIG_RAW)); - EXPECT_CALL(mockedLogcollector, AddReader(::testing::_)).Times(2) - .WillOnce(::testing::SaveArg<0>(&capturedReader1)) - .WillOnce(::testing::SaveArg<0>(&capturedReader2)); + EXPECT_CALL(mockedLogcollector, AddReader(_)).Times(2) + .WillOnce(SaveArg<0>(&capturedReader1)) + .WillOnce(SaveArg<0>(&capturedReader2)); AddPlatformSpecificReader(config, mockedLogcollector); @@ -83,20 +68,86 @@ TEST_F(WindowsEventChannel, AddReader) ASSERT_NE(capturedReader2, nullptr); } -TEST_F(WindowsEventChannel, NoneEvent) +TEST_F(WindowsEventChannel, RunMethodFailedSubscription) { - GTEST_SKIP(); + auto mockedWinAPI = std::make_shared(); + auto mockedLogcollector = LogcollectorMock(); + + auto reader = std::make_shared( + mockedLogcollector, channelName, query, defaultChannelRefresh, mockedWinAPI); + + EVT_HANDLE mockHandle = reinterpret_cast(0); + EXPECT_CALL(*mockedWinAPI, EvtSubscribe(_, _, _, _,_, _, _, _)) + .WillOnce(Return(mockHandle)); + + auto task = reader->QueryEvents(); + boost::asio::io_context ioContext; + boost::asio::co_spawn(ioContext, std::move(task), boost::asio::detached); + + EXPECT_NO_THROW(ioContext.run()); } -TEST_F(WindowsEventChannel, SingleEvent) +TEST_F(WindowsEventChannel, RunMethodSuccessfulSubscription) { + auto mockedWinAPI = std::make_shared(); auto mockedLogcollector = LogcollectorMock(); - auto reader = WindowsEventTracerReader(mockedLogcollector, channelName, query, 5000); - GenerateTestEvent(); - reader.QueryEvents(); + + auto reader = std::make_shared( + mockedLogcollector, channelName, query, defaultChannelRefresh, mockedWinAPI); + + EXPECT_CALL(*mockedWinAPI, EvtSubscribe(_, _, _, _, _, _, _, _)) + .WillOnce([&mockedWinAPI](HANDLE session, HANDLE signalEvent, LPCWSTR channelPath, + LPCWSTR query, EVT_HANDLE bookmark, PVOID context, + EVT_SUBSCRIBE_CALLBACK callback, DWORD flags) { + return mockedWinAPI->StoreCallback(session, signalEvent, channelPath, query, bookmark, + context, callback, flags); + }); + + EXPECT_CALL(*mockedWinAPI, EvtClose(mockedWinAPI->mockSubscriptionHandle)) + .WillOnce(Return(TRUE)); + + auto task = reader->QueryEvents(); + boost::asio::io_context ioContext; + boost::asio::co_spawn(ioContext, std::move(task), boost::asio::detached); + + EVT_HANDLE mockEventHandle = reinterpret_cast(2); + mockedWinAPI->TriggerCallback(EvtSubscribeActionDeliver, mockEventHandle); + + reader->Stop(); + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + + EXPECT_NO_THROW(ioContext.run()); } -TEST_F(WindowsEventChannel, Several) +TEST_F(WindowsEventChannel, ProcessEvent_SendsMessage) { + //TODO: GTEST_SKIP(); + // Arrange: Create mocks + auto mockedWinAPI = std::make_shared(); + auto mockedLogcollector = LogcollectorMock(); + + auto reader = std::make_shared( + mockedLogcollector, channelName, query, defaultChannelRefresh, mockedWinAPI); + + // Mock EvtRender to return a fake event XML string + std::wstring fakeEventXml = L"Test Log"; + DWORD fakeBufferUsed = static_cast((fakeEventXml.size() + 1) * sizeof(wchar_t)); // Include null terminator + + EXPECT_CALL(*mockedWinAPI, EvtRender(_, _, EvtRenderEventXml, 0, nullptr, _, _)) + .WillOnce(DoAll(SetArgPointee<5>(fakeBufferUsed), Return(TRUE))); + + EXPECT_CALL(*mockedWinAPI, EvtRender(_, _, EvtRenderEventXml, fakeBufferUsed, _, _, _)) + .WillOnce(DoAll(Invoke([&fakeEventXml](EVT_HANDLE, EVT_HANDLE, DWORD, DWORD bufferSize, void* buffer, DWORD*, DWORD*) { + std::memcpy(buffer, fakeEventXml.data(), bufferSize); + }), + Return(TRUE))); + + // Expect the SendMessage method to be called with the processed log + // EXPECT_CALL(*mockedLogcollector, SendMessage(channelName, "Test Log", _)) + // .Times(1); + + // Act: Simulate an event + EVT_HANDLE mockEventHandle = reinterpret_cast(2); + reader->ProcessEvent(mockEventHandle); } diff --git a/src/modules/logcollector/tests/unit/logcollector_mock.hpp b/src/modules/logcollector/tests/unit/logcollector_mock.hpp index 524b40212a..c031fe2ac1 100644 --- a/src/modules/logcollector/tests/unit/logcollector_mock.hpp +++ b/src/modules/logcollector/tests/unit/logcollector_mock.hpp @@ -22,6 +22,16 @@ class LogcollectorMock : public Logcollector { } MOCK_METHOD(void, AddReader, (std::shared_ptr reader), (override)); MOCK_METHOD(void, EnqueueTask, (Awaitable task), (override)); + // TODO: + // MOCK_METHOD(void, SendMessage, (const std::string& channel, const std::string& message, int collectorType), ()); + boost::asio::awaitable Wait([[maybe_unused]]std::chrono::milliseconds ms) + { + return Logcollector::Wait(ms); + } + void Stop() + { + Logcollector::Stop(); + } }; class PushMessageMock { From a126bb6404604024b7445ed2abddcd15309b8bf1 Mon Sep 17 00:00:00 2001 From: LucioDonda Date: Wed, 15 Jan 2025 13:33:58 +0000 Subject: [PATCH 18/21] feat: additional test and logcollector arch separation --- .../logcollector/include/logcollector.hpp | 12 ++++---- .../logcollector/src/another_reader_unix.cpp | 15 ---------- .../logcollector/src/event_reader_win.cpp | 15 ---------- src/modules/logcollector/src/logcollector.cpp | 2 +- .../logcollector/src/logcollector_unix.cpp | 17 +++++++++++ .../logcollector/src/logcollector_win.cpp | 29 +++++++++++++++++++ .../tests/unit/event_reader_mocks.hpp | 22 ++++++++++++++ .../tests/unit/event_reader_win_test.cpp | 17 ++++------- .../tests/unit/logcollector_mock.hpp | 10 ------- 9 files changed, 80 insertions(+), 59 deletions(-) delete mode 100644 src/modules/logcollector/src/another_reader_unix.cpp create mode 100644 src/modules/logcollector/src/logcollector_unix.cpp create mode 100644 src/modules/logcollector/src/logcollector_win.cpp diff --git a/src/modules/logcollector/include/logcollector.hpp b/src/modules/logcollector/include/logcollector.hpp index 6f8b074768..e9b5a1c197 100644 --- a/src/modules/logcollector/include/logcollector.hpp +++ b/src/modules/logcollector/include/logcollector.hpp @@ -48,7 +48,7 @@ class Logcollector { /// @param log Message to send /// @param collectorType type of logcollector /// @pre The message queue must be set with SetMessageQueue - void SendMessage(const std::string& location, const std::string& log, const std::string& collectorType); + virtual void SendMessage(const std::string& location, const std::string& log, const std::string& collectorType); /// @brief Enqueues an ASIO task (coroutine) /// @param task Task to enqueue @@ -71,6 +71,11 @@ class Logcollector { return s_instance; } + /// @brief Add platform specific implementation of IReader to logcollector. + /// @param ConfgurationParser where to get parameters. + /// @param logcollector instance. + void AddPlatformSpecificReader(std::shared_ptr configurationParser); + protected: /// @brief Constructor Logcollector() { } @@ -111,9 +116,4 @@ class Logcollector { std::list m_timers; }; -/// @brief Add platform specific implementation of IReader to logcollector. -/// @param ConfgurationParser where to get parameters. -/// @param logcollector instance. -void AddPlatformSpecificReader(std::shared_ptr configurationParser, Logcollector &logcollector); - } diff --git a/src/modules/logcollector/src/another_reader_unix.cpp b/src/modules/logcollector/src/another_reader_unix.cpp deleted file mode 100644 index 28e2a69a7e..0000000000 --- a/src/modules/logcollector/src/another_reader_unix.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include -#include - -#include -#include - -namespace logcollector { - -//TODO: Delete once the unix implementation is ready -void AddPlatformSpecificReader([[maybe_unused]] std::shared_ptr configurationParser, [[maybe_unused]] Logcollector &logcollector) -{ - -} - -} \ No newline at end of file diff --git a/src/modules/logcollector/src/event_reader_win.cpp b/src/modules/logcollector/src/event_reader_win.cpp index eb9828dc20..e27e250e27 100644 --- a/src/modules/logcollector/src/event_reader_win.cpp +++ b/src/modules/logcollector/src/event_reader_win.cpp @@ -9,21 +9,6 @@ namespace logcollector { -void AddPlatformSpecificReader(std::shared_ptr configurationParser, Logcollector &logcollector) -{ - const auto refreshInterval = configurationParser->GetConfig("logcollector", "channel_refresh").value_or(config::logcollector::CHANNEL_REFRESH_INTERVAL); - - const auto windowsConfig = configurationParser->GetConfig>>("logcollector", "windows").value_or( - std::vector> {}); - - for (auto& entry : windowsConfig) - { - const auto channel = entry.at("channel"); - const auto query = entry.at("query"); - logcollector.AddReader(std::make_shared(logcollector, channel, query, refreshInterval)); - } -} - WindowsEventTracerReader::WindowsEventTracerReader(Logcollector &logcollector, const std::string channel, const std::string query, diff --git a/src/modules/logcollector/src/logcollector.cpp b/src/modules/logcollector/src/logcollector.cpp index 2d5f8361c9..8209814c78 100644 --- a/src/modules/logcollector/src/logcollector.cpp +++ b/src/modules/logcollector/src/logcollector.cpp @@ -73,7 +73,7 @@ void Logcollector::Setup(std::shared_ptr configurationParser) diff --git a/src/modules/logcollector/src/logcollector_unix.cpp b/src/modules/logcollector/src/logcollector_unix.cpp new file mode 100644 index 0000000000..da09ce9227 --- /dev/null +++ b/src/modules/logcollector/src/logcollector_unix.cpp @@ -0,0 +1,17 @@ +#include +#include + +#include +#include + +#include + +namespace logcollector +{ + +void Logcollector::AddPlatformSpecificReader([[maybe_unused]] std::shared_ptr configurationParser) +{ + +} + +} diff --git a/src/modules/logcollector/src/logcollector_win.cpp b/src/modules/logcollector/src/logcollector_win.cpp new file mode 100644 index 0000000000..99ba76f834 --- /dev/null +++ b/src/modules/logcollector/src/logcollector_win.cpp @@ -0,0 +1,29 @@ +#include "event_reader_win.hpp" + +#include +#include + +#include +#include + +#include + +namespace logcollector +{ + +void Logcollector::AddPlatformSpecificReader(std::shared_ptr configurationParser) +{ + const auto refreshInterval = configurationParser->GetConfig("logcollector", "channel_refresh").value_or(config::logcollector::CHANNEL_REFRESH_INTERVAL); + + const auto windowsConfig = configurationParser->GetConfig>>("logcollector", "windows").value_or( + std::vector> {}); + + for (auto& entry : windowsConfig) + { + const auto channel = entry.at("channel"); + const auto query = entry.at("query"); + AddReader(std::make_shared(*this, channel, query, refreshInterval)); + } +} + +} diff --git a/src/modules/logcollector/tests/unit/event_reader_mocks.hpp b/src/modules/logcollector/tests/unit/event_reader_mocks.hpp index 8ee6de1265..1dfb0609bf 100644 --- a/src/modules/logcollector/tests/unit/event_reader_mocks.hpp +++ b/src/modules/logcollector/tests/unit/event_reader_mocks.hpp @@ -38,3 +38,25 @@ class MockWinAPIWrapper : public IWinAPIWrapper }; +class LogcollectorMock : public Logcollector { +public: + LogcollectorMock() { + ON_CALL(*this, AddReader(::testing::_)) + .WillByDefault(::testing::Invoke([this](std::shared_ptr reader) { + this->Logcollector::AddReader(reader); + }) + ); + } + + MOCK_METHOD(void, AddReader, (std::shared_ptr reader), (override)); + MOCK_METHOD(void, EnqueueTask, (Awaitable task), (override)); + MOCK_METHOD(void, SendMessage, (const std::string& channel, const std::string& message, const std::string& collectorType), (override)); + boost::asio::awaitable Wait([[maybe_unused]]std::chrono::milliseconds ms) + { + return Logcollector::Wait(ms); + } + void Stop() + { + Logcollector::Stop(); + } +}; diff --git a/src/modules/logcollector/tests/unit/event_reader_win_test.cpp b/src/modules/logcollector/tests/unit/event_reader_win_test.cpp index a629c5dc74..113d040ba2 100644 --- a/src/modules/logcollector/tests/unit/event_reader_win_test.cpp +++ b/src/modules/logcollector/tests/unit/event_reader_win_test.cpp @@ -4,7 +4,6 @@ #include #include #include "event_reader_mocks.hpp" -#include "logcollector_mock.hpp" #include #include @@ -62,7 +61,7 @@ TEST_F(WindowsEventChannel, AddReader) .WillOnce(SaveArg<0>(&capturedReader1)) .WillOnce(SaveArg<0>(&capturedReader2)); - AddPlatformSpecificReader(config, mockedLogcollector); + mockedLogcollector.AddPlatformSpecificReader(config); ASSERT_NE(capturedReader1, nullptr); ASSERT_NE(capturedReader2, nullptr); @@ -119,20 +118,16 @@ TEST_F(WindowsEventChannel, RunMethodSuccessfulSubscription) EXPECT_NO_THROW(ioContext.run()); } -TEST_F(WindowsEventChannel, ProcessEvent_SendsMessage) +TEST_F(WindowsEventChannel, ProcessEventSendsMessage) { - //TODO: - GTEST_SKIP(); - // Arrange: Create mocks auto mockedWinAPI = std::make_shared(); auto mockedLogcollector = LogcollectorMock(); auto reader = std::make_shared( mockedLogcollector, channelName, query, defaultChannelRefresh, mockedWinAPI); - // Mock EvtRender to return a fake event XML string std::wstring fakeEventXml = L"Test Log"; - DWORD fakeBufferUsed = static_cast((fakeEventXml.size() + 1) * sizeof(wchar_t)); // Include null terminator + DWORD fakeBufferUsed = static_cast((fakeEventXml.size() + 1) * sizeof(wchar_t)); EXPECT_CALL(*mockedWinAPI, EvtRender(_, _, EvtRenderEventXml, 0, nullptr, _, _)) .WillOnce(DoAll(SetArgPointee<5>(fakeBufferUsed), Return(TRUE))); @@ -143,11 +138,9 @@ TEST_F(WindowsEventChannel, ProcessEvent_SendsMessage) }), Return(TRUE))); - // Expect the SendMessage method to be called with the processed log - // EXPECT_CALL(*mockedLogcollector, SendMessage(channelName, "Test Log", _)) - // .Times(1); + EXPECT_CALL(mockedLogcollector, SendMessage(channelName, "Test Log", collectorType)) + .Times(1); - // Act: Simulate an event EVT_HANDLE mockEventHandle = reinterpret_cast(2); reader->ProcessEvent(mockEventHandle); } diff --git a/src/modules/logcollector/tests/unit/logcollector_mock.hpp b/src/modules/logcollector/tests/unit/logcollector_mock.hpp index c031fe2ac1..524b40212a 100644 --- a/src/modules/logcollector/tests/unit/logcollector_mock.hpp +++ b/src/modules/logcollector/tests/unit/logcollector_mock.hpp @@ -22,16 +22,6 @@ class LogcollectorMock : public Logcollector { } MOCK_METHOD(void, AddReader, (std::shared_ptr reader), (override)); MOCK_METHOD(void, EnqueueTask, (Awaitable task), (override)); - // TODO: - // MOCK_METHOD(void, SendMessage, (const std::string& channel, const std::string& message, int collectorType), ()); - boost::asio::awaitable Wait([[maybe_unused]]std::chrono::milliseconds ms) - { - return Logcollector::Wait(ms); - } - void Stop() - { - Logcollector::Stop(); - } }; class PushMessageMock { From 50bb013dc68793d04f1805f8ee9a0b57beaba278 Mon Sep 17 00:00:00 2001 From: LucioDonda Date: Wed, 15 Jan 2025 21:26:59 +0000 Subject: [PATCH 19/21] feat: new namespace and several PR changes --- etc/config/wazuh-agent.yml | 6 ------ src/cmake/config.cmake | 2 +- src/common/config/include/config.h.in | 2 +- src/modules/logcollector/README.md | 6 ------ src/modules/logcollector/include/logcollector.hpp | 1 - src/modules/logcollector/src/event_reader_win.cpp | 4 ++-- src/modules/logcollector/src/event_reader_win.hpp | 8 +++++--- src/modules/logcollector/src/logcollector_unix.cpp | 4 ---- src/modules/logcollector/src/logcollector_win.cpp | 4 ++-- src/modules/logcollector/src/winevt_wrapper_win.hpp | 5 +++-- .../logcollector/tests/unit/event_reader_mocks.hpp | 5 ++++- .../logcollector/tests/unit/event_reader_win_test.cpp | 5 +++-- src/modules/logcollector/tests/unit/logcollector_mock.hpp | 5 ++++- 13 files changed, 25 insertions(+), 32 deletions(-) diff --git a/etc/config/wazuh-agent.yml b/etc/config/wazuh-agent.yml index 843ee854d3..32bd261c2c 100644 --- a/etc/config/wazuh-agent.yml +++ b/etc/config/wazuh-agent.yml @@ -24,9 +24,3 @@ logcollector: - /var/log/auth.log reload_interval: 1m file_wait: 500ms - channel_refresh: 5s - windows: - - channel: Application - query: Event[System/EventID = 4624] - - channel: System - query: Event[System/EventID = 7040] diff --git a/src/cmake/config.cmake b/src/cmake/config.cmake index 9cd5ba2e6d..1cc282aa82 100644 --- a/src/cmake/config.cmake +++ b/src/cmake/config.cmake @@ -34,7 +34,7 @@ set(DEFAULT_FILE_WAIT 500 CACHE STRING "Default Logcollector file reading interv set(DEFAULT_RELOAD_INTERVAL 60000 CACHE STRING "Default Logcollector reload interval (1m)") -set(CHANNEL_REFRESH_INTERVAL 5000 CACHE STRING "Default Logcollector reconnect time (5000ms)") +set(DEFAULT_CHANNEL_REFRESH_INTERVAL 5000 CACHE STRING "Default Logcollector Windows eventchannel reconnect time (5000ms)") set(DEFAULT_INVENTORY_ENABLED true CACHE BOOL "Default inventory enabled") diff --git a/src/common/config/include/config.h.in b/src/common/config/include/config.h.in index 5a0b674095..a766770f02 100644 --- a/src/common/config/include/config.h.in +++ b/src/common/config/include/config.h.in @@ -32,7 +32,7 @@ namespace config constexpr auto DEFAULT_FILE_WAIT = @DEFAULT_FILE_WAIT@; constexpr auto DEFAULT_RELOAD_INTERVAL = @DEFAULT_RELOAD_INTERVAL@; constexpr auto DEFAULT_LOCALFILES = "/var/log/auth.log"; - constexpr auto CHANNEL_REFRESH_INTERVAL = @CHANNEL_REFRESH_INTERVAL@; + constexpr auto DEFAULT_CHANNEL_REFRESH_INTERVAL = @DEFAULT_CHANNEL_REFRESH_INTERVAL@; } namespace inventory diff --git a/src/modules/logcollector/README.md b/src/modules/logcollector/README.md index c0c9416219..89a3d0afa0 100644 --- a/src/modules/logcollector/README.md +++ b/src/modules/logcollector/README.md @@ -13,17 +13,11 @@ system API. ```yaml logcollector: enabled: true - channel_refresh: 5s file: - location: /var/log/*.log - age: 1d - delim-regex: "\n" - use-bookmark: true windows: - channel: Application query: Event[System/EventID = 4624] - - channel: System - query: Event[System/EventID = 7040] journald: - filter: - field: "_SYSTEMD_UNIT" diff --git a/src/modules/logcollector/include/logcollector.hpp b/src/modules/logcollector/include/logcollector.hpp index e9b5a1c197..054842d295 100644 --- a/src/modules/logcollector/include/logcollector.hpp +++ b/src/modules/logcollector/include/logcollector.hpp @@ -73,7 +73,6 @@ class Logcollector { /// @brief Add platform specific implementation of IReader to logcollector. /// @param ConfgurationParser where to get parameters. - /// @param logcollector instance. void AddPlatformSpecificReader(std::shared_ptr configurationParser); protected: diff --git a/src/modules/logcollector/src/event_reader_win.cpp b/src/modules/logcollector/src/event_reader_win.cpp index e27e250e27..da99ec40a5 100644 --- a/src/modules/logcollector/src/event_reader_win.cpp +++ b/src/modules/logcollector/src/event_reader_win.cpp @@ -6,7 +6,7 @@ #include #include -namespace logcollector +namespace logcollector::winevt { WindowsEventTracerReader::WindowsEventTracerReader(Logcollector &logcollector, @@ -18,7 +18,7 @@ WindowsEventTracerReader::WindowsEventTracerReader(Logcollector &logcollector, m_channel(channel), m_query(query), m_ChannelsRefreshInterval(channelRefreshInterval), - m_winAPI(winAPI ? winAPI : std::make_shared()) { } + m_winAPI(winAPI ? winAPI : std::make_shared()){ } Awaitable WindowsEventTracerReader::Run() { diff --git a/src/modules/logcollector/src/event_reader_win.hpp b/src/modules/logcollector/src/event_reader_win.hpp index 5c89323198..c023a96fe6 100644 --- a/src/modules/logcollector/src/event_reader_win.hpp +++ b/src/modules/logcollector/src/event_reader_win.hpp @@ -14,7 +14,8 @@ #pragma comment(lib, "wevtapi.lib") -namespace logcollector { +namespace logcollector::winevt +{ /// @brief Windows Event Tracer Reader class class WindowsEventTracerReader : public IReader @@ -61,10 +62,11 @@ class WindowsEventTracerReader : public IReader /// @brief channel query refresh interval in millisecconds. std::time_t m_ChannelsRefreshInterval; - /// @brief TODO + /// @brief winevt wrapper for overriding while testing. std::shared_ptr m_winAPI; /// @brief collector type. const std::string m_collectorType = "eventchannel"; }; -} // namespace logcollector + +} // namespace logcollector::winevt diff --git a/src/modules/logcollector/src/logcollector_unix.cpp b/src/modules/logcollector/src/logcollector_unix.cpp index da09ce9227..3a5fd75f39 100644 --- a/src/modules/logcollector/src/logcollector_unix.cpp +++ b/src/modules/logcollector/src/logcollector_unix.cpp @@ -1,8 +1,4 @@ #include -#include - -#include -#include #include diff --git a/src/modules/logcollector/src/logcollector_win.cpp b/src/modules/logcollector/src/logcollector_win.cpp index 99ba76f834..9a4a581e64 100644 --- a/src/modules/logcollector/src/logcollector_win.cpp +++ b/src/modules/logcollector/src/logcollector_win.cpp @@ -13,7 +13,7 @@ namespace logcollector void Logcollector::AddPlatformSpecificReader(std::shared_ptr configurationParser) { - const auto refreshInterval = configurationParser->GetConfig("logcollector", "channel_refresh").value_or(config::logcollector::CHANNEL_REFRESH_INTERVAL); + const auto refreshInterval = configurationParser->GetConfig("logcollector", "channel_refresh").value_or(config::logcollector::DEFAULT_CHANNEL_REFRESH_INTERVAL); const auto windowsConfig = configurationParser->GetConfig>>("logcollector", "windows").value_or( std::vector> {}); @@ -22,7 +22,7 @@ void Logcollector::AddPlatformSpecificReader(std::shared_ptr(*this, channel, query, refreshInterval)); + AddReader(std::make_shared(*this, channel, query, refreshInterval)); } } diff --git a/src/modules/logcollector/src/winevt_wrapper_win.hpp b/src/modules/logcollector/src/winevt_wrapper_win.hpp index e35c40227e..1d201e8985 100644 --- a/src/modules/logcollector/src/winevt_wrapper_win.hpp +++ b/src/modules/logcollector/src/winevt_wrapper_win.hpp @@ -6,7 +6,8 @@ #include -namespace logcollector { +namespace logcollector::winevt +{ class IWinAPIWrapper { @@ -69,4 +70,4 @@ class DefaultWinAPIWrapper : public IWinAPIWrapper } }; -} \ No newline at end of file +} diff --git a/src/modules/logcollector/tests/unit/event_reader_mocks.hpp b/src/modules/logcollector/tests/unit/event_reader_mocks.hpp index 1dfb0609bf..be0cee51a3 100644 --- a/src/modules/logcollector/tests/unit/event_reader_mocks.hpp +++ b/src/modules/logcollector/tests/unit/event_reader_mocks.hpp @@ -6,7 +6,8 @@ #include "winevt_wrapper_win.hpp" #include "logcollector.hpp" -using namespace logcollector; +namespace logcollector::winevt +{ class MockWinAPIWrapper : public IWinAPIWrapper { @@ -60,3 +61,5 @@ class LogcollectorMock : public Logcollector { Logcollector::Stop(); } }; + +} diff --git a/src/modules/logcollector/tests/unit/event_reader_win_test.cpp b/src/modules/logcollector/tests/unit/event_reader_win_test.cpp index 113d040ba2..4f872a9a73 100644 --- a/src/modules/logcollector/tests/unit/event_reader_win_test.cpp +++ b/src/modules/logcollector/tests/unit/event_reader_win_test.cpp @@ -12,6 +12,7 @@ #include using namespace testing; +using namespace logcollector::winevt; class WindowsEventChannel : public ::testing::Test { @@ -52,8 +53,8 @@ TEST_F(WindowsEventChannel, AddReader) query: Event[System/EventID = 7040] )"; - std::shared_ptr capturedReader1; - std::shared_ptr capturedReader2; + std::shared_ptr capturedReader1; + std::shared_ptr capturedReader2; auto mockedLogcollector = LogcollectorMock(); auto config = std::make_shared(std::string(CONFIG_RAW)); diff --git a/src/modules/logcollector/tests/unit/logcollector_mock.hpp b/src/modules/logcollector/tests/unit/logcollector_mock.hpp index 524b40212a..781f81ca0d 100644 --- a/src/modules/logcollector/tests/unit/logcollector_mock.hpp +++ b/src/modules/logcollector/tests/unit/logcollector_mock.hpp @@ -4,7 +4,8 @@ #include #include -using namespace logcollector; +namespace logcollector +{ class LogcollectorMock : public Logcollector { public: @@ -28,3 +29,5 @@ class PushMessageMock { public: MOCK_METHOD(int, Call, (Message), ()); }; + +} From b8b0bae5980d9ae0838bf8a240d45ad50aaa1a1d Mon Sep 17 00:00:00 2001 From: LucioDonda Date: Thu, 16 Jan 2025 18:13:03 +0000 Subject: [PATCH 20/21] feat: scaffolding rework --- src/modules/logcollector/CMakeLists.txt | 6 +- .../logcollector/include/logcollector.hpp | 2 +- .../logcollector/src/logcollector_win.cpp | 2 +- .../src}/event_reader_win.cpp | 2 +- .../src}/event_reader_win.hpp | 6 +- .../src}/winevt_wrapper_win.hpp | 0 .../logcollector/tests/unit/CMakeLists.txt | 1 + .../tests/unit/event_reader_mocks.hpp | 2 +- .../tests/unit/event_reader_win_test.cpp | 78 ------------------- 9 files changed, 13 insertions(+), 86 deletions(-) rename src/modules/logcollector/src/{ => winevt_reader/src}/event_reader_win.cpp (98%) rename src/modules/logcollector/src/{ => winevt_reader/src}/event_reader_win.hpp (96%) rename src/modules/logcollector/src/{ => winevt_reader/src}/winevt_wrapper_win.hpp (100%) diff --git a/src/modules/logcollector/CMakeLists.txt b/src/modules/logcollector/CMakeLists.txt index 3620324796..9c3f69aaf6 100644 --- a/src/modules/logcollector/CMakeLists.txt +++ b/src/modules/logcollector/CMakeLists.txt @@ -18,13 +18,16 @@ find_package(nlohmann_json CONFIG REQUIRED) find_package(OpenSSL REQUIRED) find_package(Boost REQUIRED COMPONENTS asio system) +FILE(GLOB LOGCOLLECTOR_SOURCES src/*.cpp) + if(WIN32) + FILE(GLOB WINEVT_READER_SOURCES src/winevt_reader/src/*.cpp) + list(APPEND LOGCOLLECTOR_SOURCES ${WINEVT_READER_SOURCES}) FILE(GLOB EXCLUDED_SOURCES src/*_unix.cpp) else() FILE(GLOB EXCLUDED_SOURCES src/*_win.cpp) endif() -FILE(GLOB LOGCOLLECTOR_SOURCES src/*.cpp) list(REMOVE_ITEM LOGCOLLECTOR_SOURCES ${EXCLUDED_SOURCES}) add_library(Logcollector ${LOGCOLLECTOR_SOURCES}) @@ -32,6 +35,7 @@ add_library(Logcollector ${LOGCOLLECTOR_SOURCES}) target_include_directories(Logcollector PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/../include + ${CMAKE_CURRENT_SOURCE_DIR}/src/winevt_reader/src/ ${COMMON_FOLDER} ${COMMON_FOLDER}/stringHelper/include ${COMMON_FOLDER}/timeHelper/include) diff --git a/src/modules/logcollector/include/logcollector.hpp b/src/modules/logcollector/include/logcollector.hpp index 054842d295..c4f2850230 100644 --- a/src/modules/logcollector/include/logcollector.hpp +++ b/src/modules/logcollector/include/logcollector.hpp @@ -72,7 +72,7 @@ class Logcollector { } /// @brief Add platform specific implementation of IReader to logcollector. - /// @param ConfgurationParser where to get parameters. + /// @param ConfigurationParser where to get parameters. void AddPlatformSpecificReader(std::shared_ptr configurationParser); protected: diff --git a/src/modules/logcollector/src/logcollector_win.cpp b/src/modules/logcollector/src/logcollector_win.cpp index 9a4a581e64..3a58e8445c 100644 --- a/src/modules/logcollector/src/logcollector_win.cpp +++ b/src/modules/logcollector/src/logcollector_win.cpp @@ -6,7 +6,7 @@ #include #include -#include +#include "logcollector.hpp" namespace logcollector { diff --git a/src/modules/logcollector/src/event_reader_win.cpp b/src/modules/logcollector/src/winevt_reader/src/event_reader_win.cpp similarity index 98% rename from src/modules/logcollector/src/event_reader_win.cpp rename to src/modules/logcollector/src/winevt_reader/src/event_reader_win.cpp index da99ec40a5..d0dd910901 100644 --- a/src/modules/logcollector/src/event_reader_win.cpp +++ b/src/modules/logcollector/src/winevt_reader/src/event_reader_win.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include "../../../include/logcollector.hpp" namespace logcollector::winevt { diff --git a/src/modules/logcollector/src/event_reader_win.hpp b/src/modules/logcollector/src/winevt_reader/src/event_reader_win.hpp similarity index 96% rename from src/modules/logcollector/src/event_reader_win.hpp rename to src/modules/logcollector/src/winevt_reader/src/event_reader_win.hpp index c023a96fe6..8b8d4a68b3 100644 --- a/src/modules/logcollector/src/event_reader_win.hpp +++ b/src/modules/logcollector/src/winevt_reader/src/event_reader_win.hpp @@ -9,7 +9,7 @@ #include #include -#include "reader.hpp" +#include "../../reader.hpp" #include "winevt_wrapper_win.hpp" #pragma comment(lib, "wevtapi.lib") @@ -26,7 +26,7 @@ class WindowsEventTracerReader : public IReader /// @param channel Channel name. /// @param query Query. /// @param channelRefreshInterval channel query refresh interval in millisecconds. - /// @param . + /// @param winAPI wrapper of winevt methods. WindowsEventTracerReader(Logcollector &logcollector, const std::string channel, const std::string query, @@ -40,13 +40,13 @@ class WindowsEventTracerReader : public IReader //@brief Stops the event reader. void Stop() override; +private: ///@brief Main function to query events from event channel. Awaitable QueryEvents(); ///@brief Process an individual event and print its XML representation. /// @param event subscription handle event. void ProcessEvent(EVT_HANDLE event); -private: /// @brief Function for wchar to string convertion. /// @param buffer wchar vector as input. diff --git a/src/modules/logcollector/src/winevt_wrapper_win.hpp b/src/modules/logcollector/src/winevt_reader/src/winevt_wrapper_win.hpp similarity index 100% rename from src/modules/logcollector/src/winevt_wrapper_win.hpp rename to src/modules/logcollector/src/winevt_reader/src/winevt_wrapper_win.hpp diff --git a/src/modules/logcollector/tests/unit/CMakeLists.txt b/src/modules/logcollector/tests/unit/CMakeLists.txt index a60a7b07f7..0b1d5537af 100644 --- a/src/modules/logcollector/tests/unit/CMakeLists.txt +++ b/src/modules/logcollector/tests/unit/CMakeLists.txt @@ -15,6 +15,7 @@ configure_target(logcollector_unit_tests) target_include_directories(logcollector_unit_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../src + ${CMAKE_CURRENT_SOURCE_DIR}/../../src/winevt_reader/src ) target_link_libraries(logcollector_unit_tests PRIVATE diff --git a/src/modules/logcollector/tests/unit/event_reader_mocks.hpp b/src/modules/logcollector/tests/unit/event_reader_mocks.hpp index be0cee51a3..ff6c8be93b 100644 --- a/src/modules/logcollector/tests/unit/event_reader_mocks.hpp +++ b/src/modules/logcollector/tests/unit/event_reader_mocks.hpp @@ -62,4 +62,4 @@ class LogcollectorMock : public Logcollector { } }; -} +} \ No newline at end of file diff --git a/src/modules/logcollector/tests/unit/event_reader_win_test.cpp b/src/modules/logcollector/tests/unit/event_reader_win_test.cpp index 4f872a9a73..0d10505714 100644 --- a/src/modules/logcollector/tests/unit/event_reader_win_test.cpp +++ b/src/modules/logcollector/tests/unit/event_reader_win_test.cpp @@ -67,81 +67,3 @@ TEST_F(WindowsEventChannel, AddReader) ASSERT_NE(capturedReader1, nullptr); ASSERT_NE(capturedReader2, nullptr); } - -TEST_F(WindowsEventChannel, RunMethodFailedSubscription) -{ - auto mockedWinAPI = std::make_shared(); - auto mockedLogcollector = LogcollectorMock(); - - auto reader = std::make_shared( - mockedLogcollector, channelName, query, defaultChannelRefresh, mockedWinAPI); - - EVT_HANDLE mockHandle = reinterpret_cast(0); - EXPECT_CALL(*mockedWinAPI, EvtSubscribe(_, _, _, _,_, _, _, _)) - .WillOnce(Return(mockHandle)); - - auto task = reader->QueryEvents(); - boost::asio::io_context ioContext; - boost::asio::co_spawn(ioContext, std::move(task), boost::asio::detached); - - EXPECT_NO_THROW(ioContext.run()); -} - -TEST_F(WindowsEventChannel, RunMethodSuccessfulSubscription) -{ - auto mockedWinAPI = std::make_shared(); - auto mockedLogcollector = LogcollectorMock(); - - auto reader = std::make_shared( - mockedLogcollector, channelName, query, defaultChannelRefresh, mockedWinAPI); - - EXPECT_CALL(*mockedWinAPI, EvtSubscribe(_, _, _, _, _, _, _, _)) - .WillOnce([&mockedWinAPI](HANDLE session, HANDLE signalEvent, LPCWSTR channelPath, - LPCWSTR query, EVT_HANDLE bookmark, PVOID context, - EVT_SUBSCRIBE_CALLBACK callback, DWORD flags) { - return mockedWinAPI->StoreCallback(session, signalEvent, channelPath, query, bookmark, - context, callback, flags); - }); - - EXPECT_CALL(*mockedWinAPI, EvtClose(mockedWinAPI->mockSubscriptionHandle)) - .WillOnce(Return(TRUE)); - - auto task = reader->QueryEvents(); - boost::asio::io_context ioContext; - boost::asio::co_spawn(ioContext, std::move(task), boost::asio::detached); - - EVT_HANDLE mockEventHandle = reinterpret_cast(2); - mockedWinAPI->TriggerCallback(EvtSubscribeActionDeliver, mockEventHandle); - - reader->Stop(); - std::this_thread::sleep_for(std::chrono::milliseconds(50)); - - EXPECT_NO_THROW(ioContext.run()); -} - -TEST_F(WindowsEventChannel, ProcessEventSendsMessage) -{ - auto mockedWinAPI = std::make_shared(); - auto mockedLogcollector = LogcollectorMock(); - - auto reader = std::make_shared( - mockedLogcollector, channelName, query, defaultChannelRefresh, mockedWinAPI); - - std::wstring fakeEventXml = L"Test Log"; - DWORD fakeBufferUsed = static_cast((fakeEventXml.size() + 1) * sizeof(wchar_t)); - - EXPECT_CALL(*mockedWinAPI, EvtRender(_, _, EvtRenderEventXml, 0, nullptr, _, _)) - .WillOnce(DoAll(SetArgPointee<5>(fakeBufferUsed), Return(TRUE))); - - EXPECT_CALL(*mockedWinAPI, EvtRender(_, _, EvtRenderEventXml, fakeBufferUsed, _, _, _)) - .WillOnce(DoAll(Invoke([&fakeEventXml](EVT_HANDLE, EVT_HANDLE, DWORD, DWORD bufferSize, void* buffer, DWORD*, DWORD*) { - std::memcpy(buffer, fakeEventXml.data(), bufferSize); - }), - Return(TRUE))); - - EXPECT_CALL(mockedLogcollector, SendMessage(channelName, "Test Log", collectorType)) - .Times(1); - - EVT_HANDLE mockEventHandle = reinterpret_cast(2); - reader->ProcessEvent(mockEventHandle); -} From 0709df9aed79431afd354da9628fd661c1734ef4 Mon Sep 17 00:00:00 2001 From: LucioDonda Date: Fri, 17 Jan 2025 13:42:37 +0000 Subject: [PATCH 21/21] feat: PR corrections --- src/modules/logcollector/CMakeLists.txt | 5 +++-- src/modules/logcollector/src/logcollector_win.cpp | 6 +++--- .../logcollector/src/winevt_reader/src/event_reader_win.cpp | 2 +- src/modules/logcollector/tests/unit/CMakeLists.txt | 2 +- src/modules/logcollector/tests/unit/event_reader_mocks.hpp | 4 ++-- 5 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/modules/logcollector/CMakeLists.txt b/src/modules/logcollector/CMakeLists.txt index 9c3f69aaf6..5dba623fe7 100644 --- a/src/modules/logcollector/CMakeLists.txt +++ b/src/modules/logcollector/CMakeLists.txt @@ -35,10 +35,11 @@ add_library(Logcollector ${LOGCOLLECTOR_SOURCES}) target_include_directories(Logcollector PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/../include - ${CMAKE_CURRENT_SOURCE_DIR}/src/winevt_reader/src/ ${COMMON_FOLDER} ${COMMON_FOLDER}/stringHelper/include - ${COMMON_FOLDER}/timeHelper/include) + ${COMMON_FOLDER}/timeHelper/include + PRIVATE + $<$:${CMAKE_CURRENT_SOURCE_DIR}/src/winevt_reader/src/>) target_link_libraries(Logcollector PUBLIC diff --git a/src/modules/logcollector/src/logcollector_win.cpp b/src/modules/logcollector/src/logcollector_win.cpp index 3a58e8445c..eb346540c2 100644 --- a/src/modules/logcollector/src/logcollector_win.cpp +++ b/src/modules/logcollector/src/logcollector_win.cpp @@ -15,13 +15,13 @@ void Logcollector::AddPlatformSpecificReader(std::shared_ptrGetConfig("logcollector", "channel_refresh").value_or(config::logcollector::DEFAULT_CHANNEL_REFRESH_INTERVAL); - const auto windowsConfig = configurationParser->GetConfig>>("logcollector", "windows").value_or( + auto windowsConfig = configurationParser->GetConfig>>("logcollector", "windows").value_or( std::vector> {}); for (auto& entry : windowsConfig) { - const auto channel = entry.at("channel"); - const auto query = entry.at("query"); + const auto channel = entry["channel"]; + const auto query = entry["query"]; AddReader(std::make_shared(*this, channel, query, refreshInterval)); } } diff --git a/src/modules/logcollector/src/winevt_reader/src/event_reader_win.cpp b/src/modules/logcollector/src/winevt_reader/src/event_reader_win.cpp index d0dd910901..da99ec40a5 100644 --- a/src/modules/logcollector/src/winevt_reader/src/event_reader_win.cpp +++ b/src/modules/logcollector/src/winevt_reader/src/event_reader_win.cpp @@ -4,7 +4,7 @@ #include #include -#include "../../../include/logcollector.hpp" +#include namespace logcollector::winevt { diff --git a/src/modules/logcollector/tests/unit/CMakeLists.txt b/src/modules/logcollector/tests/unit/CMakeLists.txt index 0b1d5537af..a075ec7a1e 100644 --- a/src/modules/logcollector/tests/unit/CMakeLists.txt +++ b/src/modules/logcollector/tests/unit/CMakeLists.txt @@ -15,7 +15,7 @@ configure_target(logcollector_unit_tests) target_include_directories(logcollector_unit_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../src - ${CMAKE_CURRENT_SOURCE_DIR}/../../src/winevt_reader/src + $<$:${CMAKE_CURRENT_SOURCE_DIR}/../../src/winevt_reader/src> ) target_link_libraries(logcollector_unit_tests PRIVATE diff --git a/src/modules/logcollector/tests/unit/event_reader_mocks.hpp b/src/modules/logcollector/tests/unit/event_reader_mocks.hpp index ff6c8be93b..e8e4092deb 100644 --- a/src/modules/logcollector/tests/unit/event_reader_mocks.hpp +++ b/src/modules/logcollector/tests/unit/event_reader_mocks.hpp @@ -54,7 +54,7 @@ class LogcollectorMock : public Logcollector { MOCK_METHOD(void, SendMessage, (const std::string& channel, const std::string& message, const std::string& collectorType), (override)); boost::asio::awaitable Wait([[maybe_unused]]std::chrono::milliseconds ms) { - return Logcollector::Wait(ms); + co_return; } void Stop() { @@ -62,4 +62,4 @@ class LogcollectorMock : public Logcollector { } }; -} \ No newline at end of file +}