Fix insufficient sanitization of safe strings in file transfer protocol

Change the spec to restrict safe strings further to avoid such bugs in
other implementations. Reported by Edwin Hoffman.
This commit is contained in:
Kovid Goyal 2023-07-08 09:50:28 +05:30
parent 7d2dc252f1
commit a6a3e5e67d
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
3 changed files with 16 additions and 5 deletions

View file

@ -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]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View file

@ -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

View file

@ -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 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 @@ def get_serialized_fields(self, prefix_with_osc_code: bool = False) -> Iterator[
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 @@ def handle_item(key: memoryview, val: memoryview, has_semicolons: bool) -> None:
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: