Skip to content

Commit

Permalink
Add repo name validation
Browse files Browse the repository at this point in the history
  • Loading branch information
泽华 authored and ganisback committed Jan 10, 2025
1 parent 388fe8d commit 7438d2b
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 6 deletions.
65 changes: 65 additions & 0 deletions common/utils/common/repo.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package common

import (
"errors"
"fmt"
"net/url"
"regexp"
"strings"

"opencsg.com/csghub-server/builder/store/database"
Expand Down Expand Up @@ -80,3 +82,66 @@ func buildSSHCloneURL(domain string, repoType types.RepositoryType, path string)
return fmt.Sprintf("ssh://%s/%ss/%s.git", strings.TrimSuffix(sshDomainWithoutPrefix, "/"), repoType, path)
}
}

func IsValidName(name string) (bool, error) {
// validate name
if err := validate(name); err != nil {
return false, err
}
// repeat special character check
if hasRepeatSpecialCharacter(name) {
return false, errors.New("Name contains consecutive special characters which is not allowed.")
}
return true, nil
}

func hasRepeatSpecialCharacter(s string) bool {
for i := 0; i < len(s)-1; i++ {
if strings.Contains("-_.", string(s[i])) && s[i] == s[i+1] {
return true
}
}
return false
}

func validate(name string) error {
rules := []struct {
pattern *regexp.Regexp
message string
}{
// Length mast between 2 and 64
{
pattern: regexp.MustCompile(`^.{2,64}$`),
message: "Length must be between 2 and 64 characters.",
},
// Must start with a letter
{
pattern: regexp.MustCompile(`^[a-zA-Z]`),
message: "Must start with a letter.",
},
// Must end with a letter or number
{
pattern: regexp.MustCompile(`[a-zA-Z0-9]$`),
message: "Must end with a letter or number.",
},
// Only letters, numbers, and -_. are allowed
{
pattern: regexp.MustCompile(`^[a-zA-Z0-9-_\.]+$`),
message: "Only letters, numbers, and -_. are allowed.",
},
// Final regex check
{
pattern: regexp.MustCompile(`^[a-zA-Z][a-zA-Z0-9-_\.]*[a-zA-Z0-9]$`),
message: "Name does not match the required format.",
},
}

// Validate name
for _, rule := range rules {
if !rule.pattern.MatchString(name) {
return errors.New(rule.message)
}
}

return nil
}
28 changes: 28 additions & 0 deletions common/utils/common/repo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,3 +230,31 @@ func TestBuildCloneInfo(t *testing.T) {
})
}
}

func TestIsValidName(t *testing.T) {
type args struct {
name string
}
tests := []struct {
name string
args args
want bool
}{
{name: "Test IsValidName when name is valid", args: args{name: "abc"}, want: true},
{name: "Test IsValidName when name is valid", args: args{name: "abc_def"}, want: true},
{name: "Test IsValidName when name is valid", args: args{name: "abc-def"}, want: true},
{name: "Test IsValidName when name is invalid", args: args{name: "abc/def"}, want: false},
{name: "Test IsValidName when name is invalid", args: args{name: "abc def"}, want: false},
{name: "Test IsValidName when name is invalid", args: args{name: "abc__def"}, want: false},
{name: "Test IsValidName when name is invalid", args: args{name: "a"}, want: false},
{name: "Test IsValidName when name is invalid", args: args{name: "abc..def"}, want: false},
{name: "Test IsValidName when name is invalid", args: args{name: "--def"}, want: false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got, _ := IsValidName(tt.args.name); got != tt.want {
t.Errorf("IsValidName() = %v, want %v", got, tt.want)
}
})
}
}
8 changes: 7 additions & 1 deletion component/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,12 @@ func NewRepoComponent(config *config.Config) (RepoComponent, error) {
}

func (c *repoComponentImpl) CreateRepo(ctx context.Context, req types.CreateRepoReq) (*gitserver.CreateRepoResp, *database.Repository, error) {
// Name validation
valid, err := common.IsValidName(req.Name)
if !valid {
return nil, nil, fmt.Errorf("repo name is invalid, error: %w", err)
}

namespace, err := c.namespaceStore.FindByPath(ctx, req.Namespace)
if err != nil {
return nil, nil, errors.New("namespace does not exist")
Expand Down Expand Up @@ -259,7 +265,7 @@ func (c *repoComponentImpl) CreateRepo(ctx context.Context, req types.CreateRepo
Username: req.Username,
Namespace: req.Namespace,
Name: req.Name,
Nickname: req.Name,
Nickname: req.Nickname,
License: req.License,
DefaultBranch: req.DefaultBranch,
// Readme: "Please introduce your space.",
Expand Down
10 changes: 5 additions & 5 deletions component/repo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ func TestRepoComponent_CreateRepo(t *testing.T) {
repo.mocks.gitServer.EXPECT().CreateRepo(ctx, gitserver.CreateRepoReq{
Username: "user",
Namespace: "ns",
Name: "n",
Nickname: "n",
Name: "name",
Nickname: "nn",
License: "MIT",
DefaultBranch: "main",
Readme: "rr",
Expand All @@ -56,9 +56,9 @@ func TestRepoComponent_CreateRepo(t *testing.T) {

dbrepo := &database.Repository{
UserID: 123,
Path: "ns/n",
Path: "ns/name",
GitPath: "gp",
Name: "n",
Name: "name",
Nickname: "nn",
Description: "desc",
Private: true,
Expand All @@ -73,7 +73,7 @@ func TestRepoComponent_CreateRepo(t *testing.T) {
r1, r2, err := repo.CreateRepo(ctx, types.CreateRepoReq{
Username: "user",
Namespace: "ns",
Name: "n",
Name: "name",
Nickname: "nn",
License: "MIT",
DefaultBranch: "main",
Expand Down

0 comments on commit 7438d2b

Please sign in to comment.