From 93a28c42306babc6ced6afd8f0bd997a1a31cde9 Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Thu, 12 Apr 2018 08:50:31 +0200 Subject: [PATCH] Make sure Windows environments are safe --- jedi/api/environment.py | 51 +++++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/jedi/api/environment.py b/jedi/api/environment.py index de6fb4c6..78f7d236 100644 --- a/jedi/api/environment.py +++ b/jedi/api/environment.py @@ -220,12 +220,13 @@ def get_python_environment(python): raise InvalidPythonEnvironment("Cannot find executable %s." % python) -def create_environment(path): +def create_environment(path, safe=False): """ Make it possible to create an environment by hand. + + May raise InvalidPythonEnvironment. """ - # Since this path is provided by the user, just use unsafe execution. - return Environment(path, _get_executable_path(path, safe=False)) + return Environment(path, _get_executable_path(path, safe=safe)) def from_executable(executable): @@ -280,27 +281,37 @@ def _get_executables_from_windows_registry(version): def _is_safe(executable_path): + # Resolve sym links. A venv typically is a symlink to a known Python + # binary. Only virtualenvs copy symlinks around. real_path = os.path.realpath(executable_path) - if _is_admin(): - # In case we are root or are part of Windows, just be conservative and - # only execute known paths. - # TODO add a proper Windows path. - return real_path.startswith('/usr/bin') + if os.name == 'nt': + # Just check the list of known Python versions. If it's not in there, + # it's likely an attacker or some Python that was not properly + # installed in the system. + for environment in find_python_environments(): + if environment._executable == executable_path: + return True + return False + else: + if _is_unix_admin(): + # In case we are root, just be conservative and + # only execute known paths. + return any(real_path.startswith(p) for p in '/usr/bin') - uid = os.stat(real_path).st_uid - # The interpreter needs to be owned by root. This means that it wasn't - # written by a user and therefore attacking Jedi is not as simple. - # The attack could look like the following: - # 1. A user clones a repository. - # 2. The repository has an inocent looking folder called foobar. jedi - # searches for the folder and executes foobar/bin/python --version if - # there's also a foobar/bin/activate. - # 3. The bin/python is obviously not a python script but a bash script or - # whatever the attacker wants. - return uid == 0 + uid = os.stat(real_path).st_uid + # The interpreter needs to be owned by root. This means that it wasn't + # written by a user and therefore attacking Jedi is not as simple. + # The attack could look like the following: + # 1. A user clones a repository. + # 2. The repository has an inocent looking folder called foobar. jedi + # searches for the folder and executes foobar/bin/python --version if + # there's also a foobar/bin/activate. + # 3. The bin/python is obviously not a python script but a bash script or + # whatever the attacker wants. + return uid == 0 -def _is_admin(): +def _is_unix_admin(): try: return os.getuid() == 0 except AttributeError: