Sanitize username to prevent single-instance detection failure (#12559)

---------

Co-authored-by: Jonathan White <support@dmapps.us>
This commit is contained in:
Siddhant Shekhar 2025-11-01 19:55:10 +05:30 committed by GitHub
parent f927c4c41a
commit d9ccf767d0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 61 additions and 11 deletions

View file

@ -426,6 +426,29 @@ namespace Tools
return filename.trimmed();
}
QString cleanUsername()
{
#if defined(Q_OS_WIN)
QString userName = qgetenv("USERNAME");
if (userName.isEmpty()) {
userName = qgetenv("USER");
}
#else
QString userName = qgetenv("USER");
if (userName.isEmpty()) {
userName = qgetenv("USERNAME");
}
#endif
// Sanitize username for file safety
userName = userName.trimmed();
// Replace <>:"/\|?* with _
userName.replace(QRegularExpression(R"([<>:\"\/\\|?*])"), "_");
// Remove trailing dots and spaces
userName.replace(QRegularExpression(R"([.\s]+$)"), "");
return userName;
}
QVariantMap qo2qvm(const QObject* object, const QStringList& ignoredProperties)
{
QVariantMap result;

View file

@ -48,6 +48,7 @@ namespace Tools
QString envSubstitute(const QString& filepath,
QProcessEnvironment environment = QProcessEnvironment::systemEnvironment());
QString cleanFilename(QString filename);
QString cleanUsername();
template <class T> QSet<T> asSet(const QList<T>& a)
{

View file

@ -20,6 +20,7 @@
#include "Application.h"
#include "core/Bootstrap.h"
#include "core/Tools.h"
#include "gui/MainWindow.h"
#include "gui/MessageBox.h"
#include "gui/osutils/OSUtils.h"
@ -31,6 +32,7 @@
#include <QLocalSocket>
#include <QLockFile>
#include <QPixmapCache>
#include <QRegularExpression>
#include <QSocketNotifier>
#include <QStandardPaths>
@ -63,20 +65,19 @@ Application::Application(int& argc, char** argv)
registerUnixSignals();
#endif
QString userName = qgetenv("USER");
if (userName.isEmpty()) {
userName = qgetenv("USERNAME");
}
QString identifier = "keepassxc";
if (!userName.isEmpty()) {
identifier += "-" + userName;
// Build identifier
auto identifier = QStringLiteral("keepassxc");
auto username = Tools::cleanUsername();
if (!username.isEmpty()) {
identifier += QChar('-') + username;
}
#ifdef QT_DEBUG
// In DEBUG mode don't interfere with Release instances
identifier += "-DEBUG";
// In DEBUG mode dont interfere with Release instances
identifier += QStringLiteral("-DEBUG");
#endif
QString lockName = identifier + ".lock";
m_socketName = identifier + ".socket";
QString lockName = identifier + QStringLiteral(".lock");
m_socketName = identifier + QStringLiteral(".socket");
// According to documentation we should use RuntimeLocation on *nixes, but even Qt doesn't respect
// this and creates sockets in TempLocation, so let's be consistent.

View file

@ -428,3 +428,26 @@ void TestTools::testIsTextMimeType()
QVERIFY(!Tools::isTextMimeType(noText));
}
}
// Test sanitization logic for Tools::cleanUsername
void TestTools::testCleanUsername()
{
// Test vars
QFETCH(QString, input);
QFETCH(QString, expected);
qputenv("USER", input.toUtf8());
qputenv("USERNAME", input.toUtf8());
QCOMPARE(Tools::cleanUsername(), expected);
}
void TestTools::testCleanUsername_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<QString>("expected");
QTest::newRow("Leading and trailing spaces") << " user " << "user";
QTest::newRow("Special characters") << R"(user<>:"/\|?*name)" << "user_________name";
QTest::newRow("Trailing dots and spaces") << "username... " << "username";
QTest::newRow("Combination of issues") << R"( user<>:"/\|?*name... )" << "user_________name";
}

View file

@ -41,6 +41,8 @@ private slots:
void testGetMimeType();
void testGetMimeTypeByFileInfo();
void testIsTextMimeType();
void testCleanUsername();
void testCleanUsername_data();
};
#endif // KEEPASSX_TESTTOOLS_H