Skip to content

Prevent fallback to PyPi when private index returns 401/403 (to mitigate dependency confusion risk) #12362

@bendikhk

Description

@bendikhk

I’m using uv with a setup where a private package index requires both authentication and a VPN connection. My intention is to always prefer the private index and only fall back to PyPI if the package truly does not exist on the private index.

This works as expected when the VPN and credentials are valid.

However, in cases where the VPN is disconnected or credentials are invalid (resulting in a 401 or 403), uv silently falls back to PyPI if the package is found there. This exposes a potential dependency confusion attack, where a malicious package on PyPI could be installed if the private index is temporarily unavailable.

Ideal behavior:
If the private index returns a 401 or 403, I would expect uv to not fall back to PyPI. Instead, it should treat this as a hard failure and return an error that it couldn't reach index 401/403.

I'm not 100% sure if this should be a feature request or a bug report. After reading the Package Indexes documentation I assumed it would work as as explained above. However if this is intentional, could an alternative be a stricter --index-strategy with this behaviour?

Current setup:
uv.toml

[[index]]
url = "https://private-repo.com/simple"
authenticate = "always"

.netrc

machine private-repo.com
    login user
    password PW

Example behaviour:

  1. Private index unavailable and package not on pypi
$ uv pip install private-package-not-on-pypi
× No solution found when resolving dependencies:
╰─▶ Because private-package-not-on-pypi was not found in the package registry and you require it, your requirements are unsatisfiable.

hint: An index URL (https://private-repo.com/simple) could not be queried due to a lack of valid authentication credentials (401 Unauthorized).
  1. Private index unavailable and package also on pyp
$ uv pip install private-package-also-on-pypi
→ Installs package from PyPI silently

Platform

Linux 6.11.0-19-generic x86_64 GNU/Linux

Version

0.6.9

Python version

Python 3.12.9

Metadata

Metadata

Assignees

No one assigned

    Labels

    questionAsking for clarification or support

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions