Make cloning safer

Now env vars are set after shell rc files are sourced. And the clone
request cannot specify the cmdline to execute.
This commit is contained in:
Kovid Goyal
2022-04-17 07:49:58 +05:30
parent 38e1d32065
commit 291f9e9a5e
6 changed files with 69 additions and 98 deletions

View File

@@ -245,6 +245,7 @@ class Child:
env['TERMINFO'] = tdir
env['KITTY_INSTALLATION_DIR'] = kitty_base_dir
opts = fast_data_types.get_options()
self.unmodified_argv = list(self.argv)
if 'disabled' not in opts.shell_integration:
from .shell_integration import modify_shell_environ
modify_shell_environ(opts, env, self.argv)

View File

@@ -545,10 +545,10 @@ def parse_null_env(text: str) -> Dict[str, str]:
class CloneCmd:
def __init__(self, msg: str) -> None:
self.cmdline: List[str] = []
self.args: List[str] = []
self.env: Optional[Dict[str, str]] = None
self.cwd = ''
self.shell = ''
self.envfmt = 'default'
self.pid = -1
self.parse_message(msg)
@@ -556,43 +556,55 @@ class CloneCmd:
def parse_message(self, msg: str) -> None:
import base64
import json
simple = 'pid', 'envfmt', 'shell'
for x in msg.split(','):
k, v = x.split('=', 1)
if k == 'pid':
self.pid = int(v)
continue
if k == 'envfmt':
self.envfmt = v
if k in simple:
setattr(self, k, int(v) if k == 'pid' else v)
continue
v = base64.standard_b64decode(v).decode('utf-8', 'replace')
if k == 'a':
self.args.append(v)
elif k == 'env':
env = parse_bash_env(v) if self.envfmt == 'bash' else parse_null_env(v)
self.env = {k: v for k, v in env.items() if k not in (
'HOME', 'LOGNAME', 'USER',
self.env = {k: v for k, v in env.items() if k not in {
'HOME', 'LOGNAME', 'USER', 'PWD',
# some people export these. We want the shell rc files to recreate them
'PS0', 'PS1', 'PS2', 'PS3', 'PS4', 'RPS1', 'PROMPT_COMMAND', 'SHLVL',
# conda state env vars
'CONDA_SHLVL', 'CONDA_PREFIX', 'CONDA_EXE', 'CONDA_PROMPT_MODIFIER', 'CONDA_EXE', 'CONDA_PYTHON_EXE',
# skip SSH environment variables
'SSH_CLIENT', 'SSH_CONNECTION', 'SSH_ORIGINAL_COMMAND', 'SSH_TTY', 'SSH2_TTY',
)}
}}
elif k == 'cwd':
self.cwd = v
elif k == 'argv':
self.cmdline = json.loads(v)
def clone_and_launch(msg: str, window: Window) -> None:
from .child import cmdline_of_process
from .shell_integration import serialize_env
c = CloneCmd(msg)
if c.cwd and not c.opts.cwd:
c.opts.cwd = c.cwd
c.opts.copy_colors = True
c.opts.copy_env = False
c.opts.env = list(c.opts.env) + ['KITTY_IS_CLONE_LAUNCH=1']
cmdline = c.cmdline
if c.pid > -1:
serialized_env = serialize_env(c.shell, c.env or {})
ssh_kitten_cmdline = window.ssh_kitten_cmdline()
if ssh_kitten_cmdline:
from kittens.ssh.main import set_cwd_in_cmdline, set_env_in_cmdline, patch_cmdline
cmdline = ssh_kitten_cmdline
if c.opts.cwd:
set_cwd_in_cmdline(c.opts.cwd, cmdline)
c.opts.cwd = None
if c.env:
set_env_in_cmdline({'KITTY_IS_CLONE_LAUNCH': serialized_env}, cmdline)
c.env = None
if c.opts.env:
for entry in reversed(c.opts.env):
patch_cmdline('env', entry, cmdline)
c.opts.env = []
else:
c.opts.env = list(c.opts.env) + ['KITTY_IS_CLONE_LAUNCH=' + serialized_env]
try:
cmdline = cmdline_of_process(c.pid)
except Exception:
@@ -601,18 +613,6 @@ def clone_and_launch(msg: str, window: Window) -> None:
cmdline = list(window.child.argv)
if cmdline and cmdline[0] == window.child.final_argv0:
cmdline[0] = window.child.final_exe
ssh_kitten_cmdline = window.ssh_kitten_cmdline()
if ssh_kitten_cmdline:
from kittens.ssh.main import set_cwd_in_cmdline, set_env_in_cmdline, patch_cmdline
cmdline[:] = ssh_kitten_cmdline
if c.opts.cwd:
set_cwd_in_cmdline(c.opts.cwd, cmdline)
c.opts.cwd = None
if c.env:
set_env_in_cmdline(c.env, cmdline)
c.env = None
if c.opts.env:
for entry in reversed(c.opts.env):
patch_cmdline('env', entry, cmdline)
c.opts.env = []
if cmdline and cmdline == [window.child.final_exe] + window.child.argv[1:]:
cmdline = window.child.unmodified_argv
launch(get_boss(), c.opts, cmdline, base_env=c.env, active=window)

View File

@@ -2,8 +2,6 @@
# License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>
import base64
import json
import os
import subprocess
from contextlib import suppress
@@ -135,7 +133,6 @@ def setup_bash_env(env: Dict[str, str], argv: List[str]) -> None:
return
env['ENV'] = os.path.join(shell_integration_dir, 'bash', 'kitty.bash')
env['KITTY_BASH_INJECT'] = ' '.join(inject)
env['KITTY_BASH_ORIGINAL_ARGV'] = base64.standard_b64encode(json.dumps(argv).encode('utf-8')).decode('ascii')
if posix_env:
env['KITTY_BASH_POSIX_ENV'] = posix_env
if rcfile:
@@ -155,7 +152,8 @@ def as_str_literal(x: str) -> str:
def as_fish_str_literal(x: str) -> str:
return x.replace('\\', '\\\\').replace("'", "\\'")
x = x.replace('\\', '\\\\').replace("'", "\\'")
return f"'{x}'"
def posix_serialize_env(env: Dict[str, str], prefix: str = 'builtin export', sep: str = '=') -> str:
@@ -199,6 +197,8 @@ def shell_integration_allows_rc_modification(opts: Options) -> bool:
def serialize_env(path: str, env: Dict[str, str]) -> str:
if not env:
return ''
name = get_supported_shell_name(path)
if not name:
raise ValueError(f'{path} is not a supported shell')
@@ -231,4 +231,3 @@ def modify_shell_environ(opts: Options, env: Dict[str, str], argv: List[str]) ->
import traceback
traceback.print_exc()
log_error(f'Failed to setup shell integration for: {shell}')
return