Skip to content

Hosted cargo redirect refuses a crates.io dependency declared with an explicit registry = "crates-io", treating crates.io as "another registry" #386

Description

[agent] Found by the scheduled Cargo bug-hunt routine (ledger #315).

Summary

scan --mode hosted skips a patched crate when its Cargo.toml declaration names the default registry explicitly: cfg-if = { version = "1.0.4", registry = "crates-io" }, or registry = "crates-io" in a [dependencies.cfg-if] table. It warns redirect_cargo_toml_dep_unrewritable … cannot be pinned (pinned to another registry ("crates-io")); dependency skipped (nothing rewritten), reports redirected: 0, and exits 0 with status: "success".

crates-io is Cargo's built-in name for crates.io, so this dependency is exactly the crates.io dependency hosted mode is built to redirect. Cargo resolves it identically to a declaration with no registry key: the baseline cargo build --locked succeeds, and the lock entry is source = "registry+https://github.com/rust-lang/crates.io-index". The refusal fires when it shouldn't.

Impact

Hosted mode never pins the patch for this dependency, so the project keeps building the unpatched crate. The only signal is a warning inside a success envelope. It fails closed: nothing is rewritten and nothing unpatched is attested. Vendored mode handles the same declaration correctly (vendor applies it, and cargo run --locked --offline links the patched marker), so the gap is hosted-only. Workaround: delete the redundant registry = "crates-io".

Repro

This uses the existing wiremock sparse-registry harness in crates/socket-patch-cli/tests/e2e_redirect_cargo_shapes.rs. Add a shape identical to cargo_hosted_legacy_config_is_restored_byte_for_byte, minus the config file, with this dependency line:

("Cargo.toml", consumer_manifest("cfg-if = { version = \"1.0.4\", registry = \"crates-io\" }\n")),
// or: consumer_manifest("") + "\n[dependencies.cfg-if]\nversion = \"1.0.4\"\nregistry = \"crates-io\"\n"

cargo test -p socket-patch-cli --test <file> then fails at the every patch redirected assertion:

"redirect":{"mode":"hosted","redirected":0,"rewrittenFiles":[],"skipped":[],
 "warnings":[{"code":"redirect_cargo_toml_dep_unrewritable",
   "detail":"cfg-if in Cargo.toml cannot be pinned (pinned to another registry (\"crates-io\")); dependency skipped (nothing rewritten)"}]}

The baseline steps before the scan pass: cargo generate-lockfile, and cargo build --locked with that manifest on cargo 1.93.1.

Expected vs actual

  • Expected: docs/ecosystems.md (Cargo row) says hosted mode redirects direct crates.io dependencies through a per-patch sparse registry ([registries.socket-patch-<uuid>] plus Cargo.lock source/checksum). A dependency whose registry is crates-io is a direct crates.io dependency, so the rewriter should replace the value with registry = "socket-patch-<uuid>", the same way it already replaces a stale socket-patch-* value.
  • Actual: it's refused as "another registry", and nothing is redirected.

Matrix

OS cargo inline { …, registry = "crates-io" } [dependencies.x] table control: no registry key
Linux 1.93.1 fail (2/2 runs) fail (2/2 runs) pass
macOS / Windows — not probed: a pure text rewriter with no OS-specific branch

In the same run, these shapes passed on Linux: optional = true with a [features] reference (both "cfg-if" and "dep:cfg-if"), default-features = false, a "1" caret requirement, a workspace where one member renames the dep, an idempotent re-scan, and cargo vendor after the redirect.

Suspect code

crates/socket-patch-core/src/patch/redirect/mod.rs:2547-2569 (table form) and :2665-2682 (inline form). A registry value other than the target or an is_socket_patch_registry_name value is refused. "crates-io" should be handled like a socket-patch name (replace the value) instead. The registry-index refusal next to it is a separate case, and it's fine.

Tested on main f6b7fb9 (CLI 4.0.0). I didn't bisect: the hosted cargo rewriter has had this branch since hosted mode shipped.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions