September 30, 2019·6 min read·engineeringrustsecuritycargodependencies

Old Cargo can mistake a dependency alias for a package name

CVE-2019-16760 affects Cargo versions that ignore the package key used for renamed dependencies. A manifest written for newer tools can cause an older tool to fetch a different crate under the alias name.

Ignoring an unknown manifest key can change which code a package manager downloads. Cargo's dependency-renaming feature separates the package's published name from the name used locally. Older versions ignore the key that carries the published name and can therefore request the local alias as though it were a different package.

The September 30 advisory identifies this concern as CVE-2019-16760. Rust 1.0.0 through 1.25.0 are affected, corresponding to Cargo through version 0.26.0. Rust 1.26.0 and later are outside the affected range, though the intermediate releases do not all support using the feature successfully.

The security problem is dependency identity. An attacker could register a crate under the alias name and make it appear compatible with the intended dependency. The advisory reports no evidence of exploitation in its audit of existing crates on crates.io. That finding describes the audit's scope as of disclosure, rather than a guarantee about every later or private manifest.

I find this defect useful to understand because readable metadata can still have version-dependent semantics. A dependency declaration that looks precise to its author may mean something different to an older reader. For a package manager, that difference can select code from an entirely different publisher.

The dependency key can be a local alias

The versioned Cargo reference describes package as the key selecting the published package when the dependency uses another local name. It gives motivations including multiple versions of a crate and avoiding repeated source-level aliases.

The advisory uses a renamed Serde dependency. Its manifest entry includes the published package name explicitly.

[dependencies]
serde1 = { version = "1.0", features = ["derive"], package = "serde" }

A supporting Cargo selects the package named serde and makes it available under the local dependency name serde1. Those names have different jobs. The left-hand key is not a request to obtain a package actually published as serde1 when the package value is honored.

The reference also explains that optional-dependency feature names follow the dependency's local name. That reinforces why local naming is useful independently of package identity. A program's own feature and import vocabulary can differ from the package's registry name while still referring to the intended code.

This additional meaning is carried by manifest metadata, not merely by a Rust import statement. Cargo needs to understand the metadata before it chooses which package to fetch. A source-level alias applied after downloading cannot repair a package manager that already chose the wrong package.

Ignoring the new key changes the request

For Rust 1.25.0 and earlier, the advisory says Cargo ignores package. The example then becomes equivalent to a dependency on the crate named serde1, with the same version requirement and requested feature. The ignored key has removed the only information distinguishing the intended package from the alias.

An attacker able to publish under that alias could supply a package that resembles the expected dependency but acts maliciously when built. The advisory describes squatting the alias name as the exploitation route. This is a claim about what the behavior permits, not evidence that the example name was actually used in an attack.

The problem does not depend on compromising the intended crate's account. A different registry name becomes the target of the request. Authentication of a download from the registry would therefore not establish that the build selected the package the manifest author intended.

This separates the issue from changed archive contents under one package version. Here the older tool's interpretation changes the identity before retrieval. Checking that the received archive belongs to the requested alias package would still leave the wrong selection intact.

Rejection is safer than a different interpretation

The feature first appeared as unstable implementation support in Rust 1.26.0 and became a supported feature in Rust 1.31.0. The advisory distinguishes these intervals. Rust 1.26.0 through 1.30.0 typically reject the unstable key instead of silently interpreting the dependency as the alias package.

Those intermediate versions are therefore unaffected by the security issue without necessarily providing a successful build. This is an important distinction when reporting the fixed boundary. Understanding that a feature is not yet available and stopping differs from ignoring identity metadata and continuing with another package.

Rust 1.31.0 and newer understand the key. The advisory's minimum mitigation boundary is 1.26.0 because rejecting the unsupported construct already prevents this incorrect download. A statement that every unaffected version fully supports dependency renaming would be inaccurate.

I prefer this explicit separation between compatibility and safe failure. A build error can communicate that the installed tool does not understand the manifest. A successful-looking resolution to a different dependency conceals the incompatibility and allows it to affect executable code.

Transitive manifests extend the exposure

The advisory says the issue also affects manifests published to crates.io. A developer does not need to write a renamed dependency locally to encounter a package that declares one. Building a dependency using an affected Cargo can bring its manifest into the same interpretation path.

This makes auditing only the application's top-level manifest insufficient for identifying the possible trigger. The relevant metadata can belong to another crate in the dependency graph. The security question includes which Cargo version processes that graph, alongside which manifest the developer directly edited.

It also explains why a package's behavior on its author's machine is incomplete evidence about older consumers. A newer Cargo can interpret the renamed dependency correctly and build the intended crate. Publishing that manifest then exposes it to consumers whose older tools use a different interpretation. The same text can consequently produce different package requests across those environments.

For this advisory, the version of the consumer's Cargo is therefore a direct part of the affected condition. An author using a current toolchain does not automatically establish that all downstream users are outside the affected range. The published mitigation changes the older consumer's interpretation or rejects the unsupported feature.

The team's audit inspected currently published crates using the package key and found no evidence of exploitation. It does not establish that every future crate, unpublished internal package or alternate source has been examined. The advisory asks for reports of later examples through the security process.

There is also no need to infer an attack from every renamed dependency. Renaming is a legitimate supported feature in newer Cargo. Exposure arises from the combination of that metadata, an older tool ignoring it and the possibility of an attacker-controlled package at the name the older tool requests.

The mitigation targets the interpreting tool

The team recommends updating affected toolchains to the latest available compiler, with Rust 1.26.0 or newer as the minimum boundary preventing this issue. It will not issue patch releases for versions before that boundary. The advisory instead offers patches for users of Rust 1.19.0 through 1.25.0 who must retain those versions.

This means the practical fix belongs to the Cargo doing the dependency interpretation. Reviewing the intended Serde implementation, for example, would not change how an old Cargo reads the manifest. The resolver must either understand the package key or stop instead of substituting the alias as the registry identity.

The reported timeline begins with Elichai Turkel's September 18 report. The Cargo, Core and crates.io teams discussed the response that day, with the audit and patches developed before the public announcement. These dates explain why the disclosure includes both an affected-version boundary and an audit result without announcing a new general release as its sole mitigation.

Identity metadata has execution consequences

The broader engineering issue is how a tool handles a format it only partly understands. Some optional metadata can be ignored without changing the selected input. A field that determines the package's identity has a different consequence, because omission changes which code enters the build.

This is an inference from the documented mechanism, rather than a claim that Cargo now rejects every unknown manifest field. The advisory concerns a specific key and specific older versions. Its lesson for this release history is that successful parsing alone did not establish a compatible interpretation of the dependency declaration.

The supported behavior preserves both names, the registry package to obtain and the local name used by the consumer. Updating the interpreting tool restores that distinction. The local alias can then serve its intended purpose without becoming an unintended request for a separately published crate.