mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-24 17:47:46 +01:00
Validate repo and tag names in retag
Signed-off-by: cd1989 <chende@caicloud.io>
This commit is contained in:
parent
70016cc0eb
commit
c117a23133
@ -38,7 +38,7 @@ type Image struct {
|
||||
func ParseImage(image string) (*Image, error) {
|
||||
repo := strings.SplitN(image, "/", 2)
|
||||
if len(repo) < 2 {
|
||||
return nil, fmt.Errorf("Unable to parse image from string: %s", image)
|
||||
return nil, fmt.Errorf("unable to parse image from string: %s", image)
|
||||
}
|
||||
i := strings.SplitN(repo[1], ":", 2)
|
||||
res := &Image{
|
||||
|
24
src/common/utils/validate.go
Normal file
24
src/common/utils/validate.go
Normal file
@ -0,0 +1,24 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
const nameComponent = `[a-z0-9]+((?:[._]|__|[-]*)[a-z0-9]+)*`
|
||||
|
||||
// TagRegexp is regular expression to match image tags, for example, 'v1.0'
|
||||
var TagRegexp = regexp.MustCompile(`^[\w][\w.-]{0,127}$`)
|
||||
|
||||
// RepoRegexp is regular expression to match repo name, for example, 'busybox', 'stage/busybox'
|
||||
var RepoRegexp = regexp.MustCompile(fmt.Sprintf("^%s(/%s)*$", nameComponent, nameComponent))
|
||||
|
||||
// ValidateTag validates whether a tag is valid.
|
||||
func ValidateTag(tag string) bool {
|
||||
return TagRegexp.MatchString(tag)
|
||||
}
|
||||
|
||||
// ValidateRepo validates whether a repo name is valid.
|
||||
func ValidateRepo(repo string) bool {
|
||||
return RepoRegexp.MatchString(repo)
|
||||
}
|
183
src/common/utils/validate_test.go
Normal file
183
src/common/utils/validate_test.go
Normal file
@ -0,0 +1,183 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestValidateTag(t *testing.T) {
|
||||
cases := []struct {
|
||||
tag string
|
||||
valid bool
|
||||
}{
|
||||
{
|
||||
"v1.0",
|
||||
true,
|
||||
},
|
||||
{
|
||||
"1.0.0",
|
||||
true,
|
||||
},
|
||||
{
|
||||
"v1.0-alpha.0",
|
||||
true,
|
||||
},
|
||||
{
|
||||
"1__",
|
||||
true,
|
||||
},
|
||||
{
|
||||
"__v1.0",
|
||||
true,
|
||||
},
|
||||
{
|
||||
"_...",
|
||||
true,
|
||||
},
|
||||
{
|
||||
"_-_",
|
||||
true,
|
||||
},
|
||||
{
|
||||
"--v1.0",
|
||||
false,
|
||||
},
|
||||
{
|
||||
".0.1",
|
||||
false,
|
||||
},
|
||||
{
|
||||
"-0.1",
|
||||
false,
|
||||
},
|
||||
{
|
||||
"0.1.*",
|
||||
false,
|
||||
},
|
||||
{
|
||||
"0.1.?",
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
if c.valid {
|
||||
assert.True(t, ValidateTag(c.tag))
|
||||
} else {
|
||||
assert.False(t, ValidateTag(c.tag))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateRepo(t *testing.T) {
|
||||
cases := []struct {
|
||||
repo string
|
||||
valid bool
|
||||
}{
|
||||
{
|
||||
"a",
|
||||
true,
|
||||
},
|
||||
{
|
||||
"a_a",
|
||||
true,
|
||||
},
|
||||
{
|
||||
"a__a",
|
||||
true,
|
||||
},
|
||||
{
|
||||
"a-a",
|
||||
true,
|
||||
},
|
||||
{
|
||||
"a--a",
|
||||
true,
|
||||
},
|
||||
{
|
||||
"a---a",
|
||||
true,
|
||||
},
|
||||
{
|
||||
"a.a",
|
||||
true,
|
||||
},
|
||||
{
|
||||
"a/b.b",
|
||||
true,
|
||||
},
|
||||
{
|
||||
"a_a/b-b",
|
||||
true,
|
||||
},
|
||||
{
|
||||
".a",
|
||||
false,
|
||||
},
|
||||
{
|
||||
"_a",
|
||||
false,
|
||||
},
|
||||
{
|
||||
"-a",
|
||||
false,
|
||||
},
|
||||
{
|
||||
"a.",
|
||||
false,
|
||||
},
|
||||
{
|
||||
"a_",
|
||||
false,
|
||||
},
|
||||
{
|
||||
"a-",
|
||||
false,
|
||||
},
|
||||
{
|
||||
"a..a",
|
||||
false,
|
||||
},
|
||||
{
|
||||
"a___a",
|
||||
false,
|
||||
},
|
||||
{
|
||||
"a.-a",
|
||||
false,
|
||||
},
|
||||
{
|
||||
"a_-a",
|
||||
false,
|
||||
},
|
||||
{
|
||||
"a*",
|
||||
false,
|
||||
},
|
||||
{
|
||||
"A/_a",
|
||||
false,
|
||||
},
|
||||
{
|
||||
"A/.a",
|
||||
false,
|
||||
},
|
||||
{
|
||||
"Aaaa",
|
||||
false,
|
||||
},
|
||||
{
|
||||
"aaaA",
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
if c.valid {
|
||||
assert.True(t, ValidateRepo(c.repo))
|
||||
} else {
|
||||
assert.False(t, ValidateRepo(c.repo))
|
||||
}
|
||||
}
|
||||
}
|
@ -432,6 +432,12 @@ func (ra *RepositoryAPI) Retag() {
|
||||
}
|
||||
|
||||
repoName := ra.GetString(":splat")
|
||||
project, repo := utils.ParseRepository(repoName)
|
||||
if !utils.ValidateRepo(repo) {
|
||||
ra.HandleBadRequest(fmt.Sprintf("invalid repo '%s'", repo))
|
||||
return
|
||||
}
|
||||
|
||||
request := models.RetagRequest{}
|
||||
ra.DecodeJSONReq(&request)
|
||||
srcImage, err := models.ParseImage(request.SrcImage)
|
||||
@ -440,6 +446,11 @@ func (ra *RepositoryAPI) Retag() {
|
||||
return
|
||||
}
|
||||
|
||||
if !utils.ValidateTag(request.Tag) {
|
||||
ra.HandleBadRequest(fmt.Sprintf("invalid tag '%s'", request.Tag))
|
||||
return
|
||||
}
|
||||
|
||||
// Check whether source image exists
|
||||
exist, _, err := ra.checkExistence(fmt.Sprintf("%s/%s", srcImage.Project, srcImage.Repo), srcImage.Tag)
|
||||
if err != nil {
|
||||
@ -452,7 +463,6 @@ func (ra *RepositoryAPI) Retag() {
|
||||
}
|
||||
|
||||
// Check whether target project exists
|
||||
project, repo := utils.ParseRepository(repoName)
|
||||
exist, err = ra.ProjectMgr.Exists(project)
|
||||
if err != nil {
|
||||
ra.ParseAndHandleError(fmt.Sprintf("failed to check the existence of project %s", project), err)
|
||||
@ -476,7 +486,7 @@ func (ra *RepositoryAPI) Retag() {
|
||||
}
|
||||
}
|
||||
|
||||
// Check whether use has read permission to source project
|
||||
// Check whether user has read permission to source project
|
||||
if !ra.SecurityCtx.HasReadPerm(srcImage.Project) {
|
||||
log.Errorf("user has no read permission to project '%s'", srcImage.Project)
|
||||
ra.HandleForbidden(fmt.Sprintf("%s has no read permission to project %s", ra.SecurityCtx.GetUsername(), srcImage.Project))
|
||||
|
@ -440,5 +440,33 @@ func TestRetag(t *testing.T) {
|
||||
assert.Equal(int(409), httpStatusCode, "httpStatusCode should be 409")
|
||||
}
|
||||
|
||||
// -------------------case 7 : response code = 400------------------------//
|
||||
fmt.Println("case 7 : response code = 400")
|
||||
retagReq = &apilib.Retag{
|
||||
Tag: ".0.1",
|
||||
SrcImage: "library/hello-world:latest",
|
||||
Override: true,
|
||||
}
|
||||
code, err = apiTest.RetagImage(*admin, repo, retagReq)
|
||||
if err != nil {
|
||||
t.Errorf("failed to retag: %v", err)
|
||||
} else {
|
||||
assert.Equal(int(400), code, "response code should be 400")
|
||||
}
|
||||
|
||||
// -------------------case 8 : response code = 400------------------------//
|
||||
fmt.Println("case 8 : response code = 400")
|
||||
retagReq = &apilib.Retag{
|
||||
Tag: "v0.1",
|
||||
SrcImage: "library/hello-world:latest",
|
||||
Override: true,
|
||||
}
|
||||
code, err = apiTest.RetagImage(*admin, "library/Aaaa", retagReq)
|
||||
if err != nil {
|
||||
t.Errorf("failed to retag: %v", err)
|
||||
} else {
|
||||
assert.Equal(int(400), code, "response code should be 400")
|
||||
}
|
||||
|
||||
fmt.Printf("\n")
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user