diff --git a/share/docs/man/keepassxc-cli.1 b/share/docs/man/keepassxc-cli.1 index 15d0fedc1..bcc97efae 100644 --- a/share/docs/man/keepassxc-cli.1 +++ b/share/docs/man/keepassxc-cli.1 @@ -182,6 +182,10 @@ an error if no TOTP is configured for the entry. Shows the named attributes. This option can be specified more than once, with each attribute shown one-per-line in the given order. If no attributes are specified and \fI-t\fP is not specified, a summary of the default attributes is given. +Protected attributes will be displayed in clear text if specified explicitly by this option. + +.IP "-s, --show-protected" +Shows the protected attributes in clear text. .IP "-t, --totp" Also shows the current TOTP, reporting an error if no TOTP is configured for diff --git a/src/cli/Show.cpp b/src/cli/Show.cpp index 7da1c871c..646d5d90d 100644 --- a/src/cli/Show.cpp +++ b/src/cli/Show.cpp @@ -31,6 +31,11 @@ const QCommandLineOption Show::TotpOption = QCommandLineOption(QStringList() << << "totp", QObject::tr("Show the entry's current TOTP.")); +const QCommandLineOption Show::ProtectedAttributesOption = + QCommandLineOption(QStringList() << "s" + << "show-protected", + QObject::tr("Show the protected attributes in clear text.")); + const QCommandLineOption Show::AttributesOption = QCommandLineOption( QStringList() << "a" << "attributes", @@ -46,6 +51,7 @@ Show::Show() description = QObject::tr("Show an entry's information."); options.append(Show::TotpOption); options.append(Show::AttributesOption); + options.append(Show::ProtectedAttributesOption); positionalArguments.append({QString("entry"), QObject::tr("Name of the entry to show."), QString("")}); } @@ -57,6 +63,7 @@ int Show::executeWithDatabase(QSharedPointer database, QSharedPointer< const QStringList args = parser->positionalArguments(); const QString& entryPath = args.at(1); bool showTotp = parser->isSet(Show::TotpOption); + bool showProtectedAttributes = parser->isSet(Show::ProtectedAttributesOption); QStringList attributes = parser->values(Show::AttributesOption); Entry* entry = database->rootGroup()->findEntryByPath(entryPath); @@ -78,16 +85,20 @@ int Show::executeWithDatabase(QSharedPointer database, QSharedPointer< // Iterate over the attributes and output them line-by-line. bool sawUnknownAttribute = false; - for (const QString& attribute : asConst(attributes)) { - if (!entry->attributes()->contains(attribute)) { + for (const QString& attributeName : asConst(attributes)) { + if (!entry->attributes()->contains(attributeName)) { sawUnknownAttribute = true; - errorTextStream << QObject::tr("ERROR: unknown attribute %1.").arg(attribute) << endl; + errorTextStream << QObject::tr("ERROR: unknown attribute %1.").arg(attributeName) << endl; continue; } if (showAttributeNames) { - outputTextStream << attribute << ": "; + outputTextStream << attributeName << ": "; + } + if (entry->attributes()->isProtected(attributeName) && showAttributeNames && !showProtectedAttributes) { + outputTextStream << "PROTECTED" << endl; + } else { + outputTextStream << entry->resolveMultiplePlaceholders(entry->attributes()->value(attributeName)) << endl; } - outputTextStream << entry->resolveMultiplePlaceholders(entry->attributes()->value(attribute)) << endl; } if (showTotp) { diff --git a/src/cli/Show.h b/src/cli/Show.h index 03700b465..bf76c6973 100644 --- a/src/cli/Show.h +++ b/src/cli/Show.h @@ -29,6 +29,7 @@ public: static const QCommandLineOption TotpOption; static const QCommandLineOption AttributesOption; + static const QCommandLineOption ProtectedAttributesOption; }; #endif // KEEPASSXC_SHOW_H diff --git a/tests/TestCli.cpp b/tests/TestCli.cpp index f1f39e9f5..8a9ab50ce 100644 --- a/tests/TestCli.cpp +++ b/tests/TestCli.cpp @@ -1682,14 +1682,15 @@ void TestCli::testShow() QCOMPARE(m_stdoutFile->readAll(), QByteArray("Title: Sample Entry\n" "UserName: User Name\n" - "Password: Password\n" + "Password: PROTECTED\n" "URL: http://www.somesite.com/\n" "Notes: Notes\n")); qint64 pos = m_stdoutFile->pos(); Utils::Test::setNextPassword("a"); - showCmd.execute({"show", m_dbFile->fileName(), "-q", "/Sample Entry"}); + showCmd.execute({"show", "-s", m_dbFile->fileName(), "/Sample Entry"}); m_stdoutFile->seek(pos); + m_stdoutFile->readLine(); // skip password prompt QCOMPARE(m_stdoutFile->readAll(), QByteArray("Title: Sample Entry\n" "UserName: User Name\n" @@ -1697,6 +1698,17 @@ void TestCli::testShow() "URL: http://www.somesite.com/\n" "Notes: Notes\n")); + pos = m_stdoutFile->pos(); + Utils::Test::setNextPassword("a"); + showCmd.execute({"show", m_dbFile->fileName(), "-q", "/Sample Entry"}); + m_stdoutFile->seek(pos); + QCOMPARE(m_stdoutFile->readAll(), + QByteArray("Title: Sample Entry\n" + "UserName: User Name\n" + "Password: PROTECTED\n" + "URL: http://www.somesite.com/\n" + "Notes: Notes\n")); + pos = m_stdoutFile->pos(); Utils::Test::setNextPassword("a"); showCmd.execute({"show", "-a", "Title", m_dbFile->fileName(), "/Sample Entry"}); @@ -1704,6 +1716,13 @@ void TestCli::testShow() m_stdoutFile->readLine(); // skip password prompt QCOMPARE(m_stdoutFile->readAll(), QByteArray("Sample Entry\n")); + pos = m_stdoutFile->pos(); + Utils::Test::setNextPassword("a"); + showCmd.execute({"show", "-a", "Password", m_dbFile->fileName(), "/Sample Entry"}); + m_stdoutFile->seek(pos); + m_stdoutFile->readLine(); // skip password prompt + QCOMPARE(m_stdoutFile->readAll(), QByteArray("Password\n")); + pos = m_stdoutFile->pos(); Utils::Test::setNextPassword("a"); showCmd.execute({"show", "-a", "Title", "-a", "URL", m_dbFile->fileName(), "/Sample Entry"});