CodeVetter · Repo Unpack · Risk map
astral-sh/uv
uv is designed to run on a trusted developer or CI machine while consuming untrusted package, Git, and archive bytes. The threat model in agents/references/threat-model.md is the security contract: locked operations must not change sources or hashes, credentials must stay in their realm, and archive members must not write outside the chosen root. Concrete risk in the code includes plaintext credentials by default, a non-atomic uv.lock write, build backends and uv run executing arbitrary code by design, and a very large command dispatcher that every CLI change touches. Optional features and preview flags also change security-relevant defaults between the cargo-dist binary and distro packages.
- The threat model treats the local machine, environment, project files, PATH, certificates, and caches as trusted, and treats untrusted publisher files, public package names, attacker-controlled Git refs, unauthenticated responses, and archives as attacker-controlled. During a locked operation the lockfile sources, object IDs, and hashes are authoritative and must not be replaced from upstream changes.
- Default credential storage is plaintext credentials.toml. Native keyring storage is used only when PreviewFeature::NativeAuth is enabled. The file is exclusive-locked, but the format is ordinary TOML with username, password, or bearer token. UV_CREDENTIALS_DIR redirects the directory.
- LockTarget::commit serializes with lock.to_toml and writes the destination path directly with fs_err::tokio::write. A crash during that write can leave a truncated uv.lock. Readers parse with Lock::from_toml and surface LockError::UnsupportedVersion when the major version is not 1.
- Selected packages may run arbitrary code. The threat model says interpreter startup, .pth files, bytecode compilation, and entry points change timing rather than authority, but build-backend execution is a boundary crossing if it happens before selection, escapes isolation, or runs with greater privilege. uv run will spawn whatever command the user named after syncing dependencies.
- base_client_builder forwards allow_insecure_host into BaseClientBuilder. That flag is an explicit trusted operator choice in the threat model, but it disables the normal TLS host check for the listed hosts and is therefore a high-impact misconfiguration.
- Git identity rules require a documented 40-character hex value to resolve as that immutable object; a same-named branch or tag must not win. Shorter hex identifiers can be redirected by an attacker-controlled ref. uv-git and the lockfile's recorded object ID are the code that has to uphold this.
- Self-update is compiled out unless the self-update feature is on. Distro or maturin builds take the ExternallyInstalledError path. The cargo-dist build uses axoupdater and accepts UV_GITHUB_TOKEN on uv self update. Shipping the wrong feature set either enables network self-update or makes uv self update fail closed.
- Python installs download from GitHub releases or a configured Astral mirror and record an optional sha256 on ManagedPythonDownload. The threat model treats configured HTTPS mirrors as trust roots. A custom python_downloads_json_url replaces the bundled catalog, so a bad URL is an operator-controlled supply-chain input.
- Archive extraction and wheel install are the filesystem blast radius. The threat model says metadata must not cause writes or deletions outside the chosen root and intentionally followed symlink or junction targets. uv-extract and uv-install-wheel implement that path; link mode symlink makes the environment share cache inodes.
- crates/uv/src/lib.rs is the single dispatch hotspot. The command match starts at the Auth arm and continues through pip, cache, build, venv, project, self, tool, python, publish, workspace, and build-backend. A settings or client-builder change there affects every subcommand.
- Generated shell activation is called out in the threat model: untrusted values must be quoted for the target shell, and impact rises if the script is sourced automatically. uv venv and python update-shell write those scripts. The nushell integration test only checks that activate.nu can be overlay-used.
- Preview flags change defaults without a major version bump: NativeAuth, MalwareCheck, LockWithoutMetadata, CentralizedProjectEnvs, and VenvSafeClear. Callers that enable --preview or UV_PREVIEW turn all of them on at once, including in the uv-build backend, which parses UV_PREVIEW and UV_PREVIEW_FEATURES itself because it does not use clap.