From b9e493e797ef749ea32670375c12c99c04814ca4 Mon Sep 17 00:00:00 2001 From: Don HO Date: Fri, 12 Dec 2025 23:20:06 +0100 Subject: [PATCH 1/8] Log security error automatically Log the error automatically when the signature & cetification verification fails: 1. Write error log (the security part) into %LOCALUSERDATA%\WinGUp\log\securityError.log, and add -errLogPath= argument to make it overridable. 2. Add the download URL + sha256 value of the downloaded file in the log. 3. Log also if the download URL is not what we expected. Fix #98 --- src/Common.cpp | 146 +++++++++++++++++++++++++++++++++++++-- src/Common.h | 2 + src/verifySignedfile.cpp | 57 +++------------ src/verifySignedfile.h | 10 +++ src/winmain.cpp | 43 +++++++++--- 5 files changed, 195 insertions(+), 63 deletions(-) diff --git a/src/Common.cpp b/src/Common.cpp index 6f4624fc..2f9f83cc 100644 --- a/src/Common.cpp +++ b/src/Common.cpp @@ -23,15 +23,149 @@ using namespace std; -void writeLog(const wchar_t* logFileName, const wchar_t* logSuffix, const wchar_t* log2write) +void expandEnv(wstring& s) { - FILE* f = _wfopen(logFileName, L"a+, ccs=UTF-16LE"); + wchar_t buffer[MAX_PATH] = { '\0' }; + // This returns the resulting string length or 0 in case of error. + DWORD ret = ExpandEnvironmentStrings(s.c_str(), buffer, static_cast(std::size(buffer))); + if (ret != 0) + { + if (ret == static_cast(lstrlen(buffer) + 1)) + { + s = buffer; + } + else + { + // Buffer was too small, try with a bigger buffer of the required size. + std::vector buffer2(ret, 0); + ret = ExpandEnvironmentStrings(s.c_str(), buffer2.data(), static_cast(buffer2.size())); + s = buffer2.data(); + } + } +} + +namespace +{ + constexpr wchar_t timeFmtEscapeChar = 0x1; + constexpr wchar_t middayFormat[] = L"tt"; + + // Returns AM/PM string defined by the system locale for the specified time. + // This string may be empty or customized. + wstring getMiddayString(const wchar_t* localeName, const SYSTEMTIME& st) + { + wstring midday; + midday.resize(MAX_PATH); + int ret = GetTimeFormatEx(localeName, 0, &st, middayFormat, &midday[0], static_cast(midday.size())); + if (ret > 0) + midday.resize(ret - 1); // Remove the null-terminator. + else + midday.clear(); + return midday; + } + + // Replaces conflicting time format specifiers by a special character. + bool escapeTimeFormat(wstring& format) + { + bool modified = false; + for (auto& ch : format) + { + if (ch == middayFormat[0]) + { + ch = timeFmtEscapeChar; + modified = true; + } + } + return modified; + } + + // Replaces special time format characters by actual AM/PM string. + void unescapeTimeFormat(wstring& format, const wstring& midday) + { + if (midday.empty()) + { + auto it = std::remove(format.begin(), format.end(), timeFmtEscapeChar); + if (it != format.end()) + format.erase(it, format.end()); + } + else + { + size_t i = 0; + while ((i = format.find(timeFmtEscapeChar, i)) != wstring::npos) + { + if (i + 1 < format.size() && format[i + 1] == timeFmtEscapeChar) + { + // 'tt' => AM/PM + format.erase(i, std::size(middayFormat) - 1); + format.insert(i, midday); + } + else + { + // 't' => A/P + format[i] = midday[0]; + } + } + } + } +} + +wstring getDateTimeStrFrom(const wstring& dateTimeFormat, const SYSTEMTIME& st) +{ + const wchar_t* localeName = LOCALE_NAME_USER_DEFAULT; + const DWORD flags = 0; + + constexpr int bufferSize = MAX_PATH; + wchar_t buffer[bufferSize] = {}; + int ret = 0; + + + // 1. Escape 'tt' that means AM/PM or 't' that means A/P. + // This is needed to avoid conflict with 'M' date format that stands for month. + wstring newFormat = dateTimeFormat; + const bool hasMiddayFormat = escapeTimeFormat(newFormat); + + // 2. Format the time (h/m/s/t/H). + ret = GetTimeFormatEx(localeName, flags, &st, newFormat.c_str(), buffer, bufferSize); + if (ret != 0) + { + // 3. Format the date (d/y/g/M). + // Now use the buffer as a format string to process the format specifiers not recognized by GetTimeFormatEx(). + ret = GetDateFormatEx(localeName, flags, &st, buffer, buffer, bufferSize, nullptr); + } + + if (ret != 0) + { + if (hasMiddayFormat) + { + // 4. Now format only the AM/PM string. + const wstring midday = getMiddayString(localeName, st); + wstring result = buffer; + unescapeTimeFormat(result, midday); + return result; + } + return buffer; + } + + return {}; +} + +void writeLog(const wchar_t* logFileName, const wchar_t* logPrefix, const wchar_t* log2write) +{ + wstring expandedLogFileName = logFileName; + expandEnv(expandedLogFileName); + + FILE* f = _wfopen(expandedLogFileName.c_str(), L"a+, ccs=UTF-16LE"); if (f) { - wstring log = logSuffix; - log += log2write; - log += L'\n'; - fwrite(log.c_str(), sizeof(log.c_str()[0]), log.length(), f); + SYSTEMTIME currentTime = {}; + ::GetLocalTime(¤tTime); + wstring log2writeStr = getDateTimeStrFrom(L"yyyy-MM-dd HH:mm:ss", currentTime); + + log2writeStr += L" "; + log2writeStr += logPrefix; + log2writeStr += L" "; + log2writeStr += log2write; + log2writeStr += L'\n'; + fwrite(log2writeStr.c_str(), sizeof(log2writeStr.c_str()[0]), log2writeStr.length(), f); fflush(f); fclose(f); } diff --git a/src/Common.h b/src/Common.h index 872433e9..1102ea0c 100644 --- a/src/Common.h +++ b/src/Common.h @@ -18,6 +18,8 @@ #include +void expandEnv(std::wstring& s); +std::wstring getDateTimeStrFrom(const std::wstring& dateTimeFormat, const SYSTEMTIME& st); void writeLog(const wchar_t* logFileName, const wchar_t* logSuffix, const wchar_t* log2write); std::wstring s2ws(const std::string& str); std::string ws2s(const std::wstring& wstr); diff --git a/src/verifySignedfile.cpp b/src/verifySignedfile.cpp index 1acb7a77..bfcad346 100644 --- a/src/verifySignedfile.cpp +++ b/src/verifySignedfile.cpp @@ -27,7 +27,7 @@ #include #include #include "verifySignedfile.h" -#include "Common.h" + using namespace std; @@ -42,11 +42,6 @@ bool SecurityGuard::verifySignedBinary(const std::wstring& filepath) wstring subject; wstring authority_key_id_hex; - if (doLogCertifError) - { - writeLog(L"c:\\tmp\\certifError.log", L"verifySignedBinary: ", filepath.c_str()); - } - // // Signature verification // @@ -69,9 +64,6 @@ bool SecurityGuard::verifySignedBinary(const std::wstring& filepath) if (!_doCheckRevocation) { winTEXTrust_data.fdwRevocationChecks = WTD_REVOKE_NONE; - - if (doLogCertifError) - writeLog(L"c:\\tmp\\certifError.log", L"verifySignedBinary: ", L"certificate revocation checking is disabled"); } else { @@ -87,9 +79,6 @@ bool SecurityGuard::verifySignedBinary(const std::wstring& filepath) if (!online) { winTEXTrust_data.fdwRevocationChecks = WTD_REVOKE_NONE; - - if (doLogCertifError) - writeLog(L"c:\\tmp\\certifError.log", L"verifySignedBinary: ", L"system is offline - certificate revocation won't be checked"); } } @@ -105,17 +94,13 @@ bool SecurityGuard::verifySignedBinary(const std::wstring& filepath) if (vtrust) { - if (doLogCertifError) - writeLog(L"c:\\tmp\\certifError.log", L"verifySignedBinary: ", L"trust verification failed"); - + writeSecurityError(filepath.c_str(), L": trust verification failed"); return false; } if (t2) { - if (doLogCertifError) - writeLog(L"c:\\tmp\\certifError.log", L"verifySignedBinary: ", L"error encountered while cleaning up after WinVerifyTrust"); - + writeSecurityError(filepath.c_str(), L": error encountered while cleaning up after WinVerifyTrust"); return false; } } @@ -206,9 +191,6 @@ bool SecurityGuard::verifySignedBinary(const std::wstring& filepath) } key_id_hex = ss.str(); - if (doLogCertifError) - writeLog(L"c:\\tmp\\certifError.log", L"verifySignedBinary: ", key_id_hex.c_str()); - // Getting the display name auto sze = ::CertGetNameString(context, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, NULL, 0); if (sze <= 1) @@ -265,63 +247,44 @@ bool SecurityGuard::verifySignedBinary(const std::wstring& filepath) LocalFree(pAuthKeyIdInfo); } } - else - { - // Authority Key Identifier extension not found - if (doLogCertifError) - writeLog(L"c:\\tmp\\certifError.log", L"Authority Key ID: ", L"Extension not found"); - } // --- End AKI Retrieval --- } catch (const string& s) { - if (doLogCertifError) - { - writeLog(L"c:\\tmp\\certifError.log", L" verifySignedBinary: error while getting certificate information: ", s2ws(s).c_str()); - } + writeSecurityError((filepath + L" - error while getting certificate information: ").c_str(), s2ws(s).c_str()); status = false; } catch (...) { // Unknown error - if (doLogCertifError) - writeLog(L"c:\\tmp\\certifError.log", L"verifySignedBinary: ", L"error while getting certificate information"); - + writeSecurityError(filepath.c_str(), L": Unknow error while getting certificate information"); status = false; } // - // fields verifications - if status is true, and string to compare (from the parameter) is not empty, then do compare + // fields verifications - if status is true, and demaded parameter string to compare (from the parameter) is not empty, then do compare // if (status && (!_signer_display_name.empty() && _signer_display_name != display_name)) { status = false; - - if (doLogCertifError) - writeLog(L"c:\\tmp\\certifError.log", L"verifySignedBinary: ", L"Invalid certificate display name"); + writeSecurityError(filepath.c_str(), L": Invalid certificate display name"); } if (status && (!_signer_subject.empty() && _signer_subject != subject)) { status = false; - - if (doLogCertifError) - writeLog(L"c:\\tmp\\certifError.log", L"verifySignedBinary: ", L"Invalid certificate subject"); + writeSecurityError(filepath.c_str(), L": Invalid certificate subject"); } if (status && (!_signer_key_id.empty() && stringToUpper(_signer_key_id) != key_id_hex)) { status = false; - - if (doLogCertifError) - writeLog(L"c:\\tmp\\certifError.log", L"verifySignedBinary: ", L"Invalid certificate key id"); + writeSecurityError(filepath.c_str(), L": Invalid certificate key id"); } if (status && (!_authority_key_id.empty() && stringToUpper(_authority_key_id) != authority_key_id_hex)) { status = false; - - if (doLogCertifError) - writeLog(L"c:\\tmp\\certifError.log", L"verifySignedBinary: ", L"Invalid authority key id"); + writeSecurityError(filepath.c_str(), L": Invalid authority key id"); } // Clean up. diff --git a/src/verifySignedfile.h b/src/verifySignedfile.h index 680ab8cc..4a5011a3 100644 --- a/src/verifySignedfile.h +++ b/src/verifySignedfile.h @@ -47,6 +47,7 @@ #include #include +#include "Common.h" class SecurityGuard final { @@ -61,6 +62,13 @@ class SecurityGuard final void setKeyId(const std::wstring& signer_key_id) { _signer_key_id = signer_key_id; } void setAuthorityKeyId(const std::wstring& authority_key_id) { _authority_key_id = authority_key_id; } + void setErrLogPath(std::wstring& errLogPath) { _errLogPath = errLogPath; } + std::wstring errLogPath() const { return _errLogPath; } + + void writeSecurityError(const std::wstring& prefix, const std::wstring& log2write) { + writeLog(_errLogPath.c_str(), prefix.c_str(), log2write.c_str()); + } + private: // Code signing certificate std::wstring _signer_display_name; // = L"Notepad++" @@ -70,5 +78,7 @@ class SecurityGuard final bool _doCheckRevocation = false; bool _doCheckChainOfTrust = false; + + std::wstring _errLogPath = L"%LOCALUSERDATA%\\WinGUp\\log\\securityError.log"; // By default, but overrideable }; diff --git a/src/winmain.cpp b/src/winmain.cpp index 7e3167ee..e595aee9 100644 --- a/src/winmain.cpp +++ b/src/winmain.cpp @@ -995,7 +995,6 @@ bool downloadBinary(const wstring& urlFrom, const wstring& destTo, const wstring bool ok = true; if (!sha2HashToCheck.empty()) { - char sha2hashStr[65] = { '\0' }; std::string content = getFileContentA(ws2s(destTo).c_str()); if (content.empty()) { @@ -1005,6 +1004,7 @@ bool downloadBinary(const wstring& urlFrom, const wstring& destTo, const wstring } else { + char sha2hashStr[65] = { '\0' }; uint8_t sha2hash[32]; calc_sha_256(sha2hash, reinterpret_cast(content.c_str()), content.length()); @@ -1644,24 +1644,19 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR lpszCmdLine, int) return 0; } - bool isDomaineOK = true; + wstring downloadURL; if (!forceDomain.empty()) { - const auto& downloadURL = gupDlInfo.getDownloadLocation(); + downloadURL = gupDlInfo.getDownloadLocation(); if (downloadURL.size() <= forceDomain.size() // download URL must be longer than forceDomain || downloadURL.compare(0, forceDomain.size(), forceDomain) != 0) // Check if forceDomain is a prefix of download URL { - isDomaineOK = false; + securityGuard.writeSecurityError(L"Domain is not matched for download URL:", downloadURL); + return -1; } } - if (!isDomaineOK) - { - WRITE_LOG(GUP_LOG_FILENAME, L"return -1 in Npp Updater part: ", L"Domain is not matched for download URL. The file download won't be processed."); - return -1; - } - // // Process Update Info // @@ -1754,7 +1749,35 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR lpszCmdLine, int) bool isSecured = securityGuard.verifySignedBinary(dlDest); if (!isSecured) + { + wstring dlFileSha256; + + std::string content = getFileContentA(ws2s(dlDest).c_str()); + if (content.empty()) + { + dlFileSha256 = L"No SHA-256: the file is empty."; + } + else + { + char sha2hashStr[65] {}; + uint8_t sha2hash[32]; + calc_sha_256(sha2hash, reinterpret_cast(content.c_str()), content.length()); + + for (size_t i = 0; i < 32; i++) + { + sprintf(sha2hashStr + i * 2, "%02x", sha2hash[i]); + } + + dlFileSha256 = L"Downloadeed file SHA-256: "; + dlFileSha256 += s2ws(sha2hashStr); + } + + wstring dlUrl = L"DownloadURL: "; + dlUrl += downloadURL; + + securityGuard.writeSecurityError(dlUrl, dlFileSha256); return -1; + } } // // Run executable bin From 723b38cd60f1723410d1d619cf2ce0f9cf74f08a Mon Sep 17 00:00:00 2001 From: Don HO Date: Sat, 13 Dec 2025 04:27:27 +0100 Subject: [PATCH 2/8] Add create folder and sub-folders ability --- src/Common.cpp | 6 ++---- src/verifySignedfile.cpp | 27 +++++++++++++++++++++++++++ src/verifySignedfile.h | 4 +--- 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/src/Common.cpp b/src/Common.cpp index 2f9f83cc..9dd5da7f 100644 --- a/src/Common.cpp +++ b/src/Common.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include "Common.h" @@ -150,10 +151,7 @@ wstring getDateTimeStrFrom(const wstring& dateTimeFormat, const SYSTEMTIME& st) void writeLog(const wchar_t* logFileName, const wchar_t* logPrefix, const wchar_t* log2write) { - wstring expandedLogFileName = logFileName; - expandEnv(expandedLogFileName); - - FILE* f = _wfopen(expandedLogFileName.c_str(), L"a+, ccs=UTF-16LE"); + FILE* f = _wfopen(logFileName, L"a+, ccs=UTF-16LE"); if (f) { SYSTEMTIME currentTime = {}; diff --git a/src/verifySignedfile.cpp b/src/verifySignedfile.cpp index bfcad346..6f1db6eb 100644 --- a/src/verifySignedfile.cpp +++ b/src/verifySignedfile.cpp @@ -26,6 +26,8 @@ #include #include #include +#include +#include #include "verifySignedfile.h" @@ -35,6 +37,31 @@ using namespace std; // Debug use bool doLogCertifError = false; +void SecurityGuard::writeSecurityError(const std::wstring& prefix, const std::wstring& log2write) const +{ + // Expand the environment variable + wstring expandedLogFileName = _errLogPath; + expandEnv(expandedLogFileName); + + // Create the folder & sub-folders for the log file + wchar_t logDir[MAX_PATH]; + lstrcpy(logDir, expandedLogFileName.c_str()); + ::PathRemoveFileSpec(logDir); + int result = SHCreateDirectoryEx(NULL, logDir, NULL); + + // If folder doesn't exit or folder creation failed + if (result != ERROR_SUCCESS && result != ERROR_ALREADY_EXISTS) + { + // process %TEMP% treatment + wchar_t* fileName = ::PathFindFileName(expandedLogFileName.c_str()); + expandedLogFileName = L"%TEMP%\\"; + expandedLogFileName += fileName; + expandEnv(expandedLogFileName); + } + + writeLog(expandedLogFileName.c_str(), prefix.c_str(), log2write.c_str()); +} + bool SecurityGuard::verifySignedBinary(const std::wstring& filepath) { wstring display_name; diff --git a/src/verifySignedfile.h b/src/verifySignedfile.h index 4a5011a3..f0043316 100644 --- a/src/verifySignedfile.h +++ b/src/verifySignedfile.h @@ -65,9 +65,7 @@ class SecurityGuard final void setErrLogPath(std::wstring& errLogPath) { _errLogPath = errLogPath; } std::wstring errLogPath() const { return _errLogPath; } - void writeSecurityError(const std::wstring& prefix, const std::wstring& log2write) { - writeLog(_errLogPath.c_str(), prefix.c_str(), log2write.c_str()); - } + void writeSecurityError(const std::wstring& prefix, const std::wstring& log2write) const; private: // Code signing certificate From 08c92f219a1201de3a8ee95da615bd36ebbe1c2a Mon Sep 17 00:00:00 2001 From: Don HO Date: Sat, 13 Dec 2025 04:52:44 +0100 Subject: [PATCH 3/8] Fix the env var name --- src/Common.cpp | 1 - src/verifySignedfile.h | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Common.cpp b/src/Common.cpp index 9dd5da7f..0adc7cc4 100644 --- a/src/Common.cpp +++ b/src/Common.cpp @@ -18,7 +18,6 @@ #include #include #include -#include #include #include "Common.h" diff --git a/src/verifySignedfile.h b/src/verifySignedfile.h index f0043316..3b7dc33b 100644 --- a/src/verifySignedfile.h +++ b/src/verifySignedfile.h @@ -77,6 +77,6 @@ class SecurityGuard final bool _doCheckRevocation = false; bool _doCheckChainOfTrust = false; - std::wstring _errLogPath = L"%LOCALUSERDATA%\\WinGUp\\log\\securityError.log"; // By default, but overrideable + std::wstring _errLogPath = L"%LOCALAPPDATA%\\WinGUp\\log\\securityError.log"; // By default, but overrideable }; From 52b5dd078741ea198ef617a020f99c671b4268e8 Mon Sep 17 00:00:00 2001 From: Don HO Date: Sat, 13 Dec 2025 14:16:51 +0100 Subject: [PATCH 4/8] Add more err info --- src/verifySignedfile.cpp | 8 ++++---- src/winmain.cpp | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/verifySignedfile.cpp b/src/verifySignedfile.cpp index 6f1db6eb..170265ab 100644 --- a/src/verifySignedfile.cpp +++ b/src/verifySignedfile.cpp @@ -293,25 +293,25 @@ bool SecurityGuard::verifySignedBinary(const std::wstring& filepath) if (status && (!_signer_display_name.empty() && _signer_display_name != display_name)) { status = false; - writeSecurityError(filepath.c_str(), L": Invalid certificate display name"); + writeSecurityError(filepath.c_str(), display_name + L": Invalid certificate display name"); } if (status && (!_signer_subject.empty() && _signer_subject != subject)) { status = false; - writeSecurityError(filepath.c_str(), L": Invalid certificate subject"); + writeSecurityError(filepath.c_str(), subject + L": Invalid certificate subject"); } if (status && (!_signer_key_id.empty() && stringToUpper(_signer_key_id) != key_id_hex)) { status = false; - writeSecurityError(filepath.c_str(), L": Invalid certificate key id"); + writeSecurityError(filepath.c_str(), key_id_hex + L": Invalid certificate key id"); } if (status && (!_authority_key_id.empty() && stringToUpper(_authority_key_id) != authority_key_id_hex)) { status = false; - writeSecurityError(filepath.c_str(), L": Invalid authority key id"); + writeSecurityError(filepath.c_str(), authority_key_id_hex + L": Invalid authority key id"); } // Clean up. diff --git a/src/winmain.cpp b/src/winmain.cpp index e595aee9..56e580b9 100644 --- a/src/winmain.cpp +++ b/src/winmain.cpp @@ -1332,7 +1332,7 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR lpszCmdLine, int) signer_display_name = signer_display_name.substr(1, signer_display_name.length() - 2); } - signer_display_name = stringReplace(signer_display_name, L""", L"\""); + signer_display_name = stringReplace(signer_display_name, L"{QUOTE}", L"\""); securityGuard.setDisplayName(signer_display_name); } @@ -1345,7 +1345,7 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR lpszCmdLine, int) signer_subject = signer_subject.substr(1, signer_subject.length() - 2); } - signer_subject = stringReplace(signer_subject, L""", L"\""); + signer_subject = stringReplace(signer_subject, L"{QUOTE}", L"\""); securityGuard.setSubjectName(signer_subject); } @@ -1768,7 +1768,7 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR lpszCmdLine, int) sprintf(sha2hashStr + i * 2, "%02x", sha2hash[i]); } - dlFileSha256 = L"Downloadeed file SHA-256: "; + dlFileSha256 = L"Downloaded file SHA-256: "; dlFileSha256 += s2ws(sha2hashStr); } From 1e872fd6b55415b37f0b4ee29296e6687fe9e374 Mon Sep 17 00:00:00 2001 From: Don HO Date: Sat, 13 Dec 2025 16:51:52 +0100 Subject: [PATCH 5/8] Add -errLogPath option to override the default error path --- src/verifySignedfile.cpp | 2 +- src/winmain.cpp | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/verifySignedfile.cpp b/src/verifySignedfile.cpp index 170265ab..5048f901 100644 --- a/src/verifySignedfile.cpp +++ b/src/verifySignedfile.cpp @@ -121,7 +121,7 @@ bool SecurityGuard::verifySignedBinary(const std::wstring& filepath) if (vtrust) { - writeSecurityError(filepath.c_str(), L": trust verification failed"); + writeSecurityError(filepath.c_str(), L": chain of trust verification failed"); return false; } diff --git a/src/winmain.cpp b/src/winmain.cpp index 56e580b9..47711aa6 100644 --- a/src/winmain.cpp +++ b/src/winmain.cpp @@ -99,6 +99,7 @@ static constexpr wchar_t FLAG_CHKCERT_NAME[] = L"-chkCertName="; static constexpr wchar_t FLAG_CHKCERT_SUBJECT[] = L"-chkCertSubject="; static constexpr wchar_t FLAG_CHKCERT_KEYID[] = L"-chkCertKeyId="; static constexpr wchar_t FLAG_CHKCERT_AUTHORITYKEYID[] = L"-chkCertAuthorityKeyId="; +static constexpr wchar_t FLAG_ERRLOGPATH[] = L"-errLogPath="; static constexpr wchar_t MSGID_HELP[] = L"Usage:\r\n\ @@ -134,17 +135,20 @@ gup [-vVERSION_VALUE] [-infoUrl=URL] [-forceDomain=URL_PREFIX]\r\n\ Update mode:\r\n\ \r\n\ gup [-vVERSION_VALUE] [-infoUrl=URL] [-chkCertSig=YES_NO] [-chkCertTrustChain]\r\n\ - [-chkCertRevoc] [-chkCertName=CERT_NAME] [-chkCertSubject=CERT_SUBNAME]\r\n\ + [-chkCertRevoc] [-chkCertName=\"CERT_NAME\][-chkCertSubject = \"CERT_SUBNAME\"]\r\n\ [-chkCertKeyId=CERT_KEYID] [-chkCertAuthorityKeyId=CERT_AUTHORITYKEYID]\r\n\ + [-errLogPath=\"YOUR\\ERR\\LOG\\PATH.LOG\"]\r\n\ \r\n\ -chkCertSig= : Enable signature check on downloaded binary with \"-chkCertSig=yes\".\r\n\ Otherwise all the other \"-chkCert*\" options will be ignored.\r\n\ - -chkCertTrustChain : Enable signature trust chain verification.\r\n\ + -chkCertTrustChain : Enable signature chain of trust verification.\r\n\ -chkCertRevoc : Enable the verification of certificate revocation state.\r\n\ -chkCertName= : Verify certificate name (quotes allowed for white-spaces).\r\n\ -chkCertSubject= : Verify subject name (quotes allowed for white-spaces).\r\n\ -chkCertKeyId= : Verify certificate key identifier.\r\n\ -chkCertAuthorityKeyId= : Verify certificate authority key identifier.\r\n\ + -errLogPath= : override the default error log path. The default value is:\r\n\ + \"%LOCALAPPDATA%\\WinGUp\\log\\securityError.log\"\r\n\ \r\n\ Download & unzip mode:\r\n\ \r\n\ @@ -1362,6 +1366,12 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR lpszCmdLine, int) securityGuard.setAuthorityKeyId(authority_key_id); } + wstring errLogPath; + if (getParamValFromString(FLAG_ERRLOGPATH, params, errLogPath)) + { + securityGuard.setErrLogPath(errLogPath); + } + // Object (gupParams) is moved here because we need app icon form configuration file GupParameters gupParams(L"gup.xml"); appIconFile = gupParams.getSoftwareIcon(); @@ -1652,7 +1662,7 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR lpszCmdLine, int) if (downloadURL.size() <= forceDomain.size() // download URL must be longer than forceDomain || downloadURL.compare(0, forceDomain.size(), forceDomain) != 0) // Check if forceDomain is a prefix of download URL { - securityGuard.writeSecurityError(L"Domain is not matched for download URL:", downloadURL); + securityGuard.writeSecurityError(L"Download URL does not match the expected domain:", downloadURL); return -1; } } From 58107ff12bf06caed01316593c716d8dcb874585 Mon Sep 17 00:00:00 2001 From: Don HO Date: Sat, 13 Dec 2025 17:04:50 +0100 Subject: [PATCH 6/8] Fix build err --- src/winmain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/winmain.cpp b/src/winmain.cpp index 47711aa6..fce81eb3 100644 --- a/src/winmain.cpp +++ b/src/winmain.cpp @@ -135,7 +135,7 @@ gup [-vVERSION_VALUE] [-infoUrl=URL] [-forceDomain=URL_PREFIX]\r\n\ Update mode:\r\n\ \r\n\ gup [-vVERSION_VALUE] [-infoUrl=URL] [-chkCertSig=YES_NO] [-chkCertTrustChain]\r\n\ - [-chkCertRevoc] [-chkCertName=\"CERT_NAME\][-chkCertSubject = \"CERT_SUBNAME\"]\r\n\ + [-chkCertRevoc] [-chkCertName=\"CERT_NAME\"] [-chkCertSubject=\"CERT_SUBNAME\"]\r\n\ [-chkCertKeyId=CERT_KEYID] [-chkCertAuthorityKeyId=CERT_AUTHORITYKEYID]\r\n\ [-errLogPath=\"YOUR\\ERR\\LOG\\PATH.LOG\"]\r\n\ \r\n\ From 23a1a4fe8d13e892f6f63b0231e4f11b7e843b3e Mon Sep 17 00:00:00 2001 From: Don HO Date: Sat, 13 Dec 2025 18:02:23 +0100 Subject: [PATCH 7/8] Fix _errLogPath argument not working --- src/winmain.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/winmain.cpp b/src/winmain.cpp index fce81eb3..9ce577d5 100644 --- a/src/winmain.cpp +++ b/src/winmain.cpp @@ -1369,6 +1369,10 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR lpszCmdLine, int) wstring errLogPath; if (getParamValFromString(FLAG_ERRLOGPATH, params, errLogPath)) { + if (errLogPath.length() >= 2 && (errLogPath.front() == '"' && errLogPath.back() == '"')) ^ M + { + errLogPath = errLogPath.substr(1, errLogPath.length() - 2); ^ M + } securityGuard.setErrLogPath(errLogPath); } From cd13bb5bcf575e17e418600c1d3d4aedaab4c8aa Mon Sep 17 00:00:00 2001 From: Don HO Date: Sat, 13 Dec 2025 18:09:44 +0100 Subject: [PATCH 8/8] Fix build err --- src/winmain.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/winmain.cpp b/src/winmain.cpp index 9ce577d5..71fa17f4 100644 --- a/src/winmain.cpp +++ b/src/winmain.cpp @@ -1369,9 +1369,9 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR lpszCmdLine, int) wstring errLogPath; if (getParamValFromString(FLAG_ERRLOGPATH, params, errLogPath)) { - if (errLogPath.length() >= 2 && (errLogPath.front() == '"' && errLogPath.back() == '"')) ^ M + if (errLogPath.length() >= 2 && (errLogPath.front() == '"' && errLogPath.back() == '"')) { - errLogPath = errLogPath.substr(1, errLogPath.length() - 2); ^ M + errLogPath = errLogPath.substr(1, errLogPath.length() - 2); } securityGuard.setErrLogPath(errLogPath); }