Skip to content

Commit 238df07

Browse files
committed
Crawl no Cargo sources under a config above the repository root
A project crawl stops the cargo config chain at the git root, so a .cargo/config.toml above it that replaces crates.io with a directory source went unread and the crawler fell through to the registry cache. vex could then verify a patched shared registry copy and emit not_affected while cargo builds the unpatched vendor directory, and rollback could restore a copy apply refuses to touch. Fail closed in source_roots instead, matching apply's cargo_config_outside_root gate. Co-Authored-By: Claude <noreply@anthropic.com>
1 parent f870bb0 commit 238df07

1 file changed

Lines changed: 53 additions & 0 deletions

File tree

‎crates/socket-patch-core/src/crawlers/cargo_crawler.rs‎

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,16 @@ async fn source_roots(options: &CrawlerOptions) -> (Vec<PathBuf>, bool) {
328328
return (Vec::new(), false);
329329
}
330330

331+
// Inside a git checkout, a `.cargo/config(.toml)` above the repository
332+
// root is never read, yet cargo merges it, and it may replace crates.io
333+
// with a directory source; the registry cache would then not be what
334+
// cargo builds from. Crawl nothing (fail closed), so `vex`, `scan` and
335+
// `rollback` never verify or restore a registry copy the build may not
336+
// use; `apply` refuses the same layout (`cargo_config_outside_root`).
337+
if crate::vendor::cargo_config::config_above_repo(&options.cwd).is_some() {
338+
return (Vec::new(), false);
339+
}
340+
331341
// Cargo project: crawl what cargo builds from. A config that
332342
// replaces crates.io with a directory source (`cargo vendor`) names
333343
// that directory, wherever it is; without one, cargo reads the
@@ -781,6 +791,49 @@ mod tests {
781791
);
782792
}
783793

794+
/// Inside a git checkout, a `.cargo/config.toml` above the repository
795+
/// root (which may replace crates.io) is never read, so a project
796+
/// crawl yields no source roots rather than falling through to the
797+
/// registry cache; a `--global` crawl is unaffected.
798+
#[tokio::test]
799+
async fn test_cargo_config_above_repo_yields_no_roots() {
800+
let dir = tempfile::tempdir().unwrap();
801+
let app = dir.path().join("app");
802+
tokio::fs::create_dir_all(app.join(".git")).await.unwrap();
803+
tokio::fs::create_dir_all(app.join("vendor")).await.unwrap();
804+
tokio::fs::write(app.join("Cargo.toml"), "[package]\nname = \"app\"\n")
805+
.await
806+
.unwrap();
807+
tokio::fs::create_dir_all(dir.path().join(".cargo"))
808+
.await
809+
.unwrap();
810+
tokio::fs::write(
811+
dir.path().join(".cargo").join("config.toml"),
812+
"[source.crates-io]\nreplace-with = \"v\"\n[source.v]\ndirectory = \"vendor\"\n",
813+
)
814+
.await
815+
.unwrap();
816+
817+
let crawler = CargoCrawler::new();
818+
let options = CrawlerOptions {
819+
cwd: app.clone(),
820+
global: false,
821+
global_prefix: None,
822+
};
823+
let paths = crawler.get_crate_source_paths(&options).await.unwrap();
824+
assert!(paths.is_empty(), "{paths:?}");
825+
826+
let custom = dir.path().join("registry-src");
827+
tokio::fs::create_dir_all(&custom).await.unwrap();
828+
let global = CrawlerOptions {
829+
cwd: app,
830+
global: false,
831+
global_prefix: Some(custom.clone()),
832+
};
833+
let paths = crawler.get_crate_source_paths(&global).await.unwrap();
834+
assert_eq!(paths, vec![custom]);
835+
}
836+
784837
/// A `Cargo.lock` alone (no `Cargo.toml`) is still a Rust project, so
785838
/// the vendor tree should be honored.
786839
#[tokio::test]

0 commit comments

Comments
 (0)