From 01a4b94b00db77e2544c9cbcd5d1b4b81b23d0e2 Mon Sep 17 00:00:00 2001 From: Kolya <142352140+agl-1984@users.noreply.github.com> Date: Thu, 28 Mar 2024 06:51:57 -0700 Subject: [PATCH] anti spoof changes --- Telegram/Resources/langs/lang.strings | 5 + .../SourceFiles/core/click_handler_types.cpp | 119 +++++++++++++++++- .../SourceFiles/core/click_handler_types.h | 10 +- Telegram/SourceFiles/core/ui_integration.cpp | 4 +- .../view/media/history_view_web_page.cpp | 2 +- .../SourceFiles/overview/overview_layout.cpp | 4 +- 6 files changed, 134 insertions(+), 10 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 6b501c4dbaa1bd..8556f681a1fbd4 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -2854,6 +2854,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_url_auth_login_option" = "Log in to {domain} as {user}"; "lng_url_auth_allow_messages" = "Allow {bot} to send me messages"; +"lng_open_spoof_link" = "Do you want to open link, that tries to mimic different link?"; +"lng_open_spoof_link_confirm" = "It is Safe"; +"lng_open_spoof_link_label" = "Link is shown as"; +"lng_open_spoof_link_url" = "Actual link is"; + "lng_bot_start" = "Start"; "lng_bot_choose_group" = "Select a Group"; "lng_bot_no_groups" = "You have no groups"; diff --git a/Telegram/SourceFiles/core/click_handler_types.cpp b/Telegram/SourceFiles/core/click_handler_types.cpp index 1c24dc510c10c5..7d27fb2516e2d3 100644 --- a/Telegram/SourceFiles/core/click_handler_types.cpp +++ b/Telegram/SourceFiles/core/click_handler_types.cpp @@ -27,6 +27,8 @@ For license and copyright information please follow this link: #include "window/window_session_controller_link_info.h" #include "styles/style_layers.h" +#include + namespace { // Possible context owners: media viewer, profile, history widget. @@ -81,6 +83,83 @@ bool UrlRequiresConfirmation(const QUrl &url) { RegExOption::CaseInsensitive); } +bool IsLabelImpersonateUrl(QString url, QString label) { + using namespace qthelp; + if (url == label) { + return false; + } + + // avoid ' @username' workaround + label = label.trimmed(); + QUrl urlObj(Core::TryConvertUrlToLocal(url)); + QUrl labelObj(Core::TryConvertUrlToLocal(label)); + if (labelObj.host().isEmpty()) { + static const auto IsHost = QRegularExpression( + "^\\w[\\w\\d_]+\\.\\w[^\\s]+$", + QRegularExpression::CaseInsensitiveOption); + const auto match1 = IsHost.match(labelObj.path()); + if (match1.hasMatch()) { + labelObj = QUrl("https://" + label); + } + } + + // check if tg username + if (label.startsWith("@")) { + // username? + if (urlObj.scheme() != "tg") { + // external website? ok + return false; + } + } + + if (urlObj.scheme() == "tg") + { + if (label.startsWith("@")) { + // check tg name + QUrlQuery args(urlObj); + if (args.queryItemValue("domain").compare(label.mid(1), Qt::CaseInsensitive) != 0) { + return true; + } + } + else if (labelObj.scheme() == "tg") { + // tg:// urls should be exactly the same + return (labelObj != urlObj); + } + } + + if (label.startsWith("#")) { + // hash tags should be EntityType::Hashtag, not CustomUrl + return true; + } + + if (!labelObj.host().isEmpty()) { + // check matching for schema and domain + if (!labelObj.scheme().isEmpty() + && (urlObj.scheme() != labelObj.scheme())) { + if ((urlObj.scheme() == "https") && (labelObj.scheme() == "http")) { + // accept this case, but not vice versa + } + else { + // schema is different (tg!=http, or https!=http) + return true; + } + } + if (urlObj.host() != labelObj.host()) { + return true; + } + // urls are different somewhere in path?query - fine + // potentially - accept only shorten version of url + return false; + } + return false; +} + +HiddenUrlClickHandler::HiddenUrlClickHandler(QString url, QString label) + : UrlClickHandler(url, false) + , _label(label) + , _isSpoof(IsLabelImpersonateUrl(url, label)) { +} + QString HiddenUrlClickHandler::copyToClipboardText() const { return url().startsWith(u"internal:url:"_q) ? url().mid(u"internal:url:"_q.size()) @@ -102,7 +181,7 @@ QString HiddenUrlClickHandler::dragText() const { return result.startsWith(u"internal:"_q) ? QString() : result; } -void HiddenUrlClickHandler::Open(QString url, QVariant context) { +void HiddenUrlClickHandler::Open(QString url, QVariant context, bool IsSpoof, QString label) { url = Core::TryConvertUrlToLocal(url); if (Core::InternalPassportLink(url)) { return; @@ -111,6 +190,44 @@ void HiddenUrlClickHandler::Open(QString url, QVariant context) { const auto open = [=] { UrlClickHandler::Open(url, context); }; + + if (IsSpoof) { + Core::App().hideMediaView(); + const auto displayed = url; + const auto displayUrl = ShowEncoded(displayed); + const auto my = context.value(); + const auto controller = my.sessionWindow.get(); + const auto use = controller + ? &controller->window() + : Core::App().activeWindow(); + auto box = Box([=](not_null box) { + Ui::ConfirmBox(box, { + .text = (tr::lng_open_spoof_link(tr::now)), + .confirmed = [=](Fn hide) { hide(); open(); }, + .confirmText = tr::lng_open_spoof_link_confirm(), + }); + const auto& st = st::boxLabel; + const auto& stdiv = st::boxDividerLabel; + box->addSkip(st.style.lineHeight - st::boxPadding.bottom()); + box->addRow(object_ptr(box, tr::lng_open_spoof_link_label(), stdiv)); + const auto url = box->addRow(object_ptr(box, label, st)); + box->addRow(object_ptr(box, tr::lng_open_spoof_link_url(), stdiv)); + const auto actual_url = box->addRow(object_ptr(box, displayUrl, st)); + url->setSelectable(true); + url->setContextCopyText(tr::lng_context_copy_link(tr::now)); + actual_url->setSelectable(true); + actual_url->setContextCopyText(tr::lng_context_copy_link(tr::now)); + }); + if (my.show) { + my.show->showBox(std::move(box)); + } + else if (use) { + use->show(std::move(box)); + use->activate(); + } + return; + } + if (url.startsWith(u"tg://"_q, Qt::CaseInsensitive) || url.startsWith(u"internal:"_q, Qt::CaseInsensitive)) { UrlClickHandler::Open(url, QVariant::fromValue([&] { diff --git a/Telegram/SourceFiles/core/click_handler_types.h b/Telegram/SourceFiles/core/click_handler_types.h index 15a7dc1f95e260..dd49e48cb2a727 100644 --- a/Telegram/SourceFiles/core/click_handler_types.h +++ b/Telegram/SourceFiles/core/click_handler_types.h @@ -53,22 +53,24 @@ Q_DECLARE_METATYPE(ClickHandlerContext); class HiddenUrlClickHandler : public UrlClickHandler { public: - HiddenUrlClickHandler(QString url) : UrlClickHandler(url, false) { - } + HiddenUrlClickHandler(QString url, QString label); QString copyToClipboardText() const override; QString copyToClipboardContextItemText() const override; QString dragText() const override; - static void Open(QString url, QVariant context = {}); + static void Open(QString url, QVariant context = {}, bool IsSpoof = false, QString label = QString()); void onClick(ClickContext context) const override { const auto button = context.button; if (button == Qt::LeftButton || button == Qt::MiddleButton) { - Open(url(), context.other); + Open(url(), context.other, _isSpoof, _label); } } TextEntity getTextEntity() const override; +private: + QString _label; + bool _isSpoof; }; class UserData; diff --git a/Telegram/SourceFiles/core/ui_integration.cpp b/Telegram/SourceFiles/core/ui_integration.cpp index e6b62203dc1e42..a73cb93eab05af 100644 --- a/Telegram/SourceFiles/core/ui_integration.cpp +++ b/Telegram/SourceFiles/core/ui_integration.cpp @@ -153,12 +153,12 @@ std::shared_ptr UiIntegration::createLinkHandler( case EntityType::Url: return (!data.data.isEmpty() && UrlClickHandler::IsSuspicious(data.data)) - ? std::make_shared(data.data) + ? std::make_shared(data.data, data.data) : Integration::createLinkHandler(data, context); case EntityType::CustomUrl: return !data.data.isEmpty() - ? std::make_shared(data.data) + ? std::make_shared(data.data, data.text) : Integration::createLinkHandler(data, context); case EntityType::BotCommand: diff --git a/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp b/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp index 2cf19510ad7870..72c426c4b12f14 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp @@ -323,7 +323,7 @@ QSize WebPage::countOptimalSize() { ? IvClickHandler(_data, original) : (previewOfHiddenUrl || UrlClickHandler::IsSuspicious( _data->url)) - ? std::make_shared(_data->url) + ? std::make_shared(_data->url, _data->url) : std::make_shared(_data->url, true); if (_data->document && (_data->document->isWallPaper() diff --git a/Telegram/SourceFiles/overview/overview_layout.cpp b/Telegram/SourceFiles/overview/overview_layout.cpp index 69fc9f292939ee..d691e5a3bfbfd3 100644 --- a/Telegram/SourceFiles/overview/overview_layout.cpp +++ b/Telegram/SourceFiles/overview/overview_layout.cpp @@ -1611,7 +1611,7 @@ Link::Link( const auto createHandler = [](const QString &url) { return UrlClickHandler::IsSuspicious(url) - ? std::make_shared(url) + ? std::make_shared(url, url) : std::make_shared(url, false); }; _page = media ? media->webpage() : nullptr; @@ -1941,7 +1941,7 @@ Link::LinkEntry::LinkEntry(const QString &url, const QString &text) : text(text) , width(st::normalFont->width(text)) , lnk(UrlClickHandler::IsSuspicious(url) - ? std::make_shared(url) + ? std::make_shared(url, text) : std::make_shared(url)) { }