mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-02-10 03:38:33 -05:00
Create new UrlTools class
Includes "Fix ifdefs with UrlTools"
This commit is contained in:
parent
416581b179
commit
1cbbcff259
@ -1,4 +1,4 @@
|
|||||||
# Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
|
# Copyright (C) 2023 KeePassXC Team <team@keepassxc.org>
|
||||||
# Copyright (C) 2010 Felix Geyer <debfx@fobos.de>
|
# Copyright (C) 2010 Felix Geyer <debfx@fobos.de>
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
@ -60,6 +60,7 @@ set(keepassx_SOURCES
|
|||||||
core/TimeInfo.cpp
|
core/TimeInfo.cpp
|
||||||
core/Tools.cpp
|
core/Tools.cpp
|
||||||
core/Translator.cpp
|
core/Translator.cpp
|
||||||
|
core/UrlTools.cpp
|
||||||
cli/Utils.cpp
|
cli/Utils.cpp
|
||||||
cli/TextStream.cpp
|
cli/TextStream.cpp
|
||||||
crypto/Crypto.cpp
|
crypto/Crypto.cpp
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include "BrowserMessageBuilder.h"
|
#include "BrowserMessageBuilder.h"
|
||||||
#include "BrowserSettings.h"
|
#include "BrowserSettings.h"
|
||||||
#include "core/Tools.h"
|
#include "core/Tools.h"
|
||||||
|
#include "core/UrlTools.h"
|
||||||
#include "gui/MainWindow.h"
|
#include "gui/MainWindow.h"
|
||||||
#include "gui/MessageBox.h"
|
#include "gui/MessageBox.h"
|
||||||
#include "gui/osutils/OSUtils.h"
|
#include "gui/osutils/OSUtils.h"
|
||||||
@ -499,33 +500,6 @@ bool BrowserService::isPasswordGeneratorRequested() const
|
|||||||
return m_passwordGeneratorRequested;
|
return m_passwordGeneratorRequested;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns true if URLs are identical. Paths with "/" are removed during comparison.
|
|
||||||
// URLs without scheme reverts to https.
|
|
||||||
// Special handling is needed because QUrl::matches() with QUrl::StripTrailingSlash does not strip "/" paths.
|
|
||||||
bool BrowserService::isUrlIdentical(const QString& first, const QString& second) const
|
|
||||||
{
|
|
||||||
auto trimUrl = [](QString url) {
|
|
||||||
url = url.trimmed();
|
|
||||||
if (url.endsWith("/")) {
|
|
||||||
url.remove(url.length() - 1, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return url;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (first.isEmpty() || second.isEmpty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto firstUrl = trimUrl(first);
|
|
||||||
const auto secondUrl = trimUrl(second);
|
|
||||||
if (firstUrl == secondUrl) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return QUrl(firstUrl).matches(QUrl(secondUrl), QUrl::StripTrailingSlash);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString BrowserService::storeKey(const QString& key)
|
QString BrowserService::storeKey(const QString& key)
|
||||||
{
|
{
|
||||||
auto db = getDatabase();
|
auto db = getDatabase();
|
||||||
@ -1290,18 +1264,6 @@ int BrowserService::sortPriority(const QStringList& urls, const QString& siteUrl
|
|||||||
return *std::max_element(priorityList.begin(), priorityList.end());
|
return *std::max_element(priorityList.begin(), priorityList.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BrowserService::schemeFound(const QString& url)
|
|
||||||
{
|
|
||||||
QUrl address(url);
|
|
||||||
return !address.scheme().isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BrowserService::isIpAddress(const QString& host) const
|
|
||||||
{
|
|
||||||
QHostAddress address(host);
|
|
||||||
return address.protocol() == QAbstractSocket::IPv4Protocol || address.protocol() == QAbstractSocket::IPv6Protocol;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BrowserService::removeFirstDomain(QString& hostname)
|
bool BrowserService::removeFirstDomain(QString& hostname)
|
||||||
{
|
{
|
||||||
int pos = hostname.indexOf(".");
|
int pos = hostname.indexOf(".");
|
||||||
@ -1465,7 +1427,7 @@ bool BrowserService::handleURL(const QString& entryUrl,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Match the base domain
|
// Match the base domain
|
||||||
if (getTopLevelDomainFromUrl(siteQUrl.host()) != getTopLevelDomainFromUrl(entryQUrl.host())) {
|
if (urlTools()->getBaseDomainFromUrl(siteQUrl.host()) != urlTools()->getBaseDomainFromUrl(entryQUrl.host())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1475,34 +1437,6 @@ bool BrowserService::handleURL(const QString& entryUrl,
|
|||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the base domain of URL.
|
|
||||||
*
|
|
||||||
* Returns the base domain, e.g. https://another.example.co.uk -> example.co.uk
|
|
||||||
*/
|
|
||||||
QString BrowserService::getTopLevelDomainFromUrl(const QString& url) const
|
|
||||||
{
|
|
||||||
QUrl qurl = QUrl::fromUserInput(url);
|
|
||||||
QString host = qurl.host();
|
|
||||||
|
|
||||||
// If the hostname is an IP address, return it directly
|
|
||||||
if (isIpAddress(host)) {
|
|
||||||
return host;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (host.isEmpty() || !host.contains(qurl.topLevelDomain())) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove the top level domain part from the hostname, e.g. https://another.example.co.uk -> https://another.example
|
|
||||||
host.chop(qurl.topLevelDomain().length());
|
|
||||||
// Split the URL and select the last part, e.g. https://another.example -> example
|
|
||||||
QString baseDomain = host.split('.').last();
|
|
||||||
// Append the top level domain back to the URL, e.g. example -> example.co.uk
|
|
||||||
baseDomain.append(qurl.topLevelDomain());
|
|
||||||
return baseDomain;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QSharedPointer<Database> BrowserService::getDatabase()
|
QSharedPointer<Database> BrowserService::getDatabase()
|
||||||
|
@ -83,8 +83,6 @@ public:
|
|||||||
QString getCurrentTotp(const QString& uuid);
|
QString getCurrentTotp(const QString& uuid);
|
||||||
void showPasswordGenerator(const KeyPairMessage& keyPairMessage);
|
void showPasswordGenerator(const KeyPairMessage& keyPairMessage);
|
||||||
bool isPasswordGeneratorRequested() const;
|
bool isPasswordGeneratorRequested() const;
|
||||||
bool isUrlIdentical(const QString& first, const QString& second) const;
|
|
||||||
|
|
||||||
QSharedPointer<Database> selectedDatabase();
|
QSharedPointer<Database> selectedDatabase();
|
||||||
#ifdef WITH_XC_BROWSER_PASSKEYS
|
#ifdef WITH_XC_BROWSER_PASSKEYS
|
||||||
QJsonObject
|
QJsonObject
|
||||||
@ -175,7 +173,6 @@ private:
|
|||||||
Group* getDefaultEntryGroup(const QSharedPointer<Database>& selectedDb = {});
|
Group* getDefaultEntryGroup(const QSharedPointer<Database>& selectedDb = {});
|
||||||
int sortPriority(const QStringList& urls, const QString& siteUrl, const QString& formUrl);
|
int sortPriority(const QStringList& urls, const QString& siteUrl, const QString& formUrl);
|
||||||
bool schemeFound(const QString& url);
|
bool schemeFound(const QString& url);
|
||||||
bool isIpAddress(const QString& host) const;
|
|
||||||
bool removeFirstDomain(QString& hostname);
|
bool removeFirstDomain(QString& hostname);
|
||||||
bool
|
bool
|
||||||
shouldIncludeEntry(Entry* entry, const QString& url, const QString& submitUrl, const bool omitWwwSubdomain = false);
|
shouldIncludeEntry(Entry* entry, const QString& url, const QString& submitUrl, const bool omitWwwSubdomain = false);
|
||||||
@ -194,8 +191,6 @@ private:
|
|||||||
const QString& siteUrl,
|
const QString& siteUrl,
|
||||||
const QString& formUrl,
|
const QString& formUrl,
|
||||||
const bool omitWwwSubdomain = false);
|
const bool omitWwwSubdomain = false);
|
||||||
QString getTopLevelDomainFromUrl(const QString& url) const;
|
|
||||||
QString baseDomain(const QString& hostname) const;
|
|
||||||
QSharedPointer<Database> getDatabase();
|
QSharedPointer<Database> getDatabase();
|
||||||
QString getDatabaseRootUuid();
|
QString getDatabaseRootUuid();
|
||||||
QString getDatabaseRecycleBinUuid();
|
QString getDatabaseRecycleBinUuid();
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
* Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com,
|
* Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com,
|
||||||
* author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
|
* author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
|
||||||
* Copyright (C) 2021 The Qt Company Ltd.
|
* Copyright (C) 2021 The Qt Company Ltd.
|
||||||
* Copyright (C) 2021 KeePassXC Team <team@keepassxc.org>
|
* Copyright (C) 2023 KeePassXC Team <team@keepassxc.org>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -274,35 +274,6 @@ namespace Tools
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool checkUrlValid(const QString& urlField)
|
|
||||||
{
|
|
||||||
if (urlField.isEmpty() || urlField.startsWith("cmd://", Qt::CaseInsensitive)
|
|
||||||
|| urlField.startsWith("kdbx://", Qt::CaseInsensitive)
|
|
||||||
|| urlField.startsWith("{REF:A", Qt::CaseInsensitive)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
QUrl url;
|
|
||||||
if (urlField.contains("://")) {
|
|
||||||
url = urlField;
|
|
||||||
} else {
|
|
||||||
url = QUrl::fromUserInput(urlField);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (url.scheme() != "file" && url.host().isEmpty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for illegal characters. Adds also the wildcard * to the list
|
|
||||||
QRegularExpression re("[<>\\^`{|}\\*]");
|
|
||||||
auto match = re.match(urlField);
|
|
||||||
if (match.hasMatch()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
*
|
*
|
||||||
* Copyright (C) 2020 Giuseppe D'Angelo <dangelog@gmail.com>.
|
* Copyright (C) 2020 Giuseppe D'Angelo <dangelog@gmail.com>.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2012 Felix Geyer <debfx@fobos.de>
|
* Copyright (C) 2012 Felix Geyer <debfx@fobos.de>
|
||||||
* Copyright (C) 2021 KeePassXC Team <team@keepassxc.org>
|
* Copyright (C) 2023 KeePassXC Team <team@keepassxc.org>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -38,7 +38,6 @@ namespace Tools
|
|||||||
bool isBase64(const QByteArray& ba);
|
bool isBase64(const QByteArray& ba);
|
||||||
void sleep(int ms);
|
void sleep(int ms);
|
||||||
void wait(int ms);
|
void wait(int ms);
|
||||||
bool checkUrlValid(const QString& urlField);
|
|
||||||
QString uuidToHex(const QUuid& uuid);
|
QString uuidToHex(const QUuid& uuid);
|
||||||
QUuid hexToUuid(const QString& uuid);
|
QUuid hexToUuid(const QString& uuid);
|
||||||
bool isValidUuid(const QString& uuidStr);
|
bool isValidUuid(const QString& uuidStr);
|
||||||
|
173
src/core/UrlTools.cpp
Normal file
173
src/core/UrlTools.cpp
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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 "UrlTools.h"
|
||||||
|
#if defined(WITH_XC_NETWORKING) || defined(WITH_XC_BROWSER)
|
||||||
|
#include <QHostAddress>
|
||||||
|
#include <QNetworkCookie>
|
||||||
|
#include <QNetworkCookieJar>
|
||||||
|
#endif
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
|
Q_GLOBAL_STATIC(UrlTools, s_urlTools)
|
||||||
|
|
||||||
|
UrlTools* UrlTools::instance()
|
||||||
|
{
|
||||||
|
return s_urlTools;
|
||||||
|
}
|
||||||
|
|
||||||
|
QUrl UrlTools::convertVariantToUrl(const QVariant& var) const
|
||||||
|
{
|
||||||
|
QUrl url;
|
||||||
|
if (var.canConvert<QUrl>()) {
|
||||||
|
url = var.toUrl();
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(WITH_XC_NETWORKING) || defined(WITH_XC_BROWSER)
|
||||||
|
QUrl UrlTools::getRedirectTarget(QNetworkReply* reply) const
|
||||||
|
{
|
||||||
|
QVariant var = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
|
||||||
|
QUrl url = convertVariantToUrl(var);
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the base domain of URL or hostname.
|
||||||
|
*
|
||||||
|
* Returns the base domain, e.g. https://another.example.co.uk -> example.co.uk
|
||||||
|
* Up-to-date list can be found: https://publicsuffix.org/list/public_suffix_list.dat
|
||||||
|
*/
|
||||||
|
QString UrlTools::getBaseDomainFromUrl(const QString& url) const
|
||||||
|
{
|
||||||
|
auto qUrl = QUrl::fromUserInput(url);
|
||||||
|
|
||||||
|
auto host = qUrl.host();
|
||||||
|
if (isIpAddress(host)) {
|
||||||
|
return host;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto tld = getTopLevelDomainFromUrl(qUrl.toString());
|
||||||
|
if (tld.isEmpty() || tld.length() + 1 >= host.length()) {
|
||||||
|
return host;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the top level domain part from the hostname, e.g. https://another.example.co.uk -> https://another.example
|
||||||
|
host.chop(tld.length() + 1);
|
||||||
|
// Split the URL and select the last part, e.g. https://another.example -> example
|
||||||
|
QString baseDomain = host.split('.').last();
|
||||||
|
// Append the top level domain back to the URL, e.g. example -> example.co.uk
|
||||||
|
baseDomain.append(QString(".%1").arg(tld));
|
||||||
|
|
||||||
|
return baseDomain;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the top level domain from URL.
|
||||||
|
*
|
||||||
|
* Returns the TLD e.g. https://another.example.co.uk -> co.uk
|
||||||
|
*/
|
||||||
|
QString UrlTools::getTopLevelDomainFromUrl(const QString& url) const
|
||||||
|
{
|
||||||
|
auto host = QUrl::fromUserInput(url).host();
|
||||||
|
if (isIpAddress(host)) {
|
||||||
|
return host;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto numberOfDomainParts = host.split('.').length();
|
||||||
|
static const auto dummy = QByteArrayLiteral("");
|
||||||
|
|
||||||
|
// Only loop the amount of different parts found
|
||||||
|
for (auto i = 0; i < numberOfDomainParts; ++i) {
|
||||||
|
// Cut the first part from host
|
||||||
|
host = host.mid(host.indexOf('.') + 1);
|
||||||
|
|
||||||
|
QNetworkCookie cookie(dummy, dummy);
|
||||||
|
cookie.setDomain(host);
|
||||||
|
|
||||||
|
// Check if dummy cookie's domain/TLD matches with public suffix list
|
||||||
|
if (!QNetworkCookieJar{}.setCookiesFromUrl(QList{cookie}, url)) {
|
||||||
|
return host;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return host;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UrlTools::isIpAddress(const QString& host) const
|
||||||
|
{
|
||||||
|
QHostAddress address(host);
|
||||||
|
return address.protocol() == QAbstractSocket::IPv4Protocol || address.protocol() == QAbstractSocket::IPv6Protocol;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Returns true if URLs are identical. Paths with "/" are removed during comparison.
|
||||||
|
// URLs without scheme reverts to https.
|
||||||
|
// Special handling is needed because QUrl::matches() with QUrl::StripTrailingSlash does not strip "/" paths.
|
||||||
|
bool UrlTools::isUrlIdentical(const QString& first, const QString& second) const
|
||||||
|
{
|
||||||
|
auto trimUrl = [](QString url) {
|
||||||
|
url = url.trimmed();
|
||||||
|
if (url.endsWith("/")) {
|
||||||
|
url.remove(url.length() - 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return url;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (first.isEmpty() || second.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto firstUrl = trimUrl(first);
|
||||||
|
const auto secondUrl = trimUrl(second);
|
||||||
|
if (firstUrl == secondUrl) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QUrl(firstUrl).matches(QUrl(secondUrl), QUrl::StripTrailingSlash);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UrlTools::isUrlValid(const QString& urlField) const
|
||||||
|
{
|
||||||
|
if (urlField.isEmpty() || urlField.startsWith("cmd://", Qt::CaseInsensitive)
|
||||||
|
|| urlField.startsWith("kdbx://", Qt::CaseInsensitive) || urlField.startsWith("{REF:A", Qt::CaseInsensitive)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QUrl url;
|
||||||
|
if (urlField.contains("://")) {
|
||||||
|
url = urlField;
|
||||||
|
} else {
|
||||||
|
url = QUrl::fromUserInput(urlField);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (url.scheme() != "file" && url.host().isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for illegal characters. Adds also the wildcard * to the list
|
||||||
|
QRegularExpression re("[<>\\^`{|}\\*]");
|
||||||
|
auto match = re.match(urlField);
|
||||||
|
if (match.hasMatch()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
56
src/core/UrlTools.h
Normal file
56
src/core/UrlTools.h
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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_URLTOOLS_H
|
||||||
|
#define KEEPASSXC_URLTOOLS_H
|
||||||
|
|
||||||
|
#include "config-keepassx.h"
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QVariant>
|
||||||
|
|
||||||
|
class UrlTools : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit UrlTools() = default;
|
||||||
|
static UrlTools* instance();
|
||||||
|
|
||||||
|
#if defined(WITH_XC_NETWORKING) || defined(WITH_XC_BROWSER)
|
||||||
|
QUrl getRedirectTarget(QNetworkReply* reply) const;
|
||||||
|
QString getBaseDomainFromUrl(const QString& url) const;
|
||||||
|
QString getTopLevelDomainFromUrl(const QString& url) const;
|
||||||
|
bool isIpAddress(const QString& host) const;
|
||||||
|
#endif
|
||||||
|
bool isUrlIdentical(const QString& first, const QString& second) const;
|
||||||
|
bool isUrlValid(const QString& urlField) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QUrl convertVariantToUrl(const QVariant& var) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Q_DISABLE_COPY(UrlTools);
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline UrlTools* urlTools()
|
||||||
|
{
|
||||||
|
return UrlTools::instance();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // KEEPASSXC_URLTOOLS_H
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2019 KeePassXC Team <team@keepassxc.org>
|
* Copyright (C) 2023 KeePassXC Team <team@keepassxc.org>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -18,6 +18,7 @@
|
|||||||
#include "IconDownloader.h"
|
#include "IconDownloader.h"
|
||||||
#include "core/Config.h"
|
#include "core/Config.h"
|
||||||
#include "core/NetworkManager.h"
|
#include "core/NetworkManager.h"
|
||||||
|
#include "core/UrlTools.h"
|
||||||
|
|
||||||
#include <QBuffer>
|
#include <QBuffer>
|
||||||
#include <QHostInfo>
|
#include <QHostInfo>
|
||||||
@ -40,37 +41,6 @@ IconDownloader::~IconDownloader()
|
|||||||
abortDownload();
|
abortDownload();
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
// Try to get the 2nd level domain of the host part of a QUrl. For example,
|
|
||||||
// "foo.bar.example.com" would become "example.com", and "foo.bar.example.co.uk"
|
|
||||||
// would become "example.co.uk".
|
|
||||||
QString getSecondLevelDomain(const QUrl& url)
|
|
||||||
{
|
|
||||||
QString fqdn = url.host();
|
|
||||||
fqdn.truncate(fqdn.length() - url.topLevelDomain().length());
|
|
||||||
QStringList parts = fqdn.split('.');
|
|
||||||
QString newdom = parts.takeLast() + url.topLevelDomain();
|
|
||||||
return newdom;
|
|
||||||
}
|
|
||||||
|
|
||||||
QUrl convertVariantToUrl(const QVariant& var)
|
|
||||||
{
|
|
||||||
QUrl url;
|
|
||||||
if (var.canConvert<QUrl>()) {
|
|
||||||
url = var.toUrl();
|
|
||||||
}
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
|
|
||||||
QUrl getRedirectTarget(QNetworkReply* reply)
|
|
||||||
{
|
|
||||||
QVariant var = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
|
|
||||||
QUrl url = convertVariantToUrl(var);
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
void IconDownloader::setUrl(const QString& entryUrl)
|
void IconDownloader::setUrl(const QString& entryUrl)
|
||||||
{
|
{
|
||||||
m_url = entryUrl;
|
m_url = entryUrl;
|
||||||
@ -114,7 +84,7 @@ void IconDownloader::setUrl(const QString& entryUrl)
|
|||||||
// Determine the second-level domain, if available
|
// Determine the second-level domain, if available
|
||||||
QString secondLevelDomain;
|
QString secondLevelDomain;
|
||||||
if (!hostIsIp) {
|
if (!hostIsIp) {
|
||||||
secondLevelDomain = getSecondLevelDomain(url);
|
secondLevelDomain = urlTools()->getBaseDomainFromUrl(url.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start with the "fallback" url (if enabled) to try to get the best favicon
|
// Start with the "fallback" url (if enabled) to try to get the best favicon
|
||||||
@ -202,7 +172,7 @@ void IconDownloader::fetchFinished()
|
|||||||
QString url = m_url;
|
QString url = m_url;
|
||||||
|
|
||||||
bool error = (m_reply->error() != QNetworkReply::NoError);
|
bool error = (m_reply->error() != QNetworkReply::NoError);
|
||||||
QUrl redirectTarget = getRedirectTarget(m_reply);
|
QUrl redirectTarget = urlTools()->getRedirectTarget(m_reply);
|
||||||
|
|
||||||
m_reply->deleteLater();
|
m_reply->deleteLater();
|
||||||
m_reply = nullptr;
|
m_reply = nullptr;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
|
* Copyright (C) 2023 KeePassXC Team <team@keepassxc.org>
|
||||||
* Copyright (C) 2014 Felix Geyer <debfx@fobos.de>
|
* Copyright (C) 2014 Felix Geyer <debfx@fobos.de>
|
||||||
* Copyright (C) 2020 KeePassXC Team <team@keepassxc.org>
|
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -19,6 +19,7 @@
|
|||||||
#include "URLEdit.h"
|
#include "URLEdit.h"
|
||||||
|
|
||||||
#include "core/Tools.h"
|
#include "core/Tools.h"
|
||||||
|
#include "core/UrlTools.h"
|
||||||
#include "gui/Icons.h"
|
#include "gui/Icons.h"
|
||||||
#include "gui/styles/StateColorPalette.h"
|
#include "gui/styles/StateColorPalette.h"
|
||||||
|
|
||||||
@ -44,7 +45,7 @@ void URLEdit::updateStylesheet()
|
|||||||
{
|
{
|
||||||
const QString stylesheetTemplate("QLineEdit { background: %1; }");
|
const QString stylesheetTemplate("QLineEdit { background: %1; }");
|
||||||
|
|
||||||
if (!Tools::checkUrlValid(text())) {
|
if (!urlTools()->isUrlValid(text())) {
|
||||||
StateColorPalette statePalette;
|
StateColorPalette statePalette;
|
||||||
QColor color = statePalette.color(StateColorPalette::ColorRole::Error);
|
QColor color = statePalette.color(StateColorPalette::ColorRole::Error);
|
||||||
setStyleSheet(stylesheetTemplate.arg(color.name()));
|
setStyleSheet(stylesheetTemplate.arg(color.name()));
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
#include "browser/BrowserService.h"
|
#include "browser/BrowserService.h"
|
||||||
#include "core/EntryAttributes.h"
|
#include "core/EntryAttributes.h"
|
||||||
#include "core/Tools.h"
|
#include "core/UrlTools.h"
|
||||||
#include "gui/Icons.h"
|
#include "gui/Icons.h"
|
||||||
#include "gui/styles/StateColorPalette.h"
|
#include "gui/styles/StateColorPalette.h"
|
||||||
|
|
||||||
@ -67,14 +67,14 @@ QVariant EntryURLModel::data(const QModelIndex& index, int role) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
const auto value = m_entryAttributes->value(key);
|
const auto value = m_entryAttributes->value(key);
|
||||||
const auto urlValid = Tools::checkUrlValid(value);
|
const auto urlValid = urlTools()->isUrlValid(value);
|
||||||
|
|
||||||
// Check for duplicate URLs in the attribute list. Excludes the current key/value from the comparison.
|
// Check for duplicate URLs in the attribute list. Excludes the current key/value from the comparison.
|
||||||
auto customAttributeKeys = m_entryAttributes->customKeys().filter(BrowserService::ADDITIONAL_URL);
|
auto customAttributeKeys = m_entryAttributes->customKeys().filter(BrowserService::ADDITIONAL_URL);
|
||||||
customAttributeKeys.removeOne(key);
|
customAttributeKeys.removeOne(key);
|
||||||
|
|
||||||
const auto duplicateUrl = m_entryAttributes->values(customAttributeKeys).contains(value)
|
const auto duplicateUrl =
|
||||||
|| browserService()->isUrlIdentical(value, m_entryUrl);
|
m_entryAttributes->values(customAttributeKeys).contains(value) || urlTools()->isUrlIdentical(value, m_entryUrl);
|
||||||
if (role == Qt::BackgroundRole && (!urlValid || duplicateUrl)) {
|
if (role == Qt::BackgroundRole && (!urlValid || duplicateUrl)) {
|
||||||
StateColorPalette statePalette;
|
StateColorPalette statePalette;
|
||||||
return statePalette.color(StateColorPalette::ColorRole::Error);
|
return statePalette.color(StateColorPalette::ColorRole::Error);
|
||||||
|
@ -242,6 +242,10 @@ if(WITH_XC_BROWSER)
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(WITH_XC_NETWORKING OR WITH_XC_BROWSER)
|
||||||
|
add_unit_test(NAME testurltools SOURCES TestUrlTools.cpp LIBS ${TEST_LIBRARIES})
|
||||||
|
endif()
|
||||||
|
|
||||||
add_unit_test(NAME testcli SOURCES TestCli.cpp
|
add_unit_test(NAME testcli SOURCES TestCli.cpp
|
||||||
LIBS testsupport cli ${TEST_LIBRARIES})
|
LIBS testsupport cli ${TEST_LIBRARIES})
|
||||||
target_compile_definitions(testcli PRIVATE KEEPASSX_CLI_PATH="$<TARGET_FILE:keepassxc-cli>")
|
target_compile_definitions(testcli PRIVATE KEEPASSX_CLI_PATH="$<TARGET_FILE:keepassxc-cli>")
|
||||||
|
@ -144,54 +144,6 @@ void TestBrowser::testBuildResponse()
|
|||||||
QCOMPARE(firstArr["test"].toBool(), true);
|
QCOMPARE(firstArr["test"].toBool(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests for BrowserService
|
|
||||||
*/
|
|
||||||
void TestBrowser::testTopLevelDomain()
|
|
||||||
{
|
|
||||||
QString url1 = "https://another.example.co.uk";
|
|
||||||
QString url2 = "https://www.example.com";
|
|
||||||
QString url3 = "http://test.net";
|
|
||||||
QString url4 = "http://so.many.subdomains.co.jp";
|
|
||||||
QString url5 = "https://192.168.0.1";
|
|
||||||
QString url6 = "https://192.168.0.1:8000";
|
|
||||||
|
|
||||||
QString res1 = m_browserService->getTopLevelDomainFromUrl(url1);
|
|
||||||
QString res2 = m_browserService->getTopLevelDomainFromUrl(url2);
|
|
||||||
QString res3 = m_browserService->getTopLevelDomainFromUrl(url3);
|
|
||||||
QString res4 = m_browserService->getTopLevelDomainFromUrl(url4);
|
|
||||||
QString res5 = m_browserService->getTopLevelDomainFromUrl(url5);
|
|
||||||
QString res6 = m_browserService->getTopLevelDomainFromUrl(url6);
|
|
||||||
|
|
||||||
QCOMPARE(res1, QString("example.co.uk"));
|
|
||||||
QCOMPARE(res2, QString("example.com"));
|
|
||||||
QCOMPARE(res3, QString("test.net"));
|
|
||||||
QCOMPARE(res4, QString("subdomains.co.jp"));
|
|
||||||
QCOMPARE(res5, QString("192.168.0.1"));
|
|
||||||
QCOMPARE(res6, QString("192.168.0.1"));
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestBrowser::testIsIpAddress()
|
|
||||||
{
|
|
||||||
auto host1 = "example.com"; // Not valid
|
|
||||||
auto host2 = "192.168.0.1";
|
|
||||||
auto host3 = "278.21.2.0"; // Not valid
|
|
||||||
auto host4 = "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
|
|
||||||
auto host5 = "2001:db8:0:1:1:1:1:1";
|
|
||||||
auto host6 = "fe80::1ff:fe23:4567:890a";
|
|
||||||
auto host7 = "2001:20::1";
|
|
||||||
auto host8 = "2001:0db8:85y3:0000:0000:8a2e:0370:7334"; // Not valid
|
|
||||||
|
|
||||||
QVERIFY(!m_browserService->isIpAddress(host1));
|
|
||||||
QVERIFY(m_browserService->isIpAddress(host2));
|
|
||||||
QVERIFY(!m_browserService->isIpAddress(host3));
|
|
||||||
QVERIFY(m_browserService->isIpAddress(host4));
|
|
||||||
QVERIFY(m_browserService->isIpAddress(host5));
|
|
||||||
QVERIFY(m_browserService->isIpAddress(host6));
|
|
||||||
QVERIFY(m_browserService->isIpAddress(host7));
|
|
||||||
QVERIFY(!m_browserService->isIpAddress(host8));
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestBrowser::testSortPriority()
|
void TestBrowser::testSortPriority()
|
||||||
{
|
{
|
||||||
QFETCH(QString, entryUrl);
|
QFETCH(QString, entryUrl);
|
||||||
@ -583,26 +535,6 @@ QList<Entry*> TestBrowser::createEntries(QStringList& urls, Group* root) const
|
|||||||
|
|
||||||
return entries;
|
return entries;
|
||||||
}
|
}
|
||||||
void TestBrowser::testValidURLs()
|
|
||||||
{
|
|
||||||
QHash<QString, bool> urls;
|
|
||||||
urls["https://github.com/login"] = true;
|
|
||||||
urls["https:///github.com/"] = false;
|
|
||||||
urls["http://github.com/**//*"] = false;
|
|
||||||
urls["http://*.github.com/login"] = false;
|
|
||||||
urls["//github.com"] = true;
|
|
||||||
urls["github.com/{}<>"] = false;
|
|
||||||
urls["http:/example.com"] = false;
|
|
||||||
urls["cmd://C:/Toolchains/msys2/usr/bin/mintty \"ssh jon@192.168.0.1:22\""] = true;
|
|
||||||
urls["file:///Users/testUser/Code/test.html"] = true;
|
|
||||||
urls["{REF:A@I:46C9B1FFBD4ABC4BBB260C6190BAD20C} "] = true;
|
|
||||||
|
|
||||||
QHashIterator<QString, bool> i(urls);
|
|
||||||
while (i.hasNext()) {
|
|
||||||
i.next();
|
|
||||||
QCOMPARE(Tools::checkUrlValid(i.key()), i.value());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestBrowser::testBestMatchingCredentials()
|
void TestBrowser::testBestMatchingCredentials()
|
||||||
{
|
{
|
||||||
@ -741,19 +673,3 @@ void TestBrowser::testBestMatchingWithAdditionalURLs()
|
|||||||
QCOMPARE(sorted.length(), 1);
|
QCOMPARE(sorted.length(), 1);
|
||||||
QCOMPARE(sorted[0]->url(), urls[0]);
|
QCOMPARE(sorted[0]->url(), urls[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestBrowser::testIsUrlIdentical()
|
|
||||||
{
|
|
||||||
QVERIFY(browserService()->isUrlIdentical("https://example.com", "https://example.com"));
|
|
||||||
QVERIFY(browserService()->isUrlIdentical("https://example.com", " https://example.com "));
|
|
||||||
QVERIFY(!browserService()->isUrlIdentical("https://example.com", "https://example2.com"));
|
|
||||||
QVERIFY(!browserService()->isUrlIdentical("https://example.com/", "https://example.com/#login"));
|
|
||||||
QVERIFY(browserService()->isUrlIdentical("https://example.com", "https://example.com/"));
|
|
||||||
QVERIFY(browserService()->isUrlIdentical("https://example.com/", "https://example.com"));
|
|
||||||
QVERIFY(browserService()->isUrlIdentical("https://example.com/ ", " https://example.com"));
|
|
||||||
QVERIFY(!browserService()->isUrlIdentical("https://example.com/", " example.com"));
|
|
||||||
QVERIFY(browserService()->isUrlIdentical("https://example.com/path/to/nowhere",
|
|
||||||
"https://example.com/path/to/nowhere/"));
|
|
||||||
QVERIFY(!browserService()->isUrlIdentical("https://example.com/", "://example.com/"));
|
|
||||||
QVERIFY(browserService()->isUrlIdentical("ftp://127.0.0.1/", "ftp://127.0.0.1"));
|
|
||||||
}
|
|
||||||
|
@ -37,8 +37,6 @@ private slots:
|
|||||||
void testGetBase64FromKey();
|
void testGetBase64FromKey();
|
||||||
void testIncrementNonce();
|
void testIncrementNonce();
|
||||||
void testBuildResponse();
|
void testBuildResponse();
|
||||||
void testTopLevelDomain();
|
|
||||||
void testIsIpAddress();
|
|
||||||
void testSortPriority();
|
void testSortPriority();
|
||||||
void testSortPriority_data();
|
void testSortPriority_data();
|
||||||
void testSearchEntries();
|
void testSearchEntries();
|
||||||
@ -49,10 +47,8 @@ private slots:
|
|||||||
void testSearchEntriesWithAdditionalURLs();
|
void testSearchEntriesWithAdditionalURLs();
|
||||||
void testInvalidEntries();
|
void testInvalidEntries();
|
||||||
void testSubdomainsAndPaths();
|
void testSubdomainsAndPaths();
|
||||||
void testValidURLs();
|
|
||||||
void testBestMatchingCredentials();
|
void testBestMatchingCredentials();
|
||||||
void testBestMatchingWithAdditionalURLs();
|
void testBestMatchingWithAdditionalURLs();
|
||||||
void testIsUrlIdentical();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QList<Entry*> createEntries(QStringList& urls, Group* root) const;
|
QList<Entry*> createEntries(QStringList& urls, Group* root) const;
|
||||||
|
129
tests/TestUrlTools.cpp
Normal file
129
tests/TestUrlTools.cpp
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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 "TestUrlTools.h"
|
||||||
|
#include <QTest>
|
||||||
|
|
||||||
|
QTEST_GUILESS_MAIN(TestUrlTools)
|
||||||
|
|
||||||
|
void TestUrlTools::initTestCase()
|
||||||
|
{
|
||||||
|
m_urlTools = urlTools();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestUrlTools::init()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestUrlTools::testTopLevelDomain()
|
||||||
|
{
|
||||||
|
// Create list of URLs and expected TLD responses
|
||||||
|
QList<QPair<QString, QString>> tldUrls{
|
||||||
|
{QString("https://another.example.co.uk"), QString("co.uk")},
|
||||||
|
{QString("https://www.example.com"), QString("com")},
|
||||||
|
{QString("https://github.com"), QString("com")},
|
||||||
|
{QString("http://test.net"), QString("net")},
|
||||||
|
{QString("http://so.many.subdomains.co.jp"), QString("co.jp")},
|
||||||
|
{QString("https://192.168.0.1"), QString("192.168.0.1")},
|
||||||
|
{QString("https://192.168.0.1:8000"), QString("192.168.0.1")},
|
||||||
|
{QString("https://www.nic.ar"), QString("ar")},
|
||||||
|
{QString("https://no.no.no"), QString("no")},
|
||||||
|
{QString("https://www.blogspot.com.ar"), QString("blogspot.com.ar")}, // blogspot.com.ar is a TLD
|
||||||
|
{QString("https://jap.an.ide.kyoto.jp"), QString("ide.kyoto.jp")}, // ide.kyoto.jp is a TLD
|
||||||
|
{QString("ar"), QString("ar")},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const auto& u : tldUrls) {
|
||||||
|
QCOMPARE(urlTools()->getTopLevelDomainFromUrl(u.first), u.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create list of URLs and expected base URL responses
|
||||||
|
QList<QPair<QString, QString>> baseUrls{
|
||||||
|
{QString("https://another.example.co.uk"), QString("example.co.uk")},
|
||||||
|
{QString("https://www.example.com"), QString("example.com")},
|
||||||
|
{QString("http://test.net"), QString("test.net")},
|
||||||
|
{QString("http://so.many.subdomains.co.jp"), QString("subdomains.co.jp")},
|
||||||
|
{QString("https://192.168.0.1"), QString("192.168.0.1")},
|
||||||
|
{QString("https://192.168.0.1:8000"), QString("192.168.0.1")},
|
||||||
|
{QString("https://www.nic.ar"), QString("nic.ar")},
|
||||||
|
{QString("https://www.blogspot.com.ar"), QString("www.blogspot.com.ar")}, // blogspot.com.ar is a TLD
|
||||||
|
{QString("https://www.arpa"), QString("www.arpa")},
|
||||||
|
{QString("https://jap.an.ide.kyoto.jp"), QString("an.ide.kyoto.jp")}, // ide.kyoto.jp is a TLD
|
||||||
|
{QString("https://kobe.jp"), QString("kobe.jp")},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const auto& u : baseUrls) {
|
||||||
|
QCOMPARE(urlTools()->getBaseDomainFromUrl(u.first), u.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestUrlTools::testIsIpAddress()
|
||||||
|
{
|
||||||
|
auto host1 = "example.com"; // Not valid
|
||||||
|
auto host2 = "192.168.0.1";
|
||||||
|
auto host3 = "278.21.2.0"; // Not valid
|
||||||
|
auto host4 = "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
|
||||||
|
auto host5 = "2001:db8:0:1:1:1:1:1";
|
||||||
|
auto host6 = "fe80::1ff:fe23:4567:890a";
|
||||||
|
auto host7 = "2001:20::1";
|
||||||
|
auto host8 = "2001:0db8:85y3:0000:0000:8a2e:0370:7334"; // Not valid
|
||||||
|
|
||||||
|
QVERIFY(!urlTools()->isIpAddress(host1));
|
||||||
|
QVERIFY(urlTools()->isIpAddress(host2));
|
||||||
|
QVERIFY(!urlTools()->isIpAddress(host3));
|
||||||
|
QVERIFY(urlTools()->isIpAddress(host4));
|
||||||
|
QVERIFY(urlTools()->isIpAddress(host5));
|
||||||
|
QVERIFY(urlTools()->isIpAddress(host6));
|
||||||
|
QVERIFY(urlTools()->isIpAddress(host7));
|
||||||
|
QVERIFY(!urlTools()->isIpAddress(host8));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestUrlTools::testIsUrlIdentical()
|
||||||
|
{
|
||||||
|
QVERIFY(urlTools()->isUrlIdentical("https://example.com", "https://example.com"));
|
||||||
|
QVERIFY(urlTools()->isUrlIdentical("https://example.com", " https://example.com "));
|
||||||
|
QVERIFY(!urlTools()->isUrlIdentical("https://example.com", "https://example2.com"));
|
||||||
|
QVERIFY(!urlTools()->isUrlIdentical("https://example.com/", "https://example.com/#login"));
|
||||||
|
QVERIFY(urlTools()->isUrlIdentical("https://example.com", "https://example.com/"));
|
||||||
|
QVERIFY(urlTools()->isUrlIdentical("https://example.com/", "https://example.com"));
|
||||||
|
QVERIFY(urlTools()->isUrlIdentical("https://example.com/ ", " https://example.com"));
|
||||||
|
QVERIFY(!urlTools()->isUrlIdentical("https://example.com/", " example.com"));
|
||||||
|
QVERIFY(urlTools()->isUrlIdentical("https://example.com/path/to/nowhere", "https://example.com/path/to/nowhere/"));
|
||||||
|
QVERIFY(!urlTools()->isUrlIdentical("https://example.com/", "://example.com/"));
|
||||||
|
QVERIFY(urlTools()->isUrlIdentical("ftp://127.0.0.1/", "ftp://127.0.0.1"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestUrlTools::testIsUrlValid()
|
||||||
|
{
|
||||||
|
QHash<QString, bool> urls;
|
||||||
|
urls["https://github.com/login"] = true;
|
||||||
|
urls["https:///github.com/"] = false;
|
||||||
|
urls["http://github.com/**//*"] = false;
|
||||||
|
urls["http://*.github.com/login"] = false;
|
||||||
|
urls["//github.com"] = true;
|
||||||
|
urls["github.com/{}<>"] = false;
|
||||||
|
urls["http:/example.com"] = false;
|
||||||
|
urls["cmd://C:/Toolchains/msys2/usr/bin/mintty \"ssh jon@192.168.0.1:22\""] = true;
|
||||||
|
urls["file:///Users/testUser/Code/test.html"] = true;
|
||||||
|
urls["{REF:A@I:46C9B1FFBD4ABC4BBB260C6190BAD20C} "] = true;
|
||||||
|
|
||||||
|
QHashIterator<QString, bool> i(urls);
|
||||||
|
while (i.hasNext()) {
|
||||||
|
i.next();
|
||||||
|
QCOMPARE(urlTools()->isUrlValid(i.key()), i.value());
|
||||||
|
}
|
||||||
|
}
|
41
tests/TestUrlTools.h
Normal file
41
tests/TestUrlTools.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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_TESTURLTOOLS_H
|
||||||
|
#define KEEPASSXC_TESTURLTOOLS_H
|
||||||
|
|
||||||
|
#include "core/UrlTools.h"
|
||||||
|
#include <QObject>
|
||||||
|
#include <QPointer>
|
||||||
|
|
||||||
|
class TestUrlTools : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void initTestCase();
|
||||||
|
void init();
|
||||||
|
|
||||||
|
void testTopLevelDomain();
|
||||||
|
void testIsIpAddress();
|
||||||
|
void testIsUrlIdentical();
|
||||||
|
void testIsUrlValid();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QPointer<UrlTools> m_urlTools;
|
||||||
|
};
|
||||||
|
#endif // KEEPASSXC_TESTURLTOOLS_H
|
Loading…
x
Reference in New Issue
Block a user