From bb99dc4f5be0f069d21921db8fc2b83075144a25 Mon Sep 17 00:00:00 2001 From: na Date: Sat, 22 May 2021 13:52:54 +0200 Subject: [PATCH 1/3] Adding `validate_name` methon on crate_name --- src/crate_name.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/crate_name.rs b/src/crate_name.rs index cb3d5ff6b4..343544c7b7 100644 --- a/src/crate_name.rs +++ b/src/crate_name.rs @@ -42,6 +42,33 @@ impl<'a> CrateName<'a> { self.0.contains('.') || self.0.contains('/') || self.0.contains('\\') } + /// Checks is the specified crate name is a valid, non-empty name for a crates.io crate, + /// meaning it contains only a-zA-Z, dashes, and underscores. + /// expected to be usually called as validate_name()?; + pub fn validate_name(&self) -> Result<()> { + if self.name().is_empty() { + return Err(ErrorKind::EmptyCrateName.into()); + } + + + let mut invalid_char = 'a'; + let contains_only_valid_characters = self.name().chars().all(|c| { + let is_valid = c.is_alphanumeric() || c == '-' || c == '_'; + + if !is_valid { + invalid_char = c; + } + is_valid + }); + + if !contains_only_valid_characters { + assert_ne!(invalid_char, 'a'); + return Err(ErrorKind::CrateNameContainsInvalidCharacter(self.name().to_string(), invalid_char).into()); + } + + Ok(()) + } + /// If this crate specifier includes a version (e.g. `docopt@0.8`), extract the name and /// version. pub fn parse_as_version(&self) -> Result> { From 3ebad16eee1896108047ca242a55d189e531a172 Mon Sep 17 00:00:00 2001 From: na Date: Sat, 22 May 2021 13:57:57 +0200 Subject: [PATCH 2/3] Validating input by checking crate names. Added error CrateNameContainsInvalidCharacter. Blacklisted crate name '.' --- src/bin/add/args.rs | 6 ++++++ src/bin/add/main.rs | 4 ++++ src/crate_name.rs | 2 ++ src/errors.rs | 5 +++++ src/fetch.rs | 4 +--- 5 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/bin/add/args.rs b/src/bin/add/args.rs index ef5ee9cd8e..f9ade4a64a 100644 --- a/src/bin/add/args.rs +++ b/src/bin/add/args.rs @@ -167,6 +167,10 @@ impl Args { } fn parse_single_dependency(&self, crate_name: &str) -> Result { + if crate_name == "." { + return Err(ErrorKind::AttemptedCreatingCircularDependency.into()); + } + let crate_name = CrateName::new(crate_name); if let Some(mut dependency) = crate_name.parse_as_version()? { @@ -183,6 +187,7 @@ impl Args { Ok(dependency) } else if crate_name.is_url_or_path() { + println!("hello"); Ok(crate_name.parse_crate_name_from_uri()?) } else { assert_eq!(self.git.is_some() && self.vers.is_some(), false); @@ -191,6 +196,7 @@ impl Args { assert_eq!(self.path.is_some() && self.registry.is_some(), false); let mut dependency = Dependency::new(crate_name.name()); + dbg!(&dependency); if let Some(repo) = &self.git { dependency = dependency.set_git(repo, self.branch.clone()); diff --git a/src/bin/add/main.rs b/src/bin/add/main.rs index 64d3b92e08..36d3750217 100644 --- a/src/bin/add/main.rs +++ b/src/bin/add/main.rs @@ -30,6 +30,10 @@ mod args; mod errors { error_chain! { errors { + /// Running `cargo-add .` would create a create circular dependency, this error prevents it + AttemptedCreatingCircularDependency { + description("Attempting to create circular dependency by specifying crate name `.`") + } /// Specified a dependency with both a git URL and a version. GitUrlWithVersion(git: String, version: String) { description("Specified git URL with version") diff --git a/src/crate_name.rs b/src/crate_name.rs index 343544c7b7..c996eb00fc 100644 --- a/src/crate_name.rs +++ b/src/crate_name.rs @@ -77,6 +77,8 @@ impl<'a> CrateName<'a> { let (name, version) = (xs[0], xs[1]); semver::VersionReq::parse(version).chain_err(|| "Invalid crate version requirement")?; + self.validate_name()?; + Ok(Some(Dependency::new(name).set_version(version))) } else { Ok(None) diff --git a/src/errors.rs b/src/errors.rs index 6d30599413..aa37c1c2b5 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -8,6 +8,11 @@ error_chain! { } errors { + /// A crate contains invalid symbol + CrateNameContainsInvalidCharacter(crate_name: String, symbol: char) { + description("Specified crate name(s) contains invalid symbol(s)") + display("Crate name \"{}\" is invalid, contains symbol '{}' (byte values: {})", crate_name, &symbol, symbol.escape_unicode()) + } /// Failed to read home directory ReadHomeDirFailure { description("Failed to read home directory") diff --git a/src/fetch.rs b/src/fetch.rs index fbf0d75de8..4b34924529 100644 --- a/src/fetch.rs +++ b/src/fetch.rs @@ -49,9 +49,7 @@ pub fn get_latest_dependency( return Ok(Dependency::new(crate_name).set_version(&new_version)); } - if crate_name.is_empty() { - return Err(ErrorKind::EmptyCrateName.into()); - } + crate::CrateName::new(crate_name).validate_name()?; let registry_path = match registry { Some(url) => registry_path_from_url(url)?, From 41fe392c48c4725245198a1a6d9262e4ca8cf56e Mon Sep 17 00:00:00 2001 From: na Date: Sat, 22 May 2021 14:31:40 +0200 Subject: [PATCH 3/3] Crate name validation -check if 1st char is letter --- src/crate_name.rs | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/src/crate_name.rs b/src/crate_name.rs index c996eb00fc..cf62609909 100644 --- a/src/crate_name.rs +++ b/src/crate_name.rs @@ -51,19 +51,31 @@ impl<'a> CrateName<'a> { } - let mut invalid_char = 'a'; - let contains_only_valid_characters = self.name().chars().all(|c| { - let is_valid = c.is_alphanumeric() || c == '-' || c == '_'; - - if !is_valid { - invalid_char = c; - } - is_valid - }); + let contains_only_valid_characters: bool; + let mut invalid_char: char; + + if self.name().chars().next().unwrap().is_alphabetic() { + invalid_char = 'a'; //placeholder value. Is always a valid character in a crate name. + contains_only_valid_characters = self.name().chars().all(|c| { + let is_valid = (c.is_alphanumeric() || c == '-' || c == '_') && c.is_ascii(); + + if !is_valid { + invalid_char = c; + } + is_valid + }); + } else { + invalid_char = self.name().chars().next().unwrap(); + contains_only_valid_characters = false; + } if !contains_only_valid_characters { - assert_ne!(invalid_char, 'a'); - return Err(ErrorKind::CrateNameContainsInvalidCharacter(self.name().to_string(), invalid_char).into()); + assert_ne!(invalid_char, 'a'); //check if invalid_char still does not contains its initial placeholder value. That should never happen + return Err(ErrorKind::CrateNameContainsInvalidCharacter( + self.name().to_string(), + invalid_char, + ) + .into()); } Ok(()) @@ -78,7 +90,7 @@ impl<'a> CrateName<'a> { semver::VersionReq::parse(version).chain_err(|| "Invalid crate version requirement")?; self.validate_name()?; - + Ok(Some(Dependency::new(name).set_version(version))) } else { Ok(None)