CVE-2023-5752 is a command injection vulnerability in pip before version 23.3, where installing a package from a Mercurial VCS URL with a crafted revision lets an attacker inject arbitrary configuration options into the underlying hg clone call via a leading dash. In other words, a revision string that starts with - is interpreted by Mercurial as a command-line option rather than a revision, and pip passed it through unquoted. It is classified as CWE-77, command injection, and only affects installs that pull from Mercurial.
The bug is narrow in scope but instructive: it is a textbook argument-injection flaw hiding inside a package manager millions of developers run without a second thought.
How the injection works
When you install from a version-control URL, pip shells out to the relevant VCS client. For a Mercurial source you write something like:
pip install "hg+https://example.com/repo@<revision>"
pip translates the @<revision> fragment into arguments for hg clone. In the vulnerable code, it built the argument list as ["-r", rev], then passed it to the subprocess. The problem is that rev comes straight from the URL, and argument parsers do not care where a string came from; they only care what it looks like.
If rev is a normal revision like stable or a changeset hash, everything is fine. But if rev begins with a dash, Mercurial's own argument parser reads it as a flag. Mercurial supports --config section.key=value to override configuration on the command line, and that configuration can change which repository is cloned and how, or set hooks that run external commands. By smuggling a --config option in through the revision, an attacker influences the clone in ways pip never intended.
This is not classic shell metacharacter injection; there is no ; or backtick involved. It is argument injection: attacker-controlled data lands in a position where the downstream program treats it as an option flag.
What an attacker can achieve
The realistic attack is supply-chain contamination. By controlling the Mercurial configuration for the clone, an attacker can alter how and which repository is installed, which opens the door to installing malicious code or redirecting the fetch. The prerequisite is that a victim installs from an attacker-influenced hg+ URL or a requirements entry an attacker can taint, for example a dependency reference in a shared or automatically processed requirements file.
That prerequisite keeps the practical blast radius modest for most teams: if you never install from Mercurial URLs, you are simply not affected. But automated pipelines that install from dynamically constructed requirements, or systems that accept a package spec from a user, are exactly where this becomes real.
Affected versions
Verified against the GitHub advisory and NVD:
Affected: pip < 23.3
Fixed in: pip 23.3
Class: CWE-77 command injection (argument injection)
Condition: installing from a Mercurial (hg+) VCS URL
Not affected: users who never install from Mercurial
The fix changed the argument construction so the revision can never be parsed as an option. Instead of ["-r", rev], patched pip emits the revision bound to its flag as a single token (-r=<rev> form), so a leading dash in rev is treated as part of the value, not a new option.
Remediation
Upgrade pip to 23.3 or later. This is the whole fix:
python -m pip install --upgrade "pip>=23.3"
In containers and CI, remember that the pip you invoke may not be the pip your base image ships. Pin it explicitly in your image build and in any virtualenv bootstrap. A stale pip cached in a builder layer is the usual reason a scanner keeps flagging this after you thought you patched.
If you truly cannot upgrade, the interim mitigation is procedural: do not install from Mercurial URLs, and treat any requirements source that can carry a VCS URL as untrusted input. But 23.3 is old enough now that there is little reason not to upgrade.
Because pip itself is a dependency of your build environment, this is worth catching automatically. An SCA tool that scans your build images and lockfiles will flag a pip older than 23.3 across every project, which is more reliable than remembering to check each Dockerfile.
The general lesson: never let untrusted data reach an argument slot
CVE-2023-5752 is one instance of a pattern that recurs everywhere subprocesses are invoked. Any time you build an argument list from external input, a value that starts with - can become a flag. The defenses are consistent regardless of language: use -- to terminate option parsing before positional arguments, bind values to their flags as single tokens where the tool supports it, and validate that untrusted values do not begin with a dash. If you audit your own code after reading this, grep for places where user input flows into subprocess, exec, or a shell-out and check that none of them can be reinterpreted as an option.
FAQ
Am I affected by CVE-2023-5752 if I do not use Mercurial?
No. The vulnerability only triggers when pip installs from a Mercurial (hg+) VCS URL. Installs from PyPI, Git, or local paths are not affected.
What version of pip fixes it?
pip 23.3. Any earlier version is vulnerable when installing from a Mercurial source.
Is this remote code execution?
It is argument injection that lets an attacker control the Mercurial clone configuration, which can lead to installing malicious code. It requires the victim to install from an attacker-influenced hg+ URL, so it is not unauthenticated RCE against an arbitrary target.
How do I make sure CI actually uses the patched pip?
Pin pip explicitly in your image build and virtualenv bootstrap (pip install "pip>=23.3"), and verify with pip --version at runtime. Base images and cached builder layers often ship an older pip than you expect.