[continued from previous message]
+@@ -35,10 +35,12 @@
+ #include <QHeaderView>
+ #include <QKeyEvent>
+ #include <QMenu>
++#include <QMessageBox>
+ #include <QPalette>
+ #include <QStandardItemModel>
+ #include <QUrl>
+
++#include "base/logger.h"
+ #include "base/preferences.h"
+ #include "base/search/searchdownloadhandler.h"
+ #include "base/search/searchhandler.h"
+@@ -319,15 +321,52 @@ void SearchJobWidget::downloadTorrents(c
+ 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)
+ {
+- const QString descrLink = m_proxyModel->data(
+- m_proxyModel->index(rowIndex.row(), SearchSortModel::DESC_LINK)).toString();
+- if (!descrLink.isEmpty())
+- QDesktopServices::openUrl(QUrl::fromEncoded(descrLink.toUtf8())); ++ const QString entryName = m_proxyModel->index(rowIndex.row(), SearchSortModel::NAME).data().toString();
++ const QString descrLink = m_proxyModel->index(rowIndex.row(), SearchSortModel::DESC_LINK).data().toString();
++
++ 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);
++ }
++ }
++
++ 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);
+ }
+ }
+
+--- a/src/gui/search/searchjobwidget.h
++++ b/src/gui/search/searchjobwidget.h
+@@ -127,7 +127,7 @@ private:
+ void onUIThemeChanged();
+
+ void downloadTorrents(AddTorrentOption option = AddTorrentOption::Default);
+- void openTorrentPages() const;
++ void openTorrentPages();
+ void copyTorrentURLs() const;
+ void copyTorrentDownloadLinks() const;
+ void copyTorrentNames() const;
diff -Nru qbittorrent-5.1.0/debian/patches/9b29d37d210f28f46eb4758611d7b06f4603b9d1.patch qbittorrent-5.1.0/debian/patches/9b29d37d210f28f46eb4758611d7b06f4603b9d1.patch
--- qbittorrent-5.1.0/debian/patches/9b29d37d210f28f46eb4758611d7b06f4603b9d1.patch 1970-01-01 01:00:00.000000000 +0100
+++ qbittorrent-5.1.0/debian/patches/9b29d37d210f28f46eb4758611d7b06f4603b9d1.patch 2025-07-06 16:39:44.000000000 +0200
@@ -0,0 +1,37 @@
+From 9b29d37d210f28f46eb4758611d7b06f4603b9d1 Mon Sep 17 00:00:00 2001
+From: Chocobo1 <
[email protected]>
+Date: Mon, 30 Jun 2025 01:39:03 +0800
+Subject: [PATCH] WebAPI: Trim leading whitespaces on Run External Program
+ fields
+
+Hacked qbt instances may contain malicious script placed in Run External Program and the script
+will attempt to hide itself by adding a lot whitespaces at the start of the command string.
+Users may mistake the field of being empty but is actually not.
+So trim the leading whitespaces to easily expose the malicious script.
+
+Note that GUI already trim the fields and only WebAPI doesn't trim them. This patch will unify
+the behavior.
+Related:
https://github.com/qbittorrent/docker-qbittorrent-no