Ignore environment_path in loaded project files (#2110)

A .jedi/project.json can be part of a checked out repository, so honouring its environment_path let an untrusted file point Jedi at a binary to execute. Rather than trying to gate that binary (the ownership heuristic in _is_unix_safe_simple is meaningless on Windows, where st_uid is always 0), drop environment_path from loaded project files entirely, the same way load_unsafe_extensions is already ignored. It stays available when a Project is constructed directly by the user.
This commit is contained in:
Dhanu-dynamic authored and GitHub committed 2026-09-29 13:58:51 +00:00
1 parent ed47f89162
commit f37a9f9feb
2 files changed
+13 -25

No files matched your search

+9 -11
View File
@@ -63,9 +63,6 @@ class Project:
Additionally there are functions to search a whole project.
"""
_environment = None
# Set for projects loaded from a ``.jedi/project.json``. That file can be
# part of a checked out repository, so it is not trusted to run binaries.
_loaded_from_file = False
@staticmethod
def _get_config_folder_path(base_path):
@@ -90,12 +87,14 @@ class Project:
if version == 1:
# A code base must not be able to enable the loading of its own
# extensions, see the docs about security.
# extensions or point Jedi at a binary to execute, see the docs
# about security. The project file can be part of a checked out
# repository, so these settings are ignored for loaded projects.
if data.pop('load_unsafe_extensions', False):
debug.warning('load_unsafe_extensions is ignored for loaded projects')
project = cls(**data)
project._loaded_from_file = True
return project
if data.pop('environment_path', None) is not None:
debug.warning('environment_path is ignored for loaded projects')
return cls(**data)
else:
raise WrongVersion(
"The Jedi version of this project seems newer than what we can handle."
@@ -107,7 +106,6 @@ class Project:
"""
data = dict(self.__dict__)
data.pop('_environment', None)
data.pop('_loaded_from_file', None)
data.pop('_django', None) # TODO make django setting public?
data = {k.lstrip('_'): v for k, v in data.items()}
data['path'] = str(data['path'])
@@ -252,10 +250,10 @@ class Project:
def get_environment(self):
if self._environment is None:
if self._environment_path is not None:
# An environment path from a loaded project file is checked
# like a scanned virtualenv, see find_virtualenvs.
# environment_path can only be set by the user directly, it is
# never loaded from a project file, so it is trusted.
self._environment = create_environment(
self._environment_path, safe=self._loaded_from_file)
self._environment_path, safe=False)
else:
self._environment = get_cached_default_environment()
return self._environment