With the same release in user site-packages and a system site-packages dir, agent mode patches the shadowed system copy, leaves the copy Python imports unpatched, and VEX attests it (worse since #452: now hits apt-installed packages)
Les mainteneurs répondent en général sous 1 jour
Personne n'a encore pris cette issue.
Évaluation
- Difficulté
- 4/5
- Temps estimé
- 3-5 jours
- Accessibilité débutants
- 58/100
Piste de recherche
Start with the Linux reproduction, then read crates/socket-patch-core/src/crawlers/python_crawler.rs:1316 and the well-known-directory scan, followed by crates/socket-patch-cli/src/commands/apply.rs:1861 and :1888-1898. Verify behavior with the scan, apply, vex, and rollback commands from the report. Done means the interpreter-loaded copy is patched, duplicate installs are handled consistently, and VEX does not attest an unpatched loaded copy.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Description
[agent] Found by the scheduled Poetry bug-hunt routine (ledger #311).
Summary
When one PyPI release (here six==1.16.0) is installed both in the user site (~/.local/lib/python3.X/site-packages, from pip install --user) and in a system site dir (/usr/lib/python3/dist-packages from apt, or /usr/local/lib/python3.X/dist-packages from pip), every global-scope agent run patches the system copy only:
scan -g --mode agent, and- a plain project
scan --mode agentin a Poetry project withvirtualenvs.create = false(or any layout where the project venv isn't found and the crawler falls back to the global interpreter, e.g. #476).
Python imports the user-site copy, because sys.path puts user site before the system dirs. That copy stays unpatched. The run reports success, apply re-runs say already_patched, and vex attests not_affected / inline_mitigations_already_exist. On Debian/Ubuntu the write also lands in a dpkg-owned file (/usr/lib/python3/dist-packages/six.py).
Root cause, two parts:
get_global_python_site_packagesorders paths assite.getsitepackages()first andsite.getusersitepackages()last (crates/socket-patch-core/src/crawlers/python_crawler.rs:1316, plus the well-known-dir scan after it, which also adds~/.locallast). That's the reverse of Python's import precedence.- Apply keeps PyPI on a "one representative" contract and patches only
pkg_paths.first()(crates/socket-patch-cli/src/commands/apply.rs:1861and:1888-1898). The comment there assumes "their crawlers resolve one install dir per version", which doesn't hold in global scope.rollbackdoes fan out to every copy (it reportsrolledBack: 1, alreadyOriginal: 1), so the two commands disagree on how many copies exist.
#452 (2035cbb) made this much easier to hit. Before it, apt's .egg-info installs were invisible, so with apt six plus a user-site six the user copy was the only candidate and got patched correctly. Since #452 the apt copy is listed first and wins. With two dist-info copies (/usr/local/.../dist-packages plus user site), the bug already exists in v4.0.0.
Impact
- A false VEX attestation. The imported copy is still vulnerable, and nothing warns.
- On Debian/Ubuntu (apt ships
six,requests,urllib3,idna,certifi, … as egg-info),-gwrites into dpkg-owned files while leaving the copy actually in use unpatched. - Poetry users with
virtualenvs.create = false(common in Docker images and CI) get the same result from a plain projectscan.
Repro (Linux, Debian bookworm image, Python 3.11, Poetry 2.5.1)
The mock patch API serves one agent patch for pkg:pypi/[email protected] that appends SOCKET_PATCHED = 1 to six.py. It uses the same routes as tests/vex_pypi_real_common/mod.rs, plus /patches/blob/<hash>. $A = --api-url http://127.0.0.1:18080 --api-token fake --org test-org --patch-server-url http://127.0.0.1:18080.
# apt's python3-six 1.16.0 egg-info is in /usr/lib/python3/dist-packages
python3 -m pip install --user --ignore-installed --break-system-packages six==1.16.0
python3 -c 'import six; print(six.__file__)' # -> /root/.local/lib/python3.11/site-packages/six.py
mkdir nc && cd nc
cat > pyproject.toml <<'EOF'
[tool.poetry]
name = "demo"
version = "0.1.0"
description = ""
authors = ["x <x@x>"]
package-mode = false
[tool.poetry.dependencies]
python = "^3.11"
six = "1.16.0"
EOF
printf '[virtualenvs]\ncreate = false\n' > poetry.toml
poetry lock && poetry install # "No dependencies to install or update"
poetry run python -c 'import six; print(six.__file__)' # user-site copy
socket-patch scan --mode agent --yes $A # success (same with: mkdir g && cd g && socket-patch scan -g --mode agent --yes $A)
grep -c SOCKET_PATCHED /usr/lib/python3/dist-packages/six.py # 1 <- shadowed copy patched
grep -c SOCKET_PATCHED ~/.local/lib/python3.11/site-packages/six.py # 0 <- imported copy unpatched
python3 -c 'import six; print(hasattr(six, "SOCKET_PATCHED"))' # False
socket-patch apply --json $A # skipped / already_patched
socket-patch vex --product pkg:pypi/[email protected] --json --output v.json $A # not_affected / inline_mitigations_already_exist
socket-patch rollback --json $A # rolledBack: 1, alreadyOriginal: 1 (it sees both copies)
It reproduces 2/2 for each of: scan -g --mode agent, vex -g, and the Poetry create = false project scan.
Expected vs actual
- Expected: agent mode patches the copy the interpreter loads, or every installed copy as gem/npm do (
apply.rscomment: "leaving the other store pristine is a silent false 'applied'"). VEX only attests when the loaded copy is patched. CLI_CONTRACT.md's VEX rule is that a statement is emitted only for a patch that is actually applied. - Actual: the shadowed copy is patched, the loaded copy isn't, and VEX attests anyway.
OS × version
| OS | Copies present | Binary | Result |
|---|---|---|---|
| Linux | apt egg-info + user site | main 61cfb9b, 2035cbb (#452) |
fail: apt copy patched |
| Linux | apt egg-info + user site | v4.0.0, 2035cbb^ | pass: user copy patched |
| Linux | /usr/local/.../dist-packages dist-info + user site |
main, 2035cbb^, v4.0.0 | fail: /usr/local copy patched |
| Linux | /usr/local/.../dist-packages only |
main | pass |
| macOS / Windows | — | — | untested (probe branches blocked). macOS user site (~/Library/Python/3.X/lib/python/site-packages) has the same ordering, so it's likely affected. |
Poetry versions: 2.5.1 (create = false, user-site copy kept). With create = false, Poetry 1.8.5 / 2.0.1 / 2.2.1 in this image reinstalled six into /usr/local/lib/python3.11/dist-packages, which precedes apt on sys.path, so those didn't reproduce until a user-site copy was re-added. The -g path doesn't depend on the Poetry version.
First bad commit
For the apt + user-site case: 2035cbb ("Fix Python crawler missing .egg-info installs (#447) (#452)"), bisected with release builds of 2035cbb^ and 2035cbb, 2/2 each. For two dist-info copies, it never worked (v4.0.0 fails).
Suspect code
crates/socket-patch-core/src/crawlers/python_crawler.rs:1316(user site printed last) and the well-known-dir order right after it.crates/socket-patch-cli/src/commands/apply.rs:1861,:1888-1898(PyPI patches onlypkg_paths.first()).- Related but distinct: #409 (
--system-site-packagesvenv, hosted), #476 (Poetry venv not found, so this fallback is reached).
- Langage dominant
- Rust
- Étoiles
- 8
- Forks
- 0
- Merge moyen
- 19 h 21 min
- PR mergées (30 j)
- 421
Préparer son environnement
- Aucun Dockerfile ni fichier Docker Compose
- Aucun modèle de pull request
- Lire le guide de contribution
Par où commencer
- Lisez l'issue en entier, puis le guide de contribution du projet.
- Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
- Forkez le dépôt et travaillez sur une branche.
- Ouvrez une pull request qui référence le numéro de l'issue.
Autres issues de SocketDev/socket-patch
-
agent:triaged bug bughunt pm:npm priority:p3
Difficulté 2/5 1-3 heures Accessibilité débutants 75/100
SocketDev/socket-patch#1072 · 1 commentaire ·
Les mainteneurs répondent en général sous 1 jour
-
Hosted gem `rollback` / `remove` strips the `DEPENDENCIES` `!` of a gem the user declared inside a `source "https://rubygems.org" do` block, so every frozen install fails after the unwindPeut-être pris @mikolalysenko l’a pris il y a 1 jour. Ouverteagent:triaged bug bughunt pm:bundler priority:p1
Difficulté 2/5 1-3 heures Accessibilité débutants 80/100
SocketDev/socket-patch#1056 · 1 commentaire ·
Les mainteneurs répondent en général sous 1 jour
-
agent:triaged bug bughunt pm:bundler priority:p1
Difficulté 2/5 1-3 heures Accessibilité débutants 85/100
SocketDev/socket-patch#896 · 1 commentaire ·
Les mainteneurs répondent en général sous 1 jour
-
agent:triaged bug bughunt pm:yarn-berry priority:p1
Difficulté 2/5 1-3 heures Accessibilité débutants 73/100
SocketDev/socket-patch#783 · 1 commentaire ·
Les mainteneurs répondent en général sous 1 jour
-
agent:triaged bug bughunt pm:cargo priority:p2
Difficulté 2/5 1-3 heures Accessibilité débutants 84/100
SocketDev/socket-patch#651 · 3 commentaires ·
Les mainteneurs répondent en général sous 1 jour
Toutes les issues de SocketDev/socket-patch
Issues similaires
-
good first issue help wanted
Difficulté 2/5 1-3 heures Accessibilité débutants 72/100
-
documentation
Difficulté 2/5 1-3 heures Accessibilité débutants 70/100
Les mainteneurs répondent en général sous 1 jour
-
Difficulté 2/5 1-3 heures Accessibilité débutants 62/100
NuSkooler/enigma-bbs#907 ·
Les mainteneurs répondent en général sous 1 jour
-
bug
Difficulté 2/5 1-3 heures Accessibilité débutants 62/100
Les mainteneurs répondent en général sous 1 jour
-
bug pixi-build-r
Difficulté 2/5 1-3 heures Accessibilité débutants 70/100
prefix-dev/pixi#7229 ·
Les mainteneurs répondent en général sous 1 jour