Not at the protocol level. PEP 708 proposed a way for Python package indexes to say which projects they track, so installers could refuse a lookalike from another index. After three years as provisionally accepted, it was rejected because its conditions were never met. Protection still depends on how you configure installs.
What dependency confusion means in Python
The PEP defines it plainly: a user expects package A and gets package B. In Python this almost always comes from using more than one repository, for example a private index plus PyPI. The user expects a name to come from one place, and someone publishes the same name in the other.
The root cause, in the PEP's words, is that there is no globally unique namespace. Each repository is its own namespace. When you ask for a name, the installer has to guess which repository it belongs to.
The PEP cites a real case. A project had an internal package that was meant to come only from its own download site, but that site was designed to be used alongside PyPI, and the name was not claimed on PyPI. An attacker registered it there.
What PEP 708 proposed
It would have extended the Simple Repository API with two ideas:
- A "tracks" field, so a repository can say a project on it mirrors a project on another repository.
- "Alternate locations", so a project could declare that its namespace spans several repositories on purpose.
With that metadata an installer could tell a legitimate merge from a surprise. The PEP was provisionally accepted with three conditions: an implementation in PyPI, an implementation in at least one other repository, and an opt-in implementation in pip. Its current status page says it was rejected because those conditions were never met.
What this means for your builds
Nothing changed in how pip merges indexes. If you configure more than one index, pip considers all of them. So the controls are yours:
| Control | What it does | Cost |
|---|---|---|
| One index only, with a proxy that holds internal names | Removes the second namespace | Needs a proxy you run |
| Claim your internal names on the public index | Stops a third party taking them | Public placeholder projects |
| Hash-checking mode | Refuses any file whose hash you did not record | Hash upkeep |
| Explicit per-package source | Tells the installer where a name must come from | Depends on your tool |
The PEP itself lists "rely only on hash checking" as an idea it did not choose as the default fix, because users must opt in and manage hashes, and build-time dependencies are harder to cover. That is a fair description of the trade-off, and it is also why hashes work well as a final gate in a pipeline you control. See pip require-hashes as the final gate and why extra-index-url is unsafe for private packages.
A short action list
- Run
pip config listand read every index setting in effect, including environment variables. - Remove any second index that exists only for convenience.
- For private package names, register the name publicly or serve them through a single proxy.
- Add hashes for the production install path.
- Alert when a name you consider internal appears in a public index.
Safeguard's dependency scanning and SBOM generation give you an inventory of the Python packages in a project, which makes step 5 easier to check against. You can try a free scan at gold.safeguard.sh/scan.
Try it yourself
Click through it at your own pace, or open it full screen. Look for package names you do not recognise as internal or public.
Quick check
Is PEP 708 in pip today?
No. The PEP was rejected, so there is no standard "tracks" metadata for pip to enforce.
Does a private index alone prevent dependency confusion?
Only if it is the sole index. If PyPI is also configured, the installer can still pick up a public package with the same name.
Sources
Last reviewed: 19 October 2026.