Python virtual environment variables leaking into subprocesses
NousResearch/hermes-agent · cluster 23473 · apps/desktop
Data as of
Nothing open on GitHub. Fix merged in #53792. Check it covers your case.
The short answer
- Known?
- Yes. 14 related items grouped here, 3 issues already closed.
- Fix event
- A fix was merged to main in PR #53792. Check it covers your case. No release data is tracked.
- Tracker
- The maintainers' tracker marks this
partially resolved, deciding PRs #53792, #133868. That is separate from the fix event. - Still open
- Nothing open on GitHub.
Multiple issues and fixes address VIRTUAL_ENV, CONDA_PREFIX, and PYTHONPATH environment variables leaking from the application into child processes, contaminating unrelated Python environments and tools. A few unrelated fixes (Windows signing, cron fallback) are also present in the group.
Open issues0
Open PRs0
Fixes on record
-
#53792 fix(terminal): strip VIRTUAL_ENV/CONDA_PREFIX from terminal subprocess env
files:
scripts/release.py,tests/tools/test_local_env_blocklist.py,tools/environments/local.pyShow the diff
-
scripts/release.py@@ -45,6 +45,7 @@ # Auto-extracted from noreply emails + manual overrides AUTHOR_MAP = { + "[redacted:email]": "dalenguyen", # PR #53678 salvage (strip VIRTUAL_ENV/CONDA_PREFIX from terminal subprocess env; #23473) "[redacted:email]": "Blaryxoff", # PR #32602 salvage (deep-merge PUT /api/config to preserve unrelated sections; #13396) "[redacted:email]": "DiamondEyesFox", # PR #53351 salvage (rebaseline in-place compression flushes to prevent duplicate compacted rows; #9096) "[redacted:email]": "rlaope", # PR #35075 salvage (align cron invisible-unicode set with install-time scanner; #35075) -
tests/tools/test_local_env_blocklist.py@@ -239,6 +239,54 @@ def test_force_prefix_overrides_os_environ_block(self): assert result_env["OPENAI_BASE_URL"] == "http://intended/v1" +class TestActiveVenvMarkerStripping: + """Active-virtualenv markers must not leak into terminal subprocesses (#23473). + + The gateway runs inside its own venv, so its process environment carries + VIRTUAL_ENV (and possibly CONDA_PREFIX). If those leak into commands the + agent runs against ANOTHER Python project, ``uv``/``poetry`` treat the + inherited value as the active environment and build that project's deps + into the Hermes venv path instead of the project's own ``.venv`` — + silently clobbering the Hermes environment (and, when the other project + pins a different Python, breaking the gateway outright). The Hermes venv + stays reachable via PATH, so stripping the markers is safe. + """ + + def test_virtualenv_marker_stripped_end_to_end(self): + result_env = _run_with_env(extra_os_env={ + "VIRTUAL_ENV": "/home/user/.hermes/hermes-agent/venv", + }) + assert "VIRTUAL_ENV" not in result_env + + def test_conda_prefix_marker_stripped_end_to_end(self): + result_env = _run_with_env(extra_os_env={ + "CONDA_PREFIX": "/opt/conda/envs/hermes", + }) + assert "CONDA_PREFIX" not in result_env + + def test_make_run_env_strips_markers(self): + from tools.environments.local import _make_run_env + poison = {"VIRTUAL_ENV": "/venv", "CONDA_PREFIX": "/conda", "PATH": "/usr/bin"} + with patch.dict(os.environ, poison, clear=True): + result = _make_run_env({}) + assert "VIRTUAL_ENV" not in result + assert "CONDA_PREFIX" not in result + + def test_sanitize_subprocess_env_strips_markers(self): + from tools.environments.local import _sanitize_subprocess_env + base = {"VIRTUAL_ENV": "/venv", "CONDA_PREFIX": "/conda", "HOME": "/home/user"} + # Even an explicitly-passed extra marker is stripped. + result = _sanitize_subprocess_env(base, {"VIRTUAL_ENV": "/also/venv"}) + assert "VIRTUAL_ENV" not in result + assert "CONDA_PREFIX" not in result + assert result.get("HOME") == "/home/user" + + def test_markers_constant_contents(self): + from tools.environments.local import _ACTIVE_VENV_MARKER_VARS + assert "VIRTUAL_ENV" in _ACTIVE_VENV_MARKER_VARS + assert "CONDA_PREFIX" in _ACTIVE_VENV_MARKER_VARS + + class TestBlocklistCoverage: """Sanity checks that the blocklist covers all known providers.""" -
tools/environments/local.py@@ -192,6 +192,18 @@ def _build_provider_env_blocklist() -> frozenset: _HERMES_PROVIDER_ENV_BLOCKLIST = _build_provider_env_blocklist() +# Active-virtualenv markers that must NOT leak into terminal subprocesses. +# The gateway runs inside its own venv, so its process environment carries +# VIRTUAL_ENV (and possibly CONDA_PREFIX). If those leak into commands the +# agent runs against OTHER Python projects, tools like ``uv``/``poetry`` treat +# the inherited value as the active environment and build/sync that other +# project's dependencies into the Hermes venv path instead of the project's own +# ``.venv`` — silently clobbering the Hermes environment (e.g. a project pinned +# to a different Python version overwrites it and breaks the gateway). The +# Hermes venv stays reachable via PATH (its bin dir is first), so stripping +# these markers is safe and only prevents the cross-project clobber (#23473). +_ACTIVE_VENV_MARKER_VARS = ("VIRTUAL_ENV", "CONDA_PREFIX") + def _inject_context_hermes_home(env: dict) -> None: """Bridge the context-local Hermes home override into subprocess env.""" @@ -232,6 +244,9 @@ def _sanitize_subprocess_env(base_env: dict | None, extra_env: dict | None = Non from hermes_constants import apply_subprocess_home_env apply_subprocess_home_env(sanitized) + for _marker in _ACTIVE_VENV_MARKER_VARS: + sanitized.pop(_marker, None) + return sanitized @@ -528,6 +543,9 @@ def _make_run_env(env: dict) -> dict: except Exception: pass + for _marker in _ACTIVE_VENV_MARKER_VARS: + run_env.pop(_marker, None) + return run_env
-
-
#133868 fix(gateway): stop _ensure_windows_gateway_venv_imports leaking PYTHONPATH into global environment
files:
gateway/run.py,tests/gateway/test_windows_venv_import_guard.py
9 PRs closed without merging
From the close comments: done elsewhere 5 · superseded by another PR 2 · duplicate 1 · merged via another change 1
-
#23474 fix(terminal): strip VIRTUAL_ENV from agent subprocess envs · done elsewhere →
#29980 -
#23625 fix(local-env): block virtualenv activation leaks · done elsewhere →
#53792,#23473,#53678 -
#23905 fix: strip VIRTUAL_ENV from subprocess environment to avoid venv contamination (#23473) · duplicate →
#23474,#23904,#23903 -
#24305 fix(env): Block virtual environment markers from leaking into subprocesses · done elsewhere →
#53792,#23473,#53678 -
#39593 fix(gateway): prefer installer-canonical venv/ over .venv in detection fallback · done elsewhere →
#53792,#23473,#53678
Show 4 more
-
#41524 fix(desktop): force ad-hoc signing when no real signing identity is configured · done elsewhere →
#53792,#23473,#53678 -
#45501 fix: cron model.default fallback + VIRTUAL_ENV leak into subprocesses (P1 bugs) · superseded by another PR →
#50011 - #57470 fix(gateway): stop _ensure_windows_gateway_venv_imports leaking PYTHONPATH into global environment · merged via another change
- #57854 fix(desktop): avoid leaking Python path to backend · superseded by another PR
Closed issues · 3
- Closed#23473Gateway leaks VIRTUAL_ENV into subprocesses; agent's `uv sync` in any project bricks Hermes' own venv
- Closed#57467[Bug]: _ensure_windows_gateway_venv_imports leaks PYTHONPATH into global environment, poisoning non-Hermes Python processes
- Closed#65909PYTHONPATH leaks to child processes, breaking pipx-installed CLI tools with C extension dependencies
How this cluster was grouped
-
Link evidence (
partly structural links): some members are connected by structural evidence (shared changed files, or a closing/superseded reference); the rest are joined by an automated relevance judgement over their text. - Members outside that structural core may belong to a different underlying problem.
-
Pairwise link density
0.13: the share of item pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36.1.00means every pair is linked.
Pick up a Lab task
Check whether a fix covers the open issues here, with your own agent. Bring back evidence for maintainers.
Open the Lab