feat: fix _collect_read_roots to include virtual environment root for pyvenv.cfg access

This commit is contained in:
Ivan 2026-07-18 05:35:23 -05:00
parent 63f836b03d
commit 345a1d1f8a
No known key found for this signature in database
3 changed files with 47 additions and 2 deletions

Binary file not shown.

View file

@ -353,8 +353,20 @@ def _collect_read_roots() -> list[str]:
exe_dir = _existing_dir(os.path.dirname(candidate))
if exe_dir:
roots.add(exe_dir)
# Prefer the install prefix (…/cpython-…/) so bin + lib are covered.
prefix = _existing_dir(getattr(sys, "base_prefix", None) or sys.prefix)
# Venv layouts put pyvenv.cfg next to bin/, not under it. Allowing only
# …/bin leaves child interpreters unable to read pyvenv.cfg (EACCES).
venv_root = os.path.dirname(exe_dir) if exe_dir else None
if venv_root and os.path.isfile(os.path.join(venv_root, "pyvenv.cfg")):
existing_venv = _existing_dir(venv_root)
if existing_venv:
roots.add(existing_venv)
# Prefer the install prefix (…/cpython-…/) so bin + lib are covered.
for prefix_candidate in (
getattr(sys, "base_prefix", None),
sys.prefix,
os.environ.get("VIRTUAL_ENV"),
):
prefix = _existing_dir(prefix_candidate)
if prefix:
roots.add(prefix)
return sorted(roots)

View file

@ -107,6 +107,39 @@ def test_collect_read_roots_includes_interpreter_prefix():
assert any(
prefix == root or prefix.startswith(root.rstrip("/") + "/") for root in roots
), f"prefix {prefix!r} not covered by {roots!r}"
# Active venv root (sys.prefix) must be allowed even when base_prefix differs,
# otherwise child Python cannot read pyvenv.cfg (Docker /opt/venv + rnsh).
venv_prefix = os.path.realpath(sys.prefix)
assert any(
venv_prefix == root or venv_prefix.startswith(root.rstrip("/") + "/")
for root in roots
), f"sys.prefix {venv_prefix!r} not covered by {roots!r}"
def test_collect_read_roots_includes_venv_root_for_pyvenv_cfg(tmp_path, monkeypatch):
"""Landlock must allow the venv root, not only …/bin (pyvenv.cfg sibling)."""
venv = tmp_path / "opt" / "venv"
bindir = venv / "bin"
bindir.mkdir(parents=True)
(venv / "pyvenv.cfg").write_text("home = /usr\n", encoding="utf-8")
fake_python = bindir / "python"
fake_python.write_text("#!/bin/sh\n", encoding="utf-8")
class _FakeSys:
platform = sys.platform
executable = str(fake_python)
prefix = str(venv)
base_prefix = "/usr"
path = list(sys.path)
monkeypatch.setattr(ll, "sys", _FakeSys)
monkeypatch.setattr(ll.site, "getsitepackages", lambda: [])
monkeypatch.setattr(ll.site, "getusersitepackages", lambda: "")
monkeypatch.setenv("VIRTUAL_ENV", str(venv))
roots = {os.path.realpath(r) for r in ll._collect_read_roots()}
assert os.path.realpath(str(venv)) in roots
assert os.path.realpath(str(bindir)) in roots
def test_handled_access_fs_for_abi_gates_new_rights():