Hacktoberfest 2026: những issue maintainer đã đánh dấu cho tháng Mười, đang mở và phù hợp người mới. Xem issue Hacktoberfest

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)

Đã đóng
#501 3 bình luận 0 reaction 0 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 1 ngày

Chưa có ai nhận issue này.

Đánh giá

Độ khó
4/5
Thời gian dự kiến
3-5 ngày
Mức phù hợp với người mới
58/100
Loại issue
Lỗi
Độ rõ ràng
Đặc tả rõ ràng
Mức độ hoạt động
Sôi nổi
Công nghệ
python, rust
Lĩnh vực
cli, security

Hướng nghiên cứu

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.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

agent:claimed agent:triaged bug bughunt pm:poetry priority:p1

[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 agent in a Poetry project with virtualenvs.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:

  1. get_global_python_site_packages orders paths as site.getsitepackages() first and site.getusersitepackages() last (crates/socket-patch-core/src/crawlers/python_crawler.rs:1316, plus the well-known-dir scan after it, which also adds ~/.local last). That's the reverse of Python's import precedence.
  2. Apply keeps PyPI on a "one representative" contract and patches only pkg_paths.first() (crates/socket-patch-cli/src/commands/apply.rs:1861 and :1888-1898). The comment there assumes "their crawlers resolve one install dir per version", which doesn't hold in global scope. rollback does fan out to every copy (it reports rolledBack: 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), -g writes 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 project scan.

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.rs comment: "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 only pkg_paths.first()).
  • Related but distinct: #409 (--system-site-packages venv, hosted), #476 (Poetry venv not found, so this fallback is reached).
Ngôn ngữ chính
Rust
Star
8
Fork
0
Merge trung bình
1 ngày 1 giờ
Pull request đã merge (30 ngày)
257

Chuẩn bị môi trường

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của SocketDev/socket-patch

Tất cả issue của SocketDev/socket-patch

Issue tương tự

Thêm issue về Rust

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.