From e5420709022e0e5ca5899be3919d4a4386cea6b6 Mon Sep 17 00:00:00 2001 From: Jonathan White Date: Sun, 16 Mar 2025 08:05:03 -0400 Subject: [PATCH] Allow for escape syntax to enable literal placeholders * Fixes #11890 --- docs/topics/Reference.adoc | 2 ++ src/core/Entry.cpp | 9 +++++++++ tests/TestEntry.cpp | 11 +++++++++++ 3 files changed, 22 insertions(+) diff --git a/docs/topics/Reference.adoc b/docs/topics/Reference.adoc index b677efb6b..9ea7e83c8 100644 --- a/docs/topics/Reference.adoc +++ b/docs/topics/Reference.adoc @@ -49,6 +49,8 @@ This section contains full details on advanced features available in KeePassXC. |{DB_DIR} |Absolute directory path of database file |=== +NOTE: You can insert literal placeholder strings by escaping the beginning and ending curly braces. For example, to insert the string `{USERNAME}`, you would type `++\{USERNAME\}++`. + === Entry Cross-Reference A reference to another entry's field is possible using the shorthand syntax: `{REF:<FIELD>@<SEARCH_IN>:<SEARCH_TEXT>}` diff --git a/src/core/Entry.cpp b/src/core/Entry.cpp index d43c2cedd..ef6440f88 100644 --- a/src/core/Entry.cpp +++ b/src/core/Entry.cpp @@ -1138,6 +1138,15 @@ QString Entry::resolveMultiplePlaceholdersRecursive(const QString& str, int maxD return str; } + // Short circuit if we have escaped the placeholder brackets + if (str.startsWith("\\{") && str.endsWith("\\}")) { + // Replace the escaped brackets with actuals and move on + auto ret = str; + ret.replace(0, 2, "{"); + ret.replace(ret.size() - 2, 2, "}"); + return ret; + } + QString result; auto matches = placeholderRegEx.globalMatch(str); int capEnd = 0; diff --git a/tests/TestEntry.cpp b/tests/TestEntry.cpp index 9035ac81a..18567001e 100644 --- a/tests/TestEntry.cpp +++ b/tests/TestEntry.cpp @@ -626,6 +626,17 @@ void TestEntry::testResolveReplacePlaceholders() // Test complicated and nested replacements QCOMPARE(entry2->resolveMultiplePlaceholders(entry2->url()), QString("cmd://sap.exe -system=server1 -client=12345 -user=Username2 -pw=Password1")); + + auto* entry3 = new Entry(); + entry3->setGroup(root); + entry3->setUuid(QUuid::createUuid()); + entry3->setTitle("Entry 3"); + entry3->setUsername("HMAC-SHA-256"); + entry3->setUrl("{T-REPLACE-RX:!{USERNAME}!\\{USERNAME\\}!!}"); + + // Test escaped enclosures + QCOMPARE(entry3->resolveMultiplePlaceholders(entry3->url()), entry3->username()); + // Test invalid syntax QString error; entry1->resolveRegexPlaceholder("{T-REPLACE-RX:/{USERNAME}/.*+?/test/}", &error); // invalid regex