Prompt for permission when reading from clipboard by default

Fixes #4022
This commit is contained in:
Kovid Goyal 2021-09-13 12:32:40 +05:30
parent 38a5e38f88
commit b1322fbe04
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
5 changed files with 50 additions and 15 deletions

View File

@ -18,6 +18,10 @@ To update |kitty|, :doc:`follow the instructions <binary>`.
- Allow the user to supply a custom Python function to draw tab bar. See
:opt:`tab_bar_style`
- When programs ask to read from the clipboard prompt the user to allow
the request by default instead of denying by default. See
:opt:`clipboard_control` for details (:iss:`4022`)
- Fix a regression that caused :option:`kitty --title` to not work when
opening new OS windows using :option:`kitty --single-instance` (:iss:`3893`)

View File

@ -57,9 +57,9 @@ OPTIONS = r'''
--get-clipboard
default=False
type=bool-set
Output the current contents of the clipboard to stdout. Note that this
will not work if you have not enabled the option to allow reading the clipboard
in kitty.conf
Output the current contents of the clipboard to STDOUT. Note that by default
kitty will prompt you asking to allow access to the clipboard. Can be controlled
by :opt:`clipboard_control`.
--use-primary
@ -80,8 +80,8 @@ Read or write to the system clipboard.
To set the clipboard text, pipe in the new text on stdin. Use the
:option:`--get-clipboard` option to output the current clipboard contents to
:file:`stdout`. Note that you must enable reading of clipboard in
:file:`kitty.conf` first.
:file:`stdout`. Note that reading the clipboard will cause a permission
popup, see :opt:`clipboard_control` for details.
'''
usage = ''

View File

@ -2544,16 +2544,17 @@ the config is not supported.
'''
)
opt('clipboard_control', 'write-clipboard write-primary',
opt('clipboard_control', 'write-clipboard write-primary read-clipboard-ask read-primary-ask',
option_type='clipboard_control',
long_text='''
Allow programs running in kitty to read and write from the clipboard. You can
control exactly which actions are allowed. The set of possible actions is:
write-clipboard read-clipboard write-primary read-primary. The default is to
allow writing to the clipboard and primary selection. Note that enabling the
read functionality is a security risk as it means that any program, even one
running on a remote server via SSH can read your clipboard. See also
:opt:`clipboard_max_size`.
:code:`write-clipboard read-clipboard write-primary read-primary read-clipboard-ask read-primary-ask`.
The default is to allow writing to the clipboard and primary selection and to
ask for permission when a program tries to read from the clipboard. Note that
disabling the read confirmation is a security risk as it means that any
program, even one running on a remote server via SSH can read your clipboard.
See also :opt:`clipboard_max_size`.
'''
)

View File

@ -463,7 +463,7 @@ class Options:
clear_all_mouse_actions: bool = False
clear_all_shortcuts: bool = False
click_interval: float = -1.0
clipboard_control: typing.Tuple[str, ...] = ('write-clipboard', 'write-primary')
clipboard_control: typing.Tuple[str, ...] = ('write-clipboard', 'write-primary', 'read-clipboard-ask', 'read-primary-ask')
clipboard_max_size: float = 64.0
close_on_child_death: bool = False
command_on_bell: typing.List[str] = ['none']

View File

@ -328,6 +328,7 @@ class Window:
):
self.watchers = watchers or Watchers()
self.current_mouse_event_button = 0
self.current_clipboard_read_ask: Optional[bool] = None
self.prev_osc99_cmd = NotificationCommand()
self.action_on_close: Optional[Callable] = None
self.action_on_removal: Optional[Callable] = None
@ -842,15 +843,17 @@ class Window:
if text == '?':
response = None
if 's' in where or 'c' in where:
if 'read-clipboard-ask' in cc:
return self.ask_to_read_clipboard(False)
response = get_clipboard_string() if 'read-clipboard' in cc else ''
loc = 'c'
elif 'p' in where:
if 'read-primary-ask' in cc:
return self.ask_to_read_clipboard(True)
response = get_primary_selection() if 'read-primary' in cc else ''
loc = 'p'
response = response or ''
from base64 import standard_b64encode
self.screen.send_escape_code_to_child(OSC, '52;{};{}'.format(
loc, standard_b64encode(response.encode('utf-8')).decode('ascii')))
self.send_osc52(loc, response or '')
else:
from base64 import standard_b64decode
@ -867,6 +870,33 @@ class Window:
set_primary_selection(text)
self.clipboard_pending = None
def send_osc52(self, loc: str, response: str) -> None:
from base64 import standard_b64encode
self.screen.send_escape_code_to_child(OSC, '52;{};{}'.format(
loc, standard_b64encode(response.encode('utf-8')).decode('ascii')))
def ask_to_read_clipboard(self, primary: bool = False) -> None:
if self.current_clipboard_read_ask is not None:
self.current_clipboard_read_ask = primary
return
self.current_clipboard_read_ask = primary
get_boss()._run_kitten('ask', ['--type=yesno', '--message', _(
'A program running in this window wants to read from the system clipboard.'
' Allow it do so, once?')],
window=self,
custom_callback=self.handle_clipboard_confirmation
)
def handle_clipboard_confirmation(self, data: Dict[str, Any], *a: Any) -> None:
try:
loc = 'p' if self.current_clipboard_read_ask else 'c'
response = ''
if data['response'] == 'y':
response = get_primary_selection() if self.current_clipboard_read_ask else get_clipboard_string()
self.send_osc52(loc, response)
finally:
self.current_clipboard_read_ask = None
def manipulate_title_stack(self, pop: bool, title: str, icon: Any) -> None:
if title:
if pop: