mirror of
https://github.com/goharbor/harbor.git
synced 2024-09-27 13:02:59 +02:00
Merge branch 'main' into dependabot/go_modules/src/github.com/golang-migrate/migrate/v4-4.17.1
This commit is contained in:
commit
cce1a4ae1a
@ -1192,6 +1192,8 @@ paths:
|
|||||||
$ref: '#/responses/403'
|
$ref: '#/responses/403'
|
||||||
'404':
|
'404':
|
||||||
$ref: '#/responses/404'
|
$ref: '#/responses/404'
|
||||||
|
'422':
|
||||||
|
$ref: '#/responses/422'
|
||||||
'500':
|
'500':
|
||||||
$ref: '#/responses/500'
|
$ref: '#/responses/500'
|
||||||
/projects/{project_name}/repositories/{repository_name}/artifacts/{reference}/scan/stop:
|
/projects/{project_name}/repositories/{repository_name}/artifacts/{reference}/scan/stop:
|
||||||
@ -1223,6 +1225,8 @@ paths:
|
|||||||
$ref: '#/responses/403'
|
$ref: '#/responses/403'
|
||||||
'404':
|
'404':
|
||||||
$ref: '#/responses/404'
|
$ref: '#/responses/404'
|
||||||
|
'422':
|
||||||
|
$ref: '#/responses/422'
|
||||||
'500':
|
'500':
|
||||||
$ref: '#/responses/500'
|
$ref: '#/responses/500'
|
||||||
/projects/{project_name}/repositories/{repository_name}/artifacts/{reference}/scan/{report_id}/log:
|
/projects/{project_name}/repositories/{repository_name}/artifacts/{reference}/scan/{report_id}/log:
|
||||||
@ -1476,6 +1480,8 @@ paths:
|
|||||||
$ref: '#/responses/403'
|
$ref: '#/responses/403'
|
||||||
'404':
|
'404':
|
||||||
$ref: '#/responses/404'
|
$ref: '#/responses/404'
|
||||||
|
'422':
|
||||||
|
$ref: '#/responses/422'
|
||||||
'500':
|
'500':
|
||||||
$ref: '#/responses/500'
|
$ref: '#/responses/500'
|
||||||
/projects/{project_name}/repositories/{repository_name}/artifacts/{reference}/labels:
|
/projects/{project_name}/repositories/{repository_name}/artifacts/{reference}/labels:
|
||||||
@ -4823,6 +4829,8 @@ paths:
|
|||||||
$ref: '#/responses/403'
|
$ref: '#/responses/403'
|
||||||
'404':
|
'404':
|
||||||
$ref: '#/responses/404'
|
$ref: '#/responses/404'
|
||||||
|
'422':
|
||||||
|
$ref: '#/responses/422'
|
||||||
'500':
|
'500':
|
||||||
$ref: '#/responses/500'
|
$ref: '#/responses/500'
|
||||||
/schedules:
|
/schedules:
|
||||||
@ -6456,6 +6464,14 @@ responses:
|
|||||||
type: string
|
type: string
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/Errors'
|
$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':
|
'500':
|
||||||
description: Internal server error
|
description: Internal server error
|
||||||
headers:
|
headers:
|
||||||
@ -6800,6 +6816,8 @@ definitions:
|
|||||||
format: int64
|
format: int64
|
||||||
description: 'Time in seconds required to create the report'
|
description: 'Time in seconds required to create the report'
|
||||||
example: 300
|
example: 300
|
||||||
|
scanner:
|
||||||
|
$ref: '#/definitions/Scanner'
|
||||||
NativeReportSummary:
|
NativeReportSummary:
|
||||||
type: object
|
type: object
|
||||||
description: 'The summary for the native report'
|
description: 'The summary for the native report'
|
||||||
|
@ -24,6 +24,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/controller/artifact"
|
"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"
|
||||||
"github.com/goharbor/harbor/src/controller/event/operator"
|
"github.com/goharbor/harbor/src/controller/event/operator"
|
||||||
"github.com/goharbor/harbor/src/controller/repository"
|
"github.com/goharbor/harbor/src/controller/repository"
|
||||||
@ -36,6 +37,7 @@ import (
|
|||||||
"github.com/goharbor/harbor/src/pkg"
|
"github.com/goharbor/harbor/src/pkg"
|
||||||
pkgArt "github.com/goharbor/harbor/src/pkg/artifact"
|
pkgArt "github.com/goharbor/harbor/src/pkg/artifact"
|
||||||
"github.com/goharbor/harbor/src/pkg/scan/report"
|
"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"
|
"github.com/goharbor/harbor/src/pkg/task"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -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)
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,7 +159,7 @@ func (p *PushArtifactEvent) ResolveToAuditLog() (*model.AuditLog, error) {
|
|||||||
ResourceType: "artifact"}
|
ResourceType: "artifact"}
|
||||||
|
|
||||||
if len(p.Tags) == 0 {
|
if len(p.Tags) == 0 {
|
||||||
auditLog.Resource = fmt.Sprintf("%s:%s",
|
auditLog.Resource = fmt.Sprintf("%s@%s",
|
||||||
p.Artifact.RepositoryName, p.Artifact.Digest)
|
p.Artifact.RepositoryName, p.Artifact.Digest)
|
||||||
} else {
|
} else {
|
||||||
auditLog.Resource = fmt.Sprintf("%s:%s",
|
auditLog.Resource = fmt.Sprintf("%s:%s",
|
||||||
@ -222,7 +222,7 @@ func (d *DeleteArtifactEvent) ResolveToAuditLog() (*model.AuditLog, error) {
|
|||||||
Operation: rbac.ActionDelete.String(),
|
Operation: rbac.ActionDelete.String(),
|
||||||
Username: d.Operator,
|
Username: d.Operator,
|
||||||
ResourceType: "artifact",
|
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
|
return auditLog, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -751,13 +751,34 @@ func (bc *basicController) GetSBOMSummary(ctx context.Context, art *ar.Artifact,
|
|||||||
reportContent := reports[0].Report
|
reportContent := reports[0].Report
|
||||||
result := map[string]interface{}{}
|
result := map[string]interface{}{}
|
||||||
if len(reportContent) == 0 {
|
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
|
return result, nil
|
||||||
}
|
}
|
||||||
err = json.Unmarshal([]byte(reportContent), &result)
|
err = json.Unmarshal([]byte(reportContent), &result)
|
||||||
return result, err
|
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 ...
|
// GetScanLog ...
|
||||||
func (bc *basicController) GetScanLog(ctx context.Context, artifact *ar.Artifact, uuid string) ([]byte, error) {
|
func (bc *basicController) GetScanLog(ctx context.Context, artifact *ar.Artifact, uuid string) ([]byte, error) {
|
||||||
if len(uuid) == 0 {
|
if len(uuid) == 0 {
|
||||||
|
@ -72,6 +72,7 @@ type ControllerTestSuite struct {
|
|||||||
|
|
||||||
registration *scanner.Registration
|
registration *scanner.Registration
|
||||||
artifact *artifact.Artifact
|
artifact *artifact.Artifact
|
||||||
|
wrongArtifact *artifact.Artifact
|
||||||
rawReport string
|
rawReport string
|
||||||
|
|
||||||
execMgr *tasktesting.ExecutionManager
|
execMgr *tasktesting.ExecutionManager
|
||||||
@ -101,6 +102,9 @@ func (suite *ControllerTestSuite) SetupSuite() {
|
|||||||
suite.artifact.Digest = "digest-code"
|
suite.artifact.Digest = "digest-code"
|
||||||
suite.artifact.ManifestMediaType = v1.MimeTypeDockerArtifact
|
suite.artifact.ManifestMediaType = v1.MimeTypeDockerArtifact
|
||||||
|
|
||||||
|
suite.wrongArtifact = &artifact.Artifact{Artifact: art.Artifact{ID: 2, ProjectID: 1}}
|
||||||
|
suite.wrongArtifact.Digest = "digest-wrong"
|
||||||
|
|
||||||
m := &v1.ScannerAdapterMetadata{
|
m := &v1.ScannerAdapterMetadata{
|
||||||
Scanner: &v1.Scanner{
|
Scanner: &v1.Scanner{
|
||||||
Name: "Trivy",
|
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"}`,
|
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.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.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("Get", mock.Anything, "rp-uuid-001").Return(reports[0], nil)
|
||||||
mgr.On("UpdateReportData", "rp-uuid-001", suite.rawReport, (int64)(10000)).Return(nil)
|
mgr.On("UpdateReportData", "rp-uuid-001", suite.rawReport, (int64)(10000)).Return(nil)
|
||||||
mgr.On("UpdateStatus", "the-uuid-123", "Success", (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.NotNil(dgst)
|
||||||
suite.Equal("Success", status)
|
suite.Equal("Success", status)
|
||||||
suite.Equal("sha256:1234567890", dgst)
|
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) {
|
func TestIsSBOMMimeTypes(t *testing.T) {
|
||||||
@ -683,5 +696,11 @@ func (suite *ControllerTestSuite) TestDeleteArtifactAccessories() {
|
|||||||
}
|
}
|
||||||
ctx := orm.NewContext(nil, &ormtesting.FakeOrmer{})
|
ctx := orm.NewContext(nil, &ormtesting.FakeOrmer{})
|
||||||
suite.NoError(suite.c.deleteArtifactAccessories(ctx, reports))
|
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)
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ package scan
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/lib/errors"
|
"github.com/goharbor/harbor/src/lib/errors"
|
||||||
"github.com/goharbor/harbor/src/lib/orm"
|
"github.com/goharbor/harbor/src/lib/orm"
|
||||||
@ -38,6 +39,8 @@ type DAO interface {
|
|||||||
UpdateReportData(ctx context.Context, uuid string, report string) error
|
UpdateReportData(ctx context.Context, uuid string, report string) error
|
||||||
// Update update report
|
// Update update report
|
||||||
Update(ctx context.Context, r *Report, cols ...string) error
|
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
|
// New returns an instance of the default DAO
|
||||||
@ -110,3 +113,14 @@ func (d *dao) Update(ctx context.Context, r *Report, cols ...string) error {
|
|||||||
}
|
}
|
||||||
return nil
|
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
|
||||||
|
}
|
||||||
|
@ -53,14 +53,23 @@ func (suite *ReportTestSuite) SetupTest() {
|
|||||||
RegistrationUUID: "ruuid",
|
RegistrationUUID: "ruuid",
|
||||||
MimeType: v1.MimeTypeNativeReport,
|
MimeType: v1.MimeTypeNativeReport,
|
||||||
}
|
}
|
||||||
|
|
||||||
suite.create(r)
|
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.
|
// TearDownTest clears enf for test case.
|
||||||
func (suite *ReportTestSuite) TearDownTest() {
|
func (suite *ReportTestSuite) TearDownTest() {
|
||||||
_, err := suite.dao.DeleteMany(orm.Context(), q.Query{Keywords: q.KeyWords{"uuid": "uuid"}})
|
_, err := suite.dao.DeleteMany(orm.Context(), q.Query{Keywords: q.KeyWords{"uuid": "uuid"}})
|
||||||
require.NoError(suite.T(), err)
|
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.
|
// TestReportList tests list reports with query parameters.
|
||||||
@ -95,7 +104,7 @@ func (suite *ReportTestSuite) TestReportUpdateReportData() {
|
|||||||
err := suite.dao.UpdateReportData(orm.Context(), "uuid", "{}")
|
err := suite.dao.UpdateReportData(orm.Context(), "uuid", "{}")
|
||||||
suite.Require().NoError(err)
|
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().NoError(err)
|
||||||
suite.Require().Equal(1, len(l))
|
suite.Require().Equal(1, len(l))
|
||||||
suite.Equal("{}", l[0].Report)
|
suite.Equal("{}", l[0].Report)
|
||||||
@ -104,6 +113,17 @@ func (suite *ReportTestSuite) TestReportUpdateReportData() {
|
|||||||
suite.Require().NoError(err)
|
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) {
|
func (suite *ReportTestSuite) create(r *Report) {
|
||||||
id, err := suite.dao.Create(orm.Context(), r)
|
id, err := suite.dao.Create(orm.Context(), r)
|
||||||
suite.Require().NoError(err)
|
suite.Require().NoError(err)
|
||||||
|
@ -104,6 +104,8 @@ type Manager interface {
|
|||||||
|
|
||||||
// Update update report information
|
// Update update report information
|
||||||
Update(ctx context.Context, r *scan.Report, cols ...string) error
|
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.
|
// basicManager is a default implementation of report manager.
|
||||||
@ -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 {
|
func (bm *basicManager) Update(ctx context.Context, r *scan.Report, cols ...string) error {
|
||||||
return bm.dao.Update(ctx, r, cols...)
|
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)
|
||||||
|
}
|
||||||
|
@ -27,6 +27,10 @@ const (
|
|||||||
Duration = "duration"
|
Duration = "duration"
|
||||||
// ScanStatus ...
|
// ScanStatus ...
|
||||||
ScanStatus = "scan_status"
|
ScanStatus = "scan_status"
|
||||||
|
// ReportID ...
|
||||||
|
ReportID = "report_id"
|
||||||
|
// Scanner ...
|
||||||
|
Scanner = "scanner"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Summary includes the sbom summary information
|
// Summary includes the sbom summary information
|
||||||
|
@ -87,7 +87,7 @@ func (v *scanHandler) RequiredPermissions() []*types.Policy {
|
|||||||
|
|
||||||
// PostScan defines task specific operations after the scan is complete
|
// 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) {
|
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 {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@ -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)
|
myLogger.Errorf("error when create accessory from image %v", err)
|
||||||
return "", 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
|
// annotations defines the annotations for the accessory artifact
|
||||||
func (v *scanHandler) annotations() map[string]string {
|
func (v *scanHandler) annotations() map[string]string {
|
||||||
|
t := time.Now().Format(time.RFC3339)
|
||||||
return map[string]string{
|
return map[string]string{
|
||||||
|
"created": t,
|
||||||
"created-by": "Harbor",
|
"created-by": "Harbor",
|
||||||
"org.opencontainers.artifact.created": time.Now().Format(time.RFC3339),
|
"org.opencontainers.artifact.created": t,
|
||||||
"org.opencontainers.artifact.description": "SPDX JSON SBOM",
|
"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{}
|
summary := sbom.Summary{}
|
||||||
endTime := time.Now()
|
endTime := time.Now()
|
||||||
summary[sbom.StartTime] = startTime
|
summary[sbom.StartTime] = startTime
|
||||||
@ -128,6 +130,7 @@ func (v *scanHandler) generateReport(startTime time.Time, repository, digest, st
|
|||||||
summary[sbom.SBOMRepository] = repository
|
summary[sbom.SBOMRepository] = repository
|
||||||
summary[sbom.SBOMDigest] = digest
|
summary[sbom.SBOMDigest] = digest
|
||||||
summary[sbom.ScanStatus] = status
|
summary[sbom.ScanStatus] = status
|
||||||
|
summary[sbom.Scanner] = scanner
|
||||||
rep, err := json.Marshal(summary)
|
rep, err := json.Marshal(summary)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@ -148,15 +151,15 @@ func registryFQDN(ctx context.Context) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// retrieveSBOMContent retrieves the "sbom" field from the raw report
|
// 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{}
|
rpt := vuln.Report{}
|
||||||
err := json.Unmarshal([]byte(rawReport), &rpt)
|
err := json.Unmarshal([]byte(rawReport), &rpt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
sbomContent, err := json.Marshal(rpt.SBOM)
|
sbomContent, err := json.Marshal(rpt.SBOM)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
return sbomContent, nil
|
return sbomContent, rpt.Scanner, nil
|
||||||
}
|
}
|
||||||
|
@ -78,6 +78,7 @@ export const ACTION_RESOURCE_I18N_MAP = {
|
|||||||
log: 'ROBOT_ACCOUNT.LOG',
|
log: 'ROBOT_ACCOUNT.LOG',
|
||||||
'notification-policy': 'ROBOT_ACCOUNT.NOTIFICATION_POLICY',
|
'notification-policy': 'ROBOT_ACCOUNT.NOTIFICATION_POLICY',
|
||||||
quota: 'ROBOT_ACCOUNT.QUOTA',
|
quota: 'ROBOT_ACCOUNT.QUOTA',
|
||||||
|
sbom: 'ROBOT_ACCOUNT.SBOM',
|
||||||
};
|
};
|
||||||
|
|
||||||
export function convertKey(key: string) {
|
export function convertKey(key: string) {
|
||||||
|
@ -13,10 +13,13 @@
|
|||||||
[clrIfActive]="currentTabLinkId === 'vulnerability'">
|
[clrIfActive]="currentTabLinkId === 'vulnerability'">
|
||||||
<clr-tab-content id="vulnerability-content">
|
<clr-tab-content id="vulnerability-content">
|
||||||
<hbr-artifact-vulnerabilities
|
<hbr-artifact-vulnerabilities
|
||||||
|
*ngIf="currentTabLinkId === 'vulnerability'"
|
||||||
[artifact]="artifact"
|
[artifact]="artifact"
|
||||||
[projectName]="projectName"
|
[projectName]="projectName"
|
||||||
[projectId]="projectId"
|
[projectId]="projectId"
|
||||||
[repoName]="repoName"
|
[repoName]="repoName"
|
||||||
|
[scanBtnState]="getScanBtnState()"
|
||||||
|
[hasEnabledScanner]="hasEnabledScanner()"
|
||||||
[digest]="digest"
|
[digest]="digest"
|
||||||
[vulnerabilitiesLink]="
|
[vulnerabilitiesLink]="
|
||||||
getVulnerability()
|
getVulnerability()
|
||||||
@ -24,16 +27,18 @@
|
|||||||
</clr-tab-content>
|
</clr-tab-content>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</clr-tab>
|
</clr-tab>
|
||||||
<clr-tab *ngIf="getSbom()">
|
<clr-tab *ngIf="hasScannerSupportSBOM()">
|
||||||
<button clrTabLink id="sbom" (click)="actionTab('sbom')">
|
<button clrTabLink id="sbom" (click)="actionTab('sbom')">
|
||||||
{{ 'REPOSITORY.SBOM' | translate }}
|
{{ 'REPOSITORY.SBOM' | translate }}
|
||||||
</button>
|
</button>
|
||||||
<ng-template [clrIfActive]="currentTabLinkId === 'sbom'">
|
<ng-template [clrIfActive]="currentTabLinkId === 'sbom'">
|
||||||
<clr-tab-content id="sbom-content">
|
<clr-tab-content id="sbom-content">
|
||||||
<hbr-artifact-sbom
|
<hbr-artifact-sbom
|
||||||
|
*ngIf="currentTabLinkId === 'sbom'"
|
||||||
[artifact]="artifact"
|
[artifact]="artifact"
|
||||||
[projectName]="projectName"
|
[projectName]="projectName"
|
||||||
[projectId]="projectId"
|
[projectId]="projectId"
|
||||||
|
[hasScannerSupportSBOM]="hasScannerSupportSBOM()"
|
||||||
[repoName]="repoName"
|
[repoName]="repoName"
|
||||||
[sbomDigest]="sbomDigest"></hbr-artifact-sbom>
|
[sbomDigest]="sbomDigest"></hbr-artifact-sbom>
|
||||||
</clr-tab-content>
|
</clr-tab-content>
|
||||||
@ -50,6 +55,7 @@
|
|||||||
[clrIfActive]="currentTabLinkId === 'build-history'">
|
[clrIfActive]="currentTabLinkId === 'build-history'">
|
||||||
<clr-tab-content>
|
<clr-tab-content>
|
||||||
<hbr-artifact-build-history
|
<hbr-artifact-build-history
|
||||||
|
*ngIf="currentTabLinkId === 'build-history'"
|
||||||
[buildHistoryLink]="
|
[buildHistoryLink]="
|
||||||
getBuildHistory()
|
getBuildHistory()
|
||||||
"></hbr-artifact-build-history>
|
"></hbr-artifact-build-history>
|
||||||
@ -67,6 +73,7 @@
|
|||||||
[clrIfActive]="currentTabLinkId === 'summary-link'">
|
[clrIfActive]="currentTabLinkId === 'summary-link'">
|
||||||
<clr-tab-content id="summary-content">
|
<clr-tab-content id="summary-content">
|
||||||
<hbr-artifact-summary
|
<hbr-artifact-summary
|
||||||
|
*ngIf="currentTabLinkId === 'summary-link'"
|
||||||
[summaryLink]="getSummary()"></hbr-artifact-summary>
|
[summaryLink]="getSummary()"></hbr-artifact-summary>
|
||||||
</clr-tab-content>
|
</clr-tab-content>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
@ -81,6 +88,7 @@
|
|||||||
<ng-template [clrIfActive]="currentTabLinkId === 'depend-link'">
|
<ng-template [clrIfActive]="currentTabLinkId === 'depend-link'">
|
||||||
<clr-tab-content id="depend-content">
|
<clr-tab-content id="depend-content">
|
||||||
<hbr-artifact-dependencies
|
<hbr-artifact-dependencies
|
||||||
|
*ngIf="currentTabLinkId === 'depend-link'"
|
||||||
[dependenciesLink]="
|
[dependenciesLink]="
|
||||||
getDependencies()
|
getDependencies()
|
||||||
"></hbr-artifact-dependencies>
|
"></hbr-artifact-dependencies>
|
||||||
@ -97,6 +105,7 @@
|
|||||||
<ng-template [clrIfActive]="currentTabLinkId === 'value-link'">
|
<ng-template [clrIfActive]="currentTabLinkId === 'value-link'">
|
||||||
<clr-tab-content id="value-content">
|
<clr-tab-content id="value-content">
|
||||||
<hbr-artifact-values
|
<hbr-artifact-values
|
||||||
|
*ngIf="currentTabLinkId === 'value-link'"
|
||||||
[valuesLink]="getValues()"></hbr-artifact-values>
|
[valuesLink]="getValues()"></hbr-artifact-values>
|
||||||
</clr-tab-content>
|
</clr-tab-content>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
@ -4,6 +4,8 @@ import { AdditionLinks } from '../../../../../../../ng-swagger-gen/models/additi
|
|||||||
import { CURRENT_BASE_HREF } from '../../../../../shared/units/utils';
|
import { CURRENT_BASE_HREF } from '../../../../../shared/units/utils';
|
||||||
import { SharedTestingModule } from '../../../../../shared/shared.module';
|
import { SharedTestingModule } from '../../../../../shared/shared.module';
|
||||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
|
import { ArtifactListPageService } from '../artifact-list-page/artifact-list-page.service';
|
||||||
|
import { ClrLoadingState } from '@clr/angular';
|
||||||
|
|
||||||
describe('ArtifactAdditionsComponent', () => {
|
describe('ArtifactAdditionsComponent', () => {
|
||||||
const mockedAdditionLinks: AdditionLinks = {
|
const mockedAdditionLinks: AdditionLinks = {
|
||||||
@ -12,6 +14,18 @@ describe('ArtifactAdditionsComponent', () => {
|
|||||||
href: CURRENT_BASE_HREF + '/test',
|
href: CURRENT_BASE_HREF + '/test',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
const mockedArtifactListPageService = {
|
||||||
|
hasScannerSupportSBOM(): boolean {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
hasEnabledScanner(): boolean {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
getScanBtnState(): ClrLoadingState {
|
||||||
|
return ClrLoadingState.SUCCESS;
|
||||||
|
},
|
||||||
|
init() {},
|
||||||
|
};
|
||||||
let component: ArtifactAdditionsComponent;
|
let component: ArtifactAdditionsComponent;
|
||||||
let fixture: ComponentFixture<ArtifactAdditionsComponent>;
|
let fixture: ComponentFixture<ArtifactAdditionsComponent>;
|
||||||
|
|
||||||
@ -20,6 +34,12 @@ describe('ArtifactAdditionsComponent', () => {
|
|||||||
imports: [SharedTestingModule],
|
imports: [SharedTestingModule],
|
||||||
declarations: [ArtifactAdditionsComponent],
|
declarations: [ArtifactAdditionsComponent],
|
||||||
schemas: [NO_ERRORS_SCHEMA],
|
schemas: [NO_ERRORS_SCHEMA],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: ArtifactListPageService,
|
||||||
|
useValue: mockedArtifactListPageService,
|
||||||
|
},
|
||||||
|
],
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -27,6 +47,7 @@ describe('ArtifactAdditionsComponent', () => {
|
|||||||
fixture = TestBed.createComponent(ArtifactAdditionsComponent);
|
fixture = TestBed.createComponent(ArtifactAdditionsComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
component.additionLinks = mockedAdditionLinks;
|
component.additionLinks = mockedAdditionLinks;
|
||||||
|
component.tab = 'vulnerability';
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -10,7 +10,8 @@ import { ADDITIONS } from './models';
|
|||||||
import { AdditionLinks } from '../../../../../../../ng-swagger-gen/models/addition-links';
|
import { AdditionLinks } from '../../../../../../../ng-swagger-gen/models/addition-links';
|
||||||
import { AdditionLink } from '../../../../../../../ng-swagger-gen/models/addition-link';
|
import { AdditionLink } from '../../../../../../../ng-swagger-gen/models/addition-link';
|
||||||
import { Artifact } from '../../../../../../../ng-swagger-gen/models/artifact';
|
import { Artifact } from '../../../../../../../ng-swagger-gen/models/artifact';
|
||||||
import { ClrTabs } from '@clr/angular';
|
import { ClrLoadingState, ClrTabs } from '@clr/angular';
|
||||||
|
import { ArtifactListPageService } from '../artifact-list-page/artifact-list-page.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'artifact-additions',
|
selector: 'artifact-additions',
|
||||||
@ -32,14 +33,21 @@ export class ArtifactAdditionsComponent implements AfterViewChecked, OnInit {
|
|||||||
@Input()
|
@Input()
|
||||||
tab: string;
|
tab: string;
|
||||||
|
|
||||||
@Input() currentTabLinkId: string = 'vulnerability';
|
@Input() currentTabLinkId: string = '';
|
||||||
activeTab: string = null;
|
activeTab: string = null;
|
||||||
|
|
||||||
@ViewChild('additionsTab') tabs: ClrTabs;
|
@ViewChild('additionsTab') tabs: ClrTabs;
|
||||||
constructor(private ref: ChangeDetectorRef) {}
|
constructor(
|
||||||
|
private ref: ChangeDetectorRef,
|
||||||
|
private artifactListPageService: ArtifactListPageService
|
||||||
|
) {}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.activeTab = this.tab;
|
this.activeTab = this.tab;
|
||||||
|
if (!this.activeTab) {
|
||||||
|
this.currentTabLinkId = 'vulnerability';
|
||||||
|
}
|
||||||
|
this.artifactListPageService.init(this.projectId);
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterViewChecked() {
|
ngAfterViewChecked() {
|
||||||
@ -50,6 +58,10 @@ export class ArtifactAdditionsComponent implements AfterViewChecked, OnInit {
|
|||||||
this.ref.detectChanges();
|
this.ref.detectChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hasScannerSupportSBOM(): boolean {
|
||||||
|
return this.artifactListPageService.hasScannerSupportSBOM();
|
||||||
|
}
|
||||||
|
|
||||||
getVulnerability(): AdditionLink {
|
getVulnerability(): AdditionLink {
|
||||||
if (
|
if (
|
||||||
this.additionLinks &&
|
this.additionLinks &&
|
||||||
@ -59,12 +71,7 @@ export class ArtifactAdditionsComponent implements AfterViewChecked, OnInit {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
getSbom(): AdditionLink {
|
|
||||||
if (this.additionLinks && this.additionLinks[ADDITIONS.SBOMS]) {
|
|
||||||
return this.additionLinks[ADDITIONS.SBOMS];
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
getBuildHistory(): AdditionLink {
|
getBuildHistory(): AdditionLink {
|
||||||
if (this.additionLinks && this.additionLinks[ADDITIONS.BUILD_HISTORY]) {
|
if (this.additionLinks && this.additionLinks[ADDITIONS.BUILD_HISTORY]) {
|
||||||
return this.additionLinks[ADDITIONS.BUILD_HISTORY];
|
return this.additionLinks[ADDITIONS.BUILD_HISTORY];
|
||||||
@ -93,4 +100,12 @@ export class ArtifactAdditionsComponent implements AfterViewChecked, OnInit {
|
|||||||
actionTab(tab: string): void {
|
actionTab(tab: string): void {
|
||||||
this.currentTabLinkId = tab;
|
this.currentTabLinkId = tab;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getScanBtnState(): ClrLoadingState {
|
||||||
|
return this.artifactListPageService.getScanBtnState();
|
||||||
|
}
|
||||||
|
|
||||||
|
hasEnabledScanner(): boolean {
|
||||||
|
return this.artifactListPageService.hasEnabledScanner();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,12 +32,18 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</clr-dg-action-bar>
|
</clr-dg-action-bar>
|
||||||
<clr-dg-column [clrDgField]="'package'" class="package-medium">{{
|
<clr-dg-column
|
||||||
'SBOM.GRID.COLUMN_PACKAGE' | translate
|
[clrDgSortBy]="'name'"
|
||||||
}}</clr-dg-column>
|
[clrDgField]="'name'"
|
||||||
<clr-dg-column [clrDgField]="'version'" class="version-medium">{{
|
class="package-medium"
|
||||||
'SBOM.GRID.COLUMN_VERSION' | translate
|
>{{ 'SBOM.GRID.COLUMN_PACKAGE' | translate }}</clr-dg-column
|
||||||
}}</clr-dg-column>
|
>
|
||||||
|
<clr-dg-column
|
||||||
|
[clrDgSortBy]="'versionInfo'"
|
||||||
|
[clrDgField]="'versionInfo'"
|
||||||
|
class="version-medium"
|
||||||
|
>{{ 'SBOM.GRID.COLUMN_VERSION' | translate }}</clr-dg-column
|
||||||
|
>
|
||||||
<clr-dg-column>{{
|
<clr-dg-column>{{
|
||||||
'SBOM.GRID.COLUMN_LICENSE' | translate
|
'SBOM.GRID.COLUMN_LICENSE' | translate
|
||||||
}}</clr-dg-column>
|
}}</clr-dg-column>
|
||||||
|
@ -10,7 +10,6 @@ import {
|
|||||||
} from '@ngx-translate/core';
|
} from '@ngx-translate/core';
|
||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
import { UserPermissionService } from '../../../../../../shared/services';
|
import { UserPermissionService } from '../../../../../../shared/services';
|
||||||
import { AdditionLink } from '../../../../../../../../ng-swagger-gen/models/addition-link';
|
|
||||||
import { ErrorHandler } from '../../../../../../shared/units/error-handler';
|
import { ErrorHandler } from '../../../../../../shared/units/error-handler';
|
||||||
import { SessionService } from '../../../../../../shared/services/session.service';
|
import { SessionService } from '../../../../../../shared/services/session.service';
|
||||||
import { SessionUser } from '../../../../../../shared/entities/session-user';
|
import { SessionUser } from '../../../../../../shared/entities/session-user';
|
||||||
|
@ -1,13 +1,6 @@
|
|||||||
import {
|
import { Component, Input, OnDestroy, OnInit } from '@angular/core';
|
||||||
AfterViewInit,
|
|
||||||
Component,
|
|
||||||
Input,
|
|
||||||
OnDestroy,
|
|
||||||
OnInit,
|
|
||||||
} from '@angular/core';
|
|
||||||
import { ClrDatagridStateInterface, ClrLoadingState } from '@clr/angular';
|
import { ClrDatagridStateInterface, ClrLoadingState } from '@clr/angular';
|
||||||
import { finalize } from 'rxjs/operators';
|
import { finalize } from 'rxjs/operators';
|
||||||
import { AdditionLink } from '../../../../../../../../ng-swagger-gen/models/addition-link';
|
|
||||||
import {
|
import {
|
||||||
ScannerVo,
|
ScannerVo,
|
||||||
UserPermissionService,
|
UserPermissionService,
|
||||||
@ -30,7 +23,6 @@ import {
|
|||||||
HarborEvent,
|
HarborEvent,
|
||||||
} from '../../../../../../services/event-service/event.service';
|
} from '../../../../../../services/event-service/event.service';
|
||||||
import { severityText } from '../../../../../left-side-nav/interrogation-services/vulnerability-database/security-hub.interface';
|
import { severityText } from '../../../../../left-side-nav/interrogation-services/vulnerability-database/security-hub.interface';
|
||||||
import { AppConfigService } from 'src/app/services/app-config.service';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ArtifactSbom,
|
ArtifactSbom,
|
||||||
@ -38,8 +30,7 @@ import {
|
|||||||
getArtifactSbom,
|
getArtifactSbom,
|
||||||
} from '../../artifact';
|
} from '../../artifact';
|
||||||
import { ArtifactService } from 'ng-swagger-gen/services';
|
import { ArtifactService } from 'ng-swagger-gen/services';
|
||||||
import { ScanTypes } from 'src/app/shared/entities/shared.const';
|
import { ScanTypes } from '../../../../../../shared/entities/shared.const';
|
||||||
import { ArtifactListPageService } from '../../artifact-list-page/artifact-list-page.service';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'hbr-artifact-sbom',
|
selector: 'hbr-artifact-sbom',
|
||||||
@ -56,13 +47,12 @@ export class ArtifactSbomComponent implements OnInit, OnDestroy {
|
|||||||
@Input()
|
@Input()
|
||||||
sbomDigest: string;
|
sbomDigest: string;
|
||||||
@Input() artifact: Artifact;
|
@Input() artifact: Artifact;
|
||||||
|
@Input() hasScannerSupportSBOM: boolean = false;
|
||||||
|
|
||||||
artifactSbom: ArtifactSbom;
|
artifactSbom: ArtifactSbom;
|
||||||
loading: boolean = false;
|
loading: boolean = false;
|
||||||
hasScannerSupportSBOM: boolean = false;
|
|
||||||
downloadSbomBtnState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
downloadSbomBtnState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
||||||
hasSbomPermission: boolean = false;
|
hasSbomPermission: boolean = false;
|
||||||
|
|
||||||
hasShowLoading: boolean = false;
|
hasShowLoading: boolean = false;
|
||||||
sub: Subscription;
|
sub: Subscription;
|
||||||
hasViewInitWithDelay: boolean = false;
|
hasViewInitWithDelay: boolean = false;
|
||||||
@ -73,16 +63,13 @@ export class ArtifactSbomComponent implements OnInit, OnDestroy {
|
|||||||
readonly severityText = severityText;
|
readonly severityText = severityText;
|
||||||
constructor(
|
constructor(
|
||||||
private errorHandler: ErrorHandler,
|
private errorHandler: ErrorHandler,
|
||||||
private appConfigService: AppConfigService,
|
|
||||||
private artifactService: ArtifactService,
|
private artifactService: ArtifactService,
|
||||||
private artifactListPageService: ArtifactListPageService,
|
|
||||||
private userPermissionService: UserPermissionService,
|
private userPermissionService: UserPermissionService,
|
||||||
private eventService: EventService,
|
private eventService: EventService,
|
||||||
private session: SessionService
|
private session: SessionService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.artifactListPageService.init(this.projectId);
|
|
||||||
this.getSbom();
|
this.getSbom();
|
||||||
this.getSbomPermission();
|
this.getSbomPermission();
|
||||||
if (!this.sub) {
|
if (!this.sub) {
|
||||||
@ -222,8 +209,6 @@ export class ArtifactSbomComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
canDownloadSbom(): boolean {
|
canDownloadSbom(): boolean {
|
||||||
this.hasScannerSupportSBOM =
|
|
||||||
this.artifactListPageService.hasScannerSupportSBOM();
|
|
||||||
return (
|
return (
|
||||||
this.hasScannerSupportSBOM &&
|
this.hasScannerSupportSBOM &&
|
||||||
//this.hasSbomPermission &&
|
//this.hasSbomPermission &&
|
||||||
@ -234,7 +219,12 @@ export class ArtifactSbomComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
artifactSbomPackages(): ArtifactSbomPackageItem[] {
|
artifactSbomPackages(): ArtifactSbomPackageItem[] {
|
||||||
return this.artifactSbom?.sbomPackage?.packages ?? [];
|
return (
|
||||||
|
this.artifactSbom?.sbomPackage?.packages?.filter(
|
||||||
|
item =>
|
||||||
|
item?.name || item?.versionInfo || item?.licenseConcluded
|
||||||
|
) ?? []
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
load(state: ClrDatagridStateInterface) {
|
load(state: ClrDatagridStateInterface) {
|
||||||
|
@ -50,14 +50,13 @@ export class ArtifactVulnerabilitiesComponent implements OnInit, OnDestroy {
|
|||||||
@Input()
|
@Input()
|
||||||
digest: string;
|
digest: string;
|
||||||
@Input() artifact: Artifact;
|
@Input() artifact: Artifact;
|
||||||
|
@Input() scanBtnState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
||||||
|
@Input() hasEnabledScanner: boolean = false;
|
||||||
scan_overview: any;
|
scan_overview: any;
|
||||||
scanner: ScannerVo;
|
scanner: ScannerVo;
|
||||||
projectScanner: ScannerVo;
|
|
||||||
|
|
||||||
scanningResults: VulnerabilityItem[] = [];
|
scanningResults: VulnerabilityItem[] = [];
|
||||||
loading: boolean = false;
|
loading: boolean = false;
|
||||||
hasEnabledScanner: boolean = false;
|
|
||||||
scanBtnState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
|
||||||
severitySort: ClrDatagridComparatorInterface<VulnerabilityItem>;
|
severitySort: ClrDatagridComparatorInterface<VulnerabilityItem>;
|
||||||
cvssSort: ClrDatagridComparatorInterface<VulnerabilityItem>;
|
cvssSort: ClrDatagridComparatorInterface<VulnerabilityItem>;
|
||||||
hasScanningPermission: boolean = false;
|
hasScanningPermission: boolean = false;
|
||||||
@ -112,7 +111,6 @@ export class ArtifactVulnerabilitiesComponent implements OnInit, OnDestroy {
|
|||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.getVulnerabilities();
|
this.getVulnerabilities();
|
||||||
this.getScanningPermission();
|
this.getScanningPermission();
|
||||||
this.getProjectScanner();
|
|
||||||
if (!this.sub) {
|
if (!this.sub) {
|
||||||
this.sub = this.eventService.subscribe(
|
this.sub = this.eventService.subscribe(
|
||||||
HarborEvent.UPDATE_VULNERABILITY_INFO,
|
HarborEvent.UPDATE_VULNERABILITY_INFO,
|
||||||
@ -203,30 +201,6 @@ export class ArtifactVulnerabilitiesComponent implements OnInit, OnDestroy {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getProjectScanner(): void {
|
|
||||||
this.hasEnabledScanner = false;
|
|
||||||
this.scanBtnState = ClrLoadingState.LOADING;
|
|
||||||
this.scanningService.getProjectScanner(this.projectId).subscribe(
|
|
||||||
response => {
|
|
||||||
if (
|
|
||||||
response &&
|
|
||||||
'{}' !== JSON.stringify(response) &&
|
|
||||||
!response.disabled &&
|
|
||||||
response.health === 'healthy'
|
|
||||||
) {
|
|
||||||
this.scanBtnState = ClrLoadingState.SUCCESS;
|
|
||||||
this.hasEnabledScanner = true;
|
|
||||||
} else {
|
|
||||||
this.scanBtnState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
this.projectScanner = response;
|
|
||||||
},
|
|
||||||
error => {
|
|
||||||
this.scanBtnState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
getLevel(v: VulnerabilityItem): number {
|
getLevel(v: VulnerabilityItem): number {
|
||||||
if (v && v.severity && SEVERITY_LEVEL_MAP[v.severity]) {
|
if (v && v.severity && SEVERITY_LEVEL_MAP[v.severity]) {
|
||||||
return SEVERITY_LEVEL_MAP[v.severity];
|
return SEVERITY_LEVEL_MAP[v.severity];
|
||||||
|
@ -65,10 +65,6 @@
|
|||||||
class="action-dropdown"
|
class="action-dropdown"
|
||||||
clrPosition="bottom-left"
|
clrPosition="bottom-left"
|
||||||
*clrIfOpen>
|
*clrIfOpen>
|
||||||
<div
|
|
||||||
class="dropdown-divider"
|
|
||||||
role="separator"
|
|
||||||
aria-hidden="true"></div>
|
|
||||||
<button
|
<button
|
||||||
clrDropdownItem
|
clrDropdownItem
|
||||||
id="stop-scan"
|
id="stop-scan"
|
||||||
|
@ -1099,7 +1099,7 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
|||||||
res?.filter(
|
res?.filter(
|
||||||
item =>
|
item =>
|
||||||
item.type === AccessoryType.SBOM
|
item.type === AccessoryType.SBOM
|
||||||
)?.[0]?.digest ?? null;
|
)?.[0]?.digest ?? undefined;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error: err => {
|
error: err => {
|
||||||
|
@ -76,7 +76,7 @@ export enum AccessoryType {
|
|||||||
COSIGN = 'signature.cosign',
|
COSIGN = 'signature.cosign',
|
||||||
NOTATION = 'signature.notation',
|
NOTATION = 'signature.notation',
|
||||||
NYDUS = 'accelerator.nydus',
|
NYDUS = 'accelerator.nydus',
|
||||||
SBOM = 'harbor.sbom',
|
SBOM = 'sbom.harbor',
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ArtifactType {
|
export enum ArtifactType {
|
||||||
|
@ -68,9 +68,7 @@ export class SbomTipHistogramComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get noSbom(): boolean {
|
get noSbom(): boolean {
|
||||||
return (
|
return this.sbomDigest === undefined || this.sbomDigest === '';
|
||||||
this.sbomSummary.scan_status === SBOM_SCAN_STATUS.NOT_GENERATED_SBOM
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
isThemeLight() {
|
isThemeLight() {
|
||||||
|
@ -142,7 +142,16 @@ export const errorHandler = function (error: any): string {
|
|||||||
}
|
}
|
||||||
// Not a standard error return Basically not used cover unknown error
|
// Not a standard error return Basically not used cover unknown error
|
||||||
try {
|
try {
|
||||||
|
const jsonError = JSON.parse(error.error);
|
||||||
|
if (jsonError.errors && jsonError.errors instanceof Array) {
|
||||||
|
return (
|
||||||
|
jsonError.errors?.map(error => error.message) ?? [
|
||||||
|
'UNKNOWN_ERROR',
|
||||||
|
]
|
||||||
|
).join(',');
|
||||||
|
} else {
|
||||||
return JSON.parse(error.error).message;
|
return JSON.parse(error.error).message;
|
||||||
|
}
|
||||||
} catch (err) {}
|
} catch (err) {}
|
||||||
// Not a standard error return Basically not used cover unknown error
|
// Not a standard error return Basically not used cover unknown error
|
||||||
if (typeof error.error === 'string') {
|
if (typeof error.error === 'string') {
|
||||||
|
@ -406,6 +406,7 @@
|
|||||||
"REPOSITORY": "Repository",
|
"REPOSITORY": "Repository",
|
||||||
"ARTIFACT": "Artifact",
|
"ARTIFACT": "Artifact",
|
||||||
"SCAN": "Scan",
|
"SCAN": "Scan",
|
||||||
|
"SBOM": "SBOM",
|
||||||
"TAG": "Tag",
|
"TAG": "Tag",
|
||||||
"ACCESSORY": "Accessory",
|
"ACCESSORY": "Accessory",
|
||||||
"ARTIFACT_ADDITION": "Artifact Addition",
|
"ARTIFACT_ADDITION": "Artifact Addition",
|
||||||
@ -1061,7 +1062,7 @@
|
|||||||
"GENERATE": "Generate SBOM",
|
"GENERATE": "Generate SBOM",
|
||||||
"DOWNLOAD": "Download SBOM",
|
"DOWNLOAD": "Download SBOM",
|
||||||
"Details": "SBOM details",
|
"Details": "SBOM details",
|
||||||
"STOP": "Stop SBOM",
|
"STOP": "Stop Generate SBOM",
|
||||||
"TRIGGER_STOP_SUCCESS": "Trigger stopping SBOM generation successfully"
|
"TRIGGER_STOP_SUCCESS": "Trigger stopping SBOM generation successfully"
|
||||||
},
|
},
|
||||||
"VULNERABILITY": {
|
"VULNERABILITY": {
|
||||||
|
@ -406,6 +406,7 @@
|
|||||||
"REPOSITORY": "Repository",
|
"REPOSITORY": "Repository",
|
||||||
"ARTIFACT": "Artifact",
|
"ARTIFACT": "Artifact",
|
||||||
"SCAN": "Scan",
|
"SCAN": "Scan",
|
||||||
|
"SBOM": "SBOM",
|
||||||
"TAG": "Tag",
|
"TAG": "Tag",
|
||||||
"ACCESSORY": "Accessory",
|
"ACCESSORY": "Accessory",
|
||||||
"ARTIFACT_ADDITION": "Artifact Addition",
|
"ARTIFACT_ADDITION": "Artifact Addition",
|
||||||
@ -1062,7 +1063,7 @@
|
|||||||
"GENERATE": "Generate SBOM ",
|
"GENERATE": "Generate SBOM ",
|
||||||
"DOWNLOAD": "Download SBOM",
|
"DOWNLOAD": "Download SBOM",
|
||||||
"Details": "SBOM details",
|
"Details": "SBOM details",
|
||||||
"STOP": "Stop SBOM",
|
"STOP": "Stop Generate SBOM",
|
||||||
"TRIGGER_STOP_SUCCESS": "Trigger stopping SBOM generation successfully"
|
"TRIGGER_STOP_SUCCESS": "Trigger stopping SBOM generation successfully"
|
||||||
},
|
},
|
||||||
"VULNERABILITY": {
|
"VULNERABILITY": {
|
||||||
|
@ -407,6 +407,7 @@
|
|||||||
"REPOSITORY": "Repository",
|
"REPOSITORY": "Repository",
|
||||||
"ARTIFACT": "Artifact",
|
"ARTIFACT": "Artifact",
|
||||||
"SCAN": "Scan",
|
"SCAN": "Scan",
|
||||||
|
"SBOM": "SBOM",
|
||||||
"TAG": "Tag",
|
"TAG": "Tag",
|
||||||
"ACCESSORY": "Accessory",
|
"ACCESSORY": "Accessory",
|
||||||
"ARTIFACT_ADDITION": "Artifact Addition",
|
"ARTIFACT_ADDITION": "Artifact Addition",
|
||||||
@ -1060,7 +1061,7 @@
|
|||||||
"GENERATE": "Generate SBOM",
|
"GENERATE": "Generate SBOM",
|
||||||
"DOWNLOAD": "Download SBOM",
|
"DOWNLOAD": "Download SBOM",
|
||||||
"Details": "SBOM details",
|
"Details": "SBOM details",
|
||||||
"STOP": "Stop SBOM",
|
"STOP": "Stop Generate SBOM",
|
||||||
"TRIGGER_STOP_SUCCESS": "Trigger stopping SBOM generation successfully"
|
"TRIGGER_STOP_SUCCESS": "Trigger stopping SBOM generation successfully"
|
||||||
},
|
},
|
||||||
"VULNERABILITY": {
|
"VULNERABILITY": {
|
||||||
|
@ -406,6 +406,7 @@
|
|||||||
"REPOSITORY": "Dépôt",
|
"REPOSITORY": "Dépôt",
|
||||||
"ARTIFACT": "Artefact",
|
"ARTIFACT": "Artefact",
|
||||||
"SCAN": "Scan",
|
"SCAN": "Scan",
|
||||||
|
"SBOM": "SBOM",
|
||||||
"TAG": "Tag",
|
"TAG": "Tag",
|
||||||
"ACCESSORY": "Accessoire",
|
"ACCESSORY": "Accessoire",
|
||||||
"ARTIFACT_ADDITION": "Artefact Addition",
|
"ARTIFACT_ADDITION": "Artefact Addition",
|
||||||
@ -1060,7 +1061,7 @@
|
|||||||
"GENERATE": "Generate SBOM",
|
"GENERATE": "Generate SBOM",
|
||||||
"DOWNLOAD": "Download SBOM",
|
"DOWNLOAD": "Download SBOM",
|
||||||
"Details": "SBOM details",
|
"Details": "SBOM details",
|
||||||
"STOP": "Stop SBOM",
|
"STOP": "Stop Generate SBOM",
|
||||||
"TRIGGER_STOP_SUCCESS": "Trigger stopping SBOM generation successfully"
|
"TRIGGER_STOP_SUCCESS": "Trigger stopping SBOM generation successfully"
|
||||||
},
|
},
|
||||||
"VULNERABILITY": {
|
"VULNERABILITY": {
|
||||||
|
@ -403,6 +403,7 @@
|
|||||||
"REPOSITORY": "저장소",
|
"REPOSITORY": "저장소",
|
||||||
"ARTIFACT": "아티팩트",
|
"ARTIFACT": "아티팩트",
|
||||||
"SCAN": "스캔",
|
"SCAN": "스캔",
|
||||||
|
"SBOM": "SBOM",
|
||||||
"TAG": "태그",
|
"TAG": "태그",
|
||||||
"ACCESSORY": "액세서리",
|
"ACCESSORY": "액세서리",
|
||||||
"ARTIFACT_ADDITION": "아티팩트 추가",
|
"ARTIFACT_ADDITION": "아티팩트 추가",
|
||||||
@ -1059,7 +1060,7 @@
|
|||||||
"GENERATE": "Generate SBOM",
|
"GENERATE": "Generate SBOM",
|
||||||
"DOWNLOAD": "Download SBOM",
|
"DOWNLOAD": "Download SBOM",
|
||||||
"Details": "SBOM details",
|
"Details": "SBOM details",
|
||||||
"STOP": "Stop SBOM",
|
"STOP": "Stop Generate SBOM",
|
||||||
"TRIGGER_STOP_SUCCESS": "Trigger stopping SBOM generation successfully"
|
"TRIGGER_STOP_SUCCESS": "Trigger stopping SBOM generation successfully"
|
||||||
},
|
},
|
||||||
"VULNERABILITY": {
|
"VULNERABILITY": {
|
||||||
|
@ -404,6 +404,7 @@
|
|||||||
"REPOSITORY": "Repository",
|
"REPOSITORY": "Repository",
|
||||||
"ARTIFACT": "Artifact",
|
"ARTIFACT": "Artifact",
|
||||||
"SCAN": "Scan",
|
"SCAN": "Scan",
|
||||||
|
"SBOM": "SBOM",
|
||||||
"TAG": "Tag",
|
"TAG": "Tag",
|
||||||
"ACCESSORY": "Accessory",
|
"ACCESSORY": "Accessory",
|
||||||
"ARTIFACT_ADDITION": "Artifact Addition",
|
"ARTIFACT_ADDITION": "Artifact Addition",
|
||||||
@ -1058,7 +1059,7 @@
|
|||||||
"GENERATE": "Generate SBOM",
|
"GENERATE": "Generate SBOM",
|
||||||
"DOWNLOAD": "Download SBOM",
|
"DOWNLOAD": "Download SBOM",
|
||||||
"Details": "SBOM details",
|
"Details": "SBOM details",
|
||||||
"STOP": "Stop SBOM",
|
"STOP": "Stop Generate SBOM",
|
||||||
"TRIGGER_STOP_SUCCESS": "Trigger stopping SBOM generation successfully"
|
"TRIGGER_STOP_SUCCESS": "Trigger stopping SBOM generation successfully"
|
||||||
},
|
},
|
||||||
"VULNERABILITY": {
|
"VULNERABILITY": {
|
||||||
|
@ -406,6 +406,7 @@
|
|||||||
"REPOSITORY": "Repository",
|
"REPOSITORY": "Repository",
|
||||||
"ARTIFACT": "Artifact",
|
"ARTIFACT": "Artifact",
|
||||||
"SCAN": "Scan",
|
"SCAN": "Scan",
|
||||||
|
"SBOM": "SBOM",
|
||||||
"TAG": "Tag",
|
"TAG": "Tag",
|
||||||
"ACCESSORY": "Accessory",
|
"ACCESSORY": "Accessory",
|
||||||
"ARTIFACT_ADDITION": "Artifact Addition",
|
"ARTIFACT_ADDITION": "Artifact Addition",
|
||||||
@ -1061,7 +1062,7 @@
|
|||||||
"GENERATE": "Generate SBOM",
|
"GENERATE": "Generate SBOM",
|
||||||
"DOWNLOAD": "Download SBOM",
|
"DOWNLOAD": "Download SBOM",
|
||||||
"Details": "SBOM details",
|
"Details": "SBOM details",
|
||||||
"STOP": "Stop SBOM",
|
"STOP": "Stop Generate SBOM",
|
||||||
"TRIGGER_STOP_SUCCESS": "Trigger stopping SBOM generation successfully"
|
"TRIGGER_STOP_SUCCESS": "Trigger stopping SBOM generation successfully"
|
||||||
},
|
},
|
||||||
"VULNERABILITY": {
|
"VULNERABILITY": {
|
||||||
|
@ -405,6 +405,7 @@
|
|||||||
"REPOSITORY": "仓库",
|
"REPOSITORY": "仓库",
|
||||||
"ARTIFACT": "Artifact",
|
"ARTIFACT": "Artifact",
|
||||||
"SCAN": "扫描",
|
"SCAN": "扫描",
|
||||||
|
"SBOM": "SBOM",
|
||||||
"TAG": "Tag",
|
"TAG": "Tag",
|
||||||
"ACCESSORY": "附件",
|
"ACCESSORY": "附件",
|
||||||
"ARTIFACT_ADDITION": "Artifact 额外信息",
|
"ARTIFACT_ADDITION": "Artifact 额外信息",
|
||||||
@ -1059,7 +1060,7 @@
|
|||||||
"GENERATE": "Generate SBOM",
|
"GENERATE": "Generate SBOM",
|
||||||
"DOWNLOAD": "Download SBOM",
|
"DOWNLOAD": "Download SBOM",
|
||||||
"Details": "SBOM details",
|
"Details": "SBOM details",
|
||||||
"STOP": "Stop SBOM",
|
"STOP": "Stop Generate SBOM",
|
||||||
"TRIGGER_STOP_SUCCESS": "Trigger stopping SBOM generation successfully"
|
"TRIGGER_STOP_SUCCESS": "Trigger stopping SBOM generation successfully"
|
||||||
},
|
},
|
||||||
"VULNERABILITY": {
|
"VULNERABILITY": {
|
||||||
|
@ -405,6 +405,7 @@
|
|||||||
"REPOSITORY": "Repository",
|
"REPOSITORY": "Repository",
|
||||||
"ARTIFACT": "Artifact",
|
"ARTIFACT": "Artifact",
|
||||||
"SCAN": "Scan",
|
"SCAN": "Scan",
|
||||||
|
"SBOM": "SBOM",
|
||||||
"TAG": "Tag",
|
"TAG": "Tag",
|
||||||
"ACCESSORY": "Accessory",
|
"ACCESSORY": "Accessory",
|
||||||
"ARTIFACT_ADDITION": "Artifact Addition",
|
"ARTIFACT_ADDITION": "Artifact Addition",
|
||||||
@ -1058,7 +1059,7 @@
|
|||||||
"GENERATE": "Generate SBOM",
|
"GENERATE": "Generate SBOM",
|
||||||
"DOWNLOAD": "Download SBOM",
|
"DOWNLOAD": "Download SBOM",
|
||||||
"Details": "SBOM details",
|
"Details": "SBOM details",
|
||||||
"STOP": "Stop SBOM",
|
"STOP": "Stop Generate SBOM",
|
||||||
"TRIGGER_STOP_SUCCESS": "Trigger stopping SBOM generation successfully"
|
"TRIGGER_STOP_SUCCESS": "Trigger stopping SBOM generation successfully"
|
||||||
},
|
},
|
||||||
"VULNERABILITY": {
|
"VULNERABILITY": {
|
||||||
|
@ -21,16 +21,12 @@ import (
|
|||||||
"github.com/goharbor/harbor/src/lib"
|
"github.com/goharbor/harbor/src/lib"
|
||||||
"github.com/goharbor/harbor/src/lib/log"
|
"github.com/goharbor/harbor/src/lib/log"
|
||||||
v1 "github.com/goharbor/harbor/src/pkg/scan/rest/v1"
|
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"
|
"github.com/goharbor/harbor/src/server/v2.0/handler/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
vulnerabilitiesAddition = "vulnerabilities"
|
vulnerabilitiesAddition = "vulnerabilities"
|
||||||
startTime = "start_time"
|
|
||||||
endTime = "end_time"
|
|
||||||
scanStatus = "scan_status"
|
|
||||||
sbomDigest = "sbom_digest"
|
|
||||||
duration = "duration"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewScanReportAssembler returns vul assembler
|
// 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})
|
overview, err := assembler.scanCtl.GetSummary(ctx, &artifact.Artifact, []string{v1.MimeTypeSBOMReport})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warningf("get scan summary of artifact %s@%s for %s failed, error:%v", artifact.RepositoryName, artifact.Digest, v1.MimeTypeSBOMReport, err)
|
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{}{
|
artifact.SBOMOverView = map[string]interface{}{
|
||||||
startTime: overview[startTime],
|
sbomModel.StartTime: overview[sbomModel.StartTime],
|
||||||
endTime: overview[endTime],
|
sbomModel.EndTime: overview[sbomModel.EndTime],
|
||||||
scanStatus: overview[scanStatus],
|
sbomModel.ScanStatus: overview[sbomModel.ScanStatus],
|
||||||
sbomDigest: overview[sbomDigest],
|
sbomModel.SBOMDigest: overview[sbomModel.SBOMDigest],
|
||||||
duration: overview[duration],
|
sbomModel.Duration: overview[sbomModel.Duration],
|
||||||
|
sbomModel.ReportID: overview[sbomModel.ReportID],
|
||||||
|
sbomModel.Scanner: overview[sbomModel.Scanner],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -87,6 +87,24 @@ func (_m *Manager) DeleteByDigests(ctx context.Context, digests ...string) error
|
|||||||
return r0
|
return r0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteByExtraAttr provides a mock function with given fields: ctx, mimeType, attrName, attrValue
|
||||||
|
func (_m *Manager) DeleteByExtraAttr(ctx context.Context, mimeType string, attrName string, attrValue string) error {
|
||||||
|
ret := _m.Called(ctx, mimeType, attrName, attrValue)
|
||||||
|
|
||||||
|
if len(ret) == 0 {
|
||||||
|
panic("no return value specified for DeleteByExtraAttr")
|
||||||
|
}
|
||||||
|
|
||||||
|
var r0 error
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, string, string, string) error); ok {
|
||||||
|
r0 = rf(ctx, mimeType, attrName, attrValue)
|
||||||
|
} else {
|
||||||
|
r0 = ret.Error(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
// GetBy provides a mock function with given fields: ctx, digest, registrationUUID, mimeTypes
|
// GetBy provides a mock function with given fields: ctx, digest, registrationUUID, mimeTypes
|
||||||
func (_m *Manager) GetBy(ctx context.Context, digest string, registrationUUID string, mimeTypes []string) ([]*scan.Report, error) {
|
func (_m *Manager) GetBy(ctx context.Context, digest string, registrationUUID string, mimeTypes []string) ([]*scan.Report, error) {
|
||||||
ret := _m.Called(ctx, digest, registrationUUID, mimeTypes)
|
ret := _m.Called(ctx, digest, registrationUUID, mimeTypes)
|
||||||
|
@ -89,8 +89,11 @@ copy_artifact = Permission("{}/projects/{}/repositories/target_repo/artifacts?fr
|
|||||||
delete_artifact = Permission("{}/projects/{}/repositories/target_repo/artifacts/{}".format(harbor_base_url, project_name, source_artifact_tag), "DELETE", 200)
|
delete_artifact = Permission("{}/projects/{}/repositories/target_repo/artifacts/{}".format(harbor_base_url, project_name, source_artifact_tag), "DELETE", 200)
|
||||||
|
|
||||||
# 6. Resource scan actions: ['read', 'create', 'stop']
|
# 6. Resource scan actions: ['read', 'create', 'stop']
|
||||||
|
stop_scan_payload = {
|
||||||
|
"scan_type": "vulnerability"
|
||||||
|
}
|
||||||
create_scan = Permission("{}/projects/{}/repositories/{}/artifacts/{}/scan".format(harbor_base_url, project_name, source_artifact_name, source_artifact_tag), "POST", 202)
|
create_scan = Permission("{}/projects/{}/repositories/{}/artifacts/{}/scan".format(harbor_base_url, project_name, source_artifact_name, source_artifact_tag), "POST", 202)
|
||||||
stop_scan = Permission("{}/projects/{}/repositories/{}/artifacts/{}/scan/stop".format(harbor_base_url, project_name, source_artifact_name, source_artifact_tag), "POST", 202)
|
stop_scan = Permission("{}/projects/{}/repositories/{}/artifacts/{}/scan/stop".format(harbor_base_url, project_name, source_artifact_name, source_artifact_tag), "POST", 202, stop_scan_payload)
|
||||||
read_scan = Permission("{}/projects/{}/repositories/{}/artifacts/{}/scan/83be44fd-1234-5678-b49f-4b6d6e8f5730/log".format(harbor_base_url, project_name, source_artifact_name, source_artifact_tag), "get", 404)
|
read_scan = Permission("{}/projects/{}/repositories/{}/artifacts/{}/scan/83be44fd-1234-5678-b49f-4b6d6e8f5730/log".format(harbor_base_url, project_name, source_artifact_name, source_artifact_tag), "get", 404)
|
||||||
|
|
||||||
# 7. Resource tag actions: ['list', 'create', 'delete']
|
# 7. Resource tag actions: ['list', 'create', 'delete']
|
||||||
|
@ -24,8 +24,8 @@ Create A Project Robot Account
|
|||||||
${permission_count}= Create Dictionary
|
${permission_count}= Create Dictionary
|
||||||
${total}= Set Variable 0
|
${total}= Set Variable 0
|
||||||
IF '${first_resource}' == 'all'
|
IF '${first_resource}' == 'all'
|
||||||
Set To Dictionary ${permission_count} all=56
|
Set To Dictionary ${permission_count} all=59
|
||||||
${total}= Set Variable 56
|
${total}= Set Variable 59
|
||||||
Retry Element Click //span[text()='Select all']
|
Retry Element Click //span[text()='Select all']
|
||||||
ELSE
|
ELSE
|
||||||
FOR ${item} IN @{resources}
|
FOR ${item} IN @{resources}
|
||||||
|
@ -51,3 +51,4 @@ ${scanner_password_input} //*[@id='scanner-password']
|
|||||||
${scanner_token_input} //*[@id='scanner-token']
|
${scanner_token_input} //*[@id='scanner-token']
|
||||||
${scanner_apikey_input} //*[@id='scanner-apiKey']
|
${scanner_apikey_input} //*[@id='scanner-apiKey']
|
||||||
${scanner_set_default_btn} //*[@id='set-default']
|
${scanner_set_default_btn} //*[@id='set-default']
|
||||||
|
${scanner_list_refresh_btn} //span[@class='refresh-btn']//clr-icon[@role='none']
|
||||||
|
@ -76,6 +76,7 @@ Resource Harbor-Pages/Job_Service_Dashboard_Elements.robot
|
|||||||
Resource Harbor-Pages/SecurityHub.robot
|
Resource Harbor-Pages/SecurityHub.robot
|
||||||
Resource Harbor-Pages/SecurityHub_Elements.robot
|
Resource Harbor-Pages/SecurityHub_Elements.robot
|
||||||
Resource Harbor-Pages/Verify.robot
|
Resource Harbor-Pages/Verify.robot
|
||||||
|
Resource Harbor-Pages/Vulnerability_Elements.robot
|
||||||
Resource Docker-Util.robot
|
Resource Docker-Util.robot
|
||||||
Resource CNAB_Util.robot
|
Resource CNAB_Util.robot
|
||||||
Resource Helm-Util.robot
|
Resource Helm-Util.robot
|
||||||
|
@ -745,7 +745,7 @@ Test Case - System Robot Account
|
|||||||
|
|
||||||
${robot_account_name} ${token}= Create A System Robot Account sys2${d} days days=2 description=For testing cover_all_project_resources=${true}
|
${robot_account_name} ${token}= Create A System Robot Account sys2${d} days days=2 description=For testing cover_all_project_resources=${true}
|
||||||
Push image ${ip} '${robot_account_name}' ${token} project${d} hello-world:latest
|
Push image ${ip} '${robot_account_name}' ${token} project${d} hello-world:latest
|
||||||
Retry Wait Element Visible //clr-dg-row[.//clr-dg-cell[contains(.,'${robot_account_name}')] and .//clr-icon[contains(@class, 'color-green')] and .//span[text()='All projects with'] and .//button[text()=' 56 PERMISSION(S) '] and .//span[contains(.,'1d 23h')] and .//clr-dg-cell[text()='For testing'] and .//clr-dg-cell//span[text()=' None ']]
|
Retry Wait Element Visible //clr-dg-row[.//clr-dg-cell[contains(.,'${robot_account_name}')] and .//clr-icon[contains(@class, 'color-green')] and .//span[text()='All projects with'] and .//button[text()=' 59 PERMISSION(S) '] and .//span[contains(.,'1d 23h')] and .//clr-dg-cell[text()='For testing'] and .//clr-dg-cell//span[text()=' None ']]
|
||||||
Retry Action Keyword Check System Robot Account API Permission ${robot_account_name} ${token} ${HARBOR_ADMIN} ${HARBOR_PASSWORD} all 1
|
Retry Action Keyword Check System Robot Account API Permission ${robot_account_name} ${token} ${HARBOR_ADMIN} ${HARBOR_PASSWORD} all 1
|
||||||
Retry Action Keyword Check Project Robot Account API Permission ${robot_account_name} ${token} ${HARBOR_ADMIN} ${HARBOR_PASSWORD} ${project_id} ${project_name} hello-world latest all
|
Retry Action Keyword Check Project Robot Account API Permission ${robot_account_name} ${token} ${HARBOR_ADMIN} ${HARBOR_PASSWORD} ${project_id} ${project_name} hello-world latest all
|
||||||
Close Browser
|
Close Browser
|
||||||
@ -885,13 +885,13 @@ Test Case - Audit Log And Purge
|
|||||||
# pull artifact
|
# pull artifact
|
||||||
Docker Pull ${ip}/project${d}/${image}:${tag1}
|
Docker Pull ${ip}/project${d}/${image}:${tag1}
|
||||||
Docker Logout ${ip}
|
Docker Logout ${ip}
|
||||||
Verify Log ${user} project${d}/${image}:${sha256} artifact pull
|
Verify Log ${user} project${d}/${image}@${sha256} artifact pull
|
||||||
Go Into Repo project${d} ${image}
|
Go Into Repo project${d} ${image}
|
||||||
# delete artifact
|
# delete artifact
|
||||||
@{tag_list} Create List ${tag1}
|
@{tag_list} Create List ${tag1}
|
||||||
Multi-delete Artifact @{tag_list}
|
Multi-delete Artifact @{tag_list}
|
||||||
Switch To Logs
|
Switch To Logs
|
||||||
Verify Log ${user} project${d}/${image}:${sha256} artifact delete
|
Verify Log ${user} project${d}/${image}@${sha256} artifact delete
|
||||||
Go Into Project project${d}
|
Go Into Project project${d}
|
||||||
# delete repository
|
# delete repository
|
||||||
Delete Repo project${d} ${image}
|
Delete Repo project${d} ${image}
|
||||||
@ -1151,8 +1151,8 @@ Test Case - Retain Image Last Pull Time
|
|||||||
Scan Repo ${tag} Succeed
|
Scan Repo ${tag} Succeed
|
||||||
Sleep 15
|
Sleep 15
|
||||||
Reload Page
|
Reload Page
|
||||||
Retry Wait Element Visible //clr-dg-row//clr-dg-cell[9]
|
Retry Wait Element Visible //clr-dg-row//clr-dg-cell[10]
|
||||||
${last_pull_time}= Get Text //clr-dg-row//clr-dg-cell[9]
|
${last_pull_time}= Get Text //clr-dg-row//clr-dg-cell[10]
|
||||||
Should Be Empty ${last_pull_time}
|
Should Be Empty ${last_pull_time}
|
||||||
Switch To Configuration System Setting
|
Switch To Configuration System Setting
|
||||||
Set Up Retain Image Last Pull Time disable
|
Set Up Retain Image Last Pull Time disable
|
||||||
@ -1160,8 +1160,8 @@ Test Case - Retain Image Last Pull Time
|
|||||||
Scan Repo ${tag} Succeed
|
Scan Repo ${tag} Succeed
|
||||||
Sleep 15
|
Sleep 15
|
||||||
Reload Page
|
Reload Page
|
||||||
Retry Wait Element Visible //clr-dg-row//clr-dg-cell[9]
|
Retry Wait Element Visible //clr-dg-row//clr-dg-cell[10]
|
||||||
${last_pull_time}= Get Text //clr-dg-row//clr-dg-cell[9]
|
${last_pull_time}= Get Text //clr-dg-row//clr-dg-cell[10]
|
||||||
Should Not Be Empty ${last_pull_time}
|
Should Not Be Empty ${last_pull_time}
|
||||||
Close Browser
|
Close Browser
|
||||||
|
|
||||||
|
@ -182,7 +182,7 @@ Test Case - External Scanner CRUD
|
|||||||
Filter Scanner By Name scanner${d}
|
Filter Scanner By Name scanner${d}
|
||||||
Filter Scanner By Endpoint ${SCANNER_ENDPOINT}
|
Filter Scanner By Endpoint ${SCANNER_ENDPOINT}
|
||||||
Retry Wait Element Count //clr-dg-row 1
|
Retry Wait Element Count //clr-dg-row 1
|
||||||
Retry Wait Until Page Contains Element //clr-dg-row[.//span[text()='scanner${d}'] and .//clr-dg-cell[text()='${SCANNER_ENDPOINT}'] and .//span[text()='Healthy'] and .//clr-dg-cell[text()='None']]
|
Retry Double Keywords When Error Retry Element Click xpath=${scanner_list_refresh_btn} Retry Wait Until Page Contains Element //clr-dg-row[.//span[text()='scanner${d}'] and .//clr-dg-cell[text()='${SCANNER_ENDPOINT}'] and .//span[text()='Healthy'] and .//clr-dg-cell[text()='None']]
|
||||||
# Delete this scanner
|
# Delete this scanner
|
||||||
Delete Scanner scanner${d}
|
Delete Scanner scanner${d}
|
||||||
Close Browser
|
Close Browser
|
||||||
|
Loading…
Reference in New Issue
Block a user