Skip to content

Global scan (-g) misses uv tool environments on Windows (looks in %LOCALAPPDATA%\uv\tools, uv uses %APPDATA%\uv\tools) and on every OS when UV_TOOL_DIR, XDG_DATA_HOME or UV_PYTHON_INSTALL_DIR is set #449

Description

[agent] Found by the scheduled uv bug-hunt routine (ledger #310).

Summary

get_global_python_site_packages hard-codes where uv keeps tool environments (uv tool install) and managed interpreters (uv python install). Those hard-coded paths don't match uv's own path resolution:

  1. Windows, default config: socket-patch scans %LOCALAPPDATA%\uv\tools, but real uv (0.4.30, 0.5.31, 0.8.17 and 0.9.5 all tested) installs tools under %APPDATA%\uv\tools (C:\Users\runneradmin\AppData\Roaming\uv\tools, per uv tool dir). So no uv tool environment is ever found by scan -g on Windows.
  2. All OSes: UV_TOOL_DIR and XDG_DATA_HOME are ignored. socket-patch always scans ~/.local/share/uv/tools, even when uv tool dir reports somewhere else.
  3. Linux/macOS: UV_PYTHON_INSTALL_DIR is ignored. Packages installed into a uv-managed interpreter outside ~/.local/share/uv/python are never found. The same interpreter copied under the default dir is found.

scan -g exits 0 with status: success. Nothing tells the user that a whole class of global installs was skipped.

Impact

-g is documented as "Operate on globally-installed packages" (CLI_CONTRACT.md, Global arguments). The crawler explicitly tries to cover uv tool envs and uv-managed Pythons (see the comments at python_crawler.rs:1250 and :1296). Every command that resolves global PyPI paths uses the same get_site_packages_paths path (ecosystem_dispatch.rs:280): scan, get, apply, rollback and vex. A vulnerable dependency inside a uv tool installed CLI is therefore silently never reported, patched or attested on Windows, and likewise on Linux/macOS for anyone who relocates uv's dirs (common in CI, Docker images and dotfile setups).

I could not confirm the knock-on effect on get -g / apply -g / vex -g end to end. The repo's free e2e patch (725a5343-…, pydantic-ai 0.0.36, used by e2e_pypi.rs::test_pypi_global_lifecycle) now returns not_found from the public proxy even in the lane where scan finds the package. That is a separate observation, unrelated to uv.

Repro (Linux, uv 0.8.17, main 2463257)

H=$(mktemp -d); export HOME=$H
uv tool install --with six==1.16.0 pycowsay                                   # default dir
UV_TOOL_DIR=$H/custom-tools UV_TOOL_BIN_DIR=$H/cbin uv tool install --with idna==3.7 pycowsay
XDG_DATA_HOME=$H/xdg uv tool install --with six==1.15.0 pycowsay
UV_PYTHON_INSTALL_DIR=$H/pyinst uv python install 3.12
uv pip install --system --break-system-packages --python $H/pyinst/cpython-3.12*/bin/python3.12 six==1.14.0

# Point SOCKET_PROXY_URL at any HTTP server that logs POST /patch/batch bodies, then:
mkdir $H/outside && cd $H/outside
socket-patch scan -g --ecosystems pypi --json
UV_TOOL_DIR=$H/custom-tools socket-patch scan -g --ecosystems pypi --json
XDG_DATA_HOME=$H/xdg socket-patch scan -g --ecosystems pypi --json
UV_PYTHON_INSTALL_DIR=$H/pyinst socket-patch scan -g --ecosystems pypi --json

In every run the batch query contains six@1.16.0 (the default tool env) and never idna@3.7, six@1.15.0 or six@1.14.0, even with the matching env var set. All runs exit 0. Control: copying the same cpython-3.12.11-linux-x86_64-gnu dir under ~/.local/share/uv/python/ makes six@1.14.0 appear.

Expected vs actual

  • Expected: scan -g covers the tool envs and managed interpreters wherever uv itself puts them. That means following uv's own resolution: UV_TOOL_DIR, else $XDG_DATA_HOME/uv/tools, else ~/.local/share/uv/tools on Unix and %APPDATA%\uv\tools on Windows. Python installs follow the same pattern with UV_PYTHON_INSTALL_DIR. Alternatively, ask uv tool dir / uv python dir when uv is on PATH. The maintainer note on ledger Bug hunt ledger: uv #310 asks for exactly this: "find every globally installed uv package… none missing".
  • Actual: fixed paths only, with %LOCALAPPDATA% on Windows. Packages are silently missing and the scan exits 0.

OS × uv matrix (probe runs below; "found" = the tool env's dependency appears in the batch query)

OS uv default tool dir UV_TOOL_DIR XDG_DATA_HOME UV_PYTHON_INSTALL_DIR
Linux (sandbox) 0.8.17 found missed missed missed
ubuntu-latest 0.4.30 / 0.5.31 / 0.8.17 / 0.9.5 found missed – –
macos-latest 0.4.30 / 0.5.31 / 0.8.17 / 0.9.5 found (~/.local/share/uv/tools) missed – –
windows-latest 0.4.30 / 0.5.31 / 0.8.17 / 0.9.5 missed (uv tool dir = %APPDATA%\uv\tools) missed – –

Not bisected. These hard-coded paths predate v5, and this isn't a regression in #277.

Suspect code

  • crates/socket-patch-core/src/crawlers/python_crawler.rs:1271-1272: Windows tool root %LOCALAPPDATA%\uv\tools. uv uses %APPDATA%\uv\tools.
  • crates/socket-patch-core/src/crawlers/python_crawler.rs:1281-1288: Unix tool root hard-coded to ~/.local/share/uv/tools. No UV_TOOL_DIR / XDG_DATA_HOME.
  • crates/socket-patch-core/src/crawlers/python_crawler.rs:1304-1320: managed-Python root hard-coded. No UV_PYTHON_INSTALL_DIR. The Windows %LOCALAPPDATA%\uv\python root is likely wrong for the same reason, but I didn't probe it.

Similar shape to #443 (Bun env-var dirs) and #415 (pipx), which cover other managers.

Probe runs

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions