mirror of https://github.com/goharbor/harbor.git
Support list artifact with_sbom_overview option (#20244)
Signed-off-by: stonezdj <stone.zhang@broadcom.com> Co-authored-by: stonezdj <daojunz@vmware.com>
This commit is contained in:
parent
89995075a7
commit
5d7c668028
|
@ -17,6 +17,7 @@ package scan
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -674,12 +675,23 @@ func (bc *basicController) GetReport(ctx context.Context, artifact *ar.Artifact,
|
||||||
return reports, nil
|
return reports, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isSBOMMimeTypes(mimeTypes []string) bool {
|
||||||
|
for _, mimeType := range mimeTypes {
|
||||||
|
if mimeType == v1.MimeTypeSBOMReport {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// GetSummary ...
|
// GetSummary ...
|
||||||
func (bc *basicController) GetSummary(ctx context.Context, artifact *ar.Artifact, mimeTypes []string) (map[string]interface{}, error) {
|
func (bc *basicController) GetSummary(ctx context.Context, artifact *ar.Artifact, mimeTypes []string) (map[string]interface{}, error) {
|
||||||
if artifact == nil {
|
if artifact == nil {
|
||||||
return nil, errors.New("no way to get report summaries for nil artifact")
|
return nil, errors.New("no way to get report summaries for nil artifact")
|
||||||
}
|
}
|
||||||
|
if isSBOMMimeTypes(mimeTypes) {
|
||||||
|
return bc.GetSBOMSummary(ctx, artifact, mimeTypes)
|
||||||
|
}
|
||||||
// Get reports first
|
// Get reports first
|
||||||
rps, err := bc.GetReport(ctx, artifact, mimeTypes)
|
rps, err := bc.GetReport(ctx, artifact, mimeTypes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -708,6 +720,30 @@ func (bc *basicController) GetSummary(ctx context.Context, artifact *ar.Artifact
|
||||||
return summaries, nil
|
return summaries, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (bc *basicController) GetSBOMSummary(ctx context.Context, art *ar.Artifact, mimeTypes []string) (map[string]interface{}, error) {
|
||||||
|
if art == nil {
|
||||||
|
return nil, errors.New("no way to get report summaries for nil artifact")
|
||||||
|
}
|
||||||
|
r, err := bc.sc.GetRegistrationByProject(ctx, art.ProjectID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "scan controller: get sbom summary")
|
||||||
|
}
|
||||||
|
reports, err := bc.manager.GetBy(ctx, art.Digest, r.UUID, mimeTypes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(reports) == 0 {
|
||||||
|
return map[string]interface{}{}, nil
|
||||||
|
}
|
||||||
|
reportContent := reports[0].Report
|
||||||
|
if len(reportContent) == 0 {
|
||||||
|
log.Warning("no content for current report")
|
||||||
|
}
|
||||||
|
result := map[string]interface{}{}
|
||||||
|
err = json.Unmarshal([]byte(reportContent), &result)
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
// 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 {
|
||||||
|
|
|
@ -78,7 +78,7 @@ type ControllerTestSuite struct {
|
||||||
taskMgr *tasktesting.Manager
|
taskMgr *tasktesting.Manager
|
||||||
reportMgr *reporttesting.Manager
|
reportMgr *reporttesting.Manager
|
||||||
ar artifact.Controller
|
ar artifact.Controller
|
||||||
c Controller
|
c *basicController
|
||||||
reportConverter *postprocessorstesting.ScanReportV1ToV2Converter
|
reportConverter *postprocessorstesting.ScanReportV1ToV2Converter
|
||||||
cache *mockcache.Cache
|
cache *mockcache.Cache
|
||||||
}
|
}
|
||||||
|
@ -180,7 +180,19 @@ func (suite *ControllerTestSuite) SetupSuite() {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sbomReport := []*scan.Report{
|
||||||
|
{
|
||||||
|
ID: 12,
|
||||||
|
UUID: "rp-uuid-002",
|
||||||
|
Digest: "digest-code",
|
||||||
|
RegistrationUUID: "uuid001",
|
||||||
|
MimeType: "application/vnd.scanner.adapter.sbom.report.harbor+json; version=1.0",
|
||||||
|
Status: "Success",
|
||||||
|
Report: `{"sbom_digest": "sha256:1234567890", "scan_status": "Success", "duration": 3, "start_time": "2021-09-01T00:00:00Z", "end_time": "2021-09-01T00:00:03Z"}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
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("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)
|
||||||
|
@ -620,3 +632,26 @@ func (suite *ControllerTestSuite) makeExtraAttrs(artifactID int64, reportUUIDs .
|
||||||
|
|
||||||
return extraAttrs
|
return extraAttrs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *ControllerTestSuite) TestGenerateSBOMSummary() {
|
||||||
|
sum, err := suite.c.GetSBOMSummary(context.TODO(), suite.artifact, []string{v1.MimeTypeSBOMReport})
|
||||||
|
suite.Nil(err)
|
||||||
|
suite.NotNil(sum)
|
||||||
|
status := sum["scan_status"]
|
||||||
|
suite.NotNil(status)
|
||||||
|
dgst := sum["sbom_digest"]
|
||||||
|
suite.NotNil(dgst)
|
||||||
|
suite.Equal("Success", status)
|
||||||
|
suite.Equal("sha256:1234567890", dgst)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsSBOMMimeTypes(t *testing.T) {
|
||||||
|
// Test with a slice containing the SBOM mime type
|
||||||
|
assert.True(t, isSBOMMimeTypes([]string{v1.MimeTypeSBOMReport}))
|
||||||
|
|
||||||
|
// Test with a slice not containing the SBOM mime type
|
||||||
|
assert.False(t, isSBOMMimeTypes([]string{"application/vnd.oci.image.manifest.v1+json"}))
|
||||||
|
|
||||||
|
// Test with an empty slice
|
||||||
|
assert.False(t, isSBOMMimeTypes([]string{}))
|
||||||
|
}
|
||||||
|
|
|
@ -21,12 +21,11 @@ import (
|
||||||
"github.com/goharbor/harbor/src/lib/errors"
|
"github.com/goharbor/harbor/src/lib/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
var supportedMimeTypes = []string{
|
||||||
// ScanTypeVulnerability the scan type for vulnerability
|
MimeTypeNativeReport,
|
||||||
ScanTypeVulnerability = "vulnerability"
|
MimeTypeGenericVulnerabilityReport,
|
||||||
// ScanTypeSbom the scan type for sbom
|
MimeTypeSBOMReport,
|
||||||
ScanTypeSbom = "sbom"
|
}
|
||||||
)
|
|
||||||
|
|
||||||
// Scanner represents metadata of a Scanner Adapter which allow Harbor to lookup a scanner capable of
|
// Scanner represents metadata of a Scanner Adapter which allow Harbor to lookup a scanner capable of
|
||||||
// scanning a given Artifact stored in its registry and making sure that it can interpret a
|
// scanning a given Artifact stored in its registry and making sure that it can interpret a
|
||||||
|
@ -105,7 +104,7 @@ func (md *ScannerAdapterMetadata) Validate() error {
|
||||||
// either of v1.MimeTypeNativeReport OR v1.MimeTypeGenericVulnerabilityReport is required
|
// either of v1.MimeTypeNativeReport OR v1.MimeTypeGenericVulnerabilityReport is required
|
||||||
found = false
|
found = false
|
||||||
for _, pm := range ca.ProducesMimeTypes {
|
for _, pm := range ca.ProducesMimeTypes {
|
||||||
if pm == MimeTypeNativeReport || pm == MimeTypeGenericVulnerabilityReport {
|
if isSupportedMimeType(pm) {
|
||||||
found = true
|
found = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -119,6 +118,15 @@ func (md *ScannerAdapterMetadata) Validate() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isSupportedMimeType(mimeType string) bool {
|
||||||
|
for _, mt := range supportedMimeTypes {
|
||||||
|
if mt == mimeType {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// HasCapability returns true when mine type of the artifact support by the scanner
|
// HasCapability returns true when mine type of the artifact support by the scanner
|
||||||
func (md *ScannerAdapterMetadata) HasCapability(mimeType string) bool {
|
func (md *ScannerAdapterMetadata) HasCapability(mimeType string) bool {
|
||||||
for _, capability := range md.Capabilities {
|
for _, capability := range md.Capabilities {
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestIsSupportedMimeType(t *testing.T) {
|
||||||
|
// Test with a supported mime type
|
||||||
|
assert.True(t, isSupportedMimeType(MimeTypeSBOMReport), "isSupportedMimeType should return true for supported mime types")
|
||||||
|
|
||||||
|
// Test with an unsupported mime type
|
||||||
|
assert.False(t, isSupportedMimeType("unsupported/mime-type"), "isSupportedMimeType should return false for unsupported mime types")
|
||||||
|
}
|
|
@ -39,9 +39,14 @@ const (
|
||||||
MimeTypeScanRequest = "application/vnd.scanner.adapter.scan.request+json; version=1.0"
|
MimeTypeScanRequest = "application/vnd.scanner.adapter.scan.request+json; version=1.0"
|
||||||
// MimeTypeScanResponse defines the mime type for scan response
|
// MimeTypeScanResponse defines the mime type for scan response
|
||||||
MimeTypeScanResponse = "application/vnd.scanner.adapter.scan.response+json; version=1.0"
|
MimeTypeScanResponse = "application/vnd.scanner.adapter.scan.response+json; version=1.0"
|
||||||
|
// MimeTypeSBOMReport
|
||||||
|
MimeTypeSBOMReport = "application/vnd.security.sbom.report+json; version=1.0"
|
||||||
// MimeTypeGenericVulnerabilityReport defines the MIME type for the generic report with enhanced information
|
// MimeTypeGenericVulnerabilityReport defines the MIME type for the generic report with enhanced information
|
||||||
MimeTypeGenericVulnerabilityReport = "application/vnd.security.vulnerability.report; version=1.1"
|
MimeTypeGenericVulnerabilityReport = "application/vnd.security.vulnerability.report; version=1.1"
|
||||||
|
|
||||||
|
ScanTypeVulnerability = "vulnerability"
|
||||||
|
ScanTypeSbom = "sbom"
|
||||||
|
|
||||||
apiPrefix = "/api/v1"
|
apiPrefix = "/api/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -107,8 +107,8 @@ func (a *artifactAPI) ListArtifacts(ctx context.Context, params operation.ListAr
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return a.SendError(ctx, err)
|
return a.SendError(ctx, err)
|
||||||
}
|
}
|
||||||
|
overviewOpts := model.NewOverviewOptions(model.WithSBOM(lib.BoolValue(params.WithSbomOverview)), model.WithVuln(lib.BoolValue(params.WithScanOverview)))
|
||||||
assembler := assembler.NewVulAssembler(lib.BoolValue(params.WithScanOverview), parseScanReportMimeTypes(params.XAcceptVulnerabilities))
|
assembler := assembler.NewScanReportAssembler(overviewOpts, parseScanReportMimeTypes(params.XAcceptVulnerabilities))
|
||||||
var artifacts []*models.Artifact
|
var artifacts []*models.Artifact
|
||||||
for _, art := range arts {
|
for _, art := range arts {
|
||||||
artifact := &model.Artifact{}
|
artifact := &model.Artifact{}
|
||||||
|
@ -138,8 +138,9 @@ func (a *artifactAPI) GetArtifact(ctx context.Context, params operation.GetArtif
|
||||||
}
|
}
|
||||||
art := &model.Artifact{}
|
art := &model.Artifact{}
|
||||||
art.Artifact = *artifact
|
art.Artifact = *artifact
|
||||||
|
overviewOpts := model.NewOverviewOptions(model.WithSBOM(lib.BoolValue(params.WithSbomOverview)), model.WithVuln(lib.BoolValue(params.WithScanOverview)))
|
||||||
|
|
||||||
err = assembler.NewVulAssembler(lib.BoolValue(params.WithScanOverview), parseScanReportMimeTypes(params.XAcceptVulnerabilities)).WithArtifacts(art).Assemble(ctx)
|
err = assembler.NewScanReportAssembler(overviewOpts, parseScanReportMimeTypes(params.XAcceptVulnerabilities)).WithArtifacts(art).Assemble(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warningf("failed to assemble vulnerabilities with artifact, error: %v", err)
|
log.Warningf("failed to assemble vulnerabilities with artifact, error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,43 +20,48 @@ import (
|
||||||
"github.com/goharbor/harbor/src/controller/scan"
|
"github.com/goharbor/harbor/src/controller/scan"
|
||||||
"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"
|
||||||
"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"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewVulAssembler returns vul assembler
|
// NewScanReportAssembler returns vul assembler
|
||||||
func NewVulAssembler(withScanOverview bool, mimeTypes []string) *VulAssembler {
|
func NewScanReportAssembler(option *model.OverviewOptions, mimeTypes []string) *ScanReportAssembler {
|
||||||
return &VulAssembler{
|
return &ScanReportAssembler{
|
||||||
scanChecker: scan.NewChecker(),
|
overviewOption: option,
|
||||||
scanCtl: scan.DefaultController,
|
scanChecker: scan.NewChecker(),
|
||||||
|
scanCtl: scan.DefaultController,
|
||||||
withScanOverview: withScanOverview,
|
mimeTypes: mimeTypes,
|
||||||
mimeTypes: mimeTypes,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// VulAssembler vul assembler
|
// ScanReportAssembler vul assembler
|
||||||
type VulAssembler struct {
|
type ScanReportAssembler struct {
|
||||||
scanChecker scan.Checker
|
scanChecker scan.Checker
|
||||||
scanCtl scan.Controller
|
scanCtl scan.Controller
|
||||||
|
|
||||||
artifacts []*model.Artifact
|
artifacts []*model.Artifact
|
||||||
withScanOverview bool
|
mimeTypes []string
|
||||||
mimeTypes []string
|
overviewOption *model.OverviewOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithArtifacts set artifacts for the assembler
|
// WithArtifacts set artifacts for the assembler
|
||||||
func (assembler *VulAssembler) WithArtifacts(artifacts ...*model.Artifact) *VulAssembler {
|
func (assembler *ScanReportAssembler) WithArtifacts(artifacts ...*model.Artifact) *ScanReportAssembler {
|
||||||
assembler.artifacts = artifacts
|
assembler.artifacts = artifacts
|
||||||
|
|
||||||
return assembler
|
return assembler
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assemble assemble vul for the artifacts
|
// Assemble assemble vul for the artifacts
|
||||||
func (assembler *VulAssembler) Assemble(ctx context.Context) error {
|
func (assembler *ScanReportAssembler) Assemble(ctx context.Context) error {
|
||||||
version := lib.GetAPIVersion(ctx)
|
version := lib.GetAPIVersion(ctx)
|
||||||
|
|
||||||
for _, artifact := range assembler.artifacts {
|
for _, artifact := range assembler.artifacts {
|
||||||
|
@ -72,7 +77,7 @@ func (assembler *VulAssembler) Assemble(ctx context.Context) error {
|
||||||
|
|
||||||
artifact.SetAdditionLink(vulnerabilitiesAddition, version)
|
artifact.SetAdditionLink(vulnerabilitiesAddition, version)
|
||||||
|
|
||||||
if assembler.withScanOverview {
|
if assembler.overviewOption.WithVuln {
|
||||||
for _, mimeType := range assembler.mimeTypes {
|
for _, mimeType := range assembler.mimeTypes {
|
||||||
overview, err := assembler.scanCtl.GetSummary(ctx, &artifact.Artifact, []string{mimeType})
|
overview, err := assembler.scanCtl.GetSummary(ctx, &artifact.Artifact, []string{mimeType})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -83,6 +88,20 @@ func (assembler *VulAssembler) Assemble(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if assembler.overviewOption.WithSBOM {
|
||||||
|
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 {
|
||||||
|
artifact.SBOMOverView = map[string]interface{}{
|
||||||
|
startTime: overview[startTime],
|
||||||
|
endTime: overview[endTime],
|
||||||
|
scanStatus: overview[scanStatus],
|
||||||
|
sbomDigest: overview[sbomDigest],
|
||||||
|
duration: overview[duration],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
|
@ -20,6 +20,7 @@ import (
|
||||||
|
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
|
|
||||||
|
v1sq "github.com/goharbor/harbor/src/pkg/scan/rest/v1"
|
||||||
"github.com/goharbor/harbor/src/server/v2.0/handler/model"
|
"github.com/goharbor/harbor/src/server/v2.0/handler/model"
|
||||||
"github.com/goharbor/harbor/src/testing/controller/scan"
|
"github.com/goharbor/harbor/src/testing/controller/scan"
|
||||||
"github.com/goharbor/harbor/src/testing/mock"
|
"github.com/goharbor/harbor/src/testing/mock"
|
||||||
|
@ -33,11 +34,11 @@ func (suite *VulAssemblerTestSuite) TestScannable() {
|
||||||
checker := &scan.Checker{}
|
checker := &scan.Checker{}
|
||||||
scanCtl := &scan.Controller{}
|
scanCtl := &scan.Controller{}
|
||||||
|
|
||||||
assembler := VulAssembler{
|
assembler := ScanReportAssembler{
|
||||||
scanChecker: checker,
|
scanChecker: checker,
|
||||||
scanCtl: scanCtl,
|
scanCtl: scanCtl,
|
||||||
withScanOverview: true,
|
overviewOption: model.NewOverviewOptions(model.WithVuln(true)),
|
||||||
mimeTypes: []string{"mimeType"},
|
mimeTypes: []string{"mimeType"},
|
||||||
}
|
}
|
||||||
|
|
||||||
mock.OnAnything(checker, "IsScannable").Return(true, nil)
|
mock.OnAnything(checker, "IsScannable").Return(true, nil)
|
||||||
|
@ -56,10 +57,10 @@ func (suite *VulAssemblerTestSuite) TestNotScannable() {
|
||||||
checker := &scan.Checker{}
|
checker := &scan.Checker{}
|
||||||
scanCtl := &scan.Controller{}
|
scanCtl := &scan.Controller{}
|
||||||
|
|
||||||
assembler := VulAssembler{
|
assembler := ScanReportAssembler{
|
||||||
scanChecker: checker,
|
scanChecker: checker,
|
||||||
scanCtl: scanCtl,
|
scanCtl: scanCtl,
|
||||||
withScanOverview: true,
|
overviewOption: model.NewOverviewOptions(model.WithVuln(true)),
|
||||||
}
|
}
|
||||||
|
|
||||||
mock.OnAnything(checker, "IsScannable").Return(false, nil)
|
mock.OnAnything(checker, "IsScannable").Return(false, nil)
|
||||||
|
@ -74,6 +75,32 @@ func (suite *VulAssemblerTestSuite) TestNotScannable() {
|
||||||
scanCtl.AssertNotCalled(suite.T(), "GetSummary")
|
scanCtl.AssertNotCalled(suite.T(), "GetSummary")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *VulAssemblerTestSuite) TestAssembleSBOMOverview() {
|
||||||
|
checker := &scan.Checker{}
|
||||||
|
scanCtl := &scan.Controller{}
|
||||||
|
|
||||||
|
assembler := ScanReportAssembler{
|
||||||
|
scanChecker: checker,
|
||||||
|
scanCtl: scanCtl,
|
||||||
|
overviewOption: model.NewOverviewOptions(model.WithSBOM(true)),
|
||||||
|
mimeTypes: []string{v1sq.MimeTypeSBOMReport},
|
||||||
|
}
|
||||||
|
|
||||||
|
mock.OnAnything(checker, "IsScannable").Return(true, nil)
|
||||||
|
overview := map[string]interface{}{
|
||||||
|
"sbom_digest": "sha256:123456",
|
||||||
|
"scan_status": "Success",
|
||||||
|
}
|
||||||
|
mock.OnAnything(scanCtl, "GetSummary").Return(overview, nil)
|
||||||
|
|
||||||
|
var artifact model.Artifact
|
||||||
|
err := assembler.WithArtifacts(&artifact).Assemble(context.TODO())
|
||||||
|
suite.Nil(err)
|
||||||
|
suite.Equal(artifact.SBOMOverView["sbom_digest"], "sha256:123456")
|
||||||
|
suite.Equal(artifact.SBOMOverView["scan_status"], "Success")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func TestVulAssemblerTestSuite(t *testing.T) {
|
func TestVulAssemblerTestSuite(t *testing.T) {
|
||||||
suite.Run(t, &VulAssemblerTestSuite{})
|
suite.Run(t, &VulAssemblerTestSuite{})
|
||||||
}
|
}
|
|
@ -28,7 +28,9 @@ import (
|
||||||
// Artifact model
|
// Artifact model
|
||||||
type Artifact struct {
|
type Artifact struct {
|
||||||
artifact.Artifact
|
artifact.Artifact
|
||||||
|
// TODO: rename to VulOverview
|
||||||
ScanOverview map[string]interface{} `json:"scan_overview"`
|
ScanOverview map[string]interface{} `json:"scan_overview"`
|
||||||
|
SBOMOverView map[string]interface{} `json:"sbom_overview"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToSwagger converts the artifact to the swagger model
|
// ToSwagger converts the artifact to the swagger model
|
||||||
|
@ -84,6 +86,18 @@ func (a *Artifact) ToSwagger() *models.Artifact {
|
||||||
art.ScanOverview[key] = summary
|
art.ScanOverview[key] = summary
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if len(a.SBOMOverView) > 0 {
|
||||||
|
js, err := json.Marshal(a.SBOMOverView)
|
||||||
|
if err != nil {
|
||||||
|
log.Warningf("convert sbom summary failed, error: %v", err)
|
||||||
|
}
|
||||||
|
sbomOverview := &models.SBOMOverview{}
|
||||||
|
err = json.Unmarshal(js, sbomOverview)
|
||||||
|
if err != nil {
|
||||||
|
log.Warningf("failed to get sbom summary: error: %v", err)
|
||||||
|
}
|
||||||
|
art.SbomOverview = sbomOverview
|
||||||
|
}
|
||||||
return art
|
return art
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
// Copyright Project Harbor Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package model
|
||||||
|
|
||||||
|
// OverviewOptions define the option to query overview info
|
||||||
|
type OverviewOptions struct {
|
||||||
|
WithVuln bool
|
||||||
|
WithSBOM bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Option define the func to build options
|
||||||
|
type Option func(*OverviewOptions)
|
||||||
|
|
||||||
|
// NewOverviewOptions create a new OverviewOptions
|
||||||
|
func NewOverviewOptions(options ...Option) *OverviewOptions {
|
||||||
|
opts := &OverviewOptions{}
|
||||||
|
for _, f := range options {
|
||||||
|
f(opts)
|
||||||
|
}
|
||||||
|
return opts
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithVuln set the option to query vulnerability info
|
||||||
|
func WithVuln(enable bool) Option {
|
||||||
|
return func(o *OverviewOptions) {
|
||||||
|
o.WithVuln = enable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithSBOM set the option to query SBOM info
|
||||||
|
func WithSBOM(enable bool) Option {
|
||||||
|
return func(o *OverviewOptions) {
|
||||||
|
o.WithSBOM = enable
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
// Copyright Project Harbor Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestOverviewOptions(t *testing.T) {
|
||||||
|
// Test NewOverviewOptions with WithVuln and WithSBOM
|
||||||
|
opts := NewOverviewOptions(WithVuln(true), WithSBOM(true))
|
||||||
|
assert.True(t, opts.WithVuln)
|
||||||
|
assert.True(t, opts.WithSBOM)
|
||||||
|
|
||||||
|
// Test NewOverviewOptions with WithVuln and WithSBOM set to false
|
||||||
|
opts = NewOverviewOptions(WithVuln(false), WithSBOM(false))
|
||||||
|
assert.False(t, opts.WithVuln)
|
||||||
|
assert.False(t, opts.WithSBOM)
|
||||||
|
}
|
Loading…
Reference in New Issue