From ec8d692fe6038b2d0f35310ec10fdc665ad0e1c1 Mon Sep 17 00:00:00 2001 From: "stonezdj(Daojun Zhang)" Date: Thu, 25 Apr 2024 17:00:35 +0800 Subject: [PATCH] Add scanner info and report_id to sbom_overview on listing artifact (#20358) Add scan_status and report_id when scan has a failed task Signed-off-by: stonezdj --- api/v2.0/swagger.yaml | 2 ++ src/controller/scan/base_controller.go | 21 ++++++++++++++++ src/controller/scan/base_controller_test.go | 27 ++++++++++++++++++--- src/pkg/scan/sbom/model/summary.go | 4 +++ src/pkg/scan/sbom/sbom.go | 15 ++++++------ src/server/v2.0/handler/assembler/report.go | 22 ++++++++--------- 6 files changed, 68 insertions(+), 23 deletions(-) diff --git a/api/v2.0/swagger.yaml b/api/v2.0/swagger.yaml index 6890602a6..c9e1e8a50 100644 --- a/api/v2.0/swagger.yaml +++ b/api/v2.0/swagger.yaml @@ -6816,6 +6816,8 @@ definitions: format: int64 description: 'Time in seconds required to create the report' example: 300 + scanner: + $ref: '#/definitions/Scanner' NativeReportSummary: type: object description: 'The summary for the native report' diff --git a/src/controller/scan/base_controller.go b/src/controller/scan/base_controller.go index 161481711..c70221a0f 100644 --- a/src/controller/scan/base_controller.go +++ b/src/controller/scan/base_controller.go @@ -751,6 +751,11 @@ func (bc *basicController) GetSBOMSummary(ctx context.Context, art *ar.Artifact, reportContent := reports[0].Report result := map[string]interface{}{} if len(reportContent) == 0 { + status := bc.retrieveStatusFromTask(ctx, reports[0].UUID) + if len(status) > 0 { + result[sbomModel.ReportID] = reports[0].UUID + result[sbomModel.ScanStatus] = status + } log.Debug("no content for current report") return result, nil } @@ -758,6 +763,22 @@ func (bc *basicController) GetSBOMSummary(ctx context.Context, art *ar.Artifact, return result, err } +// retrieve the status from task +func (bc *basicController) retrieveStatusFromTask(ctx context.Context, reportID string) string { + if len(reportID) == 0 { + return "" + } + tasks, err := bc.taskMgr.ListScanTasksByReportUUID(ctx, reportID) + if err != nil { + log.Warningf("can not find the task with report UUID %v, error %v", reportID, err) + return "" + } + if len(tasks) > 0 { + return tasks[0].Status + } + return "" +} + // GetScanLog ... func (bc *basicController) GetScanLog(ctx context.Context, artifact *ar.Artifact, uuid string) ([]byte, error) { if len(uuid) == 0 { diff --git a/src/controller/scan/base_controller_test.go b/src/controller/scan/base_controller_test.go index 521325d79..19a559e0e 100644 --- a/src/controller/scan/base_controller_test.go +++ b/src/controller/scan/base_controller_test.go @@ -70,9 +70,10 @@ type ControllerTestSuite struct { tagCtl *tagtesting.FakeController - registration *scanner.Registration - artifact *artifact.Artifact - rawReport string + registration *scanner.Registration + artifact *artifact.Artifact + wrongArtifact *artifact.Artifact + rawReport string execMgr *tasktesting.ExecutionManager taskMgr *tasktesting.Manager @@ -101,6 +102,9 @@ func (suite *ControllerTestSuite) SetupSuite() { suite.artifact.Digest = "digest-code" suite.artifact.ManifestMediaType = v1.MimeTypeDockerArtifact + suite.wrongArtifact = &artifact.Artifact{Artifact: art.Artifact{ID: 2, ProjectID: 1}} + suite.wrongArtifact.Digest = "digest-wrong" + m := &v1.ScannerAdapterMetadata{ Scanner: &v1.Scanner{ Name: "Trivy", @@ -202,8 +206,11 @@ func (suite *ControllerTestSuite) SetupSuite() { Report: `{"sbom_digest": "sha256:1234567890", "scan_status": "Success", "duration": 3, "start_time": "2021-09-01T00:00:00Z", "end_time": "2021-09-01T00:00:03Z"}`, }, } + + emptySBOMReport := []*scan.Report{{Report: ``, UUID: "rp-uuid-004"}} mgr.On("GetBy", mock.Anything, suite.artifact.Digest, suite.registration.UUID, []string{v1.MimeTypeNativeReport}).Return(reports, nil) mgr.On("GetBy", mock.Anything, suite.artifact.Digest, suite.registration.UUID, []string{v1.MimeTypeSBOMReport}).Return(sbomReport, nil) + mgr.On("GetBy", mock.Anything, suite.wrongArtifact.Digest, suite.registration.UUID, []string{v1.MimeTypeSBOMReport}).Return(emptySBOMReport, nil) mgr.On("Get", mock.Anything, "rp-uuid-001").Return(reports[0], nil) mgr.On("UpdateReportData", "rp-uuid-001", suite.rawReport, (int64)(10000)).Return(nil) mgr.On("UpdateStatus", "the-uuid-123", "Success", (int64)(10000)).Return(nil) @@ -654,6 +661,12 @@ func (suite *ControllerTestSuite) TestGenerateSBOMSummary() { suite.NotNil(dgst) suite.Equal("Success", status) suite.Equal("sha256:1234567890", dgst) + tasks := []*task.Task{{Status: "Error"}} + suite.taskMgr.On("ListScanTasksByReportUUID", mock.Anything, "rp-uuid-004").Return(tasks, nil).Once() + sum2, err := suite.c.GetSummary(context.TODO(), suite.wrongArtifact, []string{v1.MimeTypeSBOMReport}) + suite.Nil(err) + suite.NotNil(sum2) + } func TestIsSBOMMimeTypes(t *testing.T) { @@ -683,5 +696,11 @@ func (suite *ControllerTestSuite) TestDeleteArtifactAccessories() { } ctx := orm.NewContext(nil, &ormtesting.FakeOrmer{}) suite.NoError(suite.c.deleteArtifactAccessories(ctx, reports)) - +} + +func (suite *ControllerTestSuite) TestRetrieveStatusFromTask() { + tasks := []*task.Task{{Status: "Error"}} + suite.taskMgr.On("ListScanTasksByReportUUID", mock.Anything, "rp-uuid-004").Return(tasks, nil).Once() + status := suite.c.retrieveStatusFromTask(nil, "rp-uuid-004") + suite.Equal("Error", status) } diff --git a/src/pkg/scan/sbom/model/summary.go b/src/pkg/scan/sbom/model/summary.go index 46c870f97..0d7e6a2ef 100644 --- a/src/pkg/scan/sbom/model/summary.go +++ b/src/pkg/scan/sbom/model/summary.go @@ -27,6 +27,10 @@ const ( Duration = "duration" // ScanStatus ... ScanStatus = "scan_status" + // ReportID ... + ReportID = "report_id" + // Scanner ... + Scanner = "scanner" ) // Summary includes the sbom summary information diff --git a/src/pkg/scan/sbom/sbom.go b/src/pkg/scan/sbom/sbom.go index a3e0f8501..f8e6d2e43 100644 --- a/src/pkg/scan/sbom/sbom.go +++ b/src/pkg/scan/sbom/sbom.go @@ -87,7 +87,7 @@ func (v *scanHandler) RequiredPermissions() []*types.Policy { // PostScan defines task specific operations after the scan is complete func (v *scanHandler) PostScan(ctx job.Context, sr *v1.ScanRequest, _ *scanModel.Report, rawReport string, startTime time.Time, robot *model.Robot) (string, error) { - sbomContent, err := retrieveSBOMContent(rawReport) + sbomContent, s, err := retrieveSBOMContent(rawReport) if err != nil { return "", err } @@ -107,7 +107,7 @@ func (v *scanHandler) PostScan(ctx job.Context, sr *v1.ScanRequest, _ *scanModel myLogger.Errorf("error when create accessory from image %v", err) return "", err } - return v.generateReport(startTime, sr.Artifact.Repository, dgst, "Success") + return v.generateReport(startTime, sr.Artifact.Repository, dgst, "Success", s) } // annotations defines the annotations for the accessory artifact @@ -121,7 +121,7 @@ func (v *scanHandler) annotations() map[string]string { } } -func (v *scanHandler) generateReport(startTime time.Time, repository, digest, status string) (string, error) { +func (v *scanHandler) generateReport(startTime time.Time, repository, digest, status string, scanner *v1.Scanner) (string, error) { summary := sbom.Summary{} endTime := time.Now() summary[sbom.StartTime] = startTime @@ -130,6 +130,7 @@ func (v *scanHandler) generateReport(startTime time.Time, repository, digest, st summary[sbom.SBOMRepository] = repository summary[sbom.SBOMDigest] = digest summary[sbom.ScanStatus] = status + summary[sbom.Scanner] = scanner rep, err := json.Marshal(summary) if err != nil { return "", err @@ -150,15 +151,15 @@ func registryFQDN(ctx context.Context) string { } // retrieveSBOMContent retrieves the "sbom" field from the raw report -func retrieveSBOMContent(rawReport string) ([]byte, error) { +func retrieveSBOMContent(rawReport string) ([]byte, *v1.Scanner, error) { rpt := vuln.Report{} err := json.Unmarshal([]byte(rawReport), &rpt) if err != nil { - return nil, err + return nil, nil, err } sbomContent, err := json.Marshal(rpt.SBOM) if err != nil { - return nil, err + return nil, nil, err } - return sbomContent, nil + return sbomContent, rpt.Scanner, nil } diff --git a/src/server/v2.0/handler/assembler/report.go b/src/server/v2.0/handler/assembler/report.go index e4f9657ea..ff11e2b8f 100644 --- a/src/server/v2.0/handler/assembler/report.go +++ b/src/server/v2.0/handler/assembler/report.go @@ -21,16 +21,12 @@ import ( "github.com/goharbor/harbor/src/lib" "github.com/goharbor/harbor/src/lib/log" v1 "github.com/goharbor/harbor/src/pkg/scan/rest/v1" + sbomModel "github.com/goharbor/harbor/src/pkg/scan/sbom/model" "github.com/goharbor/harbor/src/server/v2.0/handler/model" ) const ( vulnerabilitiesAddition = "vulnerabilities" - startTime = "start_time" - endTime = "end_time" - scanStatus = "scan_status" - sbomDigest = "sbom_digest" - duration = "duration" ) // NewScanReportAssembler returns vul assembler @@ -92,17 +88,19 @@ func (assembler *ScanReportAssembler) Assemble(ctx context.Context) error { overview, err := assembler.scanCtl.GetSummary(ctx, &artifact.Artifact, []string{v1.MimeTypeSBOMReport}) if err != nil { log.Warningf("get scan summary of artifact %s@%s for %s failed, error:%v", artifact.RepositoryName, artifact.Digest, v1.MimeTypeSBOMReport, err) - } else if len(overview) > 0 { + } + if len(overview) > 0 { artifact.SBOMOverView = map[string]interface{}{ - startTime: overview[startTime], - endTime: overview[endTime], - scanStatus: overview[scanStatus], - sbomDigest: overview[sbomDigest], - duration: overview[duration], + sbomModel.StartTime: overview[sbomModel.StartTime], + sbomModel.EndTime: overview[sbomModel.EndTime], + sbomModel.ScanStatus: overview[sbomModel.ScanStatus], + sbomModel.SBOMDigest: overview[sbomModel.SBOMDigest], + sbomModel.Duration: overview[sbomModel.Duration], + sbomModel.ReportID: overview[sbomModel.ReportID], + sbomModel.Scanner: overview[sbomModel.Scanner], } } } } - return nil }