From 3c85f4b34929379240de9604e4efc5dece31d128 Mon Sep 17 00:00:00 2001 From: CJ Harries Date: Sun, 30 Jun 2019 04:20:49 -0500 Subject: [PATCH 1/4] Add MalformedReference for type checking Signed-off-by: CJ Harries --- plumbing/reference.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/plumbing/reference.go b/plumbing/reference.go index 08e908f1f..77c79ed8c 100644 --- a/plumbing/reference.go +++ b/plumbing/reference.go @@ -34,9 +34,10 @@ var ( type ReferenceType int8 const ( - InvalidReference ReferenceType = 0 - HashReference ReferenceType = 1 - SymbolicReference ReferenceType = 2 + InvalidReference ReferenceType = 0 + HashReference ReferenceType = 1 + SymbolicReference ReferenceType = 2 + MalformedReference ReferenceType = 3 ) func (r ReferenceType) String() string { @@ -47,6 +48,8 @@ func (r ReferenceType) String() string { return "hash-reference" case SymbolicReference: return "symbolic-reference" + case MalformedReference: + return "malformed-reference" } return "" From 2be66bdd4faa9df9a49d6493a97400941b3b8c6c Mon Sep 17 00:00:00 2001 From: CJ Harries Date: Sun, 30 Jun 2019 04:41:27 -0500 Subject: [PATCH 2/4] Replace the type to begin tests Signed-off-by: CJ Harries Found a better way to move forward Signed-off-by: CJ Harries Update RN usage Signed-off-by: CJ Harries Tidy the file Signed-off-by: CJ Harries Replace struct instantiation w/fnc call Signed-off-by: CJ Harries Update original reference_tests Signed-off-by: CJ Harries Add basic Name test Signed-off-by: CJ Harries Bolster the tests w/ a String quick Signed-off-by: CJ Harries Expose HasPrefix directly Signed-off-by: CJ Harries Drop HasPrefix tests into the existing architecture Signed-off-by: CJ Harries Add IsNotEmpty for branches Signed-off-by: CJ Harries Test IsNotEmpty Signed-off-by: CJ Harries Update branch to handle new Struct Signed-off-by: CJ Harries Update refspec with NewRN Signed-off-by: CJ Harries Update config Signed-off-by: CJ Harries Update advrefs Updated a NewRefName more Signed-off-by: CJ Harries Update advrefs Signed-off-by: CJ Harries Update report_status_test Signed-off-by: CJ Harries Finally hit the wall that comes with a messy backend Signed-off-by: CJ Harries Revert to master for a different solution Signed-off-by: CJ Harries Create first check method and test it Signed-off-by: CJ Harries Stub out necessary processing fncs Signed-off-by: CJ Harries Flesh out ActionOptions Signed-off-by: CJ Harries Create consistent naming Signed-off-by: CJ Harries Template all the things (regex is the best) Signed-off-by: CJ Harries Fill out regex Signed-off-by: CJ Harries Draft matching pairs Signed-off-by: CJ Harries Add Error messages Signed-off-by: CJ Harries Finish Trailing Lock condition Signed-off-by: CJ Harries Add forward slash check Signed-off-by: CJ Harries Finalize double dots Signed-off-by: CJ Harries Finalize excluded characters Signed-off-by: CJ Harries Finalize leading slashes Signed-off-by: CJ Harries Add consecutive slashes Signed-off-by: CJ Harries Add extra test case for specific event Signed-off-by: CJ Harries Add trailing dot case Signed-off-by: CJ Harries Add at-open-brace Signed-off-by: CJ Harries Add Skip tests Signed-off-by: CJ Harries Add RefSpecPattern functionality Signed-off-by: CJ Harries Done mucking around with reflection Signed-off-by: CJ Harries Build full handle runner Signed-off-by: CJ Harries Stylize the Enum values Signed-off-by: CJ Harries Add some default building tools Signed-off-by: CJ Harries Add some basic docs Signed-off-by: CJ Harries --- plumbing/reference.go | 9 +- plumbing/reference_validation.go | 294 ++++++++++++++++++ plumbing/reference_validation_testing.go | 362 +++++++++++++++++++++++ 3 files changed, 659 insertions(+), 6 deletions(-) create mode 100644 plumbing/reference_validation.go create mode 100644 plumbing/reference_validation_testing.go diff --git a/plumbing/reference.go b/plumbing/reference.go index 77c79ed8c..08e908f1f 100644 --- a/plumbing/reference.go +++ b/plumbing/reference.go @@ -34,10 +34,9 @@ var ( type ReferenceType int8 const ( - InvalidReference ReferenceType = 0 - HashReference ReferenceType = 1 - SymbolicReference ReferenceType = 2 - MalformedReference ReferenceType = 3 + InvalidReference ReferenceType = 0 + HashReference ReferenceType = 1 + SymbolicReference ReferenceType = 2 ) func (r ReferenceType) String() string { @@ -48,8 +47,6 @@ func (r ReferenceType) String() string { return "hash-reference" case SymbolicReference: return "symbolic-reference" - case MalformedReference: - return "malformed-reference" } return "" diff --git a/plumbing/reference_validation.go b/plumbing/reference_validation.go new file mode 100644 index 000000000..9f1f43118 --- /dev/null +++ b/plumbing/reference_validation.go @@ -0,0 +1,294 @@ +package plumbing + +import ( + "errors" + "regexp" + "strings" +) + +type ActionChoice int + +const ( + // This skips the check + SKIP ActionChoice = iota + // This removes the issue when possible + SANITIZE + // This throws an error and must be fixed + VALIDATE +) + +var ( + ErrRefLeadingDot = errors.New("ref name cannot begin with a dot") + ErrRefTrailingLock = errors.New("ref name cannot end with .lock") + ErrRefAtLeastOneForwardSlash = errors.New("ref name must have at least one forward slash") + ErrRefDoubleDots = errors.New("ref name cannot include two consecutive dots") + ErrRefExcludedCharacters = errors.New("ref name cannot include many special characters") + ErrRefLeadingForwardSlash = errors.New("ref name cannot start with a forward slash") + ErrRefTrailingForwardSlash = errors.New("ref name cannot end with a forward slash") + ErrRefConsecutiveForwardSlashes = errors.New("ref name cannot have consectutive forward slashes") + ErrRefTrailingDot = errors.New("ref name cannot end with a dot") + ErrRefAtOpenBrace = errors.New("ref name cannot include at-open-brace") +) + +var ( + PatternLeadingDot = regexp.MustCompile(`^\.`) + PatternTrailingLock = regexp.MustCompile(`\.lock$`) + PatternAtLeastOneForwardSlash = regexp.MustCompile(`^[^/]+$`) + PatternDoubleDots = regexp.MustCompile(`\.\.`) + PatternExcludedCharacters = regexp.MustCompile(`[\000-\037\177 ~^:?*[]+`) + PatternLeadingForwardSlash = regexp.MustCompile(`^/`) + PatternTrailingForwardSlash = regexp.MustCompile(`/$`) + PatternConsecutiveForwardSlashes = regexp.MustCompile(`//+`) + PatternTrailingDot = regexp.MustCompile(`\.$`) + PatternAtOpenBrace = regexp.MustCompile(`@{`) + PatternExcludedCharactersAlternate = regexp.MustCompile(`[\000-\037\177 ~^:?[]+`) + PatternOneAllowedAsterisk = regexp.MustCompile(`^[^*]+?\*?[^*]+?$`) +) + +type CheckRefOptions struct { + // They must contain at least one / + // If the --allow-onelevel option is used, this rule is waived. + AllowOneLevel bool + // If this option is enabled, is allowed to contain a + // single * in the refspec + RefSpecPattern bool + // Normalize refname by removing any leading slash (/) characters and + // collapsing runs of adjacent slashes between name components into + // a single slash. + Normalize bool +} + +type ActionOptions struct { + + // no slash-separated component can begin with a dot . + HandleLeadingDot ActionChoice + + // no slash-separated component can end with the sequence .lock + HandleTrailingLock ActionChoice + // They must contain at least one /. + HandleAtLeastOneForwardSlash ActionChoice + // They cannot have two consecutive dots .. anywhere. + HandleDoubleDots ActionChoice + // They cannot have ASCII control characters (i.e. bytes whose values + // are lower than \040, or \177 DEL), space, tilde ~, caret ^, or + // colon : anywhere. + // They cannot have question-mark ?, asterisk *, or open + // bracket [ anywhere + // They cannot contain a \ + HandleExcludedCharacters ActionChoice + // They cannot begin or end with a slash / + HandleLeadingForwardSlash ActionChoice + // They cannot begin or end with a slash / or contain + // multiple consecutive slashes + HandleTrailingForwardSlash ActionChoice + // They cannot contain multiple consecutive slashes + HandleConsecutiveForwardSlashes ActionChoice + // They cannot end with a dot . + HandleTrailingDot ActionChoice + // They cannot contain a sequence @{ + HandleAtOpenBrace ActionChoice +} + +// https://git-scm.com/docs/git-check-ref-format +// git-check-ref-format +type RefNameChecker struct { + Name ReferenceName + CheckRefOptions CheckRefOptions + ActionOptions ActionOptions +} + +func NewCheckRefOptions(default_value bool) *CheckRefOptions { + return &CheckRefOptions{ + AllowOneLevel: default_value, + RefSpecPattern: default_value, + Normalize: default_value, + } +} + +func NewActionOptions(default_value ActionChoice) *ActionOptions { + return &ActionOptions{ + HandleLeadingDot: default_value, + HandleTrailingLock: default_value, + HandleAtLeastOneForwardSlash: default_value, + HandleDoubleDots: default_value, + HandleExcludedCharacters: default_value, + HandleLeadingForwardSlash: default_value, + HandleTrailingForwardSlash: default_value, + HandleConsecutiveForwardSlashes: default_value, + HandleTrailingDot: default_value, + HandleAtOpenBrace: default_value, + } +} + +func NewRefNameChecker( + name ReferenceName, + ref_options CheckRefOptions, + action_options ActionOptions, +) *RefNameChecker { + return &RefNameChecker{ + Name: name, + CheckRefOptions: ref_options, + ActionOptions: action_options, + } +} + +func (v *RefNameChecker) HandleLeadingDot() error { + switch v.ActionOptions.HandleLeadingDot { + case VALIDATE: + if PatternLeadingDot.MatchString(v.Name.String()) { + return ErrRefLeadingDot + } + break + case SANITIZE: + v.Name = ReferenceName(PatternLeadingDot.ReplaceAllString(v.Name.String(), "")) + } + return nil +} + +func (v *RefNameChecker) HandleTrailingLock() error { + switch v.ActionOptions.HandleTrailingLock { + case VALIDATE: + if PatternTrailingLock.MatchString(v.Name.String()) { + return ErrRefTrailingLock + } + break + case SANITIZE: + v.Name = ReferenceName(PatternTrailingLock.ReplaceAllString(v.Name.String(), "")) + } + return nil +} + +func (v *RefNameChecker) HandleAtLeastOneForwardSlash() error { + if SKIP == v.ActionOptions.HandleAtLeastOneForwardSlash { + return nil + } + count := strings.Count(v.Name.String(), "/") + if 1 > count { + if v.CheckRefOptions.AllowOneLevel { + return nil + } + return ErrRefAtLeastOneForwardSlash + } + return nil +} + +func (v *RefNameChecker) HandleDoubleDots() error { + switch v.ActionOptions.HandleDoubleDots { + case VALIDATE: + if PatternDoubleDots.MatchString(v.Name.String()) { + return ErrRefDoubleDots + } + break + case SANITIZE: + v.Name = ReferenceName(PatternDoubleDots.ReplaceAllString(v.Name.String(), "")) + } + return nil +} + +func (v *RefNameChecker) HandleExcludedCharacters() error { + switch v.ActionOptions.HandleExcludedCharacters { + case VALIDATE: + if PatternExcludedCharacters.MatchString(v.Name.String()) { + return ErrRefExcludedCharacters + } + break + case SANITIZE: + if v.CheckRefOptions.RefSpecPattern && PatternOneAllowedAsterisk.MatchString(v.Name.String()) { + v.Name = ReferenceName(PatternExcludedCharactersAlternate.ReplaceAllString(v.Name.String(), "")) + + } else { + v.Name = ReferenceName(PatternExcludedCharacters.ReplaceAllString(v.Name.String(), "")) + } + } + return nil +} + +func (v *RefNameChecker) HandleLeadingForwardSlash() error { + switch v.ActionOptions.HandleLeadingForwardSlash { + case VALIDATE: + if PatternLeadingForwardSlash.MatchString(v.Name.String()) { + return ErrRefLeadingForwardSlash + } + break + case SANITIZE: + v.Name = ReferenceName(PatternLeadingForwardSlash.ReplaceAllString(v.Name.String(), "")) + } + return nil +} + +func (v *RefNameChecker) HandleTrailingForwardSlash() error { + switch v.ActionOptions.HandleTrailingForwardSlash { + case VALIDATE: + if PatternTrailingForwardSlash.MatchString(v.Name.String()) { + return ErrRefTrailingForwardSlash + } + break + case SANITIZE: + v.Name = ReferenceName(PatternTrailingForwardSlash.ReplaceAllString(v.Name.String(), "")) + } + return nil +} + +func (v *RefNameChecker) HandleConsecutiveForwardSlashes() error { + if SKIP == v.ActionOptions.HandleConsecutiveForwardSlashes { + return nil + } + if PatternConsecutiveForwardSlashes.MatchString(v.Name.String()) { + if SANITIZE == v.ActionOptions.HandleConsecutiveForwardSlashes { + if v.CheckRefOptions.Normalize { + v.Name = ReferenceName(PatternConsecutiveForwardSlashes.ReplaceAllString(v.Name.String(), "/")) + return nil + } + } + return ErrRefConsecutiveForwardSlashes + } + return nil +} + +func (v *RefNameChecker) HandleTrailingDot() error { + switch v.ActionOptions.HandleTrailingDot { + case VALIDATE: + if PatternTrailingDot.MatchString(v.Name.String()) { + return ErrRefTrailingDot + } + break + case SANITIZE: + v.Name = ReferenceName(PatternTrailingDot.ReplaceAllString(v.Name.String(), "")) + } + return nil +} + +func (v *RefNameChecker) HandleAtOpenBrace() error { + switch v.ActionOptions.HandleAtOpenBrace { + case VALIDATE: + if PatternAtOpenBrace.MatchString(v.Name.String()) { + return ErrRefAtOpenBrace + } + break + case SANITIZE: + v.Name = ReferenceName(PatternAtOpenBrace.ReplaceAllString(v.Name.String(), "")) + } + return nil +} + +func (v *RefNameChecker) CheckRefName() error { + handles := []func() error{ + v.HandleLeadingDot, + v.HandleTrailingLock, + v.HandleAtLeastOneForwardSlash, + v.HandleDoubleDots, + v.HandleExcludedCharacters, + v.HandleLeadingForwardSlash, + v.HandleTrailingForwardSlash, + v.HandleConsecutiveForwardSlashes, + v.HandleTrailingDot, + v.HandleAtOpenBrace, + } + for _, handle := range handles { + err := handle() + if nil != err { + return err + } + } + return nil +} diff --git a/plumbing/reference_validation_testing.go b/plumbing/reference_validation_testing.go new file mode 100644 index 000000000..5cdad6aed --- /dev/null +++ b/plumbing/reference_validation_testing.go @@ -0,0 +1,362 @@ +package plumbing + +import ( + "fmt" + . "gopkg.in/check.v1" +) + +type ReferenceValidationSuite struct { + Checker RefNameChecker +} + +var _ = Suite(&ReferenceValidationSuite{}) + +var ( + LeadingDotNames = []string{ + ".a/name", + "a/name", + } + TrailingLockNames = []string{ + "a/name.lock", + "a/name", + } + AtLeastOneForwardSlashNames = []string{ + "aname", + "a/name", + } + DoubleDotsNames = []string{ + "a..name", + "aname", + } + ExcludedCharactersNames = []string{ + `an^ame`, + "aname", + `a/lon*ger/name`, + `a/lon*ger/na*me`, + `a/longer/name`, + } + LeadingForwardSlashNames = []string{ + "/a/name", + "a/name", + } + TrailingForwardSlashNames = []string{ + "a/name/", + "a/name", + } + ConsecutiveForwardSlashesNames = []string{ + "a//name", + "a/name", + "a///longer///name", + "a/longer/name", + } + TrailingDotNames = []string{ + "a/name.", + "a/name", + } + AtOpenBraceNames = []string{ + `a/na@{me`, + `a/name`, + } +) + +func (s *ReferenceValidationSuite) TestValidateHandleLeadingDot(c *C) { + s.Checker.ActionOptions.HandleLeadingDot = VALIDATE + s.Checker.Name = ReferenceName(LeadingDotNames[0]) + err := s.Checker.HandleLeadingDot() + c.Assert(err, ErrorMatches, fmt.Sprint(ErrRefLeadingDot)) + s.Checker.Name = ReferenceName(LeadingDotNames[1]) + err = s.Checker.HandleLeadingDot() + c.Assert(err, IsNil) +} + +func (s *ReferenceValidationSuite) TestSanitizeHandleLeadingDot(c *C) { + s.Checker.ActionOptions.HandleLeadingDot = SANITIZE + s.Checker.Name = ReferenceName(LeadingDotNames[0]) + err := s.Checker.HandleLeadingDot() + c.Assert(err, IsNil) + c.Assert(s.Checker.Name.String(), Equals, LeadingDotNames[1]) +} + +func (s *ReferenceValidationSuite) TestSkipHandleLeadingDot(c *C) { + s.Checker.ActionOptions.HandleLeadingDot = SKIP + for _, name := range LeadingDotNames { + s.Checker.Name = ReferenceName(name) + err := s.Checker.HandleLeadingDot() + c.Assert(err, IsNil) + } +} + +func (s *ReferenceValidationSuite) TestValidateHandleTrailingLock(c *C) { + s.Checker.ActionOptions.HandleTrailingLock = VALIDATE + s.Checker.Name = ReferenceName(TrailingLockNames[0]) + err := s.Checker.HandleTrailingLock() + c.Assert(err, ErrorMatches, fmt.Sprint(ErrRefTrailingLock)) + s.Checker.Name = ReferenceName(TrailingLockNames[1]) + err = s.Checker.HandleTrailingLock() + c.Assert(err, IsNil) +} + +func (s *ReferenceValidationSuite) TestSkipHandleTrailingLock(c *C) { + s.Checker.ActionOptions.HandleTrailingLock = SKIP + for _, name := range TrailingLockNames { + s.Checker.Name = ReferenceName(name) + err := s.Checker.HandleTrailingLock() + c.Assert(err, IsNil) + } +} + +func (s *ReferenceValidationSuite) TestSanitizeHandleTrailingLock(c *C) { + s.Checker.ActionOptions.HandleTrailingLock = SANITIZE + s.Checker.Name = ReferenceName(TrailingLockNames[0]) + err := s.Checker.HandleTrailingLock() + c.Assert(err, IsNil) + c.Assert(s.Checker.Name.String(), Equals, TrailingLockNames[1]) +} + +func (s *ReferenceValidationSuite) TestValidateAtLeastOneForwardSlash(c *C) { + for _, setting := range []ActionChoice{VALIDATE, SANITIZE} { + s.Checker.CheckRefOptions.AllowOneLevel = false + s.Checker.ActionOptions.HandleAtLeastOneForwardSlash = setting + s.Checker.Name = ReferenceName(AtLeastOneForwardSlashNames[0]) + err := s.Checker.HandleAtLeastOneForwardSlash() + c.Assert(err, ErrorMatches, fmt.Sprint(ErrRefAtLeastOneForwardSlash)) + s.Checker.Name = ReferenceName(AtLeastOneForwardSlashNames[1]) + err = s.Checker.HandleAtLeastOneForwardSlash() + c.Assert(err, IsNil) + s.Checker.Name = ReferenceName(AtLeastOneForwardSlashNames[0]) + s.Checker.CheckRefOptions.AllowOneLevel = true + err = s.Checker.HandleAtLeastOneForwardSlash() + c.Assert(err, IsNil) + } +} + +func (s *ReferenceValidationSuite) TestSkipHandleAtLeastOneForwardSlash(c *C) { + s.Checker.ActionOptions.HandleAtLeastOneForwardSlash = SKIP + for _, name := range AtLeastOneForwardSlashNames { + s.Checker.Name = ReferenceName(name) + err := s.Checker.HandleAtLeastOneForwardSlash() + c.Assert(err, IsNil) + } +} + +func (s *ReferenceValidationSuite) TestValidateHandleDoubleDots(c *C) { + s.Checker.ActionOptions.HandleDoubleDots = VALIDATE + s.Checker.Name = ReferenceName(DoubleDotsNames[0]) + err := s.Checker.HandleDoubleDots() + c.Assert(err, ErrorMatches, fmt.Sprint(ErrRefDoubleDots)) + s.Checker.Name = ReferenceName(DoubleDotsNames[1]) + err = s.Checker.HandleDoubleDots() + c.Assert(err, IsNil) +} + +func (s *ReferenceValidationSuite) TestSanitizeHandleDoubleDots(c *C) { + s.Checker.ActionOptions.HandleDoubleDots = SANITIZE + s.Checker.Name = ReferenceName(DoubleDotsNames[0]) + err := s.Checker.HandleDoubleDots() + c.Assert(err, IsNil) + c.Assert(s.Checker.Name.String(), Equals, DoubleDotsNames[1]) +} + +func (s *ReferenceValidationSuite) TestSkipHandleDoubleDots(c *C) { + s.Checker.ActionOptions.HandleDoubleDots = SKIP + for _, name := range DoubleDotsNames { + s.Checker.Name = ReferenceName(name) + err := s.Checker.HandleDoubleDots() + c.Assert(err, IsNil) + } +} + +func (s *ReferenceValidationSuite) TestValidateHandleExcludedCharacters(c *C) { + s.Checker.ActionOptions.HandleExcludedCharacters = VALIDATE + s.Checker.Name = ReferenceName(ExcludedCharactersNames[0]) + err := s.Checker.HandleExcludedCharacters() + c.Assert(err, ErrorMatches, fmt.Sprint(ErrRefExcludedCharacters)) + s.Checker.Name = ReferenceName(ExcludedCharactersNames[1]) + err = s.Checker.HandleExcludedCharacters() + c.Assert(err, IsNil) +} + +func (s *ReferenceValidationSuite) TestSanitizeHandleExcludedCharacters(c *C) { + s.Checker.ActionOptions.HandleExcludedCharacters = SANITIZE + s.Checker.Name = ReferenceName(ExcludedCharactersNames[0]) + err := s.Checker.HandleExcludedCharacters() + c.Assert(err, IsNil) + c.Assert(s.Checker.Name.String(), Equals, ExcludedCharactersNames[1]) + s.Checker.Name = ReferenceName(ExcludedCharactersNames[2]) + err = s.Checker.HandleExcludedCharacters() + c.Assert(err, IsNil) + c.Assert(s.Checker.Name.String(), Equals, ExcludedCharactersNames[4]) + s.Checker.Name = ReferenceName(ExcludedCharactersNames[3]) + err = s.Checker.HandleExcludedCharacters() + c.Assert(err, IsNil) + c.Assert(s.Checker.Name.String(), Equals, ExcludedCharactersNames[4]) + s.Checker.CheckRefOptions.RefSpecPattern = true + s.Checker.Name = ReferenceName(ExcludedCharactersNames[2]) + err = s.Checker.HandleExcludedCharacters() + c.Assert(err, IsNil) + c.Assert(s.Checker.Name.String(), Equals, ExcludedCharactersNames[2]) + s.Checker.Name = ReferenceName(ExcludedCharactersNames[3]) + err = s.Checker.HandleExcludedCharacters() + c.Assert(err, IsNil) + c.Assert(s.Checker.Name.String(), Equals, ExcludedCharactersNames[4]) +} + +func (s *ReferenceValidationSuite) TestSkipHandleExcludedCharacters(c *C) { + s.Checker.ActionOptions.HandleExcludedCharacters = SKIP + for _, name := range ExcludedCharactersNames { + s.Checker.Name = ReferenceName(name) + err := s.Checker.HandleExcludedCharacters() + c.Assert(err, IsNil) + } +} + +func (s *ReferenceValidationSuite) TestValidateHandleLeadingForwardSlash(c *C) { + s.Checker.ActionOptions.HandleLeadingForwardSlash = VALIDATE + s.Checker.Name = ReferenceName(LeadingForwardSlashNames[0]) + err := s.Checker.HandleLeadingForwardSlash() + c.Assert(err, ErrorMatches, fmt.Sprint(ErrRefLeadingForwardSlash)) + s.Checker.Name = ReferenceName(LeadingForwardSlashNames[1]) + err = s.Checker.HandleLeadingForwardSlash() + c.Assert(err, IsNil) +} + +func (s *ReferenceValidationSuite) TestSanitizeHandleLeadingForwardSlash(c *C) { + s.Checker.ActionOptions.HandleLeadingForwardSlash = SANITIZE + s.Checker.Name = ReferenceName(LeadingForwardSlashNames[0]) + err := s.Checker.HandleLeadingForwardSlash() + c.Assert(err, IsNil) + c.Assert(s.Checker.Name.String(), Equals, LeadingForwardSlashNames[1]) +} + +func (s *ReferenceValidationSuite) TestSkipHandleLeadingForwardSlash(c *C) { + s.Checker.ActionOptions.HandleLeadingForwardSlash = SKIP + for _, name := range LeadingForwardSlashNames { + s.Checker.Name = ReferenceName(name) + err := s.Checker.HandleLeadingForwardSlash() + c.Assert(err, IsNil) + } +} + +func (s *ReferenceValidationSuite) TestValidateHandleTrailingForwardSlash(c *C) { + s.Checker.ActionOptions.HandleTrailingForwardSlash = VALIDATE + s.Checker.Name = ReferenceName(TrailingForwardSlashNames[0]) + err := s.Checker.HandleTrailingForwardSlash() + c.Assert(err, ErrorMatches, fmt.Sprint(ErrRefTrailingForwardSlash)) + s.Checker.Name = ReferenceName(TrailingForwardSlashNames[1]) + err = s.Checker.HandleTrailingForwardSlash() + c.Assert(err, IsNil) +} + +func (s *ReferenceValidationSuite) TestSanitizeHandleTrailingForwardSlash(c *C) { + s.Checker.ActionOptions.HandleTrailingForwardSlash = SANITIZE + s.Checker.Name = ReferenceName(TrailingForwardSlashNames[0]) + err := s.Checker.HandleTrailingForwardSlash() + c.Assert(err, IsNil) + c.Assert(s.Checker.Name.String(), Equals, TrailingForwardSlashNames[1]) +} + +func (s *ReferenceValidationSuite) TestSkipHandleTrailingForwardSlash(c *C) { + s.Checker.ActionOptions.HandleTrailingForwardSlash = SKIP + for _, name := range TrailingForwardSlashNames { + s.Checker.Name = ReferenceName(name) + err := s.Checker.HandleTrailingForwardSlash() + c.Assert(err, IsNil) + } +} + +func (s *ReferenceValidationSuite) TestValidateHandleConsecutiveForwardSlashes(c *C) { + s.Checker.ActionOptions.HandleConsecutiveForwardSlashes = VALIDATE + for index, name := range ConsecutiveForwardSlashesNames { + s.Checker.Name = ReferenceName(name) + err := s.Checker.HandleConsecutiveForwardSlashes() + if 1 == index%2 { + c.Assert(err, IsNil) + } else { + c.Assert(err, ErrorMatches, fmt.Sprint(ErrRefConsecutiveForwardSlashes)) + } + + } +} + +func (s *ReferenceValidationSuite) TestSanitizeHandleConsecutiveForwardSlashes(c *C) { + + for _, element := range []int{0, 2} { + s.Checker.CheckRefOptions.Normalize = true + s.Checker.ActionOptions.HandleConsecutiveForwardSlashes = SANITIZE + s.Checker.Name = ReferenceName(ConsecutiveForwardSlashesNames[element+0]) + err := s.Checker.HandleConsecutiveForwardSlashes() + c.Assert(err, IsNil) + c.Assert(s.Checker.Name.String(), Equals, ConsecutiveForwardSlashesNames[element+1]) + s.Checker.CheckRefOptions.Normalize = false + s.Checker.Name = ReferenceName(ConsecutiveForwardSlashesNames[element+0]) + err = s.Checker.HandleConsecutiveForwardSlashes() + c.Assert(err, ErrorMatches, fmt.Sprint(ErrRefConsecutiveForwardSlashes)) + } +} + +func (s *ReferenceValidationSuite) TestSkipHandleConsecutiveForwardSlashes(c *C) { + s.Checker.ActionOptions.HandleConsecutiveForwardSlashes = SKIP + for _, name := range ConsecutiveForwardSlashesNames { + s.Checker.Name = ReferenceName(name) + err := s.Checker.HandleConsecutiveForwardSlashes() + c.Assert(err, IsNil) + } +} + +func (s *ReferenceValidationSuite) TestValidateHandleTrailingDot(c *C) { + s.Checker.ActionOptions.HandleTrailingDot = VALIDATE + s.Checker.Name = ReferenceName(TrailingDotNames[0]) + err := s.Checker.HandleTrailingDot() + c.Assert(err, ErrorMatches, fmt.Sprint(ErrRefTrailingDot)) + s.Checker.Name = ReferenceName(TrailingDotNames[1]) + err = s.Checker.HandleTrailingDot() + c.Assert(err, IsNil) +} + +func (s *ReferenceValidationSuite) TestSanitizeHandleTrailingDot(c *C) { + s.Checker.ActionOptions.HandleTrailingDot = SANITIZE + s.Checker.Name = ReferenceName(TrailingDotNames[0]) + err := s.Checker.HandleTrailingDot() + c.Assert(err, IsNil) + c.Assert(s.Checker.Name.String(), Equals, TrailingDotNames[1]) +} + +func (s *ReferenceValidationSuite) TestSkipHandleTrailingDot(c *C) { + s.Checker.ActionOptions.HandleTrailingDot = SKIP + for _, name := range TrailingDotNames { + s.Checker.Name = ReferenceName(name) + err := s.Checker.HandleTrailingDot() + c.Assert(err, IsNil) + } +} + +func (s *ReferenceValidationSuite) TestValidateHandleAtOpenBrace(c *C) { + s.Checker.ActionOptions.HandleAtOpenBrace = VALIDATE + s.Checker.Name = ReferenceName(AtOpenBraceNames[0]) + err := s.Checker.HandleAtOpenBrace() + c.Assert(err, ErrorMatches, fmt.Sprint(ErrRefAtOpenBrace)) + s.Checker.Name = ReferenceName(AtOpenBraceNames[1]) + err = s.Checker.HandleAtOpenBrace() + c.Assert(err, IsNil) +} + +func (s *ReferenceValidationSuite) TestSanitizeHandleAtOpenBrace(c *C) { + s.Checker.ActionOptions.HandleAtOpenBrace = SANITIZE + s.Checker.Name = ReferenceName(AtOpenBraceNames[0]) + err := s.Checker.HandleAtOpenBrace() + c.Assert(err, IsNil) + c.Assert(s.Checker.Name.String(), Equals, AtOpenBraceNames[1]) +} + +func (s *ReferenceValidationSuite) TestSkipHandleAtOpenBrace(c *C) { + s.Checker.ActionOptions.HandleAtOpenBrace = SKIP + for _, name := range AtOpenBraceNames { + s.Checker.Name = ReferenceName(name) + err := s.Checker.HandleAtOpenBrace() + c.Assert(err, IsNil) + } +} + +// func (s *ReferenceValidationSuite) TestCheckRefName(c *C) { +// s.Checker.CheckRefName() +// } From e07788a7d6a2028ea7bdb94365b593acce203877 Mon Sep 17 00:00:00 2001 From: CJ Harries Date: Sun, 30 Jun 2019 14:21:46 -0500 Subject: [PATCH 3/4] refnames: create scaffolding to validate them Signed-off-by: CJ Harries --- plumbing/reference_validation.go | 16 +++++++++++++++ plumbing/reference_validation_testing.go | 26 ++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/plumbing/reference_validation.go b/plumbing/reference_validation.go index 9f1f43118..8147956c8 100644 --- a/plumbing/reference_validation.go +++ b/plumbing/reference_validation.go @@ -28,6 +28,7 @@ var ( ErrRefConsecutiveForwardSlashes = errors.New("ref name cannot have consectutive forward slashes") ErrRefTrailingDot = errors.New("ref name cannot end with a dot") ErrRefAtOpenBrace = errors.New("ref name cannot include at-open-brace") + ErrRefOnlyAtSign = errors.New("ref name cannot only be an at sign") ) var ( @@ -43,6 +44,7 @@ var ( PatternAtOpenBrace = regexp.MustCompile(`@{`) PatternExcludedCharactersAlternate = regexp.MustCompile(`[\000-\037\177 ~^:?[]+`) PatternOneAllowedAsterisk = regexp.MustCompile(`^[^*]+?\*?[^*]+?$`) + PatternOnlyAtSign = regexp.MustCompile(`^@$`) ) type CheckRefOptions struct { @@ -87,6 +89,8 @@ type ActionOptions struct { HandleTrailingDot ActionChoice // They cannot contain a sequence @{ HandleAtOpenBrace ActionChoice + // They cannot be the single character @ + HandleOnlyAtSign ActionChoice } // https://git-scm.com/docs/git-check-ref-format @@ -117,6 +121,7 @@ func NewActionOptions(default_value ActionChoice) *ActionOptions { HandleConsecutiveForwardSlashes: default_value, HandleTrailingDot: default_value, HandleAtOpenBrace: default_value, + HandleOnlyAtSign: default_value, } } @@ -271,6 +276,16 @@ func (v *RefNameChecker) HandleAtOpenBrace() error { return nil } +func (v *RefNameChecker) HandleOnlyAtSign() error { + if SKIP == v.ActionOptions.HandleOnlyAtSign { + return nil + } + if PatternOnlyAtSign.MatchString(v.Name.String()) { + return ErrRefOnlyAtSign + } + return nil +} + func (v *RefNameChecker) CheckRefName() error { handles := []func() error{ v.HandleLeadingDot, @@ -283,6 +298,7 @@ func (v *RefNameChecker) CheckRefName() error { v.HandleConsecutiveForwardSlashes, v.HandleTrailingDot, v.HandleAtOpenBrace, + v.HandleOnlyAtSign, } for _, handle := range handles { err := handle() diff --git a/plumbing/reference_validation_testing.go b/plumbing/reference_validation_testing.go index 5cdad6aed..81859b81a 100644 --- a/plumbing/reference_validation_testing.go +++ b/plumbing/reference_validation_testing.go @@ -57,6 +57,9 @@ var ( `a/na@{me`, `a/name`, } + OnlyAtSignNames = []string{ + `@`, + } ) func (s *ReferenceValidationSuite) TestValidateHandleLeadingDot(c *C) { @@ -357,6 +360,29 @@ func (s *ReferenceValidationSuite) TestSkipHandleAtOpenBrace(c *C) { } } +func (s *ReferenceValidationSuite) TestValidateHandleOnlyAtSign(c *C) { + s.Checker.ActionOptions.HandleOnlyAtSign = SANITIZE + s.Checker.Name = ReferenceName(OnlyAtSignNames[0]) + err := s.Checker.HandleOnlyAtSign() + c.Assert(err, ErrorMatches, fmt.Sprint(ErrRefOnlyAtSign)) +} + +func (s *ReferenceValidationSuite) TestSanitizeHandleOnlyAtSign(c *C) { + s.Checker.ActionOptions.HandleOnlyAtSign = SANITIZE + s.Checker.Name = ReferenceName(OnlyAtSignNames[0]) + err := s.Checker.HandleOnlyAtSign() + c.Assert(err, ErrorMatches, fmt.Sprint(ErrRefOnlyAtSign)) +} + +func (s *ReferenceValidationSuite) TestSkipHandleOnlyAtSign(c *C) { + s.Checker.ActionOptions.HandleOnlyAtSign = SKIP + for _, name := range OnlyAtSignNames { + s.Checker.Name = ReferenceName(name) + err := s.Checker.HandleOnlyAtSign() + c.Assert(err, IsNil) + } +} + // func (s *ReferenceValidationSuite) TestCheckRefName(c *C) { // s.Checker.CheckRefName() // } From c1f0923ea48d886b74caf43d442e9af76a11053c Mon Sep 17 00:00:00 2001 From: CJ Harries Date: Sun, 30 Jun 2019 14:46:54 -0500 Subject: [PATCH 4/4] plumbing: add final note about check-ref-format suggestion Signed-off-by: CJ Harries --- plumbing/reference_validation.go | 1 + 1 file changed, 1 insertion(+) diff --git a/plumbing/reference_validation.go b/plumbing/reference_validation.go index 8147956c8..bf3192a3a 100644 --- a/plumbing/reference_validation.go +++ b/plumbing/reference_validation.go @@ -286,6 +286,7 @@ func (v *RefNameChecker) HandleOnlyAtSign() error { return nil } +// This runs everything func (v *RefNameChecker) CheckRefName() error { handles := []func() error{ v.HandleLeadingDot,