[Installer] Name only the payload programs installed in the directory (#7684)
Some checks failed
CodeQL / Analyze (cpp) (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
Build Desktop / Configure (push) Has been cancelled
Build Desktop / Prune caches (push) Has been cancelled
Build Docker / Servatrice (arm) (push) Has been cancelled
Build Docker / Servatrice (x86) (push) Has been cancelled
Build Desktop / Debian 13 (push) Has been cancelled
Build Desktop / Debian 12 (push) Has been cancelled
Build Desktop / Fedora 44 (push) Has been cancelled
Build Desktop / Fedora 43 (push) Has been cancelled
Build Desktop / Servatrice_Debian 12 (push) Has been cancelled
Build Desktop / Ubuntu 24.04 (push) Has been cancelled
Build Desktop / Ubuntu 26.04 (push) Has been cancelled
Build Desktop / Arch (push) Has been cancelled
Build Desktop / Ubuntu 26.04 cover (push) Has been cancelled
Build Desktop / macOS 13 Intel (push) Has been cancelled
Build Desktop / macOS 14 (push) Has been cancelled
Build Desktop / macOS 15 (push) Has been cancelled
Build Desktop / macOS 26 Debug (push) Has been cancelled
Build Desktop / Windows 10 (push) Has been cancelled
Build Docker / Publish multi-platform Servatrice image (push) Has been cancelled

The running-instance lookup named every process whose image path began with the
install directory, subfolders included. Installing into a folder that holds
another application in a subfolder therefore reported that application as
holding the previous version, and nothing the guard offers could clear it: the
close asks cockatrice.exe only, and the end answer refuses anything that is not
a payload program. Both answers were no-ops, so the update could only abort and
tell the user to close a program that was never touching the files.

Name only the programs the payload installs, and only when the image lies in the
install directory itself. A program below the directory that really does hold a
payload file is still named by the Restart Manager lookup, which is the lookup
for that question.

The Windows probe check now covers a payload-named program in a subfolder and a
program the payload does not install; both have to come back as nothing.

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
This commit is contained in:
sentry[bot] 2026-10-30 13:16:50 +01:00 • committed by GitHub
parent ded83aaab5
commit 0369aaffa9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 279 additions and 89 deletions

View file

@ -178,8 +178,9 @@ cockatrice_add_test(NAME user_data_backup_test SOURCES user_data_backup_test.cpp
cockatrice_add_test(NAME password_hash_test SOURCES password_hash_test.cpp LIBS libcockatrice_utility)
cockatrice_add_test(NAME report_categories_test SOURCES report_categories_test.cpp LIBS libcockatrice_utility)
cockatrice_add_test(NAME warning_categories_test SOURCES warning_categories_test.cpp LIBS libcockatrice_utility)
# The installer's holder probe is Windows-only, but the wording it produces is plain C++
# and is what a stuck user has to act on, so it is tested everywhere.
# The installer's holder probe is Windows-only, but the wording it produces and the
# question which programs are the payload's own are plain C++ and are what a stuck user
# has to act on, so they are tested everywhere.
cockatrice_add_test(
NAME
holder_line_test
@ -189,6 +190,15 @@ cockatrice_add_test(
INCLUDE_DIRS
${CMAKE_SOURCE_DIR}/tools/installer-holder-probe
)
cockatrice_add_test(
NAME
payload_paths_test
SOURCES
${CMAKE_SOURCE_DIR}/tools/installer-holder-probe/payload_paths.cpp
payload_paths_test.cpp
INCLUDE_DIRS
${CMAKE_SOURCE_DIR}/tools/installer-holder-probe
)
cockatrice_add_test(
NAME
playmat_resolver_test

View file

@ -0,0 +1,57 @@
#include "payload_paths.h"
#include <gtest/gtest.h>
#include <string>
namespace
{
/// The directory part of a path spelled with the same case as the install directory, which
/// is the comparison the probe makes once both sides are folded - see isSameDirectory in
/// restart_manager_holders.cpp. Kept as a plain string here so the shape of a path can be
/// tested without Windows.
bool runsFrom(const std::wstring &imagePath, const std::wstring &installDir)
{
return parentDirectoryOf(imagePath) == installDir;
}
} // namespace
TEST(PayloadPaths, SplitsAnImagePathIntoItsDirectoryAndName)
{
EXPECT_EQ(fileNameOf(L"D:\\Games\\Cockatrice\\cockatrice.exe"), std::wstring(L"cockatrice.exe"));
EXPECT_EQ(parentDirectoryOf(L"D:\\Games\\Cockatrice\\cockatrice.exe"), std::wstring(L"D:\\Games\\Cockatrice"));
EXPECT_EQ(fileNameOf(L"cockatrice.exe"), std::wstring(L"cockatrice.exe"));
EXPECT_EQ(parentDirectoryOf(L"cockatrice.exe"), std::wstring());
EXPECT_EQ(parentDirectoryOf(L"D:/Games/Cockatrice/cockatrice.exe"), std::wstring(L"D:/Games/Cockatrice"));
}
TEST(PayloadPaths, OnlyAnImageInTheInstallDirectoryRunsFromIt)
{
const std::wstring installDir = L"D:\\Games\\Cockatrice";
EXPECT_TRUE(runsFrom(L"D:\\Games\\Cockatrice\\cockatrice.exe", installDir));
// A folder the user keeps other applications in is not the installation, and neither is
// a sibling whose name merely begins with the same letters.
EXPECT_FALSE(runsFrom(L"D:\\Games\\Cockatrice\\old\\cockatrice.exe", installDir));
EXPECT_FALSE(runsFrom(L"D:\\Games\\Cockatrice-old\\cockatrice.exe", installDir));
EXPECT_FALSE(runsFrom(L"D:\\Games\\cockatrice.exe", installDir));
}
TEST(PayloadPaths, KnowsTheProgramsThePayloadInstalls)
{
EXPECT_TRUE(isPayloadProgram(L"cockatrice.exe"));
EXPECT_TRUE(isPayloadProgram(L"oracle.exe"));
EXPECT_TRUE(isPayloadProgram(L"servatrice.exe"));
EXPECT_TRUE(isPayloadProgram(L"COCKATRICE.EXE"));
EXPECT_TRUE(isPayloadProgram(L"Servatrice.Exe"));
}
TEST(PayloadPaths, LeavesEveryOtherProgramAlone)
{
// A program the user keeps in the install directory, the uninstaller, a service host and
// a name that only looks like a payload program are all not instances of the payload.
EXPECT_FALSE(isPayloadProgram(L"uninstall.exe"));
EXPECT_FALSE(isPayloadProgram(L"tool.exe"));
EXPECT_FALSE(isPayloadProgram(L"svchost.exe"));
EXPECT_FALSE(isPayloadProgram(L"cockatrice2.exe"));
EXPECT_FALSE(isPayloadProgram(L"mycockatrice.exe"));
EXPECT_FALSE(isPayloadProgram(L""));
}