From 8b9fa14786c07b99471e2f0829b854c2a49afc80 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marcel=20Kemp=20Mu=C3=B1oz?= <marcel.kemp@wazuh.com>
Date: Tue, 26 Nov 2024 16:43:24 +0100
Subject: [PATCH 1/2] feat: backport WMI and WUA API integration

Merge pull request #26706 from wazuh/enhancement/25766-develop-integration-with-wmi-api-to-retrieve-installed-windows-updates

Develop Integration with WMI and WUA APIs to Retrieve Installed Windows Updates
---
 src/common/data_provider/CMakeLists.txt       |   2 +-
 src/common/data_provider/src/sysInfoWin.cpp   |  37 +++
 .../data_provider/src/utilsWrapperWin.cpp     | 255 ++++++++++++++++++
 .../data_provider/src/utilsWrapperWin.hpp     |  79 ++++++
 .../tests/sysInfoWin/CMakeLists.txt           |  12 +-
 .../tests/sysInfoWin/sysInfoWin_test.cpp      | 178 +++++++++++-
 .../tests/sysInfoWin/sysInfoWin_test.h        |  20 ++
 7 files changed, 576 insertions(+), 7 deletions(-)
 create mode 100644 src/common/data_provider/src/utilsWrapperWin.cpp
 create mode 100644 src/common/data_provider/src/utilsWrapperWin.hpp

diff --git a/src/common/data_provider/CMakeLists.txt b/src/common/data_provider/CMakeLists.txt
index e685481eeac..842b5c92aaa 100644
--- a/src/common/data_provider/CMakeLists.txt
+++ b/src/common/data_provider/CMakeLists.txt
@@ -168,7 +168,7 @@ add_library(sysinfo STATIC
 target_include_directories(sysinfo PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include/)
 
 if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
-  target_link_libraries(sysinfo PUBLIC psapi iphlpapi ws2_32)
+  target_link_libraries(sysinfo PUBLIC psapi iphlpapi ws2_32 wbemuuid uuid wuguid ole32 oleaut32)
 elseif(APPLE)
   find_library(iokit_lib IOKit)
   if(NOT iokit_lib)
diff --git a/src/common/data_provider/src/sysInfoWin.cpp b/src/common/data_provider/src/sysInfoWin.cpp
index cda3e8a9cb3..1ed90b90b5d 100644
--- a/src/common/data_provider/src/sysInfoWin.cpp
+++ b/src/common/data_provider/src/sysInfoWin.cpp
@@ -41,12 +41,15 @@
 #include "packages/packagesPYPI.hpp"
 #include "packages/packagesNPM.hpp"
 #include "packages/modernPackageDataRetriever.hpp"
+#include "utilsWrapperWin.hpp"
+
 
 constexpr auto CENTRAL_PROCESSOR_REGISTRY {"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0"};
 const std::string UNINSTALL_REGISTRY{"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"};
 constexpr auto SYSTEM_IDLE_PROCESS_NAME {"System Idle Process"};
 constexpr auto SYSTEM_PROCESS_NAME {"System"};
 
+
 static const std::map<std::string, DWORD> gs_firmwareTableProviderSignature
 {
     {"ACPI", 0x41435049},
@@ -912,9 +915,43 @@ void SysInfo::getPackages(std::function<void(nlohmann::json&)> callback) const
 
     ModernFactoryPackagesCreator<HAS_STDFILESYSTEM>::getPackages(searchPaths, callback);
 }
+
 nlohmann::json SysInfo::getHotfixes() const
 {
     std::set<std::string> hotfixes;
+    std::ostringstream oss;
+    ComHelper comHelper;
+
+    // Initialize COM
+    HRESULT hres = CoInitializeEx(0, COINIT_MULTITHREADED);
+
+    if (SUCCEEDED(hres))
+    {
+        try
+        {
+            // Query hotfixes using WMI
+            QueryWMIHotFixes(hotfixes, comHelper);
+        }
+        catch (...)
+        {
+            // Ignore the error. The OS does not support WMI API.
+        }
+
+
+        try
+        {
+            // Query hotfixes using Windows Update API
+            QueryWUHotFixes(hotfixes, comHelper);
+        }
+        catch (...)
+        {
+            // Ignore the error. The OS does not support WUA API.
+        }
+
+        // Uninitialize COM
+        CoUninitialize();
+    }
+
     PackageWindowsHelper::getHotFixFromReg(HKEY_LOCAL_MACHINE, PackageWindowsHelper::WIN_REG_HOTFIX, hotfixes);
     PackageWindowsHelper::getHotFixFromRegNT(HKEY_LOCAL_MACHINE, PackageWindowsHelper::VISTA_REG_HOTFIX, hotfixes);
     PackageWindowsHelper::getHotFixFromRegWOW(HKEY_LOCAL_MACHINE, PackageWindowsHelper::WIN_REG_WOW_HOTFIX, hotfixes);
diff --git a/src/common/data_provider/src/utilsWrapperWin.cpp b/src/common/data_provider/src/utilsWrapperWin.cpp
new file mode 100644
index 00000000000..ab7781238e0
--- /dev/null
+++ b/src/common/data_provider/src/utilsWrapperWin.cpp
@@ -0,0 +1,255 @@
+/*
+ * Wazuh SysInfo
+ * Copyright (C) 2015, Wazuh Inc.
+ * December 22, 2021.
+ *
+ * This program is free software; you can redistribute it
+ * and/or modify it under the terms of the GNU General Public
+ * License (version 2) as published by the FSF - Free Software
+ * Foundation
+ */
+
+#include <regex>
+#include <sstream>        // For std::ostringstream
+#include <iomanip>        // For std::hex
+#include <stdexcept>      // For std::runtime_error
+#include <string>         // For std::string and std::wstring
+#include <locale>         // For localization utilities (if needed for string conversion)
+
+#define WMI_WUA_DLL
+#include "utilsWrapperWin.hpp"
+
+
+// Implement WMI functions
+HRESULT ComHelper::CreateWmiLocator(IWbemLocator*& pLoc)
+{
+    return CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*)&pLoc);
+}
+
+HRESULT ComHelper::ConnectToWmiServer(IWbemLocator* pLoc, IWbemServices*& pSvc)
+{
+    return pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"), NULL, NULL, 0, 0, 0, 0, &pSvc);
+}
+
+HRESULT ComHelper::SetProxyBlanket(IWbemServices* pSvc)
+{
+    return CoSetProxyBlanket(pSvc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL,
+                             RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE);
+}
+
+HRESULT ComHelper::ExecuteWmiQuery(IWbemServices* pSvc, IEnumWbemClassObject*& pEnumerator)
+{
+    return pSvc->ExecQuery(bstr_t("WQL"), bstr_t("SELECT * FROM Win32_QuickFixEngineering"),
+                           WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator);
+}
+
+// Implement Windows Update API functions
+HRESULT ComHelper::CreateUpdateSearcher(IUpdateSearcher*& pUpdateSearcher)
+{
+    return CoCreateInstance(CLSID_UpdateSearcher, NULL, CLSCTX_INPROC_SERVER, IID_IUpdateSearcher, (LPVOID*)&pUpdateSearcher);
+}
+
+HRESULT ComHelper::GetTotalHistoryCount(IUpdateSearcher* pUpdateSearcher, LONG& count)
+{
+    return pUpdateSearcher->GetTotalHistoryCount(&count);
+}
+
+HRESULT ComHelper::QueryHistory(IUpdateSearcher* pUpdateSearcher, IUpdateHistoryEntryCollection*& pHistory, LONG& count)
+{
+    return pUpdateSearcher->QueryHistory(0, count, &pHistory);
+}
+
+HRESULT ComHelper::GetCount(IUpdateHistoryEntryCollection* pHistory, LONG& count)
+{
+    return pHistory->get_Count(&count);
+}
+
+HRESULT ComHelper::GetItem(IUpdateHistoryEntryCollection* pHistory, LONG index, IUpdateHistoryEntry** pEntry)
+{
+    return pHistory->get_Item(index, pEntry);
+}
+
+HRESULT ComHelper::GetTitle(IUpdateHistoryEntry* pEntry, BSTR& title)
+{
+    return pEntry->get_Title(&title);
+}
+
+// This function provides a minimal implementation for converting C strings to BSTRs,
+// avoiding the need to include a full COM library. This can be useful when you only need this specific functionality
+// and want to minimize dependencies.
+namespace _com_util
+{
+    BSTR ConvertStringToBSTR(const char* str)
+    {
+        int len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
+        BSTR bstr = SysAllocStringLen(0, len);
+        MultiByteToWideChar(CP_ACP, 0, str, -1, bstr, len);
+        return bstr;
+    }
+}
+
+// The BstrToString function takes a Windows-specific string (BSTR) and converts it into
+// a standard C++ string (std::string) that can be more easily used in the application.
+std::string BstrToString(BSTR bstr)
+{
+    std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
+    std::wstring wstr(bstr, SysStringLen(bstr));
+    return converter.to_bytes(wstr);
+}
+
+void QueryWMIHotFixes(std::set<std::string>& hotfixSet, IComHelper& comHelper)
+{
+    HRESULT hres;
+    IWbemLocator* pLoc = NULL;
+    IWbemServices* pSvc = NULL;
+    std::ostringstream oss;
+
+    hres = comHelper.CreateWmiLocator(pLoc);
+
+    if (FAILED(hres))
+    {
+        oss << "WMI: Error creating IWbemLocator. Code: " << std::hex << hres;
+        throw std::runtime_error(oss.str());
+    }
+
+    hres = comHelper.ConnectToWmiServer(pLoc, pSvc);
+
+    if (FAILED(hres))
+    {
+        if (pLoc) pLoc->Release();
+
+        oss << "WMI: connection failed. Code: " << std::hex << hres;
+        throw std::runtime_error(oss.str());
+    }
+
+    hres = comHelper.SetProxyBlanket(pSvc);
+
+    if (FAILED(hres))
+    {
+        if (pSvc) pSvc->Release();
+
+        if (pLoc) pLoc->Release();
+
+        oss << "WMI: security error. Code: " << std::hex << hres;
+        throw std::runtime_error(oss.str());
+    }
+
+    IEnumWbemClassObject* pEnumerator = NULL;
+    hres = comHelper.ExecuteWmiQuery(pSvc, pEnumerator);
+
+    if (FAILED(hres))
+    {
+        if (pLoc) pLoc->Release();
+
+        if (pLoc) pSvc->Release();
+
+        oss << "WMI: query error. Code: " << std::hex << hres;
+        throw std::runtime_error(oss.str());
+    }
+
+    IWbemClassObject* pclsObj = NULL;
+    ULONG uReturn = 0;
+
+    while (pEnumerator)
+    {
+        HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
+
+        if (0 == uReturn)
+        {
+            break;
+        }
+
+        VARIANT vtProp;
+        hr = pclsObj->Get(L"HotFixID", 0, &vtProp, 0, 0);
+
+        if (SUCCEEDED(hr) && vtProp.vt == VT_BSTR)
+        {
+            std::string hotfix = BstrToString(vtProp.bstrVal);
+
+            if (hotfixSet.find(hotfix) == hotfixSet.end())
+            {
+                // New HotFix found
+                hotfixSet.insert(hotfix);
+            }
+        }
+
+        VariantClear(&vtProp);
+        pclsObj->Release();
+    }
+
+    pSvc->Release();
+    pLoc->Release();
+    pEnumerator->Release();
+}
+
+void QueryWUHotFixes(std::set<std::string>& hotfixSet, IComHelper& comHelper)
+{
+    HRESULT hres;
+    IUpdateSearcher* pUpdateSearcher = NULL;
+    IUpdateHistoryEntryCollection* pHistory = NULL;
+    std::regex hotfixRegex("KB[0-9]+");
+    std::ostringstream oss;
+
+    hres = comHelper.CreateUpdateSearcher(pUpdateSearcher);
+
+    if (FAILED(hres))
+    {
+        oss << "WUA: UpdateSearcher error. Code: " << std::hex << hres;
+        throw std::runtime_error(oss.str());
+    }
+
+    LONG count;
+    hres = comHelper.GetTotalHistoryCount(pUpdateSearcher, count);
+
+    if (FAILED(hres))
+    {
+        if (pUpdateSearcher) pUpdateSearcher->Release();
+
+        oss << "WUA: Error getting total update history count. Code: 0x" << std::hex << hres;
+        throw std::runtime_error(oss.str());
+    }
+
+    hres = comHelper.QueryHistory(pUpdateSearcher, pHistory, count);
+
+    if (FAILED(hres))
+    {
+        if (pUpdateSearcher) pUpdateSearcher->Release();
+
+        oss << "WUA: Error querying update history. Code: 0x" << std::hex << hres;
+        throw std::runtime_error(oss.str());
+    }
+
+    LONG historyCount;
+    hres = comHelper.GetCount(pHistory, historyCount);
+
+    for (LONG i = 0; i < historyCount; ++i)
+    {
+        IUpdateHistoryEntry* pEntry = NULL;
+        comHelper.GetItem(pHistory, i, &pEntry);
+        BSTR title;
+        comHelper.GetTitle(pEntry, title);
+        std::string titleStr = BstrToString(title);
+
+        std::smatch match;
+
+        if (std::regex_search(titleStr, match, hotfixRegex))
+        {
+            std::string hotfix = match[0];
+
+            if (hotfixSet.find(hotfix) == hotfixSet.end())
+            {
+                // New HotFix found
+                hotfixSet.insert(hotfix);
+            }
+        }
+
+        if (pEntry) pEntry->Release();
+
+        SysFreeString(title);
+    }
+
+    if (pHistory) pHistory->Release();
+
+    if (pUpdateSearcher) pUpdateSearcher->Release();
+}
+
diff --git a/src/common/data_provider/src/utilsWrapperWin.hpp b/src/common/data_provider/src/utilsWrapperWin.hpp
new file mode 100644
index 00000000000..6422302eb70
--- /dev/null
+++ b/src/common/data_provider/src/utilsWrapperWin.hpp
@@ -0,0 +1,79 @@
+/*
+ * Wazuh SysInfo
+ * Copyright (C) 2015, Wazuh Inc.
+ * December 22, 2021.
+ *
+ * This program is free software; you can redistribute it
+ * and/or modify it under the terms of the GNU General Public
+ * License (version 2) as published by the FSF - Free Software
+ * Foundation
+ */
+#pragma once
+
+#ifdef WMI_WUA_DLL
+#ifdef WIN_EXPORT
+#define EXPORTED __declspec(dllexport)
+#else
+#define EXPORTED __declspec(dllimport)
+#endif
+#endif
+
+/* Hotfixes APIs */
+#include <set>
+#include <wbemidl.h>
+#include <wbemcli.h>
+#include <stdio.h>
+#include <comdef.h>
+#include <codecvt>
+#include "wuapi.h"
+
+
+// Define GUID manually for CLSID_UpdateSearcher
+DEFINE_GUID(CLSID_UpdateSearcher, 0x5A2A5E6E, 0xD633, 0x4C3A, 0x8A, 0x7E, 0x69, 0x4D, 0xBF, 0x9E, 0xCE, 0xD4);
+
+class EXPORTED IComHelper
+{
+    public:
+        virtual ~IComHelper() = default;
+
+        // Abstracted methods for WMI functions
+        virtual HRESULT CreateWmiLocator(IWbemLocator*& pLoc) = 0;
+        virtual HRESULT ConnectToWmiServer(IWbemLocator* pLoc, IWbemServices*& pSvc) = 0;
+        virtual HRESULT SetProxyBlanket(IWbemServices* pSvc) = 0;
+        virtual HRESULT ExecuteWmiQuery(IWbemServices* pSvc, IEnumWbemClassObject*& pEnumerator) = 0;
+
+        // Abstracted methods for Windows Update API functions
+        virtual HRESULT CreateUpdateSearcher(IUpdateSearcher*& pUpdateSearcher) = 0;
+        virtual HRESULT GetTotalHistoryCount(IUpdateSearcher* pUpdateSearcher, LONG& count) = 0;
+        virtual HRESULT QueryHistory(IUpdateSearcher* pUpdateSearcher, IUpdateHistoryEntryCollection*& pHistory, LONG& count) = 0;
+        virtual HRESULT GetCount(IUpdateHistoryEntryCollection* pHistory, LONG& count) = 0;
+        virtual HRESULT GetItem(IUpdateHistoryEntryCollection* pHistory, LONG index, IUpdateHistoryEntry** pEntry) = 0;
+        virtual HRESULT GetTitle(IUpdateHistoryEntry* pEntry, BSTR& title) = 0;
+};
+
+class EXPORTED ComHelper : public IComHelper
+{
+    public:
+        // Implement WMI functions
+        HRESULT CreateWmiLocator(IWbemLocator*& pLoc) override;
+        HRESULT ConnectToWmiServer(IWbemLocator* pLoc, IWbemServices*& pSvc) override;
+        HRESULT SetProxyBlanket(IWbemServices* pSvc) override;
+        HRESULT ExecuteWmiQuery(IWbemServices* pSvc, IEnumWbemClassObject*& pEnumerator) override;
+
+        // Implement Windows Update API functions
+        HRESULT CreateUpdateSearcher(IUpdateSearcher*& pUpdateSearcher) override;
+        HRESULT GetTotalHistoryCount(IUpdateSearcher* pUpdateSearcher, LONG& count) override;
+        HRESULT QueryHistory(IUpdateSearcher* pUpdateSearcher, IUpdateHistoryEntryCollection*& pHistory, LONG& count) override;
+        HRESULT GetCount(IUpdateHistoryEntryCollection* pHistory, LONG& count) override;
+        HRESULT GetItem(IUpdateHistoryEntryCollection* pHistory, LONG index, IUpdateHistoryEntry** pEntry) override;
+        HRESULT GetTitle(IUpdateHistoryEntry* pEntry, BSTR& title) override;
+};
+
+// Queries Windows Management Instrumentation (WMI) to retrieve installed hotfixes
+//  and stores them in the provided set.
+EXPORTED void QueryWMIHotFixes(std::set<std::string>& hotfixSet, IComHelper& comHelper);
+
+
+// Queries Windows Update Agent (WUA) for installed update history,
+// extracts hotfixes, and adds them to the provided set.
+EXPORTED void QueryWUHotFixes(std::set<std::string>& hotfixSet, IComHelper& comHelper);
diff --git a/src/common/data_provider/tests/sysInfoWin/CMakeLists.txt b/src/common/data_provider/tests/sysInfoWin/CMakeLists.txt
index ba331b2a473..7f96227b52d 100644
--- a/src/common/data_provider/tests/sysInfoWin/CMakeLists.txt
+++ b/src/common/data_provider/tests/sysInfoWin/CMakeLists.txt
@@ -7,10 +7,18 @@ set(CMAKE_CXX_FLAGS_DEBUG "-g --coverage")
 file(GLOB sysinfo_UNIT_TEST_SRC
     "*.cpp")
 
-    add_executable(sysInfoWindows_unit_test
-    ${sysinfo_UNIT_TEST_SRC})
+file(GLOB SYSINFO_SRC
+    "${CMAKE_SOURCE_DIR}/src/utilsWrapperWin.cpp")
+
+add_executable(sysInfoWindows_unit_test
+  ${sysinfo_UNIT_TEST_SRC}
+  ${SYSINFO_SRC})
+
+link_directories("${CMAKE_SOURCE_DIR}/lib/")
 
 target_link_libraries(sysInfoWindows_unit_test
+    wbemuuid
+    wuguid
     debug gtestd
     debug gmockd
     debug gtest_maind
diff --git a/src/common/data_provider/tests/sysInfoWin/sysInfoWin_test.cpp b/src/common/data_provider/tests/sysInfoWin/sysInfoWin_test.cpp
index 8cf5d58dc2f..e7e59a9991b 100644
--- a/src/common/data_provider/tests/sysInfoWin/sysInfoWin_test.cpp
+++ b/src/common/data_provider/tests/sysInfoWin/sysInfoWin_test.cpp
@@ -9,13 +9,16 @@
  * Foundation.
  */
 
-#include "sysInfoWin_test.h"
+
+#include <set>
+#include <stdio.h>
 #include "packages/packagesWindowsParserHelper.h"
+#include "sysInfoWin_test.h"
+#include <iostream>
 
-void SysInfoWinTest::SetUp() {};
 
-void SysInfoWinTest::TearDown()
-{};
+void SysInfoWinTest::SetUp() {};
+void SysInfoWinTest::TearDown() {};
 
 TEST_F(SysInfoWinTest, test_extract_HFValue_7618)
 {
@@ -102,3 +105,170 @@ TEST_F(SysInfoWinTest, testHF_PRODUCT_Valids_Format)
         EXPECT_FALSE(std::regex_match(hf, std::regex(KB_WITH_NUMBERS_AND_LETTERS_FORMAT_REGEX)));
     }
 }
