Show warning message box on opening inappropriate URL

This commit is contained in:
Vladimir Golovnev (Glassez) 2025-06-24 21:51:08 +03:00
parent d379fa3035
commit fdfdbae30c
No known key found for this signature in database
GPG key ID: 52A2C7DEE2DFA6F7
3 changed files with 84 additions and 8 deletions

View file

@ -40,6 +40,7 @@
#include <QString> #include <QString>
#include "base/global.h" #include "base/global.h"
#include "base/logger.h"
#include "base/net/downloadmanager.h" #include "base/net/downloadmanager.h"
#include "base/preferences.h" #include "base/preferences.h"
#include "base/rss/rss_article.h" #include "base/rss/rss_article.h"
@ -433,16 +434,52 @@ void RSSWidget::downloadSelectedTorrents()
// open the url of the selected RSS articles in the Web browser // open the url of the selected RSS articles in the Web browser
void RSSWidget::openSelectedArticlesUrls() void RSSWidget::openSelectedArticlesUrls()
{ {
qsizetype emptyLinkCount = 0;
qsizetype badLinkCount = 0;
QString articleTitle;
for (QListWidgetItem *item : asConst(m_ui->articleListWidget->selectedItems())) for (QListWidgetItem *item : asConst(m_ui->articleListWidget->selectedItems()))
{ {
auto *article = item->data(Qt::UserRole).value<RSS::Article *>(); auto *article = item->data(Qt::UserRole).value<RSS::Article *>();
Q_ASSERT(article); Q_ASSERT(article);
// Mark as read
article->markAsRead(); article->markAsRead();
if (const QUrl articleLink {article->link()}; !articleLink.isEmpty() && !articleLink.isLocalFile()) const QString articleLink = article->link();
QDesktopServices::openUrl(articleLink); const QUrl articleLinkURL {articleLink};
if (articleLinkURL.isEmpty()) [[unlikely]]
{
if (articleTitle.isEmpty())
articleTitle = article->title();
++emptyLinkCount;
}
else if (articleLinkURL.isLocalFile()) [[unlikely]]
{
if (badLinkCount == 0)
articleTitle = article->title();
++badLinkCount;
LogMsg(tr("Blocked opening RSS article URL. URL pointing to local file might be malicious behaviour. Article: \"%1\". URL: \"%2\".")
.arg(article->title(), articleLink), Log::WARNING);
}
else [[likely]]
{
QDesktopServices::openUrl(articleLinkURL);
}
}
if (badLinkCount > 0)
{
QString message = tr("Blocked opening RSS article URL. The following article URL is pointing to local file and it may be malicious behaviour:\n%1").arg(articleTitle);
if (badLinkCount > 1)
message.append(u"\n" + tr("There are %1 more articles with the same issue.").arg(badLinkCount - 1));
QMessageBox::warning(this, u"qBittorrent"_s, message, QMessageBox::Ok);
}
else if (emptyLinkCount > 0)
{
QString message = tr("The following article has no news URL provided:\n%1").arg(articleTitle);
if (emptyLinkCount > 1)
message.append(u"\n" + tr("There are %1 more articles with the same issue.").arg(emptyLinkCount - 1));
QMessageBox::warning(this, u"qBittorrent"_s, message, QMessageBox::Ok);
} }
} }

View file

@ -35,10 +35,12 @@
#include <QHeaderView> #include <QHeaderView>
#include <QKeyEvent> #include <QKeyEvent>
#include <QMenu> #include <QMenu>
#include <QMessageBox>
#include <QPalette> #include <QPalette>
#include <QStandardItemModel> #include <QStandardItemModel>
#include <QUrl> #include <QUrl>
#include "base/logger.h"
#include "base/preferences.h" #include "base/preferences.h"
#include "base/search/searchdownloadhandler.h" #include "base/search/searchdownloadhandler.h"
#include "base/search/searchhandler.h" #include "base/search/searchhandler.h"
@ -319,15 +321,52 @@ void SearchJobWidget::downloadTorrents(const AddTorrentOption option)
downloadTorrent(rowIndex, option); downloadTorrent(rowIndex, option);
} }
void SearchJobWidget::openTorrentPages() const void SearchJobWidget::openTorrentPages()
{ {
const QModelIndexList rows = m_ui->resultsBrowser->selectionModel()->selectedRows(); const QModelIndexList rows = m_ui->resultsBrowser->selectionModel()->selectedRows();
qsizetype emptyLinkCount = 0;
qsizetype badLinkCount = 0;
QString warningEntryName;
for (const QModelIndex &rowIndex : rows) for (const QModelIndex &rowIndex : rows)
{ {
const QString descrLink = m_proxyModel->data( const QString entryName = m_proxyModel->index(rowIndex.row(), SearchSortModel::NAME).data().toString();
m_proxyModel->index(rowIndex.row(), SearchSortModel::DESC_LINK)).toString(); const QString descrLink = m_proxyModel->index(rowIndex.row(), SearchSortModel::DESC_LINK).data().toString();
if (const QUrl descrLinkURL {descrLink}; !descrLinkURL.isEmpty() && !descrLinkURL.isLocalFile())
const QUrl descrLinkURL {descrLink};
if (descrLinkURL.isEmpty()) [[unlikely]]
{
if (warningEntryName.isEmpty())
warningEntryName = entryName;
++emptyLinkCount;
}
else if (descrLinkURL.isLocalFile()) [[unlikely]]
{
if (badLinkCount == 0)
warningEntryName = entryName;
++badLinkCount;
LogMsg(tr("Blocked opening search result description page URL. URL pointing to local file might be malicious behaviour. Name: \"%1\". URL: \"%2\".")
.arg(entryName, descrLink), Log::WARNING);
}
else [[likely]]
{
QDesktopServices::openUrl(descrLinkURL); QDesktopServices::openUrl(descrLinkURL);
}
}
if (badLinkCount > 0)
{
QString message = tr("Blocked opening search result description page URL. The following result URL is pointing to local file and it may be malicious behaviour:\n%1").arg(warningEntryName);
if (badLinkCount > 1)
message.append(u"\n" + tr("There are %1 more results with the same issue.").arg(badLinkCount - 1));
QMessageBox::warning(this, u"qBittorrent"_s, message, QMessageBox::Ok);
}
else if (emptyLinkCount > 0)
{
QString message = tr("Entry \"%1\" has no description page URL provided.").arg(warningEntryName);
if (emptyLinkCount > 1)
message.append(u"\n" + tr("There are %1 more entries with the same issue.").arg(emptyLinkCount - 1));
QMessageBox::warning(this, u"qBittorrent"_s, message, QMessageBox::Ok);
} }
} }

View file

@ -127,7 +127,7 @@ private:
void onUIThemeChanged(); void onUIThemeChanged();
void downloadTorrents(AddTorrentOption option = AddTorrentOption::Default); void downloadTorrents(AddTorrentOption option = AddTorrentOption::Default);
void openTorrentPages() const; void openTorrentPages();
void copyTorrentURLs() const; void copyTorrentURLs() const;
void copyTorrentDownloadLinks() const; void copyTorrentDownloadLinks() const;
void copyTorrentNames() const; void copyTorrentNames() const;