mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-23 18:55:18 +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) {
|
func ParseImage(image string) (*Image, error) {
|
||||||
repo := strings.SplitN(image, "/", 2)
|
repo := strings.SplitN(image, "/", 2)
|
||||||
if len(repo) < 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)
|
i := strings.SplitN(repo[1], ":", 2)
|
||||||
res := &Image{
|
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")
|
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{}
|
request := models.RetagRequest{}
|
||||||
ra.DecodeJSONReq(&request)
|
ra.DecodeJSONReq(&request)
|
||||||
srcImage, err := models.ParseImage(request.SrcImage)
|
srcImage, err := models.ParseImage(request.SrcImage)
|
||||||
@ -440,6 +446,11 @@ func (ra *RepositoryAPI) Retag() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !utils.ValidateTag(request.Tag) {
|
||||||
|
ra.HandleBadRequest(fmt.Sprintf("invalid tag '%s'", request.Tag))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Check whether source image exists
|
// Check whether source image exists
|
||||||
exist, _, err := ra.checkExistence(fmt.Sprintf("%s/%s", srcImage.Project, srcImage.Repo), srcImage.Tag)
|
exist, _, err := ra.checkExistence(fmt.Sprintf("%s/%s", srcImage.Project, srcImage.Repo), srcImage.Tag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -452,7 +463,6 @@ func (ra *RepositoryAPI) Retag() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check whether target project exists
|
// Check whether target project exists
|
||||||
project, repo := utils.ParseRepository(repoName)
|
|
||||||
exist, err = ra.ProjectMgr.Exists(project)
|
exist, err = ra.ProjectMgr.Exists(project)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ra.ParseAndHandleError(fmt.Sprintf("failed to check the existence of project %s", project), err)
|
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) {
|
if !ra.SecurityCtx.HasReadPerm(srcImage.Project) {
|
||||||
log.Errorf("user has no read permission to project '%s'", 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))
|
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")
|
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")
|
fmt.Printf("\n")
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user