From b0987920016b36625516ddec6022d7620e0abff0 Mon Sep 17 00:00:00 2001 From: Comely Date: Sun, 12 Jul 2026 17:36:53 -0700 Subject: [PATCH] fix(integrity): restore bootstrap runtime protection --- Common/CMakeLists.txt | 1 + Common/IntegrityHelper.cpp | 18 +++------------ Common/RuntimeProtectionPolicy.h | 38 ++++++++++++++++++++++++++++++++ Updater/UpdaterLogic.cpp | 23 +++---------------- 4 files changed, 45 insertions(+), 35 deletions(-) create mode 100644 Common/RuntimeProtectionPolicy.h diff --git a/Common/CMakeLists.txt b/Common/CMakeLists.txt index 8f97862..e57aea7 100644 --- a/Common/CMakeLists.txt +++ b/Common/CMakeLists.txt @@ -7,6 +7,7 @@ set(_common_sources ConfigHelper.cpp InitialStatePolicy.h ManifestBootstrapPolicy.h + RuntimeProtectionPolicy.h PolicyHelper.h PolicyHelper.cpp LocalStateHelper.h diff --git a/Common/IntegrityHelper.cpp b/Common/IntegrityHelper.cpp index 9abbf16..cf76858 100644 --- a/Common/IntegrityHelper.cpp +++ b/Common/IntegrityHelper.cpp @@ -1,5 +1,6 @@ #include "IntegrityHelper.h" #include "ConfigHelper.h" +#include "RuntimeProtectionPolicy.h" #include #include #include @@ -33,21 +34,8 @@ bool IntegrityHelper::safeRelativePath(const QString& path) const bool IntegrityHelper::runtimeProtectedPath(const QString& path) const { - const QString p = QDir::fromNativeSeparators(path).toCaseFolded(); - QSet protectedPaths{ - "client.ini", "config/app_config.json", "config/local_state.json", - "config/client_identity.dat", "config/version_policy.dat" - }; - const QString runtimePrefix = ConfigHelper::instance().runtimeRelativePath().toCaseFolded(); - if (!runtimePrefix.isEmpty()) { - const QStringList runtimeProtected{ - "client.ini", "config/app_config.json", "config/local_state.json", - "config/client_identity.dat", "config/version_policy.dat" - }; - for (const QString& protectedPath : runtimeProtected) - protectedPaths.insert(runtimePrefix + "/" + protectedPath); - } - return protectedPaths.contains(p); + return UpdateClient::isRuntimeProtectedPath( + path, ConfigHelper::instance().runtimeRelativePath()); } QString IntegrityHelper::sha256(const QString& filePath) const diff --git a/Common/RuntimeProtectionPolicy.h b/Common/RuntimeProtectionPolicy.h new file mode 100644 index 0000000..cfec0a3 --- /dev/null +++ b/Common/RuntimeProtectionPolicy.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include +#include + +namespace UpdateClient +{ +inline bool isRuntimeProtectedPath(const QString& path, + const QString& runtimeRelativePath) +{ + const QString normalizedPath = + QDir::cleanPath(QDir::fromNativeSeparators(path)).toCaseFolded(); + static const QSet protectedPaths{ + QStringLiteral("bootstrap.exe"), + QStringLiteral("client.ini"), + QStringLiteral("config/app_config.json"), + QStringLiteral("config/local_state.json"), + QStringLiteral("config/client_identity.dat"), + QStringLiteral("config/version_policy.dat") + }; + if (protectedPaths.contains(normalizedPath)) + return true; + + QString runtimePrefix = QDir::cleanPath( + QDir::fromNativeSeparators(runtimeRelativePath)).toCaseFolded(); + if (runtimePrefix.isEmpty() || runtimePrefix == QStringLiteral(".")) + return false; + while (runtimePrefix.startsWith(QStringLiteral("./"))) + runtimePrefix.remove(0, 2); + + for (const QString& protectedPath : protectedPaths) { + if (normalizedPath == runtimePrefix + QLatin1Char('/') + protectedPath) + return true; + } + return false; +} +} diff --git a/Updater/UpdaterLogic.cpp b/Updater/UpdaterLogic.cpp index 193ab63..13cbf46 100644 --- a/Updater/UpdaterLogic.cpp +++ b/Updater/UpdaterLogic.cpp @@ -19,6 +19,7 @@ #include #include #include "ConfigHelper.h" +#include "RuntimeProtectionPolicy.h" #ifdef HAVE_OPENSSL #include @@ -810,26 +811,8 @@ bool UpdaterLogic::downloadSingleFile(const QString& url, const QString& savePat bool UpdaterLogic::isRuntimeProtectedPath(const QString& path) const { - const QString normalized = QDir::fromNativeSeparators(path).toCaseFolded(); - QSet protectedPaths{ - QStringLiteral("bootstrap.exe"), - QStringLiteral("client.ini"), - QStringLiteral("config/app_config.json"), - QStringLiteral("config/local_state.json"), - QStringLiteral("config/client_identity.dat"), - QStringLiteral("config/version_policy.dat") - }; - const QString runtimePrefix = ConfigHelper::instance().runtimeRelativePath().toCaseFolded(); - if (!runtimePrefix.isEmpty()) { - const QStringList runtimeProtected{ - QStringLiteral("bootstrap.exe"), QStringLiteral("client.ini"), - QStringLiteral("config/app_config.json"), QStringLiteral("config/local_state.json"), - QStringLiteral("config/client_identity.dat"), QStringLiteral("config/version_policy.dat") - }; - for (const QString& protectedPath : runtimeProtected) - protectedPaths.insert(runtimePrefix + "/" + protectedPath); - } - return protectedPaths.contains(normalized); + return UpdateClient::isRuntimeProtectedPath( + path, ConfigHelper::instance().runtimeRelativePath()); } bool UpdaterLogic::isSafeRelativePath(const QString& path) const