Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix markup injection issues #236

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion qubes_config/global_config/rule_list_widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ def _combobox_changed(self, *_args):
self.callback()

def _format_new_value(self, new_value):
self.name_widget.set_markup(f'{self.choices[new_value]}')
self.name_widget.set_text(f'{self.choices[new_value]}')
if self.verb_description:
self.additional_text_widget.set_text(
self.verb_description.get_verb_for_action_and_target(
Expand Down
21 changes: 12 additions & 9 deletions qubes_config/global_config/thisdevice_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,14 @@

import qubesadmin.vm
from ..widgets.gtk_utils import show_error, load_icon, copy_to_global_clipboard
from ..widgets.gtk_utils import markup_format
from .page_handler import PageHandler
from .policy_manager import PolicyManager

import gi

gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from gi.repository import Gtk, GLib

import gettext
t = gettext.translation("desktop-linux-manager", fallback=True)
Expand Down Expand Up @@ -101,7 +102,7 @@ def __init__(self,
['qubes-hcl-report', '-y']).decode()
except subprocess.CalledProcessError as ex:
label_text += _("Failed to load system data: {ex}\n").format(
ex=str(ex))
ex=GLib.markup_escape_text(str(ex)))
self.hcl_check = ""

try:
Expand All @@ -115,7 +116,7 @@ def __init__(self,
label_text += _("Failed to load system data.\n")
self.data_label.get_style_context().add_class('red_code')

label_text += _("""<b>Brand:</b> {brand}
label_text += markup_format("""<b>Brand:</b> {brand}
<b>Model:</b> {model}

<b>CPU:</b> {cpu}
Expand All @@ -128,7 +129,8 @@ def __init__(self,
<b>BIOS:</b> {bios}
<b>Kernel:</b> {kernel_ver}
<b>Xen:</b> {xen_ver}
""").format(brand=self._get_data('brand'),
""",
brand=self._get_data('brand'),
model=self._get_data('model'),
cpu=self._get_data('cpu'),
chipset=self._get_data('chipset'),
Expand All @@ -139,15 +141,16 @@ def __init__(self,
kernel_ver=self._get_version('kernel'),
xen_ver=self._get_version('xen'))
self.set_state(self.compat_hvm_image, self._get_data('hvm'))
self.compat_hvm_label.set_markup(f"<b>HVM:</b> {self._get_data('hvm')}")
self.compat_hvm_label.set_markup(markup_format("<b>HVM:</b> {}",
self._get_data('hvm')))

self.set_state(self.compat_iommu_image, self._get_data('iommu'))
self.compat_iommu_label.set_markup(
f"<b>I/O MMU:</b> {self._get_data('iommu')}")
markup_format("<b>I/O MMU:</b> {}", self._get_data('iommu')))

self.set_state(self.compat_hap_image, self._get_data('slat'))
self.compat_hap_label.set_markup(
f"<b>HAP/SLAT:</b> {self._get_data('slat')}")
markup_format("<b>HAP/SLAT:</b> {}", self._get_data('slat')))

self.set_state(self.compat_tpm_image,
'yes' if self._get_data('tpm') == '1.2' else 'maybe')
Expand All @@ -166,7 +169,7 @@ def __init__(self,

self.set_state(self.compat_remapping_image, self._get_data('remap'))
self.compat_remapping_label.set_markup(
f"<b>Remapping:</b> {self._get_data('remap')}")
markup_format(_("<b>Remapping:</b> {}"), self._get_data('remap')))

self.set_policy_state()

Expand All @@ -178,7 +181,7 @@ def __init__(self,
_("<b>PV qubes:</b> {num_pvs} found").format(num_pvs=len(pv_vms)))
self.compat_pv_tooltip.set_tooltip_markup(
_("<b>The following qubes have PV virtualization mode:</b>\n - ") +
'\n - '.join([vm.name for vm in pv_vms]))
'\n - '.join([GLib.markup_escape_text(vm.name) for vm in pv_vms]))
self.compat_pv_tooltip.set_visible(bool(pv_vms))

self.data_label.set_markup(label_text)
Expand Down
2 changes: 1 addition & 1 deletion qubes_config/global_config/usb_devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,7 @@ def load_rules_for_usb_qube(self):
def disable_u2f(self, reason: str):
self.problem_fatal_box.set_visible(True)
self.problem_fatal_box.show_all()
self.problem_fatal_label.set_markup(reason)
self.problem_fatal_label.set_text(reason)
self.enable_check.set_active(False)
self.enable_check.set_sensitive(False)
self.box.set_visible(False)
Expand Down
15 changes: 14 additions & 1 deletion qubes_config/widgets/gtk_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,19 @@
pixbuf.fill(0x000)
return pixbuf

def _escape_str(s: Union[str, float, int]) -> Union[str, float, int]:
# pylint: disable=unidiomatic-typecheck
if type(s) is str:
return GLib.markup_escape_text(s)
# pylint: disable=unidiomatic-typecheck
if type(s) in (float, int, bool):
return s
raise TypeError(f"Unsupported input type {type(s)}")

Check warning on line 94 in qubes_config/widgets/gtk_utils.py

View check run for this annotation

Codecov / codecov/patch

qubes_config/widgets/gtk_utils.py#L92-L94

Added lines #L92 - L94 were not covered by tests

def markup_format(s, *args, **kwargs) -> str:
escaped_args = [_escape_str(i) for i in args]
escaped_kwargs = {k: _escape_str(v) for k, v in kwargs.items()}
return s.format(*escaped_args, **escaped_kwargs)

def show_error(parent, title, text):
"""
Expand Down Expand Up @@ -175,7 +188,7 @@

if isinstance(text, str):
label: Gtk.Label = Gtk.Label()
label.set_markup(text)
label.set_text(text)
label.set_line_wrap_mode(Gtk.WrapMode.WORD)
label.set_max_width_chars(200)
label.set_xalign(0)
Expand Down
35 changes: 19 additions & 16 deletions qui/clipboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
t = gettext.translation("desktop-linux-manager", fallback=True)
_ = t.gettext

from .utils import run_asyncio_and_show_errors
from .utils import run_asyncio_and_show_errors, markup_format

gbulb.install()

Expand Down Expand Up @@ -127,19 +127,20 @@ def _copy(self, metadata: dict) -> None:
size = clipboard_formatted_size(metadata["sent_size"])

if metadata["malformed_request"]:
body = ERROR_MALFORMED_DATA.format(vmname=metadata["vmname"])
body = markup_format(ERROR_MALFORMED_DATA,
vmname=metadata["vmname"])
icon = "dialog-error"
elif (
metadata["qrexec_clipboard"]
and metadata["sent_size"] >= metadata["buffer_size"]
):
# Microsoft Windows clipboard case
body = WARNING_POSSIBLE_TRUNCATION.format(
body = markup_format(WARNING_POSSIBLE_TRUNCATION,
vmname=metadata["vmname"], size=size
)
icon = "dialog-warning"
elif metadata["oversized_request"]:
body = ERROR_OVERSIZED_DATA.format(
body = markup_format(ERROR_OVERSIZED_DATA,
vmname=metadata["vmname"],
size=size,
limit=clipboard_formatted_size(metadata["buffer_size"]),
Expand All @@ -150,13 +151,14 @@ def _copy(self, metadata: dict) -> None:
and metadata["cleared"]
and metadata["sent_size"] == 0
):
body = WARNING_EMPTY_CLIPBOARD.format(vmname=metadata["vmname"])
body = markup_format(WARNING_EMPTY_CLIPBOARD,
vmname=metadata["vmname"])
icon = "dialog-warning"
elif not metadata["successful"]:
body = ERROR_ON_COPY.format(vmname=metadata["vmname"])
body = markup_format(ERROR_ON_COPY, vmname=metadata["vmname"])
icon = "dialog-error"
else:
body = MSG_COPY_SUCCESS.format(
body = markup_format(MSG_COPY_SUCCESS,
vmname=metadata["vmname"],
size=size,
shortcut=self.gtk_app.paste_shortcut,
Expand All @@ -173,14 +175,14 @@ def _copy(self, metadata: dict) -> None:
def _paste(self, metadata: dict) -> None:
"""Sends Paste notification via Gio.Notification."""
if not metadata["successful"] or metadata["malformed_request"]:
body = ERROR_ON_PASTE.format(vmname=metadata["vmname"])
body = markup_format(ERROR_ON_PASTE, vmname=metadata["vmname"])
body += MSG_WIPED
icon = "dialog-error"
elif (
"protocol_version_xside" in metadata.keys()
and metadata["protocol_version_xside"] >= 0x00010008
):
body = MSG_PASTE_SUCCESS_METADATA.format(
body = markup_format(MSG_PASTE_SUCCESS_METADATA,
size=clipboard_formatted_size(metadata["sent_size"]),
vmname=metadata["vmname"],
)
Expand Down Expand Up @@ -355,9 +357,9 @@ def update_clipboard_contents(

else:
self.clipboard_label.set_markup(
_(
markup_format(_(
"<i>Global clipboard contents: {0} from <b>{1}</b></i>"
).format(size, vm)
), size, vm)
)
self.icon.set_from_icon_name("edit-copy")

Expand Down Expand Up @@ -391,10 +393,10 @@ def setup_ui(self, *_args, **_kwargs):

help_label = Gtk.Label(xalign=0)
help_label.set_markup(
_(
markup_format(_(
"<i>Use <b>{copy}</b> to copy and "
"<b>{paste}</b> to paste.</i>"
).format(copy=self.copy_shortcut, paste=self.paste_shortcut)
), copy=self.copy_shortcut, paste=self.paste_shortcut)
)
help_item = Gtk.MenuItem()
help_item.set_margin_left(10)
Expand Down Expand Up @@ -442,9 +444,10 @@ def copy_dom0_clipboard(self, *_args, **_kwargs):
'"protocol_version_xside":65544,\n'
'"protocol_version_vmside":65544,\n'
"}}\n".format(
xevent_timestamp=str(Gtk.get_current_event_time()),
sent_size=os.path.getsize(DATA),
buffer_size="256000",
xevent_timestamp=json.dumps(
Gtk.get_current_event_time()),
sent_size=json.dumps(os.path.getsize(DATA)),
buffer_size=json.dumps(256000),
)
)
except Exception: # pylint: disable=broad-except
Expand Down
15 changes: 9 additions & 6 deletions qui/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from gi.repository import Gtk, Pango, GLib, GdkPixbuf # isort:skip
from qubesadmin import exc
from qubesadmin.utils import size_to_human
from .utils import markup_format

import gettext
t = gettext.translation("desktop-linux-manager", fallback=True)
Expand Down Expand Up @@ -137,10 +138,10 @@ def update_tooltip(self,
perc_storage = self.cur_storage / self.max_storage

tooltip += \
_("\nTemplate: <b>{template}</b>"
markup_format(_("\nTemplate: <b>{template}</b>"
"\nNetworking: <b>{netvm}</b>"
"\nPrivate storage: <b>{current_storage:.2f}GB/"
"{max_storage:.2f}GB ({perc_storage:.1%})</b>").format(
"{max_storage:.2f}GB ({perc_storage:.1%})</b>"),
template=self.template_name,
netvm=self.netvm_name,
current_storage=self.cur_storage,
Expand Down Expand Up @@ -177,7 +178,8 @@ def update_state(self, cpu=0, header=False):
else:
color = self.cpu_label.get_style_context() \
.get_color(Gtk.StateFlags.INSENSITIVE).to_color()
markup = f'<span color="{color.to_string()}">0%</span>'
escaped_color = GLib.markup_escape_text(color.to_string())
markup = f'<span color="{escaped_color}">0%</span>'

self.cpu_label.set_markup(markup)

Expand Down Expand Up @@ -249,8 +251,9 @@ def device_hbox(device) -> Gtk.Box:
name_label = Gtk.Label(xalign=0)
name = f"{device.backend_domain}:{device.port_id} - {device.description}"
if device.attachments:
dev_list = ", ".join(list(device.attachments))
name_label.set_markup(f'<b>{name} ({dev_list})</b>')
dev_list = GLib.markup_escape_text(", ".join(list(device.attachments)))
name_escaped = GLib.markup_escape_text(name)
name_label.set_markup(f'<b>{name_escaped} ({dev_list})</b>')
else:
name_label.set_text(name)
name_label.set_max_width_chars(64)
Expand Down Expand Up @@ -281,7 +284,7 @@ def device_domain_hbox(vm, attached: bool) -> Gtk.Box:

name = Gtk.Label(xalign=0)
if attached:
name.set_markup(f'<b>{vm.vm_name}</b>')
name.set_markup(f'<b>{GLib.markup_escape_text(vm.vm_name)}</b>')
else:
name.set_text(vm.vm_name)

Expand Down
14 changes: 8 additions & 6 deletions qui/devices/actionable_widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ def __init__(self, vm: backend.VM, size: int = 18, variant: str = 'dark',
self.backend_label = Gtk.Label(xalign=0)
backend_label: str = vm.name
if name_extension:
backend_label += ": " + name_extension
backend_label += ": " + GLib.markup_escape_text(name_extension)
self.backend_label.set_markup(backend_label)

self.pack_start(self.backend_icon, False, False, 4)
Expand Down Expand Up @@ -227,7 +227,9 @@ class DetachWidget(ActionableWidget, SimpleActionWidget):
"""Detach device from a VM"""
def __init__(self, vm: backend.VM, device: backend.Device,
variant: str = 'dark'):
super().__init__('detach', '<b>Detach from ' + vm.name + '</b>',
super().__init__('detach',
'<b>Detach from ' +
GLib.markup_escape_text(vm.name) + '</b>',
variant)
self.vm = vm
self.device = device
Expand Down Expand Up @@ -345,14 +347,14 @@ def __init__(self, device: backend.Device, variant: str = 'dark'):
super().__init__(orientation=Gtk.Orientation.VERTICAL)
# FUTURE: this is proposed layout for new API
# self.device_label = Gtk.Label()
# self.device_label.set_markup(device.name)
# self.device_label.set_text(device.name)
# self.device_label.get_style_context().add_class('device_name')
# self.edit_icon = VariantIcon('edit', 'dark', 24)
# self.detailed_description_label = Gtk.Label()
# self.detailed_description_label.set_text(device.description)
# self.backend_icon = VariantIcon(device.vm_icon, 'dark', 24)
# self.backend_label = Gtk.Label(xalign=0)
# self.backend_label.set_markup(str(device.backend_domain))
# self.backend_label.set_text(str(device.backend_domain))
#
# self.title_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
# self.title_box.add(self.device_label)
Expand All @@ -367,7 +369,7 @@ def __init__(self, device: backend.Device, variant: str = 'dark'):
# self.add(self.attachment_box)

self.device_label = Gtk.Label()
self.device_label.set_markup(device.name)
self.device_label.set_text(device.name)
self.device_label.get_style_context().add_class('device_name')
self.device_label.set_xalign(Gtk.Align.CENTER)
self.device_label.set_halign(Gtk.Align.CENTER)
Expand Down Expand Up @@ -408,7 +410,7 @@ def __init__(self, device: backend.Device, variant: str = 'dark'):

self.device_label = Gtk.Label(xalign=0)

label_markup = device.name
label_markup = GLib.markup_escape_text(device.name)
if (device.connection_timestamp and
int(time.monotonic() - device.connection_timestamp) < 120):
label_markup += ' <span foreground="#63a0ff"><b>NEW</b></span>'
Expand Down
Loading