Skip to content

Commit

Permalink
Merge branch 'main' into dependabot/go_modules/src/golang.org/x/net-0…
Browse files Browse the repository at this point in the history
….24.0
  • Loading branch information
Shengwen YU authored Apr 28, 2024
2 parents 4edc91d + fba4c40 commit ce7f3fb
Show file tree
Hide file tree
Showing 40 changed files with 283 additions and 123 deletions.
18 changes: 18 additions & 0 deletions api/v2.0/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1192,6 +1192,8 @@ paths:
$ref: '#/responses/403'
'404':
$ref: '#/responses/404'
'422':
$ref: '#/responses/422'
'500':
$ref: '#/responses/500'
/projects/{project_name}/repositories/{repository_name}/artifacts/{reference}/scan/stop:
Expand Down Expand Up @@ -1223,6 +1225,8 @@ paths:
$ref: '#/responses/403'
'404':
$ref: '#/responses/404'
'422':
$ref: '#/responses/422'
'500':
$ref: '#/responses/500'
/projects/{project_name}/repositories/{repository_name}/artifacts/{reference}/scan/{report_id}/log:
Expand Down Expand Up @@ -1476,6 +1480,8 @@ paths:
$ref: '#/responses/403'
'404':
$ref: '#/responses/404'
'422':
$ref: '#/responses/422'
'500':
$ref: '#/responses/500'
/projects/{project_name}/repositories/{repository_name}/artifacts/{reference}/labels:
Expand Down Expand Up @@ -4823,6 +4829,8 @@ paths:
$ref: '#/responses/403'
'404':
$ref: '#/responses/404'
'422':
$ref: '#/responses/422'
'500':
$ref: '#/responses/500'
/schedules:
Expand Down Expand Up @@ -6456,6 +6464,14 @@ responses:
type: string
schema:
$ref: '#/definitions/Errors'
'422':
description: Unsupported Type
headers:
X-Request-Id:
description: The ID of the corresponding request for the response
type: string
schema:
$ref: '#/definitions/Errors'
'500':
description: Internal server error
headers:
Expand Down Expand Up @@ -6800,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'
Expand Down
7 changes: 7 additions & 0 deletions src/controller/event/handler/internal/artifact.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"time"

"github.com/goharbor/harbor/src/controller/artifact"
"github.com/goharbor/harbor/src/controller/artifact/processor/sbom"
"github.com/goharbor/harbor/src/controller/event"
"github.com/goharbor/harbor/src/controller/event/operator"
"github.com/goharbor/harbor/src/controller/repository"
Expand All @@ -36,6 +37,7 @@ import (
"github.com/goharbor/harbor/src/pkg"
pkgArt "github.com/goharbor/harbor/src/pkg/artifact"
"github.com/goharbor/harbor/src/pkg/scan/report"
v1 "github.com/goharbor/harbor/src/pkg/scan/rest/v1"
"github.com/goharbor/harbor/src/pkg/task"
)

Expand Down Expand Up @@ -319,6 +321,11 @@ func (a *ArtifactEventHandler) onDelete(ctx context.Context, event *event.Artifa
log.Errorf("failed to delete scan reports of artifact %v, error: %v", unrefDigests, err)
}

if event.Artifact.Type == sbom.ArtifactTypeSBOM && len(event.Artifact.Digest) > 0 {
if err := reportMgr.DeleteByExtraAttr(ctx, v1.MimeTypeSBOMReport, "sbom_digest", event.Artifact.Digest); err != nil {
log.Errorf("failed to delete scan reports of with sbom digest %v, error: %v", event.Artifact.Digest, err)
}
}
return nil
}

Expand Down
4 changes: 2 additions & 2 deletions src/controller/event/topic.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ func (p *PushArtifactEvent) ResolveToAuditLog() (*model.AuditLog, error) {
ResourceType: "artifact"}

if len(p.Tags) == 0 {
auditLog.Resource = fmt.Sprintf("%s:%s",
auditLog.Resource = fmt.Sprintf("%s@%s",
p.Artifact.RepositoryName, p.Artifact.Digest)
} else {
auditLog.Resource = fmt.Sprintf("%s:%s",
Expand Down Expand Up @@ -222,7 +222,7 @@ func (d *DeleteArtifactEvent) ResolveToAuditLog() (*model.AuditLog, error) {
Operation: rbac.ActionDelete.String(),
Username: d.Operator,
ResourceType: "artifact",
Resource: fmt.Sprintf("%s:%s", d.Artifact.RepositoryName, d.Artifact.Digest)}
Resource: fmt.Sprintf("%s@%s", d.Artifact.RepositoryName, d.Artifact.Digest)}
return auditLog, nil
}

Expand Down
23 changes: 22 additions & 1 deletion src/controller/scan/base_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -751,13 +751,34 @@ func (bc *basicController) GetSBOMSummary(ctx context.Context, art *ar.Artifact,
reportContent := reports[0].Report
result := map[string]interface{}{}
if len(reportContent) == 0 {
log.Warning("no content for current report")
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
}
err = json.Unmarshal([]byte(reportContent), &result)
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 {
Expand Down
25 changes: 22 additions & 3 deletions src/controller/scan/base_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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)
}
14 changes: 14 additions & 0 deletions src/pkg/scan/dao/scan/report.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package scan

import (
"context"
"fmt"

"github.com/goharbor/harbor/src/lib/errors"
"github.com/goharbor/harbor/src/lib/orm"
Expand All @@ -38,6 +39,8 @@ type DAO interface {
UpdateReportData(ctx context.Context, uuid string, report string) error
// Update update report
Update(ctx context.Context, r *Report, cols ...string) error
// DeleteByExtraAttr delete the scan_report by mimeType and extra attribute
DeleteByExtraAttr(ctx context.Context, mimeType, attrName, attrValue string) error
}

// New returns an instance of the default DAO
Expand Down Expand Up @@ -110,3 +113,14 @@ func (d *dao) Update(ctx context.Context, r *Report, cols ...string) error {
}
return nil
}

func (d *dao) DeleteByExtraAttr(ctx context.Context, mimeType, attrName, attrValue string) error {
o, err := orm.FromContext(ctx)
if err != nil {
return err
}
delReportSQL := "delete from scan_report where mime_type = ? and report::jsonb @> ?"
dgstJSONStr := fmt.Sprintf(`{"%s":"%s"}`, attrName, attrValue)
_, err = o.Raw(delReportSQL, mimeType, dgstJSONStr).Exec()
return err
}
24 changes: 22 additions & 2 deletions src/pkg/scan/dao/scan/report_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,23 @@ func (suite *ReportTestSuite) SetupTest() {
RegistrationUUID: "ruuid",
MimeType: v1.MimeTypeNativeReport,
}

suite.create(r)
sbomReport := &Report{
UUID: "uuid3",
Digest: "digest1003",
RegistrationUUID: "ruuid",
MimeType: v1.MimeTypeSBOMReport,
Report: `{"sbom_digest": "sha256:abc"}`,
}
suite.create(sbomReport)
}

