Skip to content

Commit

Permalink
Sanitize notifications ids as they are retransmitted over the TTY
Browse files Browse the repository at this point in the history
  • Loading branch information
kovidgoyal committed Sep 5, 2022
1 parent c455fea commit f05783e
Show file tree
Hide file tree
Showing 4 changed files with 19 additions and 3 deletions.
4 changes: 4 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ Detailed list of changes

- Wayland: Fix remembering window size not accurate when client side decorations are present

- Fix an issue where notification identifiers were not sanitized leading to
code execution if the user clicked on a notification popup from a malicious
source. Thanks to Carter Sande for discovering this vulnerability.

0.26.1 [2022-08-30]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down
11 changes: 9 additions & 2 deletions kitty/notify.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
#!/usr/bin/env python3
# License: GPLv3 Copyright: 2019, Kovid Goyal <kovid at kovidgoyal.net>

import re
from base64 import standard_b64decode
from collections import OrderedDict
from itertools import count
from typing import Dict, Optional, Callable
from typing import Callable, Dict, Optional

from .constants import is_macos, logo_png_file
from .fast_data_types import get_boss
from .types import run_once
from .utils import log_error

NotifyImplementation = Callable[[str, str, str], None]
Expand Down Expand Up @@ -92,6 +94,11 @@ def parse_osc_777(raw: str) -> NotificationCommand:
return ans


@run_once
def sanitize_identifier_pat() -> 're.Pattern[str]':
return re.compile(r'[^a-zA-Z0-9-_+.]+')


def parse_osc_99(raw: str) -> NotificationCommand:
cmd = NotificationCommand()
metadata, payload = raw.partition(';')[::2]
Expand All @@ -107,7 +114,7 @@ def parse_osc_99(raw: str) -> NotificationCommand:
if k == 'p':
payload_type = v
elif k == 'i':
cmd.identifier = v
cmd.identifier = sanitize_identifier_pat().sub('', v)
elif k == 'e':
payload_is_encoded = v == '1'
elif k == 'd':
Expand Down
3 changes: 2 additions & 1 deletion kitty/window.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
update_window_title, update_window_visibility, wakeup_main_loop
)
from .keys import keyboard_mode_name, mod_mask
from .notify import NotificationCommand, handle_notification_cmd
from .notify import NotificationCommand, handle_notification_cmd, sanitize_identifier_pat
from .options.types import Options
from .rgb import to_color
from .terminfo import get_capabilities
Expand Down Expand Up @@ -1001,6 +1001,7 @@ def report_color(self, code: str, r: int, g: int, b: int) -> None:
self.screen.send_escape_code_to_child(OSC, f'{code};rgb:{r:04x}/{g:04x}/{b:04x}')

def report_notification_activated(self, identifier: str) -> None:
identifier = sanitize_identifier_pat().sub('', identifier)
self.screen.send_escape_code_to_child(OSC, f'99;i={identifier};')

def set_dynamic_color(self, code: int, value: Union[str, bytes]) -> None:
Expand Down
4 changes: 4 additions & 0 deletions kitty_tests/datatypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -555,3 +555,7 @@ def test_single_key(self):
self.assertEqual(hash(SingleKey(key=1)), hash(SingleKey(key=1)))
self.assertNotEqual(hash(SingleKey(key=1, mods=2)), hash(SingleKey(key=1)))
self.assertNotEqual(SingleKey(key=1, mods=2), SingleKey(key=1))

def test_notify_identifier_sanitization(self):
from kitty.notify import sanitize_identifier_pat
self.ae(sanitize_identifier_pat().sub('', '\x1b\nabc\n[*'), 'abc')

0 comments on commit f05783e

Please sign in to comment.