diff --git a/src/server/v2.0/handler/scanexport.go b/src/server/v2.0/handler/scanexport.go index 21d3f8998..e4f29192e 100644 --- a/src/server/v2.0/handler/scanexport.go +++ b/src/server/v2.0/handler/scanexport.go @@ -14,6 +14,7 @@ import ( "github.com/go-openapi/strfmt" "github.com/goharbor/harbor/src/common/rbac" + "github.com/goharbor/harbor/src/controller/project" "github.com/goharbor/harbor/src/controller/scandataexport" "github.com/goharbor/harbor/src/jobservice/job" "github.com/goharbor/harbor/src/lib/log" @@ -29,6 +30,7 @@ import ( func newScanDataExportAPI() *scanDataExportAPI { return &scanDataExportAPI{ scanDataExportCtl: scandataexport.Ctl, + proCtl: project.Ctl, sysArtifactMgr: systemartifact.Mgr, userMgr: user.Mgr, } @@ -37,6 +39,7 @@ func newScanDataExportAPI() *scanDataExportAPI { type scanDataExportAPI struct { BaseAPI scanDataExportCtl scandataexport.Controller + proCtl project.Controller sysArtifactMgr systemartifact.Manager userMgr user.Manager } @@ -47,7 +50,7 @@ func (se *scanDataExportAPI) Prepare(ctx context.Context, operation string, para func (se *scanDataExportAPI) ExportScanData(ctx context.Context, params operation.ExportScanDataParams) middleware.Responder { // validate the request params - if err := validateScanExportParams(params); err != nil { + if err := se.validateScanExportParams(ctx, params); err != nil { return se.SendError(ctx, err) } @@ -55,13 +58,6 @@ func (se *scanDataExportAPI) ExportScanData(ctx context.Context, params operatio return se.SendError(ctx, err) } - // check if the MIME type for the export is the Generic vulnerability data - if params.XScanDataType != v1.MimeTypeGenericVulnerabilityReport { - error := &models.Error{Message: fmt.Sprintf("Unsupported MIME type : %s", params.XScanDataType)} - errors := &models.Errors{Errors: []*models.Error{error}} - return operation.NewExportScanDataBadRequest().WithPayload(errors) - } - scanDataExportJob := new(models.ScanDataExportJob) secContext, err := se.GetSecurityContext(ctx) @@ -309,10 +305,17 @@ func (se *scanDataExportAPI) requireProjectsAccess(ctx context.Context, pids []i // 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 { +// 1. check the scan data type +// 2. the criteria should not be empty +// 3. currently only the export of single project is open +// 4. check the existence of project +// 5. do not allow to input space in the repo/tag/cve_id (space will lead to misjudge for doublestar filter) +func (se *scanDataExportAPI) validateScanExportParams(ctx context.Context, params operation.ExportScanDataParams) error { + // check if the MIME type for the export is the Generic vulnerability data + if params.XScanDataType != v1.MimeTypeGenericVulnerabilityReport { + return errors.BadRequestError(errors.Errorf("Unsupported MIME type : %s", params.XScanDataType)) + } + criteria := params.Criteria if criteria == nil { return errors.BadRequestError(errors.Errorf("criteria is invalid: %v", criteria)) @@ -323,6 +326,16 @@ func validateScanExportParams(params operation.ExportScanDataParams) error { return errors.BadRequestError(errors.Errorf("only support export single project, invalid value: %v", criteria.Projects)) } + // check whether the project exists + exist, err := se.proCtl.Exists(ctx, criteria.Projects[0]) + if err != nil { + return errors.UnknownError(errors.Errorf("check the existence of project error: %v", err)) + } + + if !exist { + return errors.NotFoundError(errors.Errorf("project %d not found", criteria.Projects[0])) + } + // check spaces space := " " inspectList := []string{criteria.Repositories, criteria.Tags, criteria.CVEIds} diff --git a/src/server/v2.0/handler/scanexport_test.go b/src/server/v2.0/handler/scanexport_test.go index 8c6d3d3d0..01dfff7a7 100644 --- a/src/server/v2.0/handler/scanexport_test.go +++ b/src/server/v2.0/handler/scanexport_test.go @@ -2,6 +2,7 @@ package handler import ( "bytes" + "context" "encoding/json" "fmt" "io" @@ -22,6 +23,7 @@ import ( "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/project" "github.com/goharbor/harbor/src/testing/controller/scandataexport" "github.com/goharbor/harbor/src/testing/mock" systemartifacttesting "github.com/goharbor/harbor/src/testing/pkg/systemartifact" @@ -32,6 +34,7 @@ import ( type ScanExportTestSuite struct { htesting.Suite scanExportCtl *scandataexport.Controller + proCtl *project.Controller sysArtifactMgr *systemartifacttesting.Manager userMgr *user.Manager } @@ -43,15 +46,18 @@ func (suite *ScanExportTestSuite) SetupSuite() { func (suite *ScanExportTestSuite) SetupTest() { suite.scanExportCtl = &scandataexport.Controller{} + suite.proCtl = &project.Controller{} suite.sysArtifactMgr = &systemartifacttesting.Manager{} suite.userMgr = &user.Manager{} suite.Config = &restapi.Config{ ScanDataExportAPI: &scanDataExportAPI{ scanDataExportCtl: suite.scanExportCtl, + proCtl: suite.proCtl, sysArtifactMgr: suite.sysArtifactMgr, userMgr: suite.userMgr, }, } + mock.OnAnything(suite.proCtl, "Exists").Return(true, nil) suite.Suite.SetupSuite() } @@ -95,8 +101,17 @@ func (suite *ScanExportTestSuite) TestAuthorization() { } func (suite *ScanExportTestSuite) TestValidateScanExportParams() { - // empty criteria should return error - err := validateScanExportParams(operation.ExportScanDataParams{}) + api := newScanDataExportAPI() + api.proCtl = suite.proCtl + ctx := context.TODO() + // no scan data type should return error + err := api.validateScanExportParams(ctx, operation.ExportScanDataParams{}) + suite.Error(err) + suite.True(errors.IsErr(err, errors.BadRequestCode)) + + xScanDataType := v1.MimeTypeGenericVulnerabilityReport + // empty params should return error + err = api.validateScanExportParams(ctx, operation.ExportScanDataParams{XScanDataType: xScanDataType}) suite.Error(err) suite.True(errors.IsErr(err, errors.BadRequestCode)) @@ -104,7 +119,7 @@ func (suite *ScanExportTestSuite) TestValidateScanExportParams() { criteria := models.ScanDataExportRequest{ Projects: []int64{200, 300}, } - err = validateScanExportParams(operation.ExportScanDataParams{Criteria: &criteria}) + err = api.validateScanExportParams(ctx, operation.ExportScanDataParams{XScanDataType: xScanDataType, Criteria: &criteria}) suite.Error(err) suite.True(errors.IsErr(err, errors.BadRequestCode)) @@ -116,7 +131,7 @@ func (suite *ScanExportTestSuite) TestValidateScanExportParams() { Repositories: "test-repo1, test-repo2", Tags: "{test-tag1, test-tag2}", } - err = validateScanExportParams(operation.ExportScanDataParams{Criteria: &criteria}) + err = api.validateScanExportParams(ctx, operation.ExportScanDataParams{XScanDataType: xScanDataType, Criteria: &criteria}) suite.Error(err) suite.True(errors.IsErr(err, errors.BadRequestCode)) @@ -128,8 +143,22 @@ func (suite *ScanExportTestSuite) TestValidateScanExportParams() { Repositories: "test-repo1,test-repo2", Tags: "{test-tag1,test-tag2}", } - err = validateScanExportParams(operation.ExportScanDataParams{Criteria: &criteria}) + err = api.validateScanExportParams(ctx, operation.ExportScanDataParams{XScanDataType: xScanDataType, Criteria: &criteria}) suite.NoError(err) + + // none exist project should return error + api.proCtl = &project.Controller{} + mock.OnAnything(api.proCtl, "Exists").Return(false, nil) + 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 = api.validateScanExportParams(ctx, operation.ExportScanDataParams{XScanDataType: xScanDataType, Criteria: &criteria}) + suite.Error(err) + suite.True(errors.IsErr(err, errors.NotFoundCode)) } func (suite *ScanExportTestSuite) TestExportScanData() {