From a13238c236fbab867db726208adbce01a74bfe7f Mon Sep 17 00:00:00 2001 From: trigg Date: Tue, 30 Jul 2024 09:46:00 +0100 Subject: [PATCH 1/5] panel: sanitize variables being sent to pango markup --- .../notifications/single-notification.cpp | 4 ++-- src/panel/widgets/tray/item.cpp | 8 +++---- src/util/gtk-utils.cpp | 21 +++++++++++++++++++ src/util/gtk-utils.hpp | 3 +++ 4 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/panel/widgets/notifications/single-notification.cpp b/src/panel/widgets/notifications/single-notification.cpp index 034a1e16c..14eb27f95 100644 --- a/src/panel/widgets/notifications/single-notification.cpp +++ b/src/panel/widgets/notifications/single-notification.cpp @@ -111,11 +111,11 @@ WfSingleNotification::WfSingleNotification(const Notification & notification) text.set_line_wrap_mode(Pango::WRAP_CHAR); if (notification.body.empty()) { - text.set_markup(notification.summary); + text.set_markup(sanitize_pango_markup(notification.summary)); } else { // NOTE: that is not a really right way to implement FDN markup feature, but the easiest one. - text.set_markup("" + notification.summary + "" + "\n" + notification.body); + text.set_markup("" + sanitize_pango_markup(notification.summary) + "" + "\n" + sanitize_pango_markup(notification.body)); } content.pack_start(text); diff --git a/src/panel/widgets/tray/item.cpp b/src/panel/widgets/tray/item.cpp index 98c5a1d3c..2790c94bc 100644 --- a/src/panel/widgets/tray/item.cpp +++ b/src/panel/widgets/tray/item.cpp @@ -185,10 +185,10 @@ void StatusNotifierItem::setup_tooltip() get_item_property>("ToolTip"); auto tooltip_label_text = !tooltip_text.empty() && !tooltip_title.empty() ? - "" + tooltip_title + ": " + tooltip_text : - !tooltip_title.empty() ? tooltip_title : - !tooltip_text.empty() ? tooltip_text : - get_item_property("Title"); + "" + sanitize_pango_markup(tooltip_title) + ": " + sanitize_pango_markup(tooltip_text) : + !tooltip_title.empty() ? sanitize_pango_markup(tooltip_title) : + !tooltip_text.empty() ? sanitize_pango_markup(tooltip_text) : + sanitize_pango_markup(get_item_property("Title")); const auto pixbuf = extract_pixbuf(std::move(tooltip_icon_data)); diff --git a/src/util/gtk-utils.cpp b/src/util/gtk-utils.cpp index ae8b9623d..1c483346f 100644 --- a/src/util/gtk-utils.cpp +++ b/src/util/gtk-utils.cpp @@ -115,3 +115,24 @@ void set_image_icon(Gtk::Image& image, std::string icon_name, int size, set_image_pixbuf(image, pbuff, scale); } + +std::string sanitize_pango_markup(std::string input){ + replace_all(input, "&", "&"); + replace_all(input, "<", "<"); + replace_all(input, ">", ">"); + replace_all(input, "'", "'"); + replace_all(input, "\"", """); + return input; +} + +void replace_all(std::string& haystack, const std::string from, const std::string to){ + if (from.empty()){ + return; + } + + size_t pos = 0; + while ((pos = haystack.find(from, pos)) != std::string::npos) { + haystack.replace(pos, from.length(), to); + pos += to.length(); + } +} \ No newline at end of file diff --git a/src/util/gtk-utils.hpp b/src/util/gtk-utils.hpp index e78d1b0cf..37dc9f630 100644 --- a/src/util/gtk-utils.hpp +++ b/src/util/gtk-utils.hpp @@ -30,4 +30,7 @@ void set_image_icon(Gtk::Image& image, std::string icon_name, int size, void invert_pixbuf(Glib::RefPtr& pbuff); +std::string sanitize_pango_markup(const std::string input); +void replace_all(std::string& haystack, const std::string from, const std::string to); + #endif /* end of include guard: WF_GTK_UTILS */ From e5a3d3abfe5c610e3cd185659552585cf45814f1 Mon Sep 17 00:00:00 2001 From: trigg Date: Tue, 30 Jul 2024 10:11:47 +0100 Subject: [PATCH 2/5] panel: prefer Glib helper function over brand new one --- .../notifications/single-notification.cpp | 5 +++-- src/panel/widgets/tray/item.cpp | 10 +++++---- src/util/gtk-utils.cpp | 21 ------------------- src/util/gtk-utils.hpp | 3 --- 4 files changed, 9 insertions(+), 30 deletions(-) diff --git a/src/panel/widgets/notifications/single-notification.cpp b/src/panel/widgets/notifications/single-notification.cpp index 14eb27f95..b188e21f8 100644 --- a/src/panel/widgets/notifications/single-notification.cpp +++ b/src/panel/widgets/notifications/single-notification.cpp @@ -2,6 +2,7 @@ #include "daemon.hpp" #include +#include #include #include @@ -111,11 +112,11 @@ WfSingleNotification::WfSingleNotification(const Notification & notification) text.set_line_wrap_mode(Pango::WRAP_CHAR); if (notification.body.empty()) { - text.set_markup(sanitize_pango_markup(notification.summary)); + text.set_markup(Glib::Markup::escape_text(notification.summary)); } else { // NOTE: that is not a really right way to implement FDN markup feature, but the easiest one. - text.set_markup("" + sanitize_pango_markup(notification.summary) + "" + "\n" + sanitize_pango_markup(notification.body)); + text.set_markup("" + Glib::Markup::escape_text(notification.summary) + "" + "\n" + Glib::Markup::escape_text(notification.body)); } content.pack_start(text); diff --git a/src/panel/widgets/tray/item.cpp b/src/panel/widgets/tray/item.cpp index 2790c94bc..9f4e2d5ec 100644 --- a/src/panel/widgets/tray/item.cpp +++ b/src/panel/widgets/tray/item.cpp @@ -2,6 +2,8 @@ #include +#include + #include #include @@ -185,10 +187,10 @@ void StatusNotifierItem::setup_tooltip() get_item_property>("ToolTip"); auto tooltip_label_text = !tooltip_text.empty() && !tooltip_title.empty() ? - "" + sanitize_pango_markup(tooltip_title) + ": " + sanitize_pango_markup(tooltip_text) : - !tooltip_title.empty() ? sanitize_pango_markup(tooltip_title) : - !tooltip_text.empty() ? sanitize_pango_markup(tooltip_text) : - sanitize_pango_markup(get_item_property("Title")); + "" + Glib::Markup::escape_text(tooltip_title) + ": " + Glib::Markup::escape_text(tooltip_text) : + !tooltip_title.empty() ? Glib::Markup::escape_text(tooltip_title) : + !tooltip_text.empty() ? Glib::Markup::escape_text(tooltip_text) : + Glib::Markup::escape_text(get_item_property("Title")); const auto pixbuf = extract_pixbuf(std::move(tooltip_icon_data)); diff --git a/src/util/gtk-utils.cpp b/src/util/gtk-utils.cpp index 1c483346f..ae8b9623d 100644 --- a/src/util/gtk-utils.cpp +++ b/src/util/gtk-utils.cpp @@ -115,24 +115,3 @@ void set_image_icon(Gtk::Image& image, std::string icon_name, int size, set_image_pixbuf(image, pbuff, scale); } - -std::string sanitize_pango_markup(std::string input){ - replace_all(input, "&", "&"); - replace_all(input, "<", "<"); - replace_all(input, ">", ">"); - replace_all(input, "'", "'"); - replace_all(input, "\"", """); - return input; -} - -void replace_all(std::string& haystack, const std::string from, const std::string to){ - if (from.empty()){ - return; - } - - size_t pos = 0; - while ((pos = haystack.find(from, pos)) != std::string::npos) { - haystack.replace(pos, from.length(), to); - pos += to.length(); - } -} \ No newline at end of file diff --git a/src/util/gtk-utils.hpp b/src/util/gtk-utils.hpp index 37dc9f630..e78d1b0cf 100644 --- a/src/util/gtk-utils.hpp +++ b/src/util/gtk-utils.hpp @@ -30,7 +30,4 @@ void set_image_icon(Gtk::Image& image, std::string icon_name, int size, void invert_pixbuf(Glib::RefPtr& pbuff); -std::string sanitize_pango_markup(const std::string input); -void replace_all(std::string& haystack, const std::string from, const std::string to); - #endif /* end of include guard: WF_GTK_UTILS */ From e787069b3548500b8c8899ca26e9bc576299febe Mon Sep 17 00:00:00 2001 From: trigg Date: Tue, 30 Jul 2024 10:21:42 +0100 Subject: [PATCH 3/5] uncrustify --- src/panel/widgets/notifications/single-notification.cpp | 3 ++- src/panel/widgets/tray/item.cpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/panel/widgets/notifications/single-notification.cpp b/src/panel/widgets/notifications/single-notification.cpp index b188e21f8..0fe4ef71c 100644 --- a/src/panel/widgets/notifications/single-notification.cpp +++ b/src/panel/widgets/notifications/single-notification.cpp @@ -116,7 +116,8 @@ WfSingleNotification::WfSingleNotification(const Notification & notification) } else { // NOTE: that is not a really right way to implement FDN markup feature, but the easiest one. - text.set_markup("" + Glib::Markup::escape_text(notification.summary) + "" + "\n" + Glib::Markup::escape_text(notification.body)); + text.set_markup("" + Glib::Markup::escape_text( + notification.summary) + "" + "\n" + Glib::Markup::escape_text(notification.body)); } content.pack_start(text); diff --git a/src/panel/widgets/tray/item.cpp b/src/panel/widgets/tray/item.cpp index 9f4e2d5ec..dfc0c84ff 100644 --- a/src/panel/widgets/tray/item.cpp +++ b/src/panel/widgets/tray/item.cpp @@ -187,7 +187,8 @@ void StatusNotifierItem::setup_tooltip() get_item_property>("ToolTip"); auto tooltip_label_text = !tooltip_text.empty() && !tooltip_title.empty() ? - "" + Glib::Markup::escape_text(tooltip_title) + ": " + Glib::Markup::escape_text(tooltip_text) : + "" + Glib::Markup::escape_text(tooltip_title) + ": " + + Glib::Markup::escape_text(tooltip_text) : !tooltip_title.empty() ? Glib::Markup::escape_text(tooltip_title) : !tooltip_text.empty() ? Glib::Markup::escape_text(tooltip_text) : Glib::Markup::escape_text(get_item_property("Title")); From b3c2d44e4cbb80fbede887e6ac7d28e1d148208b Mon Sep 17 00:00:00 2001 From: trigg Date: Tue, 10 Mar 2026 05:52:03 +0000 Subject: [PATCH 4/5] util: conditional escaping of strings --- .../notifications/single-notification.cpp | 7 ++-- src/panel/widgets/tray/item.cpp | 12 +++--- src/util/gtk-utils.cpp | 37 +++++++++++++++++++ src/util/gtk-utils.hpp | 4 ++ 4 files changed, 49 insertions(+), 11 deletions(-) diff --git a/src/panel/widgets/notifications/single-notification.cpp b/src/panel/widgets/notifications/single-notification.cpp index d9e5cb2ce..e4b5453d2 100644 --- a/src/panel/widgets/notifications/single-notification.cpp +++ b/src/panel/widgets/notifications/single-notification.cpp @@ -2,7 +2,6 @@ #include "daemon.hpp" #include -#include #include #include @@ -117,12 +116,12 @@ WfSingleNotification::WfSingleNotification(const Notification & notification) text.set_wrap_mode(Pango::WrapMode::CHAR); if (notification.body.empty()) { - text.set_markup(Glib::Markup::escape_text(notification.summary)); + text.set_markup(markup_escape(notification.summary)); } else { // NOTE: that is not a really right way to implement FDN markup feature, but the easiest one. - text.set_markup("" + Glib::Markup::escape_text( - notification.summary) + "" + "\n" + Glib::Markup::escape_text(notification.body)); + text.set_markup("" + markup_escape( + notification.summary) + "" + "\n" + markup_escape(notification.body)); } content.append(text); diff --git a/src/panel/widgets/tray/item.cpp b/src/panel/widgets/tray/item.cpp index 91ff53039..4b02ef99c 100644 --- a/src/panel/widgets/tray/item.cpp +++ b/src/panel/widgets/tray/item.cpp @@ -2,8 +2,6 @@ #include -#include - #include #include #include @@ -169,11 +167,11 @@ void StatusNotifierItem::setup_tooltip() get_item_property>("ToolTip"); auto tooltip_label_text = !tooltip_text.empty() && !tooltip_title.empty() ? - "" + Glib::Markup::escape_text(tooltip_title) + ": " + - Glib::Markup::escape_text(tooltip_text) : - !tooltip_title.empty() ? Glib::Markup::escape_text(tooltip_title) : - !tooltip_text.empty() ? Glib::Markup::escape_text(tooltip_text) : - Glib::Markup::escape_text(get_item_property("Title")); + "" + markup_escape(tooltip_title) + ": " + + markup_escape(tooltip_text) : + !tooltip_title.empty() ? markup_escape(tooltip_title) : + !tooltip_text.empty() ? markup_escape(tooltip_text) : + markup_escape(get_item_property("Title")); const auto pixbuf = extract_pixbuf(std::move(tooltip_icon_data)); bool icon_shown = false; diff --git a/src/util/gtk-utils.cpp b/src/util/gtk-utils.cpp index 80cec7b56..077232e0c 100644 --- a/src/util/gtk-utils.cpp +++ b/src/util/gtk-utils.cpp @@ -1,3 +1,4 @@ +#include "glibmm/markup.h" #include #include #include @@ -72,3 +73,39 @@ void image_set_icon(Gtk::Image *image, std::string path) image->set_from_icon_name(path); } } + +/* + * Check if this string appears to be markup + * + * Does not check it is *valid* markup + */ +bool is_markup(std::string input) +{ + int count_left = std::count(input.begin(), input.end(), '<'); + int count_right = std::count(input.begin(), input.end(), '>'); + int count_amp = std::count(input.begin(), input.end(), '&'); + int count_semi = std::count(input.begin(), input.end(), ';'); + + if (count_left || count_right || count_amp || count_semi) + { + /* It has some markup characters */ + if ((count_left == count_right) && (count_amp == count_semi)) + { + /* And they pair up */ + return true; + } + } + + return false; +} + +/* Escape string if it doesn't appear to be markup */ +std::string markup_escape(std::string input) +{ + if (is_markup(input)) + { + return input; + } + + return Glib::Markup::escape_text(input); +} diff --git a/src/util/gtk-utils.hpp b/src/util/gtk-utils.hpp index 8cac6c295..ef78616ef 100644 --- a/src/util/gtk-utils.hpp +++ b/src/util/gtk-utils.hpp @@ -20,3 +20,7 @@ struct WfIconLoadOptions void invert_pixbuf(Glib::RefPtr& pbuff); void image_set_icon(Gtk::Image *image, std::string path); + +bool is_markup(std::string); + +std::string markup_escape(std::string); From aea5c7d25ddff9a4b44840ab30c035a8d9053a94 Mon Sep 17 00:00:00 2001 From: trigg Date: Tue, 10 Mar 2026 06:11:57 +0000 Subject: [PATCH 5/5] util: markup check simplified --- src/util/gtk-utils.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/util/gtk-utils.cpp b/src/util/gtk-utils.cpp index 077232e0c..4b1a0f0dc 100644 --- a/src/util/gtk-utils.cpp +++ b/src/util/gtk-utils.cpp @@ -86,14 +86,10 @@ bool is_markup(std::string input) int count_amp = std::count(input.begin(), input.end(), '&'); int count_semi = std::count(input.begin(), input.end(), ';'); - if (count_left || count_right || count_amp || count_semi) + if ((count_left == count_right) && (count_amp == count_semi)) { - /* It has some markup characters */ - if ((count_left == count_right) && (count_amp == count_semi)) - { - /* And they pair up */ - return true; - } + /* And they pair up */ + return true; } return false;