[core] fix PYTHONPATH leak (#18360)

This commit is contained in:
Karl Beecken
2026-08-14 17:36:54 +12:00
committed by Jesse Hills
parent b794b7b1d1
commit c8de632764
4 changed files with 37 additions and 0 deletions
+2
View File
@@ -109,6 +109,8 @@ def _get_idf_env(version: str | None = None) -> dict[str, str]:
env_cache = _cache().env
if version not in env_cache:
env_cache[version] = os.environ.copy()
# Do not leak PYTHONPATH into child env
env_cache[version].pop("PYTHONPATH", None)
# Use provided IDF framework if available
if "IDF_PATH" not in os.environ:
+2
View File
@@ -155,6 +155,8 @@ def run_command(
_LOGGER.debug("%s - running ...", cmd_str)
run_env = os.environ.copy()
# Do not leak PYTHONPATH
run_env.pop("PYTHONPATH", None)
if env:
run_env.update(env)
+15
View File
@@ -265,6 +265,21 @@ def test_get_idf_env_sets_git_ceiling_directories(setup_core: Path) -> None:
assert str(CORE.config_dir) in env["GIT_CEILING_DIRECTORIES"].split(os.pathsep)
def test_get_idf_env_pops_inherited_pythonpath(setup_core: Path) -> None:
"""A PYTHONPATH from the parent environment must not reach idf.py.
It would override the IDF venv's isolation, shadowing its pinned
packages and failing idf.py's dependency check.
"""
toolchain._cache().env.clear()
with patch.dict(
os.environ,
{"IDF_PATH": str(setup_core), "PYTHONPATH": "/outside/site-packages"},
):
env = toolchain._get_idf_env(version="5.5.4")
assert "PYTHONPATH" not in env
def test_get_cmake_output_without_build_dir(setup_core: Path) -> None:
"""A build dir that was never created raises EsphomeError.
@@ -188,6 +188,24 @@ def test_run_command_passes_env(mock_subprocess_run: Mock) -> None:
assert mock_subprocess_run.call_args[1]["env"]["MY_VAR"] == "42"
def test_run_command_pops_inherited_pythonpath(mock_subprocess_run: Mock) -> None:
"""A PYTHONPATH from the parent environment must not leak into subprocesses."""
mock_subprocess_run.return_value = Mock(returncode=0, stdout="", stderr="")
with patch.dict(os.environ, {"PYTHONPATH": "/outside/site-packages"}):
run_command(["cmd"])
assert "PYTHONPATH" not in mock_subprocess_run.call_args[1]["env"]
def test_run_command_env_pythonpath_preferred_over_pop(
mock_subprocess_run: Mock,
) -> None:
"""A PYTHONPATH set explicitly via ``env`` is passed through."""
mock_subprocess_run.return_value = Mock(returncode=0, stdout="", stderr="")
with patch.dict(os.environ, {"PYTHONPATH": "/outside/site-packages"}):
run_command(["cmd"], env={"PYTHONPATH": "/idf/tools"})
assert mock_subprocess_run.call_args[1]["env"]["PYTHONPATH"] == "/idf/tools"
def test_run_command_passes_cwd(mock_subprocess_run: Mock, tmp_path: Path) -> None:
mock_subprocess_run.return_value = Mock(returncode=0, stdout="", stderr="")
run_command(["cmd"], cwd=str(tmp_path))