Skip to content

Commit c8361d8

Browse files
committed
crates_io_tarball: Disallow paths differing only by case
1 parent 3dc1848 commit c8361d8

File tree

1 file changed

+38
-13
lines changed
  • crates/crates_io_tarball/src

1 file changed

+38
-13
lines changed

crates/crates_io_tarball/src/lib.rs

+38-13
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::manifest::validate_manifest;
99
pub use crate::vcs_info::CargoVcsInfo;
1010
pub use cargo_manifest::{Manifest, StringOrBool};
1111
use flate2::read::GzDecoder;
12-
use std::collections::BTreeMap;
12+
use std::collections::{BTreeMap, HashSet};
1313
use std::io::Read;
1414
use std::path::{Path, PathBuf};
1515
use std::str::FromStr;
@@ -33,6 +33,8 @@ pub enum TarballError {
3333
Malformed(#[source] std::io::Error),
3434
#[error("invalid path found: {0}")]
3535
InvalidPath(String),
36+
#[error("duplicate path found: {0}")]
37+
DuplicatePath(String),
3638
#[error("unexpected symlink or hard link found: {0}")]
3739
UnexpectedSymlink(String),
3840
#[error("Cargo.toml manifest is missing")]
@@ -41,8 +43,6 @@ pub enum TarballError {
4143
InvalidManifest(#[from] cargo_manifest::Error),
4244
#[error("Cargo.toml manifest is incorrectly cased: {0:?}")]
4345
IncorrectlyCasedManifest(PathBuf),
44-
#[error("more than one Cargo.toml manifest in tarball: {0:?}")]
45-
TooManyManifests(Vec<PathBuf>),
4646
#[error(transparent)]
4747
IO(#[from] std::io::Error),
4848
}
@@ -67,6 +67,7 @@ pub fn process_tarball<R: Read>(
6767

6868
let mut vcs_info = None;
6969
let mut manifests = BTreeMap::new();
70+
let mut paths = HashSet::new();
7071

7172
for entry in archive.entries()? {
7273
let mut entry = entry.map_err(TarballError::Malformed)?;
@@ -93,6 +94,13 @@ pub fn process_tarball<R: Read>(
9394
));
9495
}
9596

97+
let lowercase_path = entry_path.as_os_str().to_ascii_lowercase();
98+
if !paths.insert(lowercase_path) {
99+
return Err(TarballError::DuplicatePath(
100+
entry_path.display().to_string(),
101+
));
102+
}
103+
96104
// Let's go hunting for the VCS info and crate manifest. The only valid place for these is
97105
// in the package root in the tarball.
98106
if entry_path.parent() == Some(pkg_root) {
@@ -116,13 +124,6 @@ pub fn process_tarball<R: Read>(
116124
}
117125
}
118126

119-
if manifests.len() > 1 {
120-
// There are no scenarios where we want to accept a crate file with multiple manifests.
121-
return Err(TarballError::TooManyManifests(
122-
manifests.into_keys().collect(),
123-
));
124-
}
125-
126127
// Although we're interested in all possible cases of `Cargo.toml` above to protect users
127128
// on case-insensitive filesystems, to match the behaviour of cargo we should only actually
128129
// accept `Cargo.toml` and (the now deprecated) `cargo.toml` as valid options for the
@@ -301,12 +302,36 @@ mod tests {
301302
};
302303

303304
let err = assert_err!(process(vec!["cargo.toml", "Cargo.toml"]));
304-
assert_snapshot!(err, @r###"more than one Cargo.toml manifest in tarball: ["foo-0.0.1/Cargo.toml", "foo-0.0.1/cargo.toml"]"###);
305+
assert_snapshot!(err, @"duplicate path found: foo-0.0.1/Cargo.toml");
305306

306307
let err = assert_err!(process(vec!["Cargo.toml", "Cargo.Toml"]));
307-
assert_snapshot!(err, @r###"more than one Cargo.toml manifest in tarball: ["foo-0.0.1/Cargo.Toml", "foo-0.0.1/Cargo.toml"]"###);
308+
assert_snapshot!(err, @"duplicate path found: foo-0.0.1/Cargo.Toml");
308309

309310
let err = assert_err!(process(vec!["Cargo.toml", "cargo.toml", "CARGO.TOML"]));
310-
assert_snapshot!(err, @r###"more than one Cargo.toml manifest in tarball: ["foo-0.0.1/CARGO.TOML", "foo-0.0.1/Cargo.toml", "foo-0.0.1/cargo.toml"]"###);
311+
assert_snapshot!(err, @"duplicate path found: foo-0.0.1/cargo.toml");
312+
}
313+
314+
#[test]
315+
fn test_duplicate_paths() {
316+
let tarball = TarballBuilder::new()
317+
.add_file("foo-0.0.1/Cargo.toml", MANIFEST)
318+
.add_file("foo-0.0.1/foo.rs", b"")
319+
.add_file("foo-0.0.1/foo.rs", b"")
320+
.build();
321+
322+
let err = assert_err!(process_tarball("foo-0.0.1", &*tarball, MAX_SIZE));
323+
assert_snapshot!(err, @"duplicate path found: foo-0.0.1/foo.rs")
324+
}
325+
326+
#[test]
327+
fn test_case_insensitivity() {
328+
let tarball = TarballBuilder::new()
329+
.add_file("foo-0.0.1/Cargo.toml", MANIFEST)
330+
.add_file("foo-0.0.1/foo.rs", b"")
331+
.add_file("foo-0.0.1/FOO.rs", b"")
332+
.build();
333+
334+
let err = assert_err!(process_tarball("foo-0.0.1", &*tarball, MAX_SIZE));
335+
assert_snapshot!(err, @"duplicate path found: foo-0.0.1/FOO.rs")
311336
}
312337
}

0 commit comments

Comments
 (0)