Skip to content

Commit

Permalink
Escape application names for GMarkup
Browse files Browse the repository at this point in the history
GLib provides a parser called GMarkup, which implements a subset of XML.
Application names may contain XML metacharacters, such as "<" and "&".
These must be escaped to prevent XML injection, but the app menu didn't
do that.

The GMarkup documentation explicitly states that GMarkup must not be
used to parse untrusted input [1].  Therefore, parsing malicious markup
may have undefined results.  Fortunately, there is no security problem
because the only allowed character with special meaning in XML is "&"
and ";" is not allowed.  Therefore, there is no way to create a valid
XML entity or inject tags.  The worst that can happen is the creation of
ill-formed markup that that GLib rejects.

This patch also addresses a URL construction bug: filenames need to be
URL-encoded in file:// URLs.

[1]: https://github.com/GNOME/glib/blob/3304a517d9a7bdbb52d60394fdae6f9903f0f4f3/glib/gmarkup.c#L50-L51
  • Loading branch information
DemiMarie committed Nov 26, 2024
1 parent 9d665ee commit feccd44
Show file tree
Hide file tree
Showing 2 changed files with 16 additions and 11 deletions.
3 changes: 2 additions & 1 deletion qubes_menu/app_widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"""
import subprocess
import logging
import urllib.parse
from typing import Optional, List
from functools import reduce

Expand Down Expand Up @@ -74,7 +75,7 @@ def __init__(self, app_info: ApplicationInfo, **properties):
self.connect("drag-data-get", self._on_drag_data_get)

def _on_drag_data_get(self, _widget, _drag_context, data, _info, _time):
data.set_uris(['file://' + str(self.app_info.file_path)])
data.set_uris(['file://' + urllib.parse.quote(str(self.app_info.file_path))])

def show_menu(self, _widget, event):
"""
Expand Down
24 changes: 14 additions & 10 deletions qubes_menu/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,14 @@ def load_icon(icon_name,
pixbuf.fill(0x000)
return pixbuf


def show_error(title, text):
"""
Helper function to display error messages.
"""
dialog = Gtk.MessageDialog(
None, 0, Gtk.MessageType.ERROR, Gtk.ButtonsType.OK)
dialog.set_title(title)
dialog.set_markup(text)
dialog.set_markup(GLib.markup_escape_text(text))
dialog.connect("response", lambda *x: dialog.destroy())
dialog.show()

Expand Down Expand Up @@ -91,7 +90,7 @@ def text_search(search_word: str, text_words: List[str]):


def highlight_words(labels: List[Gtk.Label], search_words: List[str],
hl_tag: Optional[str] = None):
hl_tag: Optional[str] = None) -> None:
"""Highlight provided search_words in the provided labels."""
if not labels:
return
Expand All @@ -110,7 +109,7 @@ def highlight_words(labels: List[Gtk.Label], search_words: List[str],
for label in labels:
text = label.get_text()
# remove existing highlighting
label.set_markup(text)
label.set_markup(GLib.markup_escape_text(text))
search_text = text.lower()
found_intervals = []
for word in search_words:
Expand All @@ -131,12 +130,17 @@ def highlight_words(labels: List[Gtk.Label], search_words: List[str],
else:
result_intervals.append(interval)

for interval in reversed(result_intervals):
start, end = interval
text = text[:start] + hl_tag + \
text[start:end] + '</span>' + text[end:]

label.set_markup(text)
markup_list = []
last_start = 0
for start, end in reversed(result_intervals):
markup_list.append(GLib.markup_escape_text(text[last_start:start]))
markup_list.append(hl_tag)
markup_list.append(GLib.markup_escape_text(text[start:end]))
markup_list.append('</span>')
last_start = end
markup_list.append(GLib.markup_escape_text(text[last_start:]))

label.set_markup("".join(markup_list))


def get_visible_child(widget: Gtk.Container, reverse=False):
Expand Down

0 comments on commit feccd44

Please sign in to comment.