When pasting in bracketed paste mode and the cursor is at a shell prompt, strip out C0 control codes

Some shells incorrectly interpret these allowing escape from bracketed paste mode. Thanks to David Leadbetter for discovering.
This commit is contained in:
Kovid Goyal 2023-10-20 12:17:13 +05:30
parent f098240ace
commit 56963c693e
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
4 changed files with 8 additions and 2 deletions

View File

@ -72,6 +72,8 @@ Detailed list of changes
- Two new event types for :ref:`watchers <watchers>`, :code:`on_title_change` and :code:`on_set_user_var`
- When pasting in bracketed paste mode and the cursor is at a shell prompt, strip out C0 control codes as some shells incorrectly interpret these allowing escape from bracketed paste mode. Thanks to David Leadbetter for discovering.
0.30.1 [2023-10-05]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -1135,13 +1135,16 @@ def docs_url(which: str = '', local_docs_root: Optional[str] = '') -> str:
return url
def sanitize_for_bracketed_paste(text: bytes) -> bytes:
def sanitize_for_bracketed_paste(text: bytes, at_shell_prompt: bool = False) -> bytes:
pat = re.compile(b'(?:(?:\033\\\x5b)|(?:\x9b))201~')
while True:
new_text = pat.sub(b'', text)
if new_text == text:
break
text = new_text
if at_shell_prompt:
# some shells dont handle C0 control codes in pasted text correctly
text = re.sub(b'[\x00-\x1f]+', b'', text)
return text

View File

@ -1494,7 +1494,7 @@ def paste_text(self, text: Union[str, bytes]) -> None:
if isinstance(text, str):
text = text.encode('utf-8')
if self.screen.in_bracketed_paste_mode:
text = sanitize_for_bracketed_paste(text)
text = sanitize_for_bracketed_paste(text, self.at_prompt)
else:
# Workaround for broken editors like nano that cannot handle
# newlines in pasted text see https://github.com/kovidgoyal/kitty/issues/994

View File

@ -590,6 +590,7 @@ def test_bracketed_paste_sanitizer(self):
self.assertNotIn(b'\x1b[201~', q)
self.assertNotIn('\x9b201~'.encode('utf-8'), q)
self.assertIn(b'ab', q)
self.assertNotIn(b'\x03', sanitize_for_bracketed_paste(b'hi\x03world', True))
def test_expand_ansi_c_escapes(self):
for src, expected in {