GeezerVison is showing github.com — 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 · Issue #409 · SocketDev/socket-patch
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 #409Description[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 = true in pyvenv.cfg), the Python crawler only looks in the venv's own site-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's site-packages. This has three effects after a hosted (default) scan of requirements.txt:
pip install -r requirements.txt sees six==1.16.0 already 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.
- The Python stale-install guard doesn't fire, so there is no
redirect_pypi_stale_install. It does fire for the same unpatched copy inside a plain venv.
socket-patch vex finds "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 documented vendored_tree_out_of_sync warning ("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 as not 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-requests and others as distro packages, and python3 -m venv --system-site-packages is a standard way to reuse them.
Repro
Uses a local mock of the patch API that serves a patched six-1.16.0 wheel, the same shape as tests/vex_pypi_real_common::RealApi. It runs on Linux (Debian-based sandbox, where /usr/lib/python3/dist-packages/six.py is 1.16.0). The probe reproduces it portably by running python -m pip install six==1.16.0 into the base interpreter.
A="--api-url http://127.0.0.1:8765 --api-token fake --org test-org --patch-server-url http://127.0.0.1:8765"
mkdir p && cd p && printf 'six==1.16.0\n' > requirements.txt
python3 -m venv --system-site-packages .venv
socket-patch scan --json $A | jq '.redirect | {redirected, warnings}' # {"redirected":1,"warnings":[]}
.venv/bin/pip install -r requirements.txt; echo $? # Collecting six@ http://…/six-1.16.0-py2.py3-none-any.whl … → 0
.venv/bin/python -c 'import six; print(six.__file__, getattr(six,"SOCKET_PATCHED",0))'
# /usr/lib/python3/dist-packages/six.py 0 ← unpatched
socket-patch vex $A --product pkg:pypi/app@1 --output vex.json
# Wrote OpenVEX document with 1 statement to vex.json
jq '.statements[] | {status, justification, impact_statement}' vex.json
# "not_affected", "inline_mitigations_already_exist", "Patched via Socket patch 5a6b7c8d-… (redirected)"
Control: the same steps in a plain python3 -m venv .venv with upstream six==1.16.0 installed. The scan warns redirect_pypi_stale_install, and vex exits 1 with No applied patches with vulnerability metadata to attest. That's the documented behaviour.
Expected vs actual
- Expected: CLI_CONTRACT.md, "Verification basis", says that for hosted wiring "The installed copies the build consumes through the hosted wiring are hash-verified when any exist … Installed evidence wins:
hash_mismatch / not_applied are 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's afterHash emits redirect_pypi_stale_install. The venv imports the base copy, so that copy is the one the build consumes.
- Actual: the crawler never looks at it.
vex treats the package as not installed and attests from the pin, and the guard stays silent.
OS × version
| OS |
Python / pip |
pip installs patched? |
stale warning |
vex |
| Linux (sandbox, Debian dist-packages) |
3.10 / 20.3.4 |
no (exit 0) |
none |
attests not_affected |
| Linux (sandbox) |
3.11 / 23.3.2 |
no (exit 0) |
none |
attests not_affected |
| Linux (sandbox) |
3.12 / 24.3.1 |
no (exit 0) |
none |
attests not_affected |
| Linux (sandbox) |
3.13 / 26.2.1 |
no (exit 0) |
none |
attests not_affected |
ubuntu-latest (probe, six in base site-packages) |
3.8 / 20.3.4 |
no (exit 0; imports /opt/hostedtoolcache/Python/3.x/…/site-packages/six.py) |
none (hosted and vendored) |
attests not_affected (hosted and vendored) |
ubuntu-latest (probe, six in base site-packages) |
3.8 / 25.0.1 |
no (exit 0; imports /opt/hostedtoolcache/Python/3.x/…/site-packages/six.py) |
none (hosted and vendored) |
attests not_affected (hosted and vendored) |
ubuntu-latest (probe, six in base site-packages) |
3.13 / 26.2.1 |
no (exit 0; imports /opt/hostedtoolcache/Python/3.x/…/site-packages/six.py) |
none (hosted and vendored) |
attests not_affected (hosted and vendored) |
macos-latest (probe, six in base site-packages) |
3.8 / 20.3.4 |
no (exit 0; imports /Library/Frameworks/Python.framework/…/site-packages/six.py) |
none (hosted and vendored) |
attests not_affected (hosted and vendored) |
macos-latest (probe, six in base site-packages) |
3.8 / 25.0.1 |
no (exit 0; imports /Library/Frameworks/Python.framework/…/site-packages/six.py) |
none (hosted and vendored) |
attests not_affected (hosted and vendored) |
macos-latest (probe, six in base site-packages) |
3.13 / 26.2.1 |
no (exit 0; imports /Library/Frameworks/Python.framework/…/site-packages/six.py) |
none (hosted and vendored) |
attests not_affected (hosted and vendored) |
windows-latest (probe, six in base site-packages) |
3.8 / 20.3.4 |
no (exit 0; imports C:\hostedtoolcache\windows\Python\…\site-packages\six.py) |
none (hosted and vendored) |
attests not_affected (hosted and vendored) |
windows-latest (probe, six in base site-packages) |
3.8 / 25.0.1 |
no (exit 0; imports C:\hostedtoolcache\windows\Python\…\site-packages\six.py) |
none (hosted and vendored) |
attests not_affected (hosted and vendored) |
windows-latest (probe, six in base site-packages) |
3.13 / 26.2.1 |
no (exit 0; imports C:\hostedtoolcache\windows\Python\…\site-packages\six.py) |
none (hosted and vendored) |
attests not_affected (hosted and vendored) |
| all 3 OS (probe) |
3.13 / 20.3.4 |
blocked: pip 20.3.4 can't run on 3.13 |
|
|
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 gets omitted: pkg:pypi/[email protected] (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 (and Lib/site-packages). It never reads pyvenv.cfg's include-system-site-packages, and get_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 Activitymikolalysenko commented on Oct 1, 2026 CollaboratorAuthor More 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_packages never reads include-system-site-packages from pyvenv.cfg, and get_site_packages_paths returns 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 CollaboratorAuthor More 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 main 61cfb9b, Linux, Debian /usr/lib/python3/dist-packages/six.py 1.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/[email protected] (not installed; run your package manager's install first ...) -> exit 0
| Poetry |
poetry install puts six in .venv? |
agent scan |
hosted scan → poetry install → vex |
| 2.5.1 |
no (system copy counts as installed) |
package_not_installed, exit 0, copy the app imports stays unpatched |
pass: Poetry sees the source change ("Updating six (1.16.0 -> 1.16.0 )") and installs the patched wheel into .venv; vex verifies it |
| 1.8.5 |
yes |
pass (patches .venv) |
pass, and redirect_pypi_stale_install fires before the reinstall |
So 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 install has nothing to do. Agent vex correctly finds nothing to attest.
Generated by Claude Code mikolalysenko commented on Oct 5, 2026 CollaboratorAuthor More 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 patched six-1.16.0 wheel. 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 --deploy in a fresh --site-packages venv |
stale warning on re-scan |
vex |
| 2018.11.26 (3.8) |
hosted |
unpatched base copy |
none |
not_affected |
| 2022.12.19 (3.8) |
hosted |
unpatched base copy |
none |
not_affected |
| 2023.12.1 (3.11) |
hosted |
unpatched base copy |
none |
not_affected |
| 2026.8.0 (3.11) |
hosted |
unpatched base copy |
none |
not_affected |
| 2018.11.26 (3.8) |
vendored |
unpatched base copy |
none (no vendored_tree_out_of_sync either) |
not_affected |
| 2026.8.0 (3.11) |
vendored |
unpatched base copy |
none |
not_affected |
2026.8.0 (3.11), warm --site-packages venv |
agent |
No patches selected |
n/a |
refuses (nothing to attest), correct |
2026.8.0 (3.11), control: plain venv, no --site-packages |
hosted |
patched |
none |
not_affected, correct |
Pipenv-specific notes:
- With a warm venv the result is the same:
pipenv sync and pipenv install --deploy both 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 its pyvenv.cfg include-system-site-packages = true is never read.
Generated by Claude Code mikolalysenko commented on Oct 9, 2026 CollaboratorAuthor More actions
v5 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. mikolalysenko commented on Oct 12, 2026 CollaboratorAuthor More actions [agent] Claiming this issue (shared root cause: the Python crawler never reads include-system-site-packages from a venv's pyvenv.cfg, so the base interpreter's site dirs behind a --system-site-packages venv are never crawled). Branch: agent/fix-python-venv-system-site-packages. Claim-ID: 2026-10-12T05:21:22Z-977e9a
Generated by Claude Code mikolalysenko commented on Oct 12, 2026 CollaboratorAuthor More actions
MetadataMetadataDevelopmentNo branches or pull requestsIssue actions
|