feat(supply-chain): wrap uvx for age enforcement (PPSC-934)#219
Conversation
There was a problem hiding this comment.
Pull request overview
Adds supply-chain age-enforcement coverage for Python’s uvx tool runner by treating it as a uv-paired runner (similar to npx↔npm) and routing it through the existing transparent PyPI proxy path, with corresponding init detection, docs, and tests.
Changes:
- Add
uvxas a supported/allowed package manager and route it through the PyPI transparent proxy (UV_INDEX_URL), mappinguvxto theuvecosystem for scoping. - Update
supply-chain initdetection to pairuvxwithuv(guarded by PATH presence) and annotate it in the init preview. - Expand unit tests and documentation (README/help text/CHANGELOG) to reflect
uvxsupport and its cache-related coverage caveat.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| README.md | Adds uvx to the supported Python ecosystem list. |
| internal/cmd/supply_chain.go | Updates help text to document uvx and its cache behavior caveat. |
| internal/cmd/supply_chain_wrap.go | Adds pmUVX, allowlists it, routes it through PyPI proxy env/config, and maps it to EcosystemUV. |
| internal/cmd/supply_chain_wrap_pm_test.go | Extends PM table/unit tests to cover uvx canonicalization, env override, scoping mapping, and routing. |
| internal/cmd/supply_chain_init.go | Pairs uvx with uv during init detection and updates the init preview summarization. |
| internal/cmd/supply_chain_init_test.go | Adds pairing/scoping/PATH-guard tests for uvx and updates summary rendering coverage. |
| docs/CHANGELOG.md | Documents the new uvx wrapping behavior and the cache caveat in Unreleased. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // - npx is annotated "(paired with npm)" and uvx "(paired with uv)" rather than | ||
| // listed as plain finds, because they are the entries init adds without | ||
| // detecting them: each ships with / shares config with its base PM and is | ||
| // wrapped wherever that PM is in scope (see detectWrappablePMs). Making that | ||
| // explicit keeps the summary honest about what was on PATH vs. inferred. |
There was a problem hiding this comment.
Good catch — fixed in e242fea. You're right that the pairing is PATH-gated: detectWrappablePMs only adds npx/uvx when IsOnPath confirms the runner is present (and its base PM is in scope). The "without detecting them" phrasing was inherited from the original npx-only comment and was inaccurate. Reworded to distinguish "omitted from the primary detection set (allSupportedPMs) and paired with the base PM, still PATH-gated" from "not detected at all."
Test Coverage Reporttotal: (statements) 72.1% Coverage by function |
uvx (uv's on-demand tool runner; `uvx X` ≡ `uv tool run X`) fetches a tool from PyPI and runs it — exactly the supply-chain vector the proxy guards — but was not wrapped, so it bypassed age enforcement even where uv itself was wrapped. Wrap it as the PyPI analogue of how npx is paired with npm. - Add uvx to the allowlist; route it through the transparent PyPI proxy (not the pre-install block), sharing uv's UV_INDEX_URL override. - pmToEcosystem maps uvx -> EcosystemUV so the config `ecosystems` scope treats it exactly like uv. - supply-chain init pairs uvx with uv (guarded by a PATH check, after ecosystem scoping so it inherits uv's in/out-of-scope decision); the init preview annotates it "(paired with uv)". - Update help text (supply-chain + init), README ecosystem list, and CHANGELOG. - Tests: 4 new uvx pairing tests plus uvx rows in the canonicalPM / requiresPreInstallBlock / pmToEcosystem / registryEnvForPM / summarizeDetectedPMs tables. Enforcement covers tools uvx fetches from the registry; a tool already in uv's tool cache runs without a registry round-trip and is not re-checked (documented), mirroring the npx cache behavior.
ec36790 to
75e539f
Compare
The summarizeDetectedPMs comment said npx/uvx are added "without detecting them", which is misleading: detectWrappablePMs pairs each only when its base PM is in scope AND IsOnPath confirms the runner is present on PATH. Reword to say they are omitted from the primary detection set (allSupportedPMs) and paired with their base PM, still PATH-gated — distinguishing "not in the main scan" from "not detected at all". Addresses PR #219 review feedback.
Summary
Adds
uvxsupport to the supply-chain feature — the PyPI analogue of hownpxis paired withnpm.uvx <tool>(≡uv tool run <tool>) fetches a tool from PyPI on demand and runs it — exactly the supply-chain vector the proxy guards. Previously it was not wrapped, souvxbypassed age enforcement entirely, even on machines whereuvitself was wrapped. This closes that gap.What changed
Core (
internal/cmd/supply_chain_wrap.go)pmUVXconstant + to the allowlistuvxthrough the transparent PyPI proxy (not the pre-install block), sharing uv'sUV_INDEX_URLoverride —uvxshares uv's resolver and configpmUVXcase toexecPM's hardcoded-name switchpmToEcosystemmapsuvx → EcosystemUVso the configecosystemsscope treats it exactly likeuvInit / detection (
internal/cmd/supply_chain_init.go)uvxwithuv(guarded by a PATH check, after ecosystem scoping so it inherits uv's in/out-of-scope decision)(paired with uv)Docs — help text (
supply-chain+init), README ecosystem list, CHANGELOG[Unreleased]Tests — 4 new uvx pairing tests (
PairsUvxWithUv,UvxNotPairedWithoutUv,UvxNotWrappedWhenAbsentFromPath,UvxAbsentWhenUvScopedOut) plus uvx rows added to thecanonicalPM/requiresPreInstallBlock/pmToEcosystem/registryEnvForPM/summarizeDetectedPMstablesCoverage caveat (documented)
Enforcement covers tools
uvxfetches from the registry; a tool already in uv's tool cache runs without a registry round-trip and is not re-checked — mirroring the existing npx cache behavior. This is stated explicitly in the help text and CHANGELOG so coverage isn't overstated.Testing
go build ./...— passesgo test ./internal/cmd/ ./internal/supplychain/...— all passgolangci-lint run— 0 issuesinit --mode envemits bothuv()anduvx()wrappers;supply-chain wrap uvxis accepted and routes to execTicket
PPSC-934 (under epic PPSC-875 — Supply Chain Package Age Policy Enforcement)