diff --git a/src/browser/BrowserAction.cpp b/src/browser/BrowserAction.cpp index 0a33bdae3..91b023999 100644 --- a/src/browser/BrowserAction.cpp +++ b/src/browser/BrowserAction.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 KeePassXC Team + * Copyright (C) 2021 KeePassXC Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,6 +22,7 @@ #include "BrowserShared.h" #include "config-keepassx.h" #include "core/Global.h" +#include "core/Tools.h" #include #include @@ -51,7 +52,8 @@ namespace ERROR_KEEPASS_NO_URL_PROVIDED = 14, ERROR_KEEPASS_NO_LOGINS_FOUND = 15, ERROR_KEEPASS_NO_GROUPS_FOUND = 16, - ERROR_KEEPASS_CANNOT_CREATE_NEW_GROUP = 17 + ERROR_KEEPASS_CANNOT_CREATE_NEW_GROUP = 17, + ERROR_KEEPASS_NO_VALID_UUID_PROVIDED = 18 }; } @@ -112,6 +114,8 @@ QJsonObject BrowserAction::handleAction(const QJsonObject& json) return handleCreateNewGroup(json, action); } else if (action.compare("get-totp", Qt::CaseSensitive) == 0) { return handleGetTotp(json, action); + } else if (action.compare("delete-entry", Qt::CaseSensitive) == 0) { + return handleDeleteEntry(json, action); } // Action was not recognized @@ -360,6 +364,10 @@ QJsonObject BrowserAction::handleSetLogin(const QJsonObject& json, const QString if (uuid.isEmpty()) { browserService()->addEntry(id, login, password, url, submitUrl, realm, group, groupUuid); } else { + if (!Tools::isValidUuid(uuid)) { + return getErrorReply(action, ERROR_KEEPASS_NO_VALID_UUID_PROVIDED); + } + result = browserService()->updateEntry(id, uuid, login, password, url, submitUrl); } @@ -490,6 +498,9 @@ QJsonObject BrowserAction::handleGetTotp(const QJsonObject& json, const QString& } const QString uuid = decrypted.value("uuid").toString(); + if (!Tools::isValidUuid(uuid)) { + return getErrorReply(action, ERROR_KEEPASS_NO_VALID_UUID_PROVIDED); + } // Get the current TOTP const auto totp = browserService()->getCurrentTotp(uuid); @@ -501,6 +512,39 @@ QJsonObject BrowserAction::handleGetTotp(const QJsonObject& json, const QString& return buildResponse(action, message, newNonce); } +QJsonObject BrowserAction::handleDeleteEntry(const QJsonObject& json, const QString& action) +{ + const QString nonce = json.value("nonce").toString(); + const QString encrypted = json.value("message").toString(); + + if (!m_associated) { + return getErrorReply(action, ERROR_KEEPASS_ASSOCIATION_FAILED); + } + + const QJsonObject decrypted = decryptMessage(encrypted, nonce); + if (decrypted.isEmpty()) { + return getErrorReply(action, ERROR_KEEPASS_CANNOT_DECRYPT_MESSAGE); + } + + QString command = decrypted.value("action").toString(); + if (command.isEmpty() || command.compare("delete-entry", Qt::CaseSensitive) != 0) { + return getErrorReply(action, ERROR_KEEPASS_INCORRECT_ACTION); + } + + const auto uuid = decrypted.value("uuid").toString(); + if (!Tools::isValidUuid(uuid)) { + return getErrorReply(action, ERROR_KEEPASS_NO_VALID_UUID_PROVIDED); + } + + const auto result = browserService()->deleteEntry(uuid); + + const QString newNonce = incrementNonce(nonce); + QJsonObject message = buildMessage(newNonce); + message["success"] = result ? TRUE_STR : FALSE_STR; + + return buildResponse(action, message, newNonce); +} + QJsonObject BrowserAction::getErrorReply(const QString& action, const int errorCode) const { QJsonObject response; @@ -564,6 +608,8 @@ QString BrowserAction::getErrorMessage(const int errorCode) const return QObject::tr("No groups found"); case ERROR_KEEPASS_CANNOT_CREATE_NEW_GROUP: return QObject::tr("Cannot create new group"); + case ERROR_KEEPASS_NO_VALID_UUID_PROVIDED: + return QObject::tr("No valid UUID provided"); default: return QObject::tr("Unknown error"); } diff --git a/src/browser/BrowserAction.h b/src/browser/BrowserAction.h index c9bc02120..cfe90e7ec 100644 --- a/src/browser/BrowserAction.h +++ b/src/browser/BrowserAction.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 KeePassXC Team + * Copyright (C) 2021 KeePassXC Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -43,6 +43,7 @@ private: QJsonObject handleGetDatabaseGroups(const QJsonObject& json, const QString& action); QJsonObject handleCreateNewGroup(const QJsonObject& json, const QString& action); QJsonObject handleGetTotp(const QJsonObject& json, const QString& action); + QJsonObject handleDeleteEntry(const QJsonObject& json, const QString& action); QJsonObject buildMessage(const QString& nonce) const; QJsonObject buildResponse(const QString& action, const QJsonObject& message, const QString& nonce); diff --git a/src/browser/BrowserService.cpp b/src/browser/BrowserService.cpp index 988bf0184..f89f45d0f 100644 --- a/src/browser/BrowserService.cpp +++ b/src/browser/BrowserService.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2013 Francois Ferrand * Copyright (C) 2017 Sami Vänttinen - * Copyright (C) 2020 KeePassXC Team + * Copyright (C) 2021 KeePassXC Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -578,6 +578,32 @@ bool BrowserService::updateEntry(const QString& dbid, return result; } +bool BrowserService::deleteEntry(const QString& uuid) +{ + auto db = selectedDatabase(); + if (!db) { + return false; + } + + auto* entry = db->rootGroup()->findEntryByUuid(Tools::hexToUuid(uuid)); + if (!entry) { + return false; + } + + auto dialogResult = MessageBox::warning(nullptr, + tr("KeePassXC: Delete entry"), + tr("A request for deleting entry \"%1\" has been received.\n" + "Do you want to delete the entry?\n") + .arg(entry->title()), + MessageBox::Yes | MessageBox::No); + if (dialogResult != MessageBox::Yes) { + return false; + } + + db->recycleEntry(entry); + return true; +} + QList BrowserService::searchEntries(const QSharedPointer& db, const QString& siteUrlStr, const QString& formUrlStr) { diff --git a/src/browser/BrowserService.h b/src/browser/BrowserService.h index e45fd083e..e24ad98e9 100644 --- a/src/browser/BrowserService.h +++ b/src/browser/BrowserService.h @@ -1,7 +1,7 @@ /* * Copyright (C) 2013 Francois Ferrand * Copyright (C) 2017 Sami Vänttinen - * Copyright (C) 2020 KeePassXC Team + * Copyright (C) 2021 KeePassXC Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -71,6 +71,7 @@ public: const QString& password, const QString& siteUrlStr, const QString& formUrlStr); + bool deleteEntry(const QString& uuid); QJsonArray findMatchingEntries(const QString& dbid, const QString& siteUrlStr, diff --git a/src/core/Tools.cpp b/src/core/Tools.cpp index 50145acd0..22460d2d1 100644 --- a/src/core/Tools.cpp +++ b/src/core/Tools.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2012 Felix Geyer * Copyright (C) 2017 Lennart Glauer - * Copyright (C) 2017 KeePassXC Team + * Copyright (C) 2021 KeePassXC Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -317,6 +317,20 @@ namespace Tools return QUuid::fromRfc4122(QByteArray::fromHex(uuid.toLatin1())); } + bool isValidUuid(const QString& uuidStr) + { + if (uuidStr.isEmpty() || uuidStr.length() != 32 || !isHex(uuidStr.toLatin1())) { + return false; + } + + const auto uuid = hexToUuid(uuidStr); + if (uuid.isNull() || uuid.version() == QUuid::VerUnknown) { + return false; + } + + return true; + } + QString envSubstitute(const QString& filepath, QProcessEnvironment environment) { QString subbed = filepath; diff --git a/src/core/Tools.h b/src/core/Tools.h index 45bc86669..8ebcef1c5 100644 --- a/src/core/Tools.h +++ b/src/core/Tools.h @@ -1,6 +1,6 @@ /* * Copyright (C) 2012 Felix Geyer - * Copyright (C) 2017 KeePassXC Team + * Copyright (C) 2021 KeePassXC Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -40,6 +40,7 @@ namespace Tools bool checkUrlValid(const QString& urlField); QString uuidToHex(const QUuid& uuid); QUuid hexToUuid(const QString& uuid); + bool isValidUuid(const QString& uuidStr); QRegularExpression convertToRegex(const QString& string, bool useWildcards = false, bool exactMatch = false, diff --git a/tests/TestTools.cpp b/tests/TestTools.cpp index c79af477c..312af3da0 100644 --- a/tests/TestTools.cpp +++ b/tests/TestTools.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 KeePassXC Team + * Copyright (C) 2021 KeePassXC Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -88,3 +88,20 @@ void TestTools::testEnvSubstitute() QCOMPARE(Tools::envSubstitute("start/$EMPTY$$EMPTY$HOME/end", environment), QString("start/$/home/user/end")); #endif } + +void TestTools::testValidUuid() +{ + auto validUuid = Tools::uuidToHex(QUuid::createUuid()); + auto nonValidUuid = "1234567890abcdef1234567890abcdef"; + auto emptyUuid = QString(); + auto shortUuid = validUuid.left(10); + auto longUuid = validUuid + "baddata"; + auto nonHexUuid = Tools::uuidToHex(QUuid::createUuid()).replace(0, 1, 'p'); + + QVERIFY(Tools::isValidUuid(validUuid)); + QVERIFY(not Tools::isValidUuid(nonValidUuid)); + QVERIFY(not Tools::isValidUuid(emptyUuid)); + QVERIFY(not Tools::isValidUuid(shortUuid)); + QVERIFY(not Tools::isValidUuid(longUuid)); + QVERIFY(not Tools::isValidUuid(nonHexUuid)); +} diff --git a/tests/TestTools.h b/tests/TestTools.h index dd646fcc4..24554b6fd 100644 --- a/tests/TestTools.h +++ b/tests/TestTools.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 KeePassXC Team + * Copyright (C) 2021 KeePassXC Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -28,6 +28,7 @@ private slots: void testIsHex(); void testIsBase64(); void testEnvSubstitute(); + void testValidUuid(); }; #endif // KEEPASSX_TESTTOOLS_H