Repository navigation
In a --system-site-packages venv, pip keeps the base interpreter's unpatched copy after a hosted rewrite, no stale-install warning fires, and vex attests it as patched #409
Description
Activity
- addedbugSomething isn't workingSomething isn't workingbughuntFound by a scheduled package-manager bug-hunt agentFound by a scheduled package-manager bug-hunt agentpm:pippip / requirements.txtpip / requirements.txt
on Oct 1, 2026 mikolalysenko commented
on Oct 1, 2026 CollaboratorAuthorMore actions[agent] Triaged:
priority:p1(pip). It's a false VEX attestation, so it's a correctness/security bug. Not a duplicate, and no open or merged PR covers it. The cause is in the Python crawler:find_local_venv_site_packagesnever readsinclude-system-site-packagesfrompyvenv.cfg, andget_site_packages_pathsreturns early once it finds a venv. That's a different problem from the Pipenv (#334/#384, PR #388) and Poetry (#327/#329, PR #330) venv-selection clusters. Those choose the wrong venv. This one picks the right venv but never looks at the base interpreter's site dirs behind it.
Generated by Claude Code
mikolalysenko commented
on Oct 2, 2026 CollaboratorAuthorMore actions[agent] From the scheduled Poetry bug-hunt routine (ledger #311): Poetry 2.x users hit the agent-mode side of this through a Poetry setting,
virtualenvs.options.system-site-packages = true. Tested on main61cfb9b, Linux, Debian/usr/lib/python3/dist-packages/six.py1.16.0. Reproduced 2/2.printf '[virtualenvs]\nin-project = true\n[virtualenvs.options]\nsystem-site-packages = true\n' > poetry.toml # pyproject: package-mode = false, six = "1.16.0" poetry lock && poetry install # 2.5.1: "No dependencies to install or update" poetry run python -c 'import six; print(six.__file__)' # /usr/lib/python3/dist-packages/six.py socket-patch scan --mode agent --yes $A --ecosystems pypi # [skip] pkg:pypi/six@1.16.0 (not installed; run your package manager's install first ...) -> exit 0
Poetry poetry installputs six in.venv?agent scan hosted scan → poetry install→vex2.5.1 no (system copy counts as installed) package_not_installed, exit 0, copy the app imports stays unpatchedpass: Poetry sees the source change ("Updating six (1.16.0 -> 1.16.0 )") and installs the patched wheel into .venv;vexverifies it1.8.5 yes pass (patches .venv)pass, and redirect_pypi_stale_installfires before the reinstallSo on Poetry the hosted path is protected by Poetry's own reinstall, and the false hosted VEX from the pip repro doesn't happen here. The agent-mode miss does. Advising "run your package manager's install first" can't help, because
poetry installhas nothing to do. Agentvexcorrectly finds nothing to attest.
Generated by Claude Code
mikolalysenko commented
on Oct 5, 2026 CollaboratorAuthorMore actions[agent] From the scheduled Pipenv bug-hunt routine (ledger #313): Pipenv users hit the hosted and vendored false VEX through a Pipenv setting,
pipenv --site-packages/PIPENV_SITE_PACKAGES=1. Unlike Poetry 2.x, Pipenv doesn't protect the hosted path, and it fails even in a fresh venv created after the rewrite.Tested on main
045d7ec, Linux. six 1.16.0 was installed into the base interpreter (a uv-managed CPython). I used a local mock patch API serving a patchedsix-1.16.0wheel. Every cell reproduced 2/2.# Pipfile: six = "==1.16.0"; Pipfile.lock from `pipenv lock`; no venv yet (lock-only checkout) socket-patch scan --mode hosted --yes # or: scan --vendor --yes → Pipfile.lock six → "file": <hosted / .socket/vendor wheel> PIPENV_SITE_PACKAGES=1 pipenv --python "$BASE_PY" # same as `pipenv --site-packages` pipenv install --deploy # exit 0; pip treats six==1.16.0 as already satisfied by the base copy pipenv run python -c 'import six; print(six.__file__, getattr(six, "SOCKET_PATCHED", 0))' # <base>/lib/python3.x/site-packages/six.py 0 ← unpatched socket-patch scan --mode hosted --yes # no stale-install warning socket-patch vex --product pkg:pypi/app@1 # "status": "not_affected" ← false
Pipenv (Python) mode install --deployin a fresh--site-packagesvenvstale warning on re-scan vex2018.11.26 (3.8) hosted unpatched base copy none not_affected2022.12.19 (3.8) hosted unpatched base copy none not_affected2023.12.1 (3.11) hosted unpatched base copy none not_affected2026.8.0 (3.11) hosted unpatched base copy none not_affected2018.11.26 (3.8) vendored unpatched base copy none (no vendored_tree_out_of_synceither)not_affected2026.8.0 (3.11) vendored unpatched base copy none not_affected2026.8.0 (3.11), warm --site-packagesvenvagent No patches selectedn/a refuses (nothing to attest), correct 2026.8.0 (3.11), control: plain venv, no --site-packageshosted patched none not_affected, correctPipenv-specific notes:
- With a warm venv the result is the same:
pipenv syncandpipenv install --deployboth keep the base copy. - The stale-install remedy Pipenv users get elsewhere (
pipenv run pip uninstall -y six && pipenv sync) would uninstall from the venv's view. It can't help here, because six isn't in the venv at all, and nothing tells the user to run it. - Pipenv's venv discovery (
find_pipenv_virtualenv_site_packages) finds the right WORKON_HOME venv. As in the original report, the gap is that itspyvenv.cfginclude-system-site-packages = trueis never read.
Generated by Claude Code
- With a warm venv the result is the same:
mikolalysenko commented
on Oct 9, 2026 CollaboratorAuthorMore actionsv5 triage: P2, not a release blocker. Retain inherited system-site-packages detection at P2; the ordinary isolated-venv and lockfile-first paths take precedence.
This follows the maintainer's release scope: one normally completing CLI instance, prioritizing valid-lockfile patch/install behavior, compatibility, and actionable CLI UX.
[agent] Found by the scheduled pip / requirements.txt bug-hunt routine (ledger #309).
Summary
In a virtualenv created with
--system-site-packages(include-system-site-packages = trueinpyvenv.cfg), the Python crawler only looks in the venv's ownsite-packages. Packages the venv's interpreter actually imports from the base interpreter are invisible to it: Debian/Ubuntu/usr/lib/python3/dist-packages,/usr/local/lib/python3.X/dist-packages, the user site, and a base install'ssite-packages. This has three effects after a hosted (default) scan ofrequirements.txt:pip install -r requirements.txtseessix==1.16.0already satisfied by the base interpreter's copy. It downloads the hosted wheel but does not install it, and exits 0. The venv keeps importing the unpatched base copy.redirect_pypi_stale_install. It does fire for the same unpatched copy inside a plain venv.socket-patch vexfinds "nothing installed", so it attests from the lockfile pin:not_affected/inline_mitigations_already_exist, "Patched via Socket patch …", for code that is not patched.Vendored mode has the same blind spot. pip keeps the base copy, and
vex's documentedvendored_tree_out_of_syncwarning ("the installed tree does not match its vendored artifact") is missing, while it does print for the same situation in a plain venv. Agent mode reports the package asnot installed; run your package manager's install first. Running the install doesn't change that, because pip says the requirement is already satisfied.Impact
A false VEX attestation for an unpatched dependency, with no warning anywhere in the flow. This is a common layout: Debian/Ubuntu images ship
python3-six,python3-yaml,python3-requestsand others as distro packages, andpython3 -m venv --system-site-packagesis a standard way to reuse them.Repro
Uses a local mock of the patch API that serves a patched
six-1.16.0wheel, the same shape astests/vex_pypi_real_common::RealApi. It runs on Linux (Debian-based sandbox, where/usr/lib/python3/dist-packages/six.pyis 1.16.0). The probe reproduces it portably by runningpython -m pip install six==1.16.0into the base interpreter.Control: the same steps in a plain
python3 -m venv .venvwith upstreamsix==1.16.0installed. The scan warnsredirect_pypi_stale_install, andvexexits 1 withNo applied patches with vulnerability metadata to attest. That's the documented behaviour.Expected vs actual
hash_mismatch/not_appliedare omitted", and that "not installed" has to mean the crawler looked. The "Python stale-install guard" paragraph says a readable file that differs from the patch'safterHashemitsredirect_pypi_stale_install. The venv imports the base copy, so that copy is the one the build consumes.vextreats the package as not installed and attests from the pin, and the guard stays silent.OS × version
vexnot_affectednot_affectednot_affectednot_affectedsite-packages)/opt/hostedtoolcache/Python/3.x/…/site-packages/six.py)not_affected(hosted and vendored)site-packages)/opt/hostedtoolcache/Python/3.x/…/site-packages/six.py)not_affected(hosted and vendored)site-packages)/opt/hostedtoolcache/Python/3.x/…/site-packages/six.py)not_affected(hosted and vendored)site-packages)/Library/Frameworks/Python.framework/…/site-packages/six.py)not_affected(hosted and vendored)site-packages)/Library/Frameworks/Python.framework/…/site-packages/six.py)not_affected(hosted and vendored)site-packages)/Library/Frameworks/Python.framework/…/site-packages/six.py)not_affected(hosted and vendored)site-packages)C:\hostedtoolcache\windows\Python\…\site-packages\six.py)not_affected(hosted and vendored)site-packages)C:\hostedtoolcache\windows\Python\…\site-packages\six.py)not_affected(hosted and vendored)site-packages)C:\hostedtoolcache\windows\Python\…\site-packages\six.py)not_affected(hosted and vendored)On Linux, the 3.11 / 24.0 cell also reproduced on two separate runs.
First bad
The false VEX attestation is new in
2463257(#277, "with nothing installed … attests from that pin"). On v4.0.0 the same project getsomitted: pkg:pypi/six@1.16.0 (package_not_found)and no attestation. The blind spot in the crawler predates that.Suspect code
crates/socket-patch-core/src/crawlers/python_crawler.rs:274(find_local_venv_site_packages) returns only<venv>/lib/python3.*/site-packages(andLib/site-packages). It never readspyvenv.cfg'sinclude-system-site-packages, andget_site_packages_paths(:1395) returns early once a venv is found.crates/socket-patch-cli/src/commands/vex.rs:582-600: the hosted "nothing installed → attest from lock pin" excuse relies on that crawl being complete.Probe run: https://github.com/SocketDev/socket-patch/actions/runs/36806312653