+
+//  Test: Windows Management Instrumentation (WMI) to retrieve installed hotfixes
+TEST_F(SysInfoWinTest, WmiLocatorCreationFailure)
+{
+    MockComHelper mockHelper;
+    std::set<std::string> hotfixSet;
+
+    EXPECT_CALL(mockHelper, CreateWmiLocator(::testing::_))
+    .WillOnce(testing::Return(E_FAIL));
+
+    EXPECT_THROW(QueryWMIHotFixes(hotfixSet, mockHelper), std::runtime_error);
+}
+
+TEST_F(SysInfoWinTest, WmiConnectToWmiServerFailure)
+{
+    MockComHelper mockComHelper;
+    std::set<std::string> hotfixSet;
+
+    EXPECT_CALL(mockComHelper, CreateWmiLocator(testing::_))
+    .WillOnce(testing::Return(S_OK));
+
+    EXPECT_CALL(mockComHelper, ConnectToWmiServer(testing::_, testing::_))
+    .WillOnce(testing::Return(E_FAIL));
+
+    EXPECT_THROW(QueryWMIHotFixes(hotfixSet, mockComHelper), std::runtime_error);
+}
+
+TEST_F(SysInfoWinTest, WmiSetProxyBlanket)
+{
+    MockComHelper mockComHelper;
+    std::set<std::string> hotfixSet;
+
+    EXPECT_CALL(mockComHelper, CreateWmiLocator(testing::_))
+    .WillOnce(testing::Return(S_OK));
+
+    EXPECT_CALL(mockComHelper, ConnectToWmiServer(testing::_, testing::_))
+    .WillOnce(testing::Return(S_OK));
+
+    EXPECT_CALL(mockComHelper, SetProxyBlanket(testing::_))
+    .WillOnce(testing::Return(E_FAIL));
+
+    EXPECT_THROW(QueryWMIHotFixes(hotfixSet, mockComHelper), std::runtime_error);
+}
+
+TEST_F(SysInfoWinTest, WmiExecuteQuery)
+{
+    MockComHelper mockComHelper;
+    std::set<std::string> hotfixSet;
+
+    EXPECT_CALL(mockComHelper, CreateWmiLocator(testing::_))
+    .WillOnce(testing::Return(S_OK));
+
+    EXPECT_CALL(mockComHelper, ConnectToWmiServer(testing::_, testing::_))
+    .WillOnce(testing::Return(S_OK));
+
+    EXPECT_CALL(mockComHelper, SetProxyBlanket(testing::_))
+    .WillOnce(testing::Return(S_OK));
+
+    EXPECT_CALL(mockComHelper, ExecuteWmiQuery(testing::_, testing::_))
+    .WillOnce(testing::Return(E_FAIL));
+
+    EXPECT_THROW(QueryWMIHotFixes(hotfixSet, mockComHelper), std::runtime_error);
+}
+
+TEST_F(SysInfoWinTest, WmiPopulatesWMIHotfixSetCorrectly)
+{
+    std::set<std::string> hotfixSet;
+    ComHelper comHelper;
+
+    HRESULT hres = CoInitializeEx(0, COINIT_MULTITHREADED);
+    EXPECT_TRUE(SUCCEEDED(hres)) << "COM Initialization failed with HRESULT: " << std::hex << hres;
+
+    QueryWMIHotFixes(hotfixSet, comHelper);
+
+    constexpr auto KB_NO_NUMBERS_FORMAT_REGEX { "(KB+[a-z])"};
+    constexpr auto KB_WITH_NUMBERS_AND_LETTERS_FORMAT_REGEX { "(KB+[0-9]{6,}+[aA-zZ])"};
+
+    for (const auto& hf : hotfixSet)
+    {
+        EXPECT_FALSE(std::regex_match(hf, std::regex(KB_NO_NUMBERS_FORMAT_REGEX)));
+        EXPECT_FALSE(std::regex_match(hf, std::regex(KB_WITH_NUMBERS_AND_LETTERS_FORMAT_REGEX)));
+    }
+
+    CoUninitialize();
+}
+
+// Test: Windows Update Agent (WUA) for installed update history,
+TEST_F(SysInfoWinTest, WuaLocatorCreationFailure)
+{
+    MockComHelper mockHelper;
+    std::set<std::string> hotfixSet;
+
+    EXPECT_CALL(mockHelper, CreateUpdateSearcher(::testing::_))
+    .WillOnce(testing::Return(E_FAIL));
+
+    EXPECT_THROW(QueryWUHotFixes(hotfixSet, mockHelper), std::runtime_error);
+}
+
+TEST_F(SysInfoWinTest, WuaGetTotalHistoryCount)
+{
+    MockComHelper mockHelper;
+    std::set<std::string> hotfixSet;
+
+    EXPECT_CALL(mockHelper, CreateUpdateSearcher(::testing::_))
+    .WillOnce(testing::Return(S_OK));
+
+    EXPECT_CALL(mockHelper, GetTotalHistoryCount(::testing::_, ::testing::_))
+    .WillOnce(testing::Return(E_FAIL));
+
+    EXPECT_THROW(QueryWUHotFixes(hotfixSet, mockHelper), std::runtime_error);
+}
+
+TEST_F(SysInfoWinTest, WuaQueryHistory)
+{
+    MockComHelper mockHelper;
+    std::set<std::string> hotfixSet;
+
+    EXPECT_CALL(mockHelper, CreateUpdateSearcher(::testing::_))
+    .WillOnce(testing::Return(S_OK));
+
+    EXPECT_CALL(mockHelper, GetTotalHistoryCount(::testing::_, ::testing::_))
+    .WillOnce(testing::Return(S_OK));
+
+    EXPECT_CALL(mockHelper, QueryHistory(::testing::_, ::testing::_, ::testing::_))
+    .WillOnce(testing::Return(E_FAIL));
+
+    EXPECT_THROW(QueryWUHotFixes(hotfixSet, mockHelper), std::runtime_error);
+}
+
+TEST_F(SysInfoWinTest, GetHistoryTest)
+{
+    MockComHelper mockHelper;
+    std::set<std::string> hotfixSet;
+
+    EXPECT_CALL(mockHelper, CreateUpdateSearcher(::testing::_))
+    .WillOnce(testing::Return(S_OK));
+
+    EXPECT_CALL(mockHelper, GetTotalHistoryCount(::testing::_, ::testing::_))
+    .WillOnce(testing::Return(S_OK));
+
+    EXPECT_CALL(mockHelper, QueryHistory(::testing::_, ::testing::_, ::testing::_))
+    .WillOnce(testing::Return(S_OK));
+
+    long count = 4;
+    EXPECT_CALL(mockHelper, GetCount(testing::_, testing::_))
+    .WillOnce(testing::DoAll(testing::SetArgReferee<1>(count), testing::Return(S_OK)));
+
+    for (int i = 0 ; i < count; i++)
+    {
+
+        EXPECT_CALL(mockHelper, GetItem(testing::_, i, testing::_))
+        .WillOnce(testing::Return(S_OK));
+
+        // Simulate getting the title
+        EXPECT_CALL(mockHelper, GetTitle(testing::_, testing::_))
+        .WillRepeatedly(testing::Invoke([](IUpdateHistoryEntry*, BSTR & title) -> HRESULT
+        {
+            title = SysAllocString(L"Security Update KB123456");
+            return S_OK;
+        }));
+    }
+
+    QueryWUHotFixes(hotfixSet, mockHelper);
+
+    EXPECT_EQ(hotfixSet.size(), static_cast<unsigned int>(1));
+    EXPECT_EQ(*hotfixSet.begin(), "KB123456");
+}
diff --git a/src/common/data_provider/tests/sysInfoWin/sysInfoWin_test.h b/src/common/data_provider/tests/sysInfoWin/sysInfoWin_test.h
index dfdfefa8fd1..7dbfa43b8ac 100644
--- a/src/common/data_provider/tests/sysInfoWin/sysInfoWin_test.h
+++ b/src/common/data_provider/tests/sysInfoWin/sysInfoWin_test.h
@@ -15,6 +15,9 @@
 #include "gtest/gtest.h"
 #include "gmock/gmock.h"
 
+#define WMI_WUA_DLL
+#include "utilsWrapperWin.hpp"
+
 class SysInfoWinTest : public ::testing::Test
 {
     protected:
@@ -26,4 +29,21 @@ class SysInfoWinTest : public ::testing::Test
         void TearDown() override;
 };
 
+
+class MockComHelper : public IComHelper
+{
+    public:
+        MOCK_METHOD(HRESULT, CreateWmiLocator, (IWbemLocator*& pLoc), (override));
+        MOCK_METHOD(HRESULT, ConnectToWmiServer, (IWbemLocator* pLoc, IWbemServices*& pSvc), (override));
+        MOCK_METHOD(HRESULT, SetProxyBlanket, (IWbemServices* pSvc), (override));
+        MOCK_METHOD(HRESULT, ExecuteWmiQuery, (IWbemServices* pSvc, IEnumWbemClassObject*& pEnumerator), (override));
+        MOCK_METHOD(HRESULT, CreateUpdateSearcher, (IUpdateSearcher*& pUpdateSearcher), (override));
+        MOCK_METHOD(HRESULT, GetTotalHistoryCount, (IUpdateSearcher* pUpdateSearcher, LONG& count), (override));
+        MOCK_METHOD(HRESULT, QueryHistory, (IUpdateSearcher* pUpdateSearcher, IUpdateHistoryEntryCollection*& pHistory,  LONG& count), (override));
+        MOCK_METHOD(HRESULT, GetCount, (IUpdateHistoryEntryCollection* pHistory, LONG& count), (override));
+        MOCK_METHOD(HRESULT, GetItem, (IUpdateHistoryEntryCollection* pHistory, LONG index, IUpdateHistoryEntry** pEntry), (override));
+        MOCK_METHOD(HRESULT, GetTitle, (IUpdateHistoryEntry* pEntry, BSTR& title), (override));
+};
+
+
 #endif //_SYSINFO_WIN_TEST_H

From fdac8bdf19f95ee849905c0c3226938e8898118b Mon Sep 17 00:00:00 2001
From: Luis Enrique Chico Capistrano <luis.chico@wazuh.com>
Date: Thu, 30 Jan 2025 21:35:09 -0300
Subject: [PATCH 2/2] feat: enhancement code

---
 src/common/data_provider/CMakeLists.txt                    | 1 +
 src/common/data_provider/src/utilsWrapperWin.cpp           | 1 -
 src/common/data_provider/src/utilsWrapperWin.hpp           | 2 +-
 .../data_provider/tests/sysInfoWin/sysInfoWin_test.h       | 7 +------
 4 files changed, 3 insertions(+), 8 deletions(-)

diff --git a/src/common/data_provider/CMakeLists.txt b/src/common/data_provider/CMakeLists.txt
index 842b5c92aaa..a8361e503e9 100644
--- a/src/common/data_provider/CMakeLists.txt
+++ b/src/common/data_provider/CMakeLists.txt
@@ -65,6 +65,7 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
 set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
 if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
     set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
+    add_definitions(-DWIN32=1 -DWIN_EXPORT)
 else()
   set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
 endif()
diff --git a/src/common/data_provider/src/utilsWrapperWin.cpp b/src/common/data_provider/src/utilsWrapperWin.cpp
index ab7781238e0..738f6c4ad9e 100644
--- a/src/common/data_provider/src/utilsWrapperWin.cpp
+++ b/src/common/data_provider/src/utilsWrapperWin.cpp
@@ -16,7 +16,6 @@
 #include <string>         // For std::string and std::wstring
 #include <locale>         // For localization utilities (if needed for string conversion)
 
-#define WMI_WUA_DLL
 #include "utilsWrapperWin.hpp"
 
 
diff --git a/src/common/data_provider/src/utilsWrapperWin.hpp b/src/common/data_provider/src/utilsWrapperWin.hpp
index 6422302eb70..4107fb7d683 100644
--- a/src/common/data_provider/src/utilsWrapperWin.hpp
+++ b/src/common/data_provider/src/utilsWrapperWin.hpp
@@ -10,7 +10,7 @@
  */
 #pragma once
 
-#ifdef WMI_WUA_DLL
+#ifdef _WIN32
 #ifdef WIN_EXPORT
 #define EXPORTED __declspec(dllexport)
 #else
diff --git a/src/common/data_provider/tests/sysInfoWin/sysInfoWin_test.h b/src/common/data_provider/tests/sysInfoWin/sysInfoWin_test.h
index 7dbfa43b8ac..9069410b81b 100644
--- a/src/common/data_provider/tests/sysInfoWin/sysInfoWin_test.h
+++ b/src/common/data_provider/tests/sysInfoWin/sysInfoWin_test.h
@@ -9,13 +9,11 @@
  * Foundation.
  */
 
-#ifndef _SYSINFO_WIN_TEST_H
-#define _SYSINFO_WIN_TEST_H
+#pragma once
 
 #include "gtest/gtest.h"
 #include "gmock/gmock.h"
 
-#define WMI_WUA_DLL
 #include "utilsWrapperWin.hpp"
 
 class SysInfoWinTest : public ::testing::Test
@@ -44,6 +42,3 @@ class MockComHelper : public IComHelper
         MOCK_METHOD(HRESULT, GetItem, (IUpdateHistoryEntryCollection* pHistory, LONG index, IUpdateHistoryEntry** pEntry), (override));
         MOCK_METHOD(HRESULT, GetTitle, (IUpdateHistoryEntry* pEntry, BSTR& title), (override));
 };
-
-
-#endif //_SYSINFO_WIN_TEST_H