Hosted PDM rollback and remove replace a private index's static_urls with files.pythonhosted.org, so PDM bypasses the mirror (or fails on 2.12 behind a firewall)
Maintainer antworten meist innerhalb von 1 Tag
Dieses Issue hat noch niemand übernommen.
Bewertung
- Schwierigkeit
- 4/5
- Geschätzter Aufwand
- 3-5 Tage
- Anfängerfreundlichkeit
- 48/100
Rechercherichtung
Start at crates/socket-patch-core/src/patch/redirect/upstream/pypi_locks.rs:341-367 and compare restore_pdm with the guards in upstream/uv.rs:366-368 and upstream/pypi.rs:193. Reproduce the PDM private-index case, then verify against CLI_CONTRACT.md and docs/testing/pdm-compatibility.md that rollback preserves the lock's file locations or refuses clearly without bypassing the mirror.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Beschreibung
[agent] Found by the scheduled PDM bug-hunt routine (ledger #312).
Summary
Take a project whose pyproject.toml replaces PyPI with a private index or mirror ([[tool.pdm.source]] name = "pypi") and whose lock uses the static_urls strategy. On v5 main, a hosted rollback (or remove <purl>) rewrites the restored files entries to https://files.pythonhosted.org/... URLs taken from PyPI's JSON API. The mirror URLs the lock had before the scan are gone. The command reports success, and --dry-run doesn't warn either.
The uv restore refuses a lock whose registry isn't PyPI (upstream/uv.rs:368, "the lock's registry … is not PyPI"), and the Pipenv restore checks _meta.sources the same way (pipenv_index_is_pypi, upstream/pypi.rs:193). restore_pdm has no equivalent check. It never looks at [[tool.pdm.source]] or at the original file URLs.
This is a regression from v4. v4 (f6b7fb9) replayed the recorded original fragment and round-tripped the lock byte-exactly. #277 (2463257) replaced that replay with the upstream restore.
Impact
- PDM 2.20 / 2.29: after rollback,
pdm syncdownloads the wheel directly fromfiles.pythonhosted.org, and the configured index gets zero requests. That silently bypasses an organisation's mirror, proxy or allow-list policy. - PDM 2.12.4: on a network where only the mirror is reachable,
pdm syncfails after rollback (ProxyError … host='files.pythonhosted.org'). The original lock installs fine on the same network. - The lock committed after rollback no longer matches what PDM writes for this project, and
pdm lock --checkdoesn't notice, becausecontent_hashis unchanged.
Repro
Requirements: PDM, a local PEP 503 index serving urllib3-1.26.18 (wheel and sdist) at http://127.0.0.1:18780/simple, and a mock patch API on :18765 that grants a hosted wheel for pkg:pypi/[email protected] (the routes from vex_e2e_common/uv.rs::ScanApi). Set SOCKET_PATCH_SERVER_URL to the mock's origin.
cat > pyproject.toml <<'EOF'
[project]
name = "proj"
version = "0.1.0"
requires-python = ">=3.8"
dependencies = ["urllib3==1.26.18"]
[tool.pdm]
distribution = false
[[tool.pdm.source]]
name = "pypi"
url = "http://127.0.0.1:18780/simple"
verify_ssl = false
EOF
pdm lock --static-urls && cp pdm.lock pdm.lock.orig
socket-patch scan --json --yes --ecosystems pypi # redirected: 1
socket-patch rollback --json --yes # status: success, hosted.reverted: [urllib3]
diff pdm.lock.orig pdm.lock
# - {url = "http://127.0.0.1:18780/files/urllib3-1.26.18-py2.py3-none-any.whl", hash = "sha256:34b9…"},
# - {url = "http://127.0.0.1:18780/files/urllib3-1.26.18.tar.gz", hash = "sha256:f8ec…"},
# + {url = "https://files.pythonhosted.org/packages/0c/39/…/urllib3-1.26.18.tar.gz", hash = "sha256:f8ec…"},
# + {url = "https://files.pythonhosted.org/packages/b0/53/…/urllib3-1.26.18-py2.py3-none-any.whl", hash = "sha256:34b9…"},
rm -rf .venv; pdm sync -v | grep Downloading # unearth: Downloading https://files.pythonhosted.org/… (mirror log: 0 hits)
# Mirror-only network: the original lock syncs, the rolled-back lock doesn't (PDM 2.12.4):
HTTPS_PROXY=http://127.0.0.1:9 NO_PROXY=127.0.0.1 pdm sync # ProxyError host='files.pythonhosted.org'
socket-patch remove pkg:pypi/[email protected] produces the same lock. Each cell below was reproduced at least twice.
Expected vs actual
- Expected: CLI_CONTRACT.md "Hosted unwind coverage" says that "only the hosted entries change and every other byte stays the file's own". The uv and Pipenv restores refuse when the lock's index isn't PyPI, because "the upstream hashes cannot be re-derived". docs/testing/pdm-compatibility.md also says the backtest's rollback "restores the lock … byte for byte". For a PDM lock whose source isn't PyPI, the restore should either keep the lock's own file locations or refuse, telling the user to restore it from version control the way the uv restore does.
- Actual: the restore reports
successand swaps the project's index for PyPI's CDN.
Matrix (Linux)
| PDM | lock_version / strategy | v5 2463257 |
v4 f6b7fb9 |
|---|---|---|---|
| 2.29.2 | 4.5.1, inherit_metadata, static_urls |
fail: URLs → pythonhosted, sync bypasses the mirror | pass (byte-exact) |
| 2.20.1 | 4.5.0, inherit_metadata, static_urls |
fail: same | — |
| 2.12.4 | 4.4.1, cross_platform, inherit_metadata, static_urls |
fail: same, and the mirror-only pdm sync fails |
— |
macOS and Windows weren't probed; the restore is platform-independent logic.
First bad commit: 2463257 (#277, "consolidate the v5 patching workflow"). f6b7fb9 is good.
Suspect code
crates/socket-patch-core/src/patch/redirect/upstream/pypi_locks.rs:341-367(restore_pdm):static_urls→files_value(release, static_urls)rendersPypiFile.urlfrom PyPI's JSON API, with no check of the project's source.- Compare
crates/socket-patch-core/src/patch/redirect/upstream/uv.rs:366-368andupstream/pypi.rs:193(pipenv_index_is_pypi).
The same root cause probably affects a non-static_urls lock whose private index serves different bytes under the same name and version: the restored hashes would be PyPI's. I haven't tested that.
- Vorherrschende Sprache
- Rust
- Sterne
- 8
- Forks
- 0
- Ø Merge
- 18 Std. 4 Min.
- Gemergte PRs (30 T.)
- 70
Entwicklungsumgebung
- Kein Dockerfile und keine Docker-Compose-Datei
- Keine Pull-Request-Vorlage
- Beitragsleitfaden lesen
Erste Schritte
- Lesen Sie das ganze Issue und danach den Beitragsleitfaden des Projekts.
- Schreiben Sie ins Issue, dass Sie es übernehmen — das erspart doppelte Arbeit.
- Forken Sie das Repository und arbeiten Sie in einem Branch.
- Öffnen Sie einen Pull Request, der die Issue-Nummer nennt.
Mehr aus SocketDev/socket-patch
-
agent:triaged bug bughunt pm:composer priority:p2
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 90/100
SocketDev/socket-patch#515 · 1 Kommentar ·
Maintainer antworten meist innerhalb von 1 Tag
-
agent:triaged bug bughunt pm:npm priority:p1
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 82/100
SocketDev/socket-patch#464 · 1 Kommentar ·
Maintainer antworten meist innerhalb von 1 Tag
-
agent:triaged bug bughunt pm:npm priority:p1
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 82/100
SocketDev/socket-patch#433 · 1 Kommentar ·
Maintainer antworten meist innerhalb von 1 Tag
-
agent:triaged bug bughunt pm:uv priority:p1
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 78/100
SocketDev/socket-patch#408 · 1 Kommentar ·
Maintainer antworten meist innerhalb von 1 Tag
-
agent:triaged bug bughunt pm:yarn-berry priority:p1
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 82/100
SocketDev/socket-patch#370 · 2 Kommentare ·
Maintainer antworten meist innerhalb von 1 Tag
Alle Issues in SocketDev/socket-patch
Ähnliche Issues
-
discover: `sudo RTK_DISABLED=$VAR …` is not detected as a bypass when `sudo` is a transparent prefixOffenarea:cli bug good first issue priority:medium
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 88/100
rtk-ai/rtk#4412 · 1 Kommentar ·
Maintainer antworten meist innerhalb von 1 Tag
-
skill:code-review
Schwierigkeit 1/5 1-3 Stunden Anfängerfreundlichkeit 88/100
Maintainer antworten meist innerhalb von 1 Tag
-
component:sight
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 84/100
agentic-os-org/ANOLISA#4115 · 1 Kommentar ·
Maintainer antworten meist innerhalb von 1 Tag
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 78/100
rivet-dev/rivet#5819 · 1 Kommentar ·
Maintainer antworten meist innerhalb von 1 Tag
-
A-io-database bug needs triage python
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 86/100
Maintainer antworten meist innerhalb von 1 Tag