fix(runtime): request minor line for SQLite runtime repair + tests

Follow-up on the #70186 salvage. The cherry-picked repair pinned the
candidate to the exact current CPython patch (e.g. 3.11.14). Verified
live with uv 0.11.19: every published python-build-standalone artifact
for 3.11.14 links vulnerable SQLite 3.50.4 — even with --reinstall — so
the exact-patch pin made the repair permanently impossible on the
installs that need it most (repair_vulnerable_runtime returned
'failed: could not provision a fixed private Python runtime').

Request the minor line (3.11) instead — the same resolution a fresh
'uv python install' would make, still inside requires-python — and
tighten the drift gate to 'same minor, no downgrade'. E2E-verified
end-to-end on a real vulnerable venv: repair_vulnerable_runtime()
provisioned 3.11.15, built + smoke-tested the sibling venv, cut over,
and reported SQLite 3.50.4 → 3.53.1 with the old venv parked for
rollback.
This commit is contained in:
teknium1 2026-07-24 12:44:30 -07:00 committed by Teknium
parent 05a799e41c
commit be633c1c33
2 changed files with 95 additions and 5 deletions

View file

@ -351,8 +351,17 @@ def _make_world_traversable(path: Path) -> None:
def _runtime_request(info: SQLiteRuntimeInfo) -> str:
"""Pin the candidate to the current exact CPython patch."""
return ".".join(str(part) for part in info.python_version)
"""Pin the candidate to the current CPython minor line (e.g. ``3.11``).
Requesting the exact patch can never repair some installs: for a given
patch, python-build-standalone may have no artifact with fixed SQLite at
all (e.g. every published 3.11.14 build links SQLite 3.50.4; the fix
only exists from 3.11.15). A newer patch on the same minor is what
``uv python install`` would resolve for a fresh install, stays inside
``requires-python``, and the locked ``uv sync`` + import smoke tests gate
compatibility before any cutover.
"""
return ".".join(str(part) for part in info.python_version[:2])
def _install_safe_python_generation(
@ -438,10 +447,12 @@ def _install_safe_python_generation(
logger.warning("could not probe candidate Python runtime: %s", python)
_remove_tree(generation, boundary=python_root)
return None
if candidate.python_version != current.python_version:
if candidate.python_version[:2] != current.python_version[:2] or (
candidate.python_version < current.python_version
):
logger.warning(
"candidate Python patch drifted from %s to %s",
current.python_version,
"candidate Python drifted off the %s minor line or downgraded: %s",
".".join(str(p) for p in current.python_version[:2]),
candidate.python_version,
)
_remove_tree(generation, boundary=python_root)

View file

@ -697,3 +697,82 @@ class TestInstallUvInternals:
mock_windows.assert_called_once()
call_env = mock_windows.call_args[0][0]
assert call_env["UV_INSTALL_DIR"] == str(tmp_path / "bin")
class TestRuntimeRequestMinorLine:
"""The repair must request the CPython minor line, not the exact patch.
Real-world constraint (verified live, July 2026): every published
python-build-standalone artifact for 3.11.14 links vulnerable SQLite
3.50.4 even with --reinstall. The fixed SQLite (3.53.1) only exists
from 3.11.15. An exact-patch pin makes the repair permanently
impossible on such installs.
"""
def test_requests_minor_line(self):
from hermes_cli.managed_uv import _runtime_request
info = _runtime_info(Path("/venv/bin/python"), (3, 50, 4))
assert _runtime_request(info) == "3.11"
@staticmethod
def _run_generation(tmp_path, monkeypatch, current_version, candidate_version):
"""Drive _install_safe_python_generation with fakes; return result."""
import hermes_cli.managed_uv as managed_uv
from hermes_cli.sqlite_runtime import SQLiteRuntimeInfo
state = {}
def fake_run(cmd, **kwargs):
if "install" in cmd:
state["generation"] = Path(kwargs["env"]["UV_PYTHON_INSTALL_DIR"])
return SimpleNamespace(returncode=0, stdout="", stderr="")
# uv python find → a path inside the generation dir
python = state["generation"] / "cpython" / "bin" / "python3"
python.parent.mkdir(parents=True, exist_ok=True)
python.touch()
return SimpleNamespace(returncode=0, stdout=str(python), stderr="")
def fake_probe(python, **kwargs):
return SQLiteRuntimeInfo(
executable=Path(python),
base_prefix=Path(python).parent.parent,
python_version=candidate_version,
sqlite_version=(3, 53, 1),
sqlite_version_string="3.53.1",
sqlite_source_id="fixed",
)
current = SQLiteRuntimeInfo(
executable=Path("/venv/bin/python"),
base_prefix=Path("/venv"),
python_version=current_version,
sqlite_version=(3, 50, 4),
sqlite_version_string="3.50.4",
sqlite_source_id="old",
)
monkeypatch.setattr(managed_uv.subprocess, "run", fake_run)
monkeypatch.setattr(managed_uv, "probe_sqlite_runtime", fake_probe)
return managed_uv._install_safe_python_generation(
"uv", project_root=tmp_path, current=current
)
def test_accepts_newer_patch_same_minor(self, tmp_path, monkeypatch):
result = self._run_generation(
tmp_path, monkeypatch, (3, 11, 14), (3, 11, 15)
)
assert result is not None
_, _, candidate = result
assert candidate.python_version == (3, 11, 15)
def test_rejects_minor_drift(self, tmp_path, monkeypatch):
assert (
self._run_generation(tmp_path, monkeypatch, (3, 11, 14), (3, 12, 1))
is None
)
def test_rejects_patch_downgrade(self, tmp_path, monkeypatch):
assert (
self._run_generation(tmp_path, monkeypatch, (3, 11, 14), (3, 11, 13))
is None
)