Repository: keepassxreboot/keepassxc (C++)

Issue:
Single instance detection doesn't work when USER env var is a non-standard value
### Have you searched for an existing issue?

- [x] Yes, I tried searching and reviewed the pinned issues

### Brief Summary

The Option "Start only a single instance of KeePassXC" does not work. Even if I enabled it, I can start a second, third etc. session.

I think it's because on my computer there exists an environment variable "USER" with a stupid value (like "/../../ABC123/user/..") and this takes priority over the environment variable "USERNAME". As a result the ".lock"-File can not be created. 

Unfortunately the "USER"-Variable is needed by an important other application, so i can not delete it.

My proposal ist to insert something like this to "gui\Application.cpp":
#if defined (Q_OS_WIN)
    QString userName = qgetenv("USERNAME");
#else
    QString userName = qgetenv("USER");
    if (userName.isEmpty()) {
        userName = qgetenv("USERNAME");
    }
#endif

This way the behaviour only changes on Windows. Linux, MaxOS and others are not affected.


### Steps to Reproduce

1. Create an environment variable "USER" with value like "/../../ABC123/user/.." at user level
2. Start KeePassXC
3. Start KeePassXC again
4. search for %temp%\keepassxc-%username%.lock"

### Expected Versus Actual Behavior

If I enable the option "Start only a single instance of KeePassXC" only one instane of KeePassXC should run. All other starts should bring up this instance.

### KeePassXC Debug Information

```Text

```

### Operating System

Windows

### Linux Desktop Environment

None

### Linux Windowing System

None


Discussion: Hmmmm I see now we are kind of assuming a simple text string in the env var USER:

https://github.com/keepassxreboot/keepassxc/blob/f53c7e5af54974c332b460e36cbe2c2bd0a34b12/src/gui/Application.cpp#L66-L79

For the time being, you may have to create a batch script that launches KeePassXC with USER unset:

 ```
set USER=
"C:\Program Files\KeePassXC\KeePassXC.exe"
```

Or do the batch script idea for the crazy app that is expecting some value in USER
The batch-script is a possible workaround. But it does not work if you check the option "Automatically launch KeePassXC at system startup", because it added the "KeePassXC.exe" to the registry key "HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run".

USER is set at logon and I can't change this.

But I can add the batch script to autorun till you fixed this issue. Thank you.
Can I work on this issue?


sure thing



Fix (patch):
diff --git a/src/core/Tools.cpp b/src/core/Tools.cpp
index 1d0bb1ba0c..4ac9edfc08 100644
--- a/src/core/Tools.cpp
+++ b/src/core/Tools.cpp
@@ -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;
diff --git a/src/core/Tools.h b/src/core/Tools.h
index ee5ef612e0..a9a6458461 100644
--- a/src/core/Tools.h
+++ b/src/core/Tools.h
@@ -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)
     {
diff --git a/src/gui/Application.cpp b/src/gui/Application.cpp
index a533172bd6..dcbe2508f0 100644
--- a/src/gui/Application.cpp
+++ b/src/gui/Application.cpp
@@ -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 don’t 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.