From df9b13fb74e7dd389c0a3dfdf4e51a027023ec2f Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 16 Apr 2022 20:04:26 +0530 Subject: [PATCH] Ask for confirmation before doing a clone by default --- kitty/options/definition.py | 9 +++++++++ kitty/options/parse.py | 8 ++++++++ kitty/options/types.py | 4 ++++ kitty/window.py | 12 ++++++++++-- 4 files changed, 31 insertions(+), 2 deletions(-) diff --git a/kitty/options/definition.py b/kitty/options/definition.py index 309dd200d..80a1b3d72 100644 --- a/kitty/options/definition.py +++ b/kitty/options/definition.py @@ -2774,6 +2774,15 @@ pager, etc. on supported shells. Set to ``disabled`` to turn off shell integration, completely. See :ref:`shell_integration` for details. ''') +opt('allow_cloning', 'ask', choices=('yes', 'no', 'ask'), long_text=''' +Control whether programs running in the terminal can request new +windows to be created. The canonical example is :code:`clone-in-kitty`. +By default, kitty will ask for permission for each clone request. +Allowing cloning unconditionally gives programs running in the terminal +(including over SSH) permission to execute arbitrary code as the user +the terminal is running as on the computer the terminal is running on. +''') + opt('term', 'xterm-kitty', long_text=''' The value of the TERM environment variable to set. Changing this can break many diff --git a/kitty/options/parse.py b/kitty/options/parse.py index a01517196..929bd1c24 100644 --- a/kitty/options/parse.py +++ b/kitty/options/parse.py @@ -50,6 +50,14 @@ class Parser: def adjust_line_height(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: ans['adjust_line_height'] = adjust_line_height(val) + def allow_cloning(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + val = val.lower() + if val not in self.choices_for_allow_cloning: + raise ValueError(f"The value {val} is not a valid choice for allow_cloning") + ans["allow_cloning"] = val + + choices_for_allow_cloning = frozenset(('yes', 'no', 'ask')) + def allow_hyperlinks(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: ans['allow_hyperlinks'] = allow_hyperlinks(val) diff --git a/kitty/options/types.py b/kitty/options/types.py index 476e779ce..7ffed73ad 100644 --- a/kitty/options/types.py +++ b/kitty/options/types.py @@ -13,6 +13,7 @@ from kitty.types import FloatEdges, SingleKey import kitty.types if typing.TYPE_CHECKING: + choices_for_allow_cloning = typing.Literal['yes', 'no', 'ask'] choices_for_background_image_layout = typing.Literal['mirror-tiled', 'scaled', 'tiled', 'clamped'] choices_for_default_pointer_shape = typing.Literal['arrow', 'beam', 'hand'] choices_for_linux_display_server = typing.Literal['auto', 'wayland', 'x11'] @@ -27,6 +28,7 @@ if typing.TYPE_CHECKING: choices_for_tab_switch_strategy = typing.Literal['last', 'left', 'previous', 'right'] choices_for_window_logo_position = typing.Literal['top-left', 'top', 'top-right', 'left', 'center', 'right', 'bottom-left', 'bottom', 'bottom-right'] else: + choices_for_allow_cloning = str choices_for_background_image_layout = str choices_for_default_pointer_shape = str choices_for_linux_display_server = str @@ -51,6 +53,7 @@ option_names = ( # {{{ 'adjust_baseline', 'adjust_column_width', 'adjust_line_height', + 'allow_cloning', 'allow_hyperlinks', 'allow_remote_control', 'background', @@ -461,6 +464,7 @@ class Options: adjust_baseline: typing.Union[int, float] = 0 adjust_column_width: typing.Union[int, float] = 0 adjust_line_height: typing.Union[int, float] = 0 + allow_cloning: choices_for_allow_cloning = 'ask' allow_hyperlinks: int = 1 allow_remote_control: str = 'n' background: Color = Color(0, 0, 0) diff --git a/kitty/window.py b/kitty/window.py index d49fa7fb5..77dee1b2e 100644 --- a/kitty/window.py +++ b/kitty/window.py @@ -1015,8 +1015,11 @@ class Window: if not msg: if self.current_clone_data: cdata, self.current_clone_data = self.current_clone_data, '' - from .launch import clone_and_launch - clone_and_launch(cdata, self) + get_boss().confirm(_( + 'A program running in this window wants to clone it into another window.' + ' Allow it do so, once?'), + partial(self.handle_remote_clone_confirmation, cdata), window=self, + ) self.current_clone_data = '' return num, rest = msg.split(':', 1) @@ -1024,6 +1027,11 @@ class Window: self.current_clone_data = '' self.current_clone_data += rest + def handle_remote_clone_confirmation(self, cdata: str, confirmed: bool) -> None: + if confirmed: + from .launch import clone_and_launch + clone_and_launch(cdata, self) + def handle_remote_askpass(self, msg: str) -> None: from .shm import SharedMemory with SharedMemory(name=msg, readonly=True) as shm: