diff --git a/docs/changelog.rst b/docs/changelog.rst index 6394adb90..72f87dd0f 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -87,7 +87,9 @@ Detailed list of changes - macOS: Fix window shadows not being drawn for transparent windows (:iss:`2827`, :pull:`6416`) -- Do not echo invalid DECRQSS queries back, behavior inherited from xterm (CVE-2008-2383) +- Do not echo invalid DECRQSS queries back, behavior inherited from xterm (CVE-2008-2383). Similarly, fix an echo + bug in the file transfer protocol due to insufficient sanitization of safe strings. + 0.28.1 [2023-04-21] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/file-transfer-protocol.rst b/docs/file-transfer-protocol.rst index 783437e9f..86e64fa79 100644 --- a/docs/file-transfer-protocol.rst +++ b/docs/file-transfer-protocol.rst @@ -508,7 +508,7 @@ enum ac=file safe_string - A string consisting only of characters from the set ``[0-9a-zA-Z_:.,/!@#$%^&*()[]{}~`?"'\\|=+-]`` + A string consisting only of characters from the set ``[0-9a-zA-Z_:./@-]`` Note that the semi-colon is missing from this set. integer diff --git a/kitty/file_transmission.py b/kitty/file_transmission.py index 840b4e23f..2d9e86edb 100644 --- a/kitty/file_transmission.py +++ b/kitty/file_transmission.py @@ -3,6 +3,7 @@ import errno import os +import re import stat import tempfile from base64 import standard_b64decode, standard_b64encode @@ -21,7 +22,7 @@ from kittens.transfer.utils import IdentityCompressor, ZlibCompressor, abspath, from kitty.fast_data_types import FILE_TRANSFER_CODE, OSC, add_timer, get_boss, get_options from kitty.types import run_once -from .utils import log_error, sanitize_control_codes +from .utils import log_error EXPIRE_TIME = 10 # minutes MAX_ACTIVE_RECEIVES = MAX_ACTIVE_SENDS = 10 @@ -32,6 +33,14 @@ def escape_semicolons(x: str) -> str: return x.replace(';', ';;') +def safe_string_pat() -> 're.Pattern[str]': + return re.compile(r'[^0-9a-zA-Z_:./@-]') + + +def safe_string(x: str) -> str: + return safe_string_pat().sub('', x) + + def as_unicode(x: Union[str, bytes]) -> str: if isinstance(x, bytes): x = x.decode('ascii') @@ -307,7 +316,7 @@ class FileTransmissionCommand: if k.metadata.get('base64'): yield standard_b64encode(val.encode('utf-8')) else: - yield escape_semicolons(sanitize_control_codes(val)) + yield escape_semicolons(safe_string(val)) elif k.type is int: yield str(val) else: @@ -339,7 +348,7 @@ class FileTransmissionCommand: sval = decode_utf8_buffer(val) if has_semicolons: sval = sval.replace(';;', ';') - setattr(ans, field.name, sanitize_control_codes(sval)) + setattr(ans, field.name, safe_string(sval)) parse_ftc(data, handle_item) if ans.action is Action.invalid: