diff --git a/docs/changelog.rst b/docs/changelog.rst index c2cf04247..24ce7a200 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -72,6 +72,8 @@ Detailed list of changes - Two new event types for :ref:`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] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/kitty/utils.py b/kitty/utils.py index dbd4444b3..250712fc2 100644 --- a/kitty/utils.py +++ b/kitty/utils.py @@ -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 diff --git a/kitty/window.py b/kitty/window.py index 0b1e32f29..32a8c37c8 100644 --- a/kitty/window.py +++ b/kitty/window.py @@ -1494,7 +1494,7 @@ class Window: 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 diff --git a/kitty_tests/datatypes.py b/kitty_tests/datatypes.py index a90420a5e..cbb5e012e 100644 --- a/kitty_tests/datatypes.py +++ b/kitty_tests/datatypes.py @@ -590,6 +590,7 @@ class TestDataTypes(BaseTest): 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 {