From 5577ad303caa037124935f996d65d063aa510acd Mon Sep 17 00:00:00 2001 From: Bob Sira Date: Thu, 6 Aug 2026 18:08:28 +0100 Subject: [PATCH 1/3] fix(logmonitor): allow absolute /Config paths again Restore support for absolute config paths while preserving relative path hardening for /Config. Fixes regression introduced in 2.2.0 (issue #229). --- LogMonitor/src/LogMonitor/Main.cpp | 71 +++++++++++++++++++----------- 1 file changed, 45 insertions(+), 26 deletions(-) diff --git a/LogMonitor/src/LogMonitor/Main.cpp b/LogMonitor/src/LogMonitor/Main.cpp index 8d94d1d..b8da8b9 100644 --- a/LogMonitor/src/LogMonitor/Main.cpp +++ b/LogMonitor/src/LogMonitor/Main.cpp @@ -249,6 +249,50 @@ void CreateEtwMonitor( } } +bool ResolveConfigPath(const std::wstring& userConfigName, std::wstring& resolvedConfigPath) +{ + // Preserve support for absolute paths passed via /Config. + if (!PathIsRelativeW(userConfigName.c_str())) + { + resolvedConfigPath = userConfigName; + return true; + } + + // Relative paths must be plain file names rooted under the executable directory. + if (userConfigName.find(L"..") != std::wstring::npos || + userConfigName.find(L'/') != std::wstring::npos || + userConfigName.find(L'\\') != std::wstring::npos || + userConfigName.find(L':') != std::wstring::npos) + { + return false; + } + + WCHAR modulePath[MAX_PATH] = {}; + if (GetModuleFileNameW(nullptr, modulePath, _countof(modulePath)) == 0) + { + logWriter.TraceError( + Utility::FormatString(L"Failed to get module path. Error: %d", GetLastError()).c_str() + ); + return false; + } + + if (!PathRemoveFileSpecW(modulePath)) + { + logWriter.TraceError(L"Failed to resolve executable directory."); + return false; + } + + WCHAR combinedPath[MAX_PATH] = {}; + if (PathCombineW(combinedPath, modulePath, userConfigName.c_str()) == nullptr) + { + logWriter.TraceError(L"Failed to resolve configuration file path."); + return false; + } + + resolvedConfigPath = combinedPath; + return true; +} + /// /// Start the monitors by delegating to the helper functions based on log source type /// @@ -378,37 +422,12 @@ int __cdecl wmain(int argc, WCHAR *argv[]) configFileName = argv[2]; std::wstring userConfigName = argv[2]; - // Reject paths with traversal sequences or absolute path indicators - if (userConfigName.find(L"..") != std::wstring::npos || - userConfigName.find(L'/') != std::wstring::npos || - userConfigName.find(L'\\') != std::wstring::npos || - userConfigName.find(L':') != std::wstring::npos) + if (!ResolveConfigPath(userConfigName, resolvedConfigPath)) { logWriter.TraceError(L"Invalid configuration file name."); return 0; } - // Anchor the config file to the executable's directory - WCHAR modulePath[MAX_PATH] = {}; - if (GetModuleFileNameW(nullptr, modulePath, _countof(modulePath)) == 0) - { - logWriter.TraceError( - Utility::FormatString(L"Failed to get module path. Error: %d", GetLastError()).c_str() - ); - return 0; - } - if (!PathRemoveFileSpecW(modulePath)) - { - logWriter.TraceError(L"Failed to resolve executable directory."); - return 0; - } - WCHAR combinedPath[MAX_PATH] = {}; - if (PathCombineW(combinedPath, modulePath, userConfigName.c_str()) == nullptr) - { - logWriter.TraceError(L"Failed to resolve configuration file path."); - return 0; - } - resolvedConfigPath = combinedPath; indexCommandArgument = 3; } } From 4ebb293d600356a8a6ae9c36cbf51510d794a1ca Mon Sep 17 00:00:00 2001 From: Bob Sira Date: Wed, 12 Aug 2026 14:08:55 +0100 Subject: [PATCH 2/3] fix(logmonitor): harden config path validation Allow legitimate filenames containing consecutive periods and detect truncated executable paths before resolving relative config names. --- LogMonitor/src/LogMonitor/Main.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/LogMonitor/src/LogMonitor/Main.cpp b/LogMonitor/src/LogMonitor/Main.cpp index b8da8b9..fa97405 100644 --- a/LogMonitor/src/LogMonitor/Main.cpp +++ b/LogMonitor/src/LogMonitor/Main.cpp @@ -259,7 +259,9 @@ bool ResolveConfigPath(const std::wstring& userConfigName, std::wstring& resolve } // Relative paths must be plain file names rooted under the executable directory. - if (userConfigName.find(L"..") != std::wstring::npos || + if (userConfigName.empty() || + userConfigName == L"." || + userConfigName == L".." || userConfigName.find(L'/') != std::wstring::npos || userConfigName.find(L'\\') != std::wstring::npos || userConfigName.find(L':') != std::wstring::npos) @@ -268,7 +270,8 @@ bool ResolveConfigPath(const std::wstring& userConfigName, std::wstring& resolve } WCHAR modulePath[MAX_PATH] = {}; - if (GetModuleFileNameW(nullptr, modulePath, _countof(modulePath)) == 0) + DWORD modulePathLength = GetModuleFileNameW(nullptr, modulePath, _countof(modulePath)); + if (modulePathLength == 0) { logWriter.TraceError( Utility::FormatString(L"Failed to get module path. Error: %d", GetLastError()).c_str() @@ -276,6 +279,12 @@ bool ResolveConfigPath(const std::wstring& userConfigName, std::wstring& resolve return false; } + if (modulePathLength >= _countof(modulePath)) + { + logWriter.TraceError(L"Module path exceeds the maximum supported length."); + return false; + } + if (!PathRemoveFileSpecW(modulePath)) { logWriter.TraceError(L"Failed to resolve executable directory."); From aeebe907c128ab10d2c95ee644d8031c7cd0a510 Mon Sep 17 00:00:00 2001 From: Bob Sira Date: Wed, 12 Aug 2026 15:08:59 +0100 Subject: [PATCH 3/3] chore(logmonitor): bump version to 2.2.2 --- LogMonitor/src/LogMonitor/version.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/LogMonitor/src/LogMonitor/version.h b/LogMonitor/src/LogMonitor/version.h index e3ae814..af9fd6f 100644 --- a/LogMonitor/src/LogMonitor/version.h +++ b/LogMonitor/src/LogMonitor/version.h @@ -7,8 +7,8 @@ #define LOGMONITOR_SRC_LOGMONITOR_VERSION_H_ #define LM_MAJORNUMBER 2 -#define LM_MINORNUMBER 1 -#define LM_PATCHNUMBER 1 +#define LM_MINORNUMBER 2 +#define LM_PATCHNUMBER 2 // removed in support of semantic versioning - https://semver.org // major.minor.patch // #define LM_BUILDMINORVERSION 0