fix(plugins): keep pinned program paths out of the verbatim form (#2012)

std::fs::canonicalize returns \\?\-prefixed paths on Windows, so a process grant's pinned program path was stored in that form. Canonicalize the program's directory with dunce, which keeps the usual form.

Fixes #2001

Co-authored-by: zaojiaoci <31303376+zaojiaoci@users.noreply.github.com>
This commit is contained in:
zaojiaoci
2026-11-10 23:19:14 +08:00
committed by GitHub
co-authored by zaojiaoci
parent a60293929a
commit 2ad642cdc9
2 changed files with 19 additions and 2 deletions
+1
View File
@@ -18,6 +18,7 @@ semver = "1"
blake3.workspace = true
zip.workspace = true
base64.workspace = true
dunce.workspace = true
ed25519-dalek = "3"
[dev-dependencies]
+18 -2
View File
@@ -441,11 +441,13 @@ pub fn resolve_program(name: &str, path_var: Option<&OsStr>) -> Result<PathBuf,
/// `p` with its parent canonicalized but the file name as found: PATH
/// entries like `/var` → `/private/var` (macOS) or `~` links collapse to
/// their real directory, while a linked program (`cargo` → rustup) keeps
/// its link name so the shim still dispatches on argv[0].
/// its link name so the shim still dispatches on argv[0]. `dunce` keeps the
/// path in its usual form on Windows: a `\\?\`-prefixed pin would never
/// match the path the program was actually started with.
fn canonical_dir(p: &Path) -> PathBuf {
let Some(dir) = p.parent() else { return p.to_path_buf() };
let Some(name) = p.file_name() else { return p.to_path_buf() };
std::fs::canonicalize(dir).map(|d| d.join(name)).unwrap_or_else(|_| p.to_path_buf())
dunce::canonicalize(dir).map(|d| d.join(name)).unwrap_or_else(|_| p.to_path_buf())
}
fn is_executable(p: &Path) -> bool {
@@ -498,6 +500,20 @@ mod tests {
list.iter().map(|s| s.parse().unwrap()).collect()
}
#[test]
#[cfg(windows)]
fn pinned_program_path_has_no_verbatim_prefix() {
// std::fs::canonicalize returns \\?\-prefixed paths on Windows; a pin
// stored in that form would never match the plain path the program is
// started with. dunce keeps the usual form.
let dir = tempfile::tempdir().unwrap();
std::fs::write(dir.path().join("cargo.exe"), b"").unwrap();
let pinned = resolve_program("cargo.exe", Some(dir.path().as_os_str())).unwrap();
let shown = pinned.to_string_lossy();
assert!(!shown.starts_with(r"\\?\"), "verbatim prefix leaked into the pin: {shown}");
assert!(pinned.is_absolute());
}
fn req<'a>(version: &'a str, hash: &'a str, required: &[&str], optional: &[&str]) -> Request<'a> {
Request {
subject: Subject::Installed("p".into()),