mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-10-01 01:26:01 -04:00
parent
6c4a82bd51
commit
d16fc2d62a
@ -7561,6 +7561,74 @@ Please consider generating a new key file.</source>
|
|||||||
<source>Use custom character set</source>
|
<source>Use custom character set</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Location</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Database created</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Last saved</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Unsaved changes</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>yes</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>no</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Number of groups</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Number of entries</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Number of expired entries</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Unique passwords</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Non-unique passwords</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Maximum password reuse</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Number of short passwords</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Number of weak passwords</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Entries excluded from reports</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Average password length</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>%1 characters</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>QtIOCompressor</name>
|
<name>QtIOCompressor</name>
|
||||||
|
@ -39,6 +39,7 @@ set(keepassx_SOURCES
|
|||||||
core/Config.cpp
|
core/Config.cpp
|
||||||
core/CustomData.cpp
|
core/CustomData.cpp
|
||||||
core/Database.cpp
|
core/Database.cpp
|
||||||
|
core/DatabaseStats.cpp
|
||||||
core/Entry.cpp
|
core/Entry.cpp
|
||||||
core/EntryAttachments.cpp
|
core/EntryAttachments.cpp
|
||||||
core/EntryAttributes.cpp
|
core/EntryAttributes.cpp
|
||||||
|
@ -18,7 +18,9 @@
|
|||||||
#include "Info.h"
|
#include "Info.h"
|
||||||
|
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
|
#include "core/DatabaseStats.h"
|
||||||
#include "core/Global.h"
|
#include "core/Global.h"
|
||||||
|
#include "core/Group.h"
|
||||||
#include "core/Metadata.h"
|
#include "core/Metadata.h"
|
||||||
|
|
||||||
#include <QCommandLineParser>
|
#include <QCommandLineParser>
|
||||||
@ -47,5 +49,25 @@ int Info::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<
|
|||||||
} else {
|
} else {
|
||||||
out << QObject::tr("Recycle bin is not enabled.") << endl;
|
out << QObject::tr("Recycle bin is not enabled.") << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DatabaseStats stats(database);
|
||||||
|
out << QObject::tr("Location") << ": " << database->filePath() << endl;
|
||||||
|
out << QObject::tr("Database created") << ": "
|
||||||
|
<< database->rootGroup()->timeInfo().creationTime().toString(Qt::DefaultLocaleShortDate) << endl;
|
||||||
|
out << QObject::tr("Last saved") << ": " << stats.modified.toString(Qt::DefaultLocaleShortDate) << endl;
|
||||||
|
out << QObject::tr("Unsaved changes") << ": " << (database->isModified() ? QObject::tr("yes") : QObject::tr("no"))
|
||||||
|
<< endl;
|
||||||
|
out << QObject::tr("Number of groups") << ": " << QString::number(stats.groupCount) << endl;
|
||||||
|
out << QObject::tr("Number of entries") << ": " << QString::number(stats.entryCount) << endl;
|
||||||
|
out << QObject::tr("Number of expired entries") << ": " << QString::number(stats.expiredEntries) << endl;
|
||||||
|
out << QObject::tr("Unique passwords") << ": " << QString::number(stats.uniquePasswords) << endl;
|
||||||
|
out << QObject::tr("Non-unique passwords") << ": " << QString::number(stats.reusedPasswords) << endl;
|
||||||
|
out << QObject::tr("Maximum password reuse") << ": " << QString::number(stats.maxPwdReuse()) << endl;
|
||||||
|
out << QObject::tr("Number of short passwords") << ": " << QString::number(stats.shortPasswords) << endl;
|
||||||
|
out << QObject::tr("Number of weak passwords") << ": " << QString::number(stats.weakPasswords) << endl;
|
||||||
|
out << QObject::tr("Entries excluded from reports") << ": " << QString::number(stats.excludedEntries) << endl;
|
||||||
|
out << QObject::tr("Average password length") << ": " << QObject::tr("%1 characters").arg(stats.averagePwdLength())
|
||||||
|
<< endl;
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
119
src/core/DatabaseStats.cpp
Normal file
119
src/core/DatabaseStats.cpp
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 KeePassXC Team <team@keepassxc.org>
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* the Free Software Foundation, either version 2 or (at your option)
|
||||||
|
* version 3 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include "DatabaseStats.h"
|
||||||
|
|
||||||
|
// Ctor does all the work
|
||||||
|
DatabaseStats::DatabaseStats(QSharedPointer<Database> db)
|
||||||
|
: modified(QFileInfo(db->filePath()).lastModified())
|
||||||
|
, m_db(db)
|
||||||
|
{
|
||||||
|
gatherStats(db->rootGroup()->groupsRecursive(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get average password length
|
||||||
|
int DatabaseStats::averagePwdLength() const
|
||||||
|
{
|
||||||
|
const auto passwords = uniquePasswords + reusedPasswords;
|
||||||
|
return passwords == 0 ? 0 : std::round(totalPasswordLength / double(passwords));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get max number of password reuse (=how many entries
|
||||||
|
// share the same password)
|
||||||
|
int DatabaseStats::maxPwdReuse() const
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
for (const auto& count : m_passwords) {
|
||||||
|
ret = std::max(ret, count);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A warning sign is displayed if one of the
|
||||||
|
// following returns true.
|
||||||
|
bool DatabaseStats::isAnyExpired() const
|
||||||
|
{
|
||||||
|
return expiredEntries > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseStats::areTooManyPwdsReused() const
|
||||||
|
{
|
||||||
|
return reusedPasswords > uniquePasswords / 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseStats::arePwdsReusedTooOften() const
|
||||||
|
{
|
||||||
|
return maxPwdReuse() > 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseStats::isAvgPwdTooShort() const
|
||||||
|
{
|
||||||
|
return averagePwdLength() < 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatabaseStats::gatherStats(const QList<Group*>& groups)
|
||||||
|
{
|
||||||
|
auto checker = HealthChecker(m_db);
|
||||||
|
|
||||||
|
for (const auto* group : groups) {
|
||||||
|
// Don't count anything in the recycle bin
|
||||||
|
if (group->isRecycled()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
++groupCount;
|
||||||
|
|
||||||
|
for (const auto* entry : group->entries()) {
|
||||||
|
// Don't count anything in the recycle bin
|
||||||
|
if (entry->isRecycled()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
++entryCount;
|
||||||
|
|
||||||
|
if (entry->isExpired()) {
|
||||||
|
++expiredEntries;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get password statistics
|
||||||
|
const auto pwd = entry->password();
|
||||||
|
if (!pwd.isEmpty()) {
|
||||||
|
if (!m_passwords.contains(pwd)) {
|
||||||
|
++uniquePasswords;
|
||||||
|
} else {
|
||||||
|
++reusedPasswords;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pwd.size() < PasswordHealth::Length::Short) {
|
||||||
|
++shortPasswords;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Speed up Zxcvbn process by excluding very long passwords and most passphrases
|
||||||
|
if (pwd.size() < PasswordHealth::Length::Long
|
||||||
|
&& checker.evaluate(entry)->quality() <= PasswordHealth::Quality::Weak) {
|
||||||
|
++weakPasswords;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry->excludeFromReports()) {
|
||||||
|
++excludedEntries;
|
||||||
|
}
|
||||||
|
|
||||||
|
totalPasswordLength += pwd.size();
|
||||||
|
m_passwords[pwd]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
59
src/core/DatabaseStats.h
Normal file
59
src/core/DatabaseStats.h
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 KeePassXC Team <team@keepassxc.org>
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* the Free Software Foundation, either version 2 or (at your option)
|
||||||
|
* version 3 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef KEEPASSXC_DATABASESTATS_H
|
||||||
|
#define KEEPASSXC_DATABASESTATS_H
|
||||||
|
#include "PasswordHealth.h"
|
||||||
|
#include "core/Group.h"
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <cmath>
|
||||||
|
class DatabaseStats
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// The statistics we collect:
|
||||||
|
QDateTime modified; // File modification time
|
||||||
|
int groupCount = 0; // Number of groups in the database
|
||||||
|
int entryCount = 0; // Number of entries (across all groups)
|
||||||
|
int expiredEntries = 0; // Number of expired entries
|
||||||
|
int excludedEntries = 0; // Number of known bad entries
|
||||||
|
int weakPasswords = 0; // Number of weak or poor passwords
|
||||||
|
int shortPasswords = 0; // Number of passwords 8 characters or less in size
|
||||||
|
int uniquePasswords = 0; // Number of unique passwords
|
||||||
|
int reusedPasswords = 0; // Number of non-unique passwords
|
||||||
|
int totalPasswordLength = 0; // Total length of all passwords
|
||||||
|
|
||||||
|
explicit DatabaseStats(QSharedPointer<Database> db);
|
||||||
|
|
||||||
|
int averagePwdLength() const;
|
||||||
|
|
||||||
|
int maxPwdReuse() const;
|
||||||
|
|
||||||
|
bool isAnyExpired() const;
|
||||||
|
|
||||||
|
bool areTooManyPwdsReused() const;
|
||||||
|
|
||||||
|
bool arePwdsReusedTooOften() const;
|
||||||
|
|
||||||
|
bool isAvgPwdTooShort() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QSharedPointer<Database> m_db;
|
||||||
|
QHash<QString, int> m_passwords;
|
||||||
|
|
||||||
|
void gatherStats(const QList<Group*>& groups);
|
||||||
|
};
|
||||||
|
#endif // KEEPASSXC_DATABASESTATS_H
|
@ -82,6 +82,12 @@ public:
|
|||||||
return m_entropy;
|
return m_entropy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Length
|
||||||
|
{
|
||||||
|
static const int Short = 8;
|
||||||
|
static const int Long = 25;
|
||||||
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int m_score = 0;
|
int m_score = 0;
|
||||||
double m_entropy = 0.0;
|
double m_entropy = 0.0;
|
||||||
|
@ -19,138 +19,14 @@
|
|||||||
#include "ui_ReportsWidgetStatistics.h"
|
#include "ui_ReportsWidgetStatistics.h"
|
||||||
|
|
||||||
#include "core/AsyncTask.h"
|
#include "core/AsyncTask.h"
|
||||||
|
#include "core/DatabaseStats.h"
|
||||||
#include "core/Group.h"
|
#include "core/Group.h"
|
||||||
#include "core/Metadata.h"
|
#include "core/Metadata.h"
|
||||||
#include "core/PasswordHealth.h"
|
#include "core/PasswordHealth.h"
|
||||||
#include "gui/Icons.h"
|
#include "gui/Icons.h"
|
||||||
|
|
||||||
#include <QFileInfo>
|
|
||||||
#include <QStandardItemModel>
|
#include <QStandardItemModel>
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
class Stats
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
// The statistics we collect:
|
|
||||||
QDateTime modified; // File modification time
|
|
||||||
int groupCount = 0; // Number of groups in the database
|
|
||||||
int entryCount = 0; // Number of entries (across all groups)
|
|
||||||
int expiredEntries = 0; // Number of expired entries
|
|
||||||
int excludedEntries = 0; // Number of known bad entries
|
|
||||||
int weakPasswords = 0; // Number of weak or poor passwords
|
|
||||||
int shortPasswords = 0; // Number of passwords 8 characters or less in size
|
|
||||||
int uniquePasswords = 0; // Number of unique passwords
|
|
||||||
int reusedPasswords = 0; // Number of non-unique passwords
|
|
||||||
int totalPasswordLength = 0; // Total length of all passwords
|
|
||||||
|
|
||||||
// Ctor does all the work
|
|
||||||
explicit Stats(QSharedPointer<Database> db)
|
|
||||||
: modified(QFileInfo(db->filePath()).lastModified())
|
|
||||||
, m_db(db)
|
|
||||||
{
|
|
||||||
gatherStats(db->rootGroup()->groupsRecursive(true));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get average password length
|
|
||||||
int averagePwdLength() const
|
|
||||||
{
|
|
||||||
const auto passwords = uniquePasswords + reusedPasswords;
|
|
||||||
return passwords == 0 ? 0 : std::round(totalPasswordLength / double(passwords));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get max number of password reuse (=how many entries
|
|
||||||
// share the same password)
|
|
||||||
int maxPwdReuse() const
|
|
||||||
{
|
|
||||||
int ret = 0;
|
|
||||||
for (const auto& count : m_passwords) {
|
|
||||||
ret = std::max(ret, count);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A warning sign is displayed if one of the
|
|
||||||
// following returns true.
|
|
||||||
bool isAnyExpired() const
|
|
||||||
{
|
|
||||||
return expiredEntries > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool areTooManyPwdsReused() const
|
|
||||||
{
|
|
||||||
return reusedPasswords > uniquePasswords / 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool arePwdsReusedTooOften() const
|
|
||||||
{
|
|
||||||
return maxPwdReuse() > 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isAvgPwdTooShort() const
|
|
||||||
{
|
|
||||||
return averagePwdLength() < 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
QSharedPointer<Database> m_db;
|
|
||||||
QHash<QString, int> m_passwords;
|
|
||||||
|
|
||||||
void gatherStats(const QList<Group*>& groups)
|
|
||||||
{
|
|
||||||
auto checker = HealthChecker(m_db);
|
|
||||||
|
|
||||||
for (const auto* group : groups) {
|
|
||||||
// Don't count anything in the recycle bin
|
|
||||||
if (group->isRecycled()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
++groupCount;
|
|
||||||
|
|
||||||
for (const auto* entry : group->entries()) {
|
|
||||||
// Don't count anything in the recycle bin
|
|
||||||
if (entry->isRecycled()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
++entryCount;
|
|
||||||
|
|
||||||
if (entry->isExpired()) {
|
|
||||||
++expiredEntries;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get password statistics
|
|
||||||
const auto pwd = entry->password();
|
|
||||||
if (!pwd.isEmpty()) {
|
|
||||||
if (!m_passwords.contains(pwd)) {
|
|
||||||
++uniquePasswords;
|
|
||||||
} else {
|
|
||||||
++reusedPasswords;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pwd.size() < 8) {
|
|
||||||
++shortPasswords;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Speed up Zxcvbn process by excluding very long passwords and most passphrases
|
|
||||||
if (pwd.size() < 25 && checker.evaluate(entry)->quality() <= PasswordHealth::Quality::Weak) {
|
|
||||||
++weakPasswords;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entry->excludeFromReports()) {
|
|
||||||
++excludedEntries;
|
|
||||||
}
|
|
||||||
|
|
||||||
totalPasswordLength += pwd.size();
|
|
||||||
m_passwords[pwd]++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
ReportsWidgetStatistics::ReportsWidgetStatistics(QWidget* parent)
|
ReportsWidgetStatistics::ReportsWidgetStatistics(QWidget* parent)
|
||||||
: QWidget(parent)
|
: QWidget(parent)
|
||||||
, m_ui(new Ui::ReportsWidgetStatistics())
|
, m_ui(new Ui::ReportsWidgetStatistics())
|
||||||
@ -205,7 +81,8 @@ void ReportsWidgetStatistics::showEvent(QShowEvent* event)
|
|||||||
|
|
||||||
void ReportsWidgetStatistics::calculateStats()
|
void ReportsWidgetStatistics::calculateStats()
|
||||||
{
|
{
|
||||||
const QScopedPointer<Stats> stats(AsyncTask::runAndWaitForFuture([this] { return new Stats(m_db); }));
|
const QScopedPointer<DatabaseStats> stats(
|
||||||
|
AsyncTask::runAndWaitForFuture([this] { return new DatabaseStats(m_db); }));
|
||||||
|
|
||||||
m_referencesModel->clear();
|
m_referencesModel->clear();
|
||||||
addStatsRow(tr("Database name"), m_db->metadata()->name());
|
addStatsRow(tr("Database name"), m_db->metadata()->name());
|
||||||
|
@ -858,6 +858,22 @@ void TestCli::testInfo()
|
|||||||
QCOMPARE(m_stdout->readLine(), QByteArray("Cipher: AES 256-bit\n"));
|
QCOMPARE(m_stdout->readLine(), QByteArray("Cipher: AES 256-bit\n"));
|
||||||
QCOMPARE(m_stdout->readLine(), QByteArray("KDF: AES (6000 rounds)\n"));
|
QCOMPARE(m_stdout->readLine(), QByteArray("KDF: AES (6000 rounds)\n"));
|
||||||
QCOMPARE(m_stdout->readLine(), QByteArray("Recycle bin is enabled.\n"));
|
QCOMPARE(m_stdout->readLine(), QByteArray("Recycle bin is enabled.\n"));
|
||||||
|
QVERIFY(m_stdout->readLine().contains(m_dbFile->fileName().toUtf8()));
|
||||||
|
QVERIFY(m_stdout->readLine().contains(
|
||||||
|
QByteArray("Database created: "))); // date changes often, so just test for the first part
|
||||||
|
QVERIFY(m_stdout->readLine().contains(
|
||||||
|
QByteArray("Last saved: "))); // date changes often, so just test for the first part
|
||||||
|
QCOMPARE(m_stdout->readLine(), QByteArray("Unsaved changes: no\n"));
|
||||||
|
QCOMPARE(m_stdout->readLine(), QByteArray("Number of groups: 8\n"));
|
||||||
|
QCOMPARE(m_stdout->readLine(), QByteArray("Number of entries: 2\n"));
|
||||||
|
QCOMPARE(m_stdout->readLine(), QByteArray("Number of expired entries: 0\n"));
|
||||||
|
QCOMPARE(m_stdout->readLine(), QByteArray("Unique passwords: 2\n"));
|
||||||
|
QCOMPARE(m_stdout->readLine(), QByteArray("Non-unique passwords: 0\n"));
|
||||||
|
QCOMPARE(m_stdout->readLine(), QByteArray("Maximum password reuse: 1\n"));
|
||||||
|
QCOMPARE(m_stdout->readLine(), QByteArray("Number of short passwords: 0\n"));
|
||||||
|
QCOMPARE(m_stdout->readLine(), QByteArray("Number of weak passwords: 2\n"));
|
||||||
|
QCOMPARE(m_stdout->readLine(), QByteArray("Entries excluded from reports: 0\n"));
|
||||||
|
QCOMPARE(m_stdout->readLine(), QByteArray("Average password length: 11 characters\n"));
|
||||||
|
|
||||||
// Test with quiet option.
|
// Test with quiet option.
|
||||||
setInput("a");
|
setInput("a");
|
||||||
|
Loading…
Reference in New Issue
Block a user