[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

@ -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""));
}