From 91d29c36ee4641ff2f9fc6452900dad26ee8ffb2 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Thu, 1 Aug 2024 20:32:29 +0200 Subject: [PATCH] fix: prohibit full names with a trailing `.`. Just like in Git, it's considered invalid. --- gix-validate/src/reference.rs | 12 +++++------- gix-validate/src/tag.rs | 6 ++++++ gix-validate/tests/reference/mod.rs | 16 ++++++++++++++-- gix-validate/tests/tag/mod.rs | 2 +- 4 files changed, 26 insertions(+), 10 deletions(-) diff --git a/gix-validate/src/reference.rs b/gix-validate/src/reference.rs index ac222ed105c..15a596a8346 100644 --- a/gix-validate/src/reference.rs +++ b/gix-validate/src/reference.rs @@ -15,8 +15,8 @@ pub mod name { StartsWithSlash, #[error("Multiple slashes in a row are not allowed as they may change the reference's meaning")] RepeatedSlash, - #[error("Names must not be a single '.', but may contain it.")] - SingleDot, + #[error("Path components must not start with '.'")] + StartsWithDot, } impl From for Error { @@ -28,8 +28,8 @@ pub mod name { use bstr::BStr; -/// Validate a reference name running all the tests in the book. This disallows lower-case references, but allows -/// ones like `HEAD`. +/// Validate a reference name running all the tests in the book. This disallows lower-case references like `lower`, but also allows +/// ones like `HEAD`, and `refs/lower`. pub fn name(path: &BStr) -> Result<&BStr, name::Error> { validate(path, Mode::Complete) } @@ -51,19 +51,17 @@ fn validate(path: &BStr, mode: Mode) -> Result<&BStr, name::Error> { return Err(name::Error::StartsWithSlash); } let mut previous = 0; - let mut one_before_previous = 0; let mut saw_slash = false; for byte in path.iter() { match *byte { - b'/' if previous == b'.' && one_before_previous == b'/' => return Err(name::Error::SingleDot), b'/' if previous == b'/' => return Err(name::Error::RepeatedSlash), + b'.' if previous == b'/' => return Err(name::Error::StartsWithDot), _ => {} } if *byte == b'/' { saw_slash = true; } - one_before_previous = previous; previous = *byte; } diff --git a/gix-validate/src/tag.rs b/gix-validate/src/tag.rs index eb5b7e12066..78202e39a5f 100644 --- a/gix-validate/src/tag.rs +++ b/gix-validate/src/tag.rs @@ -21,6 +21,8 @@ pub mod name { Asterisk, #[error("A ref must not start with a '.'")] StartsWithDot, + #[error("A ref must not end with a '.'")] + EndsWithDot, #[error("A ref must not end with a '/'")] EndsWithSlash, #[error("A ref must not be empty")] @@ -29,6 +31,7 @@ pub mod name { } /// Assure the given `input` resemble a valid git tag name, which is returned unchanged on success. +/// Tag names are provided as names, lik` v1.0` or `alpha-1`, without paths. pub fn name(input: &BStr) -> Result<&BStr, name::Error> { if input.is_empty() { return Err(name::Error::Empty); @@ -55,6 +58,9 @@ pub fn name(input: &BStr) -> Result<&BStr, name::Error> { if input[0] == b'.' { return Err(name::Error::StartsWithDot); } + if input[input.len() - 1] == b'.' { + return Err(name::Error::EndsWithDot); + } if input.ends_with(b".lock") { return Err(name::Error::LockFileSuffix); } diff --git a/gix-validate/tests/reference/mod.rs b/gix-validate/tests/reference/mod.rs index 9afbc1602f0..e6923617219 100644 --- a/gix-validate/tests/reference/mod.rs +++ b/gix-validate/tests/reference/mod.rs @@ -55,7 +55,7 @@ mod name_partial { mktest!( refs_path_component_is_singular_dot, b"refs/./still-inside-but-not-cool", - RefError::SingleDot + RefError::StartsWithDot ); mktest!(any_path_starts_with_slash, b"/etc/foo", RefError::StartsWithSlash); mktest!(empty_path, b"", RefError::Tag(TagError::Empty)); @@ -107,6 +107,7 @@ mod name { mktest!(all_uppercase, b"MAIN"); mktest!(all_uppercase_with_underscore, b"NEW_HEAD"); mktest!(chinese_utf8, "refs/heads/你好吗".as_bytes()); + mktest!(dot_in_directory_component, b"this./totally./works"); } mod invalid { @@ -136,10 +137,21 @@ mod name { b".refs/somewhere", RefError::Tag(TagError::StartsWithDot) ); + + mktest!( + refs_path_name_starts_with_dot_in_name, + b"refs/.somewhere", + RefError::StartsWithDot + ); + mktest!( + refs_path_name_ends_with_dot_in_name, + b"refs/somewhere.", + RefError::Tag(TagError::EndsWithDot) + ); mktest!( refs_path_component_is_singular_dot, b"refs/./still-inside-but-not-cool", - RefError::SingleDot + RefError::StartsWithDot ); mktest!(capitalized_name_without_path, b"Main", RefError::SomeLowercase); mktest!(lowercase_name_without_path, b"main", RefError::SomeLowercase); diff --git a/gix-validate/tests/tag/mod.rs b/gix-validate/tests/tag/mod.rs index 95dc2be5063..96838071c3f 100644 --- a/gix-validate/tests/tag/mod.rs +++ b/gix-validate/tests/tag/mod.rs @@ -19,7 +19,6 @@ mod name { mktest!(contains_brackets, b"this_{is-fine}_too"); mktest!(contains_brackets_and_at, b"this_{@is-fine@}_too"); mktest!(dot_in_the_middle, b"token.other"); - mktest!(dot_at_the_end, b"hello."); mktest!(slash_inbetween, b"hello/world"); } @@ -76,6 +75,7 @@ mod name { mktestb!(contains_newline, b"prefix\nsuffix"); mktestb!(contains_carriage_return, b"prefix\rsuffix"); mktest!(starts_with_dot, b".with-dot", StartsWithDot); + mktest!(ends_with_dot, b"with-dot.", EndsWithDot); mktest!(empty, b"", Empty); } }