// TearDownTest clears enf for test case.
func (suite *ReportTestSuite) TearDownTest() {
_, err := suite.dao.DeleteMany(orm.Context(), q.Query{Keywords: q.KeyWords{"uuid": "uuid"}})
require.NoError(suite.T(), err)
_, err = suite.dao.DeleteMany(orm.Context(), q.Query{Keywords: q.KeyWords{"uuid": "uuid3"}})
require.NoError(suite.T(), err)
}

// TestReportList tests list reports with query parameters.
Expand Down Expand Up @@ -95,7 +104,7 @@ func (suite *ReportTestSuite) TestReportUpdateReportData() {
err := suite.dao.UpdateReportData(orm.Context(), "uuid", "{}")
suite.Require().NoError(err)

l, err := suite.dao.List(orm.Context(), nil)
l, err := suite.dao.List(orm.Context(), q.New(q.KeyWords{"uuid": "uuid"}))
suite.Require().NoError(err)
suite.Require().Equal(1, len(l))
suite.Equal("{}", l[0].Report)
Expand All @@ -104,6 +113,17 @@ func (suite *ReportTestSuite) TestReportUpdateReportData() {
suite.Require().NoError(err)
}

func (suite *ReportTestSuite) TestDeleteReportBySBOMDigest() {
l, err := suite.dao.List(orm.Context(), nil)
suite.Require().NoError(err)
suite.Equal(2, len(l))
err = suite.dao.DeleteByExtraAttr(orm.Context(), v1.MimeTypeSBOMReport, "sbom_digest", "sha256:abc")
suite.Require().NoError(err)
l2, err := suite.dao.List(orm.Context(), nil)
suite.Require().NoError(err)
suite.Equal(1, len(l2))
}

func (suite *ReportTestSuite) create(r *Report) {
id, err := suite.dao.Create(orm.Context(), r)
suite.Require().NoError(err)
Expand Down
6 changes: 6 additions & 0 deletions src/pkg/scan/report/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ type Manager interface {

// Update update report information
Update(ctx context.Context, r *scan.Report, cols ...string) error
// DeleteByExtraAttr delete scan_report by sbom_digest
DeleteByExtraAttr(ctx context.Context, mimeType, attrName, attrValue string) error
}

// basicManager is a default implementation of report manager.
Expand Down Expand Up @@ -226,3 +228,7 @@ func (bm *basicManager) List(ctx context.Context, query *q.Query) ([]*scan.Repor
func (bm *basicManager) Update(ctx context.Context, r *scan.Report, cols ...string) error {
return bm.dao.Update(ctx, r, cols...)
}

func (bm *basicManager) DeleteByExtraAttr(ctx context.Context, mimeType, attrName, attrValue string) error {
return bm.dao.DeleteByExtraAttr(ctx, mimeType, attrName, attrValue)
}
4 changes: 4 additions & 0 deletions src/pkg/scan/sbom/model/summary.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ const (
Duration = "duration"
// ScanStatus ...
ScanStatus = "scan_status"
// ReportID ...
ReportID = "report_id"
// Scanner ...
Scanner = "scanner"
)

// Summary includes the sbom summary information
Expand Down
21 changes: 12 additions & 9 deletions src/pkg/scan/sbom/sbom.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -107,19 +107,21 @@ 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
func (v *scanHandler) annotations() map[string]string {
t := time.Now().Format(time.RFC3339)
return map[string]string{
"created-by": "Harbor",
"org.opencontainers.artifact.created": time.Now().Format(time.RFC3339),
"created": t,
"created-by": "Harbor",
"org.opencontainers.artifact.created": t,
"org.opencontainers.artifact.description": "SPDX JSON SBOM",
}
}

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
Expand All @@ -128,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
Expand All @@ -148,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
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ export const ACTION_RESOURCE_I18N_MAP = {
log: 'ROBOT_ACCOUNT.LOG',
'notification-policy': 'ROBOT_ACCOUNT.NOTIFICATION_POLICY',
quota: 'ROBOT_ACCOUNT.QUOTA',
sbom: 'ROBOT_ACCOUNT.SBOM',
};

export function convertKey(key: string) {
Expand Down
Loading

0 comments on commit ce7f3fb

Please sign in to comment.