mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-28 11:37:42 +01:00
fix: validate export cve request params (#17341)
1. Validate export cve request params in the API handler 2. Trim space for request in the scan export job Closes: #17326 Signed-off-by: chlins <chenyuzh@vmware.com>
This commit is contained in:
parent
49d73fa57d
commit
1e13999fff
@ -285,7 +285,20 @@ func (sde *ScanDataExport) extractCriteria(params job.Parameters) (*export.Reque
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return criteria, nil
|
||||
|
||||
// sterilize trim spaces for some fields.
|
||||
sterilize := func(c *export.Request) *export.Request {
|
||||
if c != nil {
|
||||
space, empty := " ", ""
|
||||
c.Repositories = strings.ReplaceAll(c.Repositories, space, empty)
|
||||
c.Tags = strings.ReplaceAll(c.Tags, space, empty)
|
||||
c.CVEIds = strings.ReplaceAll(c.CVEIds, space, empty)
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
return sterilize(criteria), nil
|
||||
}
|
||||
|
||||
func (sde *ScanDataExport) calculateFileHash(fileName string) (digest.Digest, error) {
|
||||
|
@ -144,6 +144,25 @@ func (suite *ScanDataExportJobTestSuite) TestRunAttributeUpdateError() {
|
||||
|
||||
}
|
||||
|
||||
func (suite *ScanDataExportJobTestSuite) TestExtractCriteria() {
|
||||
// empty request should return error
|
||||
_, err := suite.job.extractCriteria(job.Parameters{})
|
||||
suite.Error(err)
|
||||
// invalid request should return error
|
||||
_, err = suite.job.extractCriteria(job.Parameters{"Request": ""})
|
||||
suite.Error(err)
|
||||
// valid request should not return error and trim space
|
||||
c, err := suite.job.extractCriteria(job.Parameters{"Request": map[string]interface{}{
|
||||
"CVEIds": "CVE-123, CVE-456 ",
|
||||
"Repositories": " test-repo1 ",
|
||||
"Tags": "test-tag1, test-tag2",
|
||||
}})
|
||||
suite.NoError(err)
|
||||
suite.Equal("CVE-123,CVE-456", c.CVEIds)
|
||||
suite.Equal("test-repo1", c.Repositories)
|
||||
suite.Equal("test-tag1,test-tag2", c.Tags)
|
||||
}
|
||||
|
||||
func (suite *ScanDataExportJobTestSuite) TestRunWithCriteria() {
|
||||
{
|
||||
data := suite.createDataRecords(3, 1)
|
||||
|
@ -46,19 +46,12 @@ func (se *scanDataExportAPI) Prepare(ctx context.Context, operation string, para
|
||||
}
|
||||
|
||||
func (se *scanDataExportAPI) ExportScanData(ctx context.Context, params operation.ExportScanDataParams) middleware.Responder {
|
||||
// validate project id, currently we only support single project
|
||||
criteria := params.Criteria
|
||||
if criteria == nil {
|
||||
err := errors.New(errors.Errorf("criteria is invalid: %v", criteria)).WithCode(errors.BadRequestCode)
|
||||
// validate the request params
|
||||
if err := validateScanExportParams(params); err != nil {
|
||||
return se.SendError(ctx, err)
|
||||
}
|
||||
|
||||
if len(criteria.Projects) != 1 {
|
||||
err := errors.New(errors.Errorf("only support export single project, invalid value: %v", criteria.Projects)).WithCode(errors.BadRequestCode)
|
||||
return se.SendError(ctx, err)
|
||||
}
|
||||
|
||||
if err := se.RequireProjectAccess(ctx, criteria.Projects[0], rbac.ActionCreate, rbac.ResourceExportCVE); err != nil {
|
||||
if err := se.RequireProjectAccess(ctx, params.Criteria.Projects[0], rbac.ActionCreate, rbac.ResourceExportCVE); err != nil {
|
||||
return se.SendError(ctx, err)
|
||||
}
|
||||
|
||||
@ -312,3 +305,32 @@ func (se *scanDataExportAPI) requireProjectsAccess(ctx context.Context, pids []i
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateScanExportParams validates scan data export request parameters by
|
||||
// following policies.
|
||||
// rules:
|
||||
// 1. the criteria should not be empty
|
||||
// 2. currently only the export of single project is open
|
||||
// 3. do not allow to input space in the repo/tag/cve_id (space will lead to misjudge for doublestar filter)
|
||||
func validateScanExportParams(params operation.ExportScanDataParams) error {
|
||||
criteria := params.Criteria
|
||||
if criteria == nil {
|
||||
return errors.BadRequestError(errors.Errorf("criteria is invalid: %v", criteria))
|
||||
}
|
||||
|
||||
// validate project id, currently we only support single project
|
||||
if len(criteria.Projects) != 1 {
|
||||
return errors.BadRequestError(errors.Errorf("only support export single project, invalid value: %v", criteria.Projects))
|
||||
}
|
||||
|
||||
// check spaces
|
||||
space := " "
|
||||
inspectList := []string{criteria.Repositories, criteria.Tags, criteria.CVEIds}
|
||||
for _, s := range inspectList {
|
||||
if strings.Contains(s, space) {
|
||||
return errors.BadRequestError(errors.Errorf("invalid criteria value, please remove additional spaces for input: %s", s))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ package handler
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@ -13,6 +12,7 @@ import (
|
||||
"time"
|
||||
|
||||
beegoorm "github.com/beego/beego/orm"
|
||||
"github.com/goharbor/harbor/src/lib/errors"
|
||||
testifymock "github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
@ -21,6 +21,7 @@ import (
|
||||
v1 "github.com/goharbor/harbor/src/pkg/scan/rest/v1"
|
||||
"github.com/goharbor/harbor/src/server/v2.0/models"
|
||||
"github.com/goharbor/harbor/src/server/v2.0/restapi"
|
||||
operation "github.com/goharbor/harbor/src/server/v2.0/restapi/operations/scan_data_export"
|
||||
"github.com/goharbor/harbor/src/testing/controller/scandataexport"
|
||||
"github.com/goharbor/harbor/src/testing/mock"
|
||||
systemartifacttesting "github.com/goharbor/harbor/src/testing/pkg/systemartifact"
|
||||
@ -61,7 +62,7 @@ func (suite *ScanExportTestSuite) TestAuthorization() {
|
||||
Labels: []int64{100},
|
||||
Projects: []int64{200},
|
||||
Repositories: "test-repo",
|
||||
Tags: "{test-tag1, test-tag2}",
|
||||
Tags: "{test-tag1,test-tag2}",
|
||||
}
|
||||
|
||||
reqs := []struct {
|
||||
@ -92,6 +93,45 @@ func (suite *ScanExportTestSuite) TestAuthorization() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *ScanExportTestSuite) TestValidateScanExportParams() {
|
||||
// empty criteria should return error
|
||||
err := validateScanExportParams(operation.ExportScanDataParams{})
|
||||
suite.Error(err)
|
||||
suite.True(errors.IsErr(err, errors.BadRequestCode))
|
||||
|
||||
// multiple projects in input should return error
|
||||
criteria := models.ScanDataExportRequest{
|
||||
Projects: []int64{200, 300},
|
||||
}
|
||||
err = validateScanExportParams(operation.ExportScanDataParams{Criteria: &criteria})
|
||||
suite.Error(err)
|
||||
suite.True(errors.IsErr(err, errors.BadRequestCode))
|
||||
|
||||
// spaces in input should return error
|
||||
criteria = models.ScanDataExportRequest{
|
||||
CVEIds: "CVE-123, CVE-456",
|
||||
Labels: []int64{100},
|
||||
Projects: []int64{200},
|
||||
Repositories: "test-repo1, test-repo2",
|
||||
Tags: "{test-tag1, test-tag2}",
|
||||
}
|
||||
err = validateScanExportParams(operation.ExportScanDataParams{Criteria: &criteria})
|
||||
suite.Error(err)
|
||||
suite.True(errors.IsErr(err, errors.BadRequestCode))
|
||||
|
||||
// valid params should pass validator
|
||||
criteria = models.ScanDataExportRequest{
|
||||
CVEIds: "CVE-123,CVE-456",
|
||||
Labels: []int64{100},
|
||||
Projects: []int64{200},
|
||||
Repositories: "test-repo1,test-repo2",
|
||||
Tags: "{test-tag1,test-tag2}",
|
||||
}
|
||||
err = validateScanExportParams(operation.ExportScanDataParams{Criteria: &criteria})
|
||||
suite.NoError(err)
|
||||
}
|
||||
|
||||
func (suite *ScanExportTestSuite) TestExportScanData() {
|
||||
suite.Security.On("GetUsername").Return("test-user")
|
||||
suite.Security.On("Can", mock.Anything, mock.Anything, mock.Anything).Return(true).Once()
|
||||
@ -107,7 +147,7 @@ func (suite *ScanExportTestSuite) TestExportScanData() {
|
||||
Labels: []int64{100},
|
||||
Projects: []int64{200},
|
||||
Repositories: "test-repo",
|
||||
Tags: "{test-tag1, test-tag2}",
|
||||
Tags: "{test-tag1,test-tag2}",
|
||||
}
|
||||
|
||||
data, err := json.Marshal(criteria)
|
||||
@ -128,7 +168,7 @@ func (suite *ScanExportTestSuite) TestExportScanData() {
|
||||
|
||||
// validate job name and user name set in the request for job execution
|
||||
jobRequestMatcher := testifymock.MatchedBy(func(req export.Request) bool {
|
||||
return req.UserName == "test-user" && req.JobName == "test-job" && req.Tags == "{test-tag1, test-tag2}" && req.UserID == 1000
|
||||
return req.UserName == "test-user" && req.JobName == "test-job" && req.Tags == "{test-tag1,test-tag2}" && req.UserID == 1000
|
||||
})
|
||||
suite.scanExportCtl.AssertCalled(suite.T(), "Start", mock.Anything, jobRequestMatcher)
|
||||
}
|
||||
@ -144,7 +184,7 @@ func (suite *ScanExportTestSuite) TestExportScanData() {
|
||||
Labels: []int64{100},
|
||||
Projects: []int64{200},
|
||||
Repositories: "test-repo",
|
||||
Tags: "{test-tag1, test-tag2}",
|
||||
Tags: "{test-tag1,test-tag2}",
|
||||
}
|
||||
|
||||
data, err := json.Marshal(criteria)
|
||||
@ -170,7 +210,7 @@ func (suite *ScanExportTestSuite) TestExportScanData() {
|
||||
Labels: []int64{100},
|
||||
Projects: []int64{200, 300},
|
||||
Repositories: "test-repo",
|
||||
Tags: "{test-tag1, test-tag2}",
|
||||
Tags: "{test-tag1,test-tag2}",
|
||||
}
|
||||
|
||||
data, err := json.Marshal(criteria)
|
||||
@ -201,7 +241,7 @@ func (suite *ScanExportTestSuite) TestExportScanDataGetUserIdError() {
|
||||
Labels: []int64{100},
|
||||
Projects: []int64{200},
|
||||
Repositories: "test-repo",
|
||||
Tags: "{test-tag1, test-tag2}",
|
||||
Tags: "{test-tag1,test-tag2}",
|
||||
}
|
||||
|
||||
data, err := json.Marshal(criteria)
|
||||
@ -234,7 +274,7 @@ func (suite *ScanExportTestSuite) TestExportScanDataGetUserIdNotFound() {
|
||||
Labels: []int64{100},
|
||||
Projects: []int64{200},
|
||||
Repositories: "test-repo",
|
||||
Tags: "{test-tag1, test-tag2}",
|
||||
Tags: "{test-tag1,test-tag2}",
|
||||
}
|
||||
|
||||
data, err := json.Marshal(criteria)
|
||||
@ -264,7 +304,7 @@ func (suite *ScanExportTestSuite) TestExportScanDataNoPrivileges() {
|
||||
Labels: []int64{100},
|
||||
Projects: []int64{200},
|
||||
Repositories: "test-repo",
|
||||
Tags: "{test-tag1, test-tag2}",
|
||||
Tags: "{test-tag1,test-tag2}",
|
||||
}
|
||||
|
||||
data, err := json.Marshal(criteria)
|
||||
|
Loading…
Reference in New Issue
Block a user