mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-30 20:48:23 +01:00
refactor: remove allowlist in GetSummary of scan controller (#14836)
Signed-off-by: He Weiwei <hweiwei@vmware.com>
This commit is contained in:
parent
1a3335edc5
commit
0c315d8aee
@ -17,8 +17,6 @@ package preheat
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/goharbor/harbor/src/lib/config"
|
|
||||||
"github.com/goharbor/harbor/src/lib/orm"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
tk "github.com/docker/distribution/registry/auth/token"
|
tk "github.com/docker/distribution/registry/auth/token"
|
||||||
@ -29,8 +27,10 @@ import (
|
|||||||
"github.com/goharbor/harbor/src/controller/tag"
|
"github.com/goharbor/harbor/src/controller/tag"
|
||||||
"github.com/goharbor/harbor/src/core/service/token"
|
"github.com/goharbor/harbor/src/core/service/token"
|
||||||
"github.com/goharbor/harbor/src/jobservice/job"
|
"github.com/goharbor/harbor/src/jobservice/job"
|
||||||
|
"github.com/goharbor/harbor/src/lib/config"
|
||||||
"github.com/goharbor/harbor/src/lib/errors"
|
"github.com/goharbor/harbor/src/lib/errors"
|
||||||
"github.com/goharbor/harbor/src/lib/log"
|
"github.com/goharbor/harbor/src/lib/log"
|
||||||
|
"github.com/goharbor/harbor/src/lib/orm"
|
||||||
"github.com/goharbor/harbor/src/lib/q"
|
"github.com/goharbor/harbor/src/lib/q"
|
||||||
"github.com/goharbor/harbor/src/lib/selector"
|
"github.com/goharbor/harbor/src/lib/selector"
|
||||||
"github.com/goharbor/harbor/src/pkg/label/model"
|
"github.com/goharbor/harbor/src/pkg/label/model"
|
||||||
@ -40,8 +40,6 @@ import (
|
|||||||
"github.com/goharbor/harbor/src/pkg/p2p/preheat/models/provider"
|
"github.com/goharbor/harbor/src/pkg/p2p/preheat/models/provider"
|
||||||
"github.com/goharbor/harbor/src/pkg/p2p/preheat/policy"
|
"github.com/goharbor/harbor/src/pkg/p2p/preheat/policy"
|
||||||
pr "github.com/goharbor/harbor/src/pkg/p2p/preheat/provider"
|
pr "github.com/goharbor/harbor/src/pkg/p2p/preheat/provider"
|
||||||
"github.com/goharbor/harbor/src/pkg/scan/report"
|
|
||||||
v1 "github.com/goharbor/harbor/src/pkg/scan/rest/v1"
|
|
||||||
"github.com/goharbor/harbor/src/pkg/scan/vuln"
|
"github.com/goharbor/harbor/src/pkg/scan/vuln"
|
||||||
"github.com/goharbor/harbor/src/pkg/task"
|
"github.com/goharbor/harbor/src/pkg/task"
|
||||||
)
|
)
|
||||||
@ -483,8 +481,7 @@ func (de *defaultEnforcer) startTask(ctx context.Context, executionID int64, can
|
|||||||
|
|
||||||
// getVulnerabilitySev gets the severity code value for the given artifact with allowlist option set
|
// getVulnerabilitySev gets the severity code value for the given artifact with allowlist option set
|
||||||
func (de *defaultEnforcer) getVulnerabilitySev(ctx context.Context, p *models.Project, art *artifact.Artifact) (uint, error) {
|
func (de *defaultEnforcer) getVulnerabilitySev(ctx context.Context, p *models.Project, art *artifact.Artifact) (uint, error) {
|
||||||
al := p.CVEAllowlist.CVESet()
|
vulnerable, err := de.scanCtl.GetVulnerable(ctx, art, p.CVEAllowlist.CVESet())
|
||||||
r, err := de.scanCtl.GetSummary(ctx, art, []string{v1.MimeTypeNativeReport, v1.MimeTypeGenericVulnerabilityReport}, report.WithCVEAllowlist(&al))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.IsNotFoundErr(err) {
|
if errors.IsNotFoundErr(err) {
|
||||||
// no vulnerability report
|
// no vulnerability report
|
||||||
@ -494,25 +491,17 @@ func (de *defaultEnforcer) getVulnerabilitySev(ctx context.Context, p *models.Pr
|
|||||||
return defaultSeverityCode, errors.Wrap(err, "get vulnerability severity")
|
return defaultSeverityCode, errors.Wrap(err, "get vulnerability severity")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Severity is based on the native report format or the generic vulnerability report format.
|
if !vulnerable.IsScanSuccess() {
|
||||||
// In case no supported report format, treat as same to the no report scenario
|
// scan status may running or error
|
||||||
sum, ok := r[v1.MimeTypeNativeReport]
|
|
||||||
if !ok {
|
|
||||||
// check if a report with MimeTypeGenericVulnerabilityReport is present.
|
|
||||||
// return the default severity code only if it does not exist
|
|
||||||
sum, ok = r[v1.MimeTypeGenericVulnerabilityReport]
|
|
||||||
if !ok {
|
|
||||||
return defaultSeverityCode, nil
|
return defaultSeverityCode, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// no vulnerability found
|
||||||
|
if vulnerable.Severity == nil {
|
||||||
|
return (uint)(vuln.None.Code()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
sm, ok := sum.(*vuln.NativeReportSummary)
|
return (uint)(vulnerable.Severity.Code()), nil
|
||||||
if !ok {
|
|
||||||
return defaultSeverityCode, errors.New("malformed native summary report")
|
|
||||||
}
|
|
||||||
|
|
||||||
return (uint)(sm.Severity.Code()), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// toCandidates converts the artifacts to filtering candidates
|
// toCandidates converts the artifacts to filtering candidates
|
||||||
|
@ -24,6 +24,7 @@ import (
|
|||||||
|
|
||||||
"github.com/goharbor/harbor/src/common/models"
|
"github.com/goharbor/harbor/src/common/models"
|
||||||
car "github.com/goharbor/harbor/src/controller/artifact"
|
car "github.com/goharbor/harbor/src/controller/artifact"
|
||||||
|
"github.com/goharbor/harbor/src/controller/scan"
|
||||||
"github.com/goharbor/harbor/src/controller/tag"
|
"github.com/goharbor/harbor/src/controller/tag"
|
||||||
"github.com/goharbor/harbor/src/lib/selector"
|
"github.com/goharbor/harbor/src/lib/selector"
|
||||||
models2 "github.com/goharbor/harbor/src/pkg/allowlist/models"
|
models2 "github.com/goharbor/harbor/src/pkg/allowlist/models"
|
||||||
@ -33,12 +34,11 @@ import (
|
|||||||
pr "github.com/goharbor/harbor/src/pkg/p2p/preheat/models/provider"
|
pr "github.com/goharbor/harbor/src/pkg/p2p/preheat/models/provider"
|
||||||
"github.com/goharbor/harbor/src/pkg/p2p/preheat/provider"
|
"github.com/goharbor/harbor/src/pkg/p2p/preheat/provider"
|
||||||
"github.com/goharbor/harbor/src/pkg/p2p/preheat/provider/auth"
|
"github.com/goharbor/harbor/src/pkg/p2p/preheat/provider/auth"
|
||||||
v1 "github.com/goharbor/harbor/src/pkg/scan/rest/v1"
|
|
||||||
"github.com/goharbor/harbor/src/pkg/scan/vuln"
|
"github.com/goharbor/harbor/src/pkg/scan/vuln"
|
||||||
ta "github.com/goharbor/harbor/src/pkg/tag/model/tag"
|
ta "github.com/goharbor/harbor/src/pkg/tag/model/tag"
|
||||||
"github.com/goharbor/harbor/src/testing/controller/artifact"
|
"github.com/goharbor/harbor/src/testing/controller/artifact"
|
||||||
"github.com/goharbor/harbor/src/testing/controller/project"
|
"github.com/goharbor/harbor/src/testing/controller/project"
|
||||||
"github.com/goharbor/harbor/src/testing/controller/scan"
|
scantesting "github.com/goharbor/harbor/src/testing/controller/scan"
|
||||||
"github.com/goharbor/harbor/src/testing/mock"
|
"github.com/goharbor/harbor/src/testing/mock"
|
||||||
"github.com/goharbor/harbor/src/testing/pkg/p2p/preheat/instance"
|
"github.com/goharbor/harbor/src/testing/pkg/p2p/preheat/instance"
|
||||||
"github.com/goharbor/harbor/src/testing/pkg/p2p/preheat/policy"
|
"github.com/goharbor/harbor/src/testing/pkg/p2p/preheat/policy"
|
||||||
@ -104,13 +104,13 @@ func (suite *EnforcerTestSuite) SetupSuite() {
|
|||||||
mock.AnythingOfType("*artifact.Option"),
|
mock.AnythingOfType("*artifact.Option"),
|
||||||
).Return(mockArtifacts(), nil)
|
).Return(mockArtifacts(), nil)
|
||||||
|
|
||||||
fakeScanCtl := &scan.Controller{}
|
low := vuln.Low
|
||||||
fakeScanCtl.On("GetSummary",
|
fakeScanCtl := &scantesting.Controller{}
|
||||||
|
fakeScanCtl.On("GetVulnerable",
|
||||||
context.TODO(),
|
context.TODO(),
|
||||||
mock.AnythingOfType("*artifact.Artifact"),
|
mock.AnythingOfType("*artifact.Artifact"),
|
||||||
[]string{v1.MimeTypeNativeReport, v1.MimeTypeGenericVulnerabilityReport},
|
mock.AnythingOfType("models.CVESet"),
|
||||||
mock.AnythingOfType("report.Option"),
|
).Return(&scan.Vulnerable{Severity: &low, ScanStatus: "Success"}, nil)
|
||||||
).Return(mockVulnerabilitySummary(), nil)
|
|
||||||
|
|
||||||
fakeProCtl := &project.Controller{}
|
fakeProCtl := &project.Controller{}
|
||||||
fakeProCtl.On("Get",
|
fakeProCtl.On("Get",
|
||||||
@ -304,16 +304,3 @@ func mockArtifacts() []*car.Artifact {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// mock vulnerability summary
|
|
||||||
func mockVulnerabilitySummary() map[string]interface{} {
|
|
||||||
// skip all unused properties
|
|
||||||
return map[string]interface{}{
|
|
||||||
v1.MimeTypeNativeReport: &vuln.NativeReportSummary{
|
|
||||||
Severity: vuln.Low,
|
|
||||||
},
|
|
||||||
v1.MimeTypeGenericVulnerabilityReport: &vuln.NativeReportSummary{
|
|
||||||
Severity: vuln.Low,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -18,7 +18,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/goharbor/harbor/src/lib/config"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
@ -28,10 +28,12 @@ import (
|
|||||||
sc "github.com/goharbor/harbor/src/controller/scanner"
|
sc "github.com/goharbor/harbor/src/controller/scanner"
|
||||||
"github.com/goharbor/harbor/src/jobservice/job"
|
"github.com/goharbor/harbor/src/jobservice/job"
|
||||||
"github.com/goharbor/harbor/src/lib"
|
"github.com/goharbor/harbor/src/lib"
|
||||||
|
"github.com/goharbor/harbor/src/lib/config"
|
||||||
"github.com/goharbor/harbor/src/lib/errors"
|
"github.com/goharbor/harbor/src/lib/errors"
|
||||||
"github.com/goharbor/harbor/src/lib/log"
|
"github.com/goharbor/harbor/src/lib/log"
|
||||||
"github.com/goharbor/harbor/src/lib/orm"
|
"github.com/goharbor/harbor/src/lib/orm"
|
||||||
"github.com/goharbor/harbor/src/lib/q"
|
"github.com/goharbor/harbor/src/lib/q"
|
||||||
|
allowlist "github.com/goharbor/harbor/src/pkg/allowlist/models"
|
||||||
"github.com/goharbor/harbor/src/pkg/permission/types"
|
"github.com/goharbor/harbor/src/pkg/permission/types"
|
||||||
"github.com/goharbor/harbor/src/pkg/robot/model"
|
"github.com/goharbor/harbor/src/pkg/robot/model"
|
||||||
sca "github.com/goharbor/harbor/src/pkg/scan"
|
sca "github.com/goharbor/harbor/src/pkg/scan"
|
||||||
@ -533,7 +535,7 @@ func (bc *basicController) GetReport(ctx context.Context, artifact *ar.Artifact,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetSummary ...
|
// GetSummary ...
|
||||||
func (bc *basicController) GetSummary(ctx context.Context, artifact *ar.Artifact, mimeTypes []string, options ...report.Option) (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")
|
||||||
}
|
}
|
||||||
@ -546,7 +548,7 @@ func (bc *basicController) GetSummary(ctx context.Context, artifact *ar.Artifact
|
|||||||
|
|
||||||
summaries := make(map[string]interface{}, len(rps))
|
summaries := make(map[string]interface{}, len(rps))
|
||||||
for _, rp := range rps {
|
for _, rp := range rps {
|
||||||
sum, err := report.GenerateSummary(rp, options...)
|
sum, err := report.GenerateSummary(rp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -699,6 +701,85 @@ func (bc *basicController) DeleteReports(ctx context.Context, digests ...string)
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (bc *basicController) GetVulnerable(ctx context.Context, artifact *ar.Artifact, allowlist allowlist.CVESet) (*Vulnerable, error) {
|
||||||
|
if artifact == nil {
|
||||||
|
return nil, errors.New("no way to get vulnerable for nil artifact")
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
mimeType string
|
||||||
|
reports []*scan.Report
|
||||||
|
)
|
||||||
|
for _, m := range []string{v1.MimeTypeNativeReport, v1.MimeTypeGenericVulnerabilityReport} {
|
||||||
|
rps, err := bc.GetReport(ctx, artifact, []string{m})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(rps) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
mimeType = m
|
||||||
|
reports = rps
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(reports) == 0 {
|
||||||
|
return nil, errors.NotFoundError(nil).WithMessage("report not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
scanStatus := reports[0].Status
|
||||||
|
for _, report := range reports {
|
||||||
|
scanStatus = vuln.MergeScanStatus(scanStatus, report.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
vulnerable := &Vulnerable{
|
||||||
|
ScanStatus: scanStatus,
|
||||||
|
}
|
||||||
|
|
||||||
|
if !vulnerable.IsScanSuccess() {
|
||||||
|
return vulnerable, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
raw, err := report.Reports(reports).ResolveData(mimeType)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rp, ok := raw.(*vuln.Report)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.Errorf("type mismatch: expect *vuln.Report but got %s", reflect.TypeOf(raw).String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if vuls := rp.GetVulnerabilityItemList().Items(); len(vuls) > 0 {
|
||||||
|
vulnerable.VulnerabilitiesCount = len(vuls)
|
||||||
|
|
||||||
|
var severity vuln.Severity
|
||||||
|
|
||||||
|
for _, v := range vuls {
|
||||||
|
if allowlist.Contains(v.ID) {
|
||||||
|
// Append the by passed CVEs specified in the allowlist
|
||||||
|
vulnerable.CVEBypassed = append(vulnerable.CVEBypassed, v.ID)
|
||||||
|
|
||||||
|
vulnerable.VulnerabilitiesCount--
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if severity == "" || v.Severity.Code() > severity.Code() {
|
||||||
|
severity = v.Severity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if severity != "" {
|
||||||
|
vulnerable.Severity = &severity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return vulnerable, nil
|
||||||
|
}
|
||||||
|
|
||||||
// makeRobotAccount creates a robot account based on the arguments for scanning.
|
// makeRobotAccount creates a robot account based on the arguments for scanning.
|
||||||
func (bc *basicController) makeRobotAccount(ctx context.Context, projectID int64, repository string, registration *scanner.Registration) (*robot.Robot, error) {
|
func (bc *basicController) makeRobotAccount(ctx context.Context, projectID int64, repository string, registration *scanner.Registration) (*robot.Robot, error) {
|
||||||
// Use uuid as name to avoid duplicated entries.
|
// Use uuid as name to avoid duplicated entries.
|
||||||
|
@ -18,14 +18,27 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/controller/artifact"
|
"github.com/goharbor/harbor/src/controller/artifact"
|
||||||
|
"github.com/goharbor/harbor/src/jobservice/job"
|
||||||
|
allowlist "github.com/goharbor/harbor/src/pkg/allowlist/models"
|
||||||
sca "github.com/goharbor/harbor/src/pkg/scan"
|
sca "github.com/goharbor/harbor/src/pkg/scan"
|
||||||
"github.com/goharbor/harbor/src/pkg/scan/dao/scan"
|
"github.com/goharbor/harbor/src/pkg/scan/dao/scan"
|
||||||
"github.com/goharbor/harbor/src/pkg/scan/report"
|
"github.com/goharbor/harbor/src/pkg/scan/vuln"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Vulnerable ...
|
||||||
|
type Vulnerable struct {
|
||||||
|
VulnerabilitiesCount int
|
||||||
|
ScanStatus string
|
||||||
|
Severity *vuln.Severity
|
||||||
|
CVEBypassed []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsScanSuccess returns true when the artifact scanned success
|
||||||
|
func (v *Vulnerable) IsScanSuccess() bool {
|
||||||
|
return v.ScanStatus == job.SuccessStatus.String()
|
||||||
|
}
|
||||||
|
|
||||||
// Controller provides the related operations for triggering scan.
|
// Controller provides the related operations for triggering scan.
|
||||||
// TODO: Here the artifact object is reused the v1 one which is sent to the adapter,
|
|
||||||
// it should be pointed to the general artifact object in future once it's ready.
|
|
||||||
type Controller interface {
|
type Controller interface {
|
||||||
// Scan the given artifact
|
// Scan the given artifact
|
||||||
//
|
//
|
||||||
@ -56,12 +69,11 @@ type Controller interface {
|
|||||||
// ctx context.Context : the context for this method
|
// ctx context.Context : the context for this method
|
||||||
// artifact *artifact.Artifact : the scanned artifact
|
// artifact *artifact.Artifact : the scanned artifact
|
||||||
// mimeTypes []string : the mime types of the reports
|
// mimeTypes []string : the mime types of the reports
|
||||||
// options ...report.Option : optional report options, specify if needed
|
|
||||||
//
|
//
|
||||||
// Returns:
|
// Returns:
|
||||||
// map[string]interface{} : report summaries indexed by mime types
|
// map[string]interface{} : report summaries indexed by mime types
|
||||||
// error : non nil error if any errors occurred
|
// error : non nil error if any errors occurred
|
||||||
GetSummary(ctx context.Context, artifact *artifact.Artifact, mimeTypes []string, options ...report.Option) (map[string]interface{}, error)
|
GetSummary(ctx context.Context, artifact *artifact.Artifact, mimeTypes []string) (map[string]interface{}, error)
|
||||||
|
|
||||||
// Get the scan log for the specified artifact with the given digest
|
// Get the scan log for the specified artifact with the given digest
|
||||||
//
|
//
|
||||||
@ -103,4 +115,15 @@ type Controller interface {
|
|||||||
// Returns:
|
// Returns:
|
||||||
// error : non nil error if any errors occurred
|
// error : non nil error if any errors occurred
|
||||||
ScanAll(ctx context.Context, trigger string, async bool) (int64, error)
|
ScanAll(ctx context.Context, trigger string, async bool) (int64, error)
|
||||||
|
|
||||||
|
// GetVulnerable returns the vulnerable of the artifact for the allowlist
|
||||||
|
//
|
||||||
|
// Arguments:
|
||||||
|
// ctx context.Context : the context for this method
|
||||||
|
// artifact *artifact.Artifact : artifact to be scanned
|
||||||
|
//
|
||||||
|
// Returns
|
||||||
|
// *Vulnerable : the vulnerable
|
||||||
|
// error : non nil error if any errors occurred
|
||||||
|
GetVulnerable(ctx context.Context, artifact *artifact.Artifact, allowlist allowlist.CVESet) (*Vulnerable, error)
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ package report
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/goharbor/harbor/src/lib/errors"
|
"github.com/goharbor/harbor/src/lib/errors"
|
||||||
|
"github.com/goharbor/harbor/src/pkg/scan/dao/scan"
|
||||||
v1 "github.com/goharbor/harbor/src/pkg/scan/rest/v1"
|
v1 "github.com/goharbor/harbor/src/pkg/scan/rest/v1"
|
||||||
"github.com/goharbor/harbor/src/pkg/scan/vuln"
|
"github.com/goharbor/harbor/src/pkg/scan/vuln"
|
||||||
)
|
)
|
||||||
@ -53,3 +54,36 @@ func MergeNativeReport(r1, r2 interface{}) (interface{}, error) {
|
|||||||
|
|
||||||
return nr1.Merge(nr2), nil
|
return nr1.Merge(nr2), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reports slice of scan.Reports pointer
|
||||||
|
type Reports []*scan.Report
|
||||||
|
|
||||||
|
// ResolveData resolve the data from the reports and merge them together
|
||||||
|
func (l Reports) ResolveData(mimeType string) (interface{}, error) {
|
||||||
|
var result interface{}
|
||||||
|
|
||||||
|
for _, rp := range l {
|
||||||
|
// Resolve scan report data only when it is ready and its mime type equal the given one
|
||||||
|
if len(rp.Report) == 0 || rp.MimeType != mimeType {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
vrp, err := ResolveData(rp.MimeType, []byte(rp.Report), WithArtifactDigest(rp.Digest))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if result == nil {
|
||||||
|
result = vrp
|
||||||
|
} else {
|
||||||
|
r, err := Merge(rp.MimeType, result, vrp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result = r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
@ -19,7 +19,6 @@ import (
|
|||||||
|
|
||||||
"github.com/goharbor/harbor/src/jobservice/job"
|
"github.com/goharbor/harbor/src/jobservice/job"
|
||||||
"github.com/goharbor/harbor/src/lib/errors"
|
"github.com/goharbor/harbor/src/lib/errors"
|
||||||
models2 "github.com/goharbor/harbor/src/pkg/allowlist/models"
|
|
||||||
"github.com/goharbor/harbor/src/pkg/scan/dao/scan"
|
"github.com/goharbor/harbor/src/pkg/scan/dao/scan"
|
||||||
v1 "github.com/goharbor/harbor/src/pkg/scan/rest/v1"
|
v1 "github.com/goharbor/harbor/src/pkg/scan/rest/v1"
|
||||||
"github.com/goharbor/harbor/src/pkg/scan/vuln"
|
"github.com/goharbor/harbor/src/pkg/scan/vuln"
|
||||||
@ -29,20 +28,11 @@ import (
|
|||||||
type Options struct {
|
type Options struct {
|
||||||
// If it is set, the returned report will contains artifact digest for the vulnerabilities
|
// If it is set, the returned report will contains artifact digest for the vulnerabilities
|
||||||
ArtifactDigest string
|
ArtifactDigest string
|
||||||
// If it is set, the returned summary will not count the CVEs in the list in.
|
|
||||||
CVEAllowlist models2.CVESet
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Option for getting the report w/ summary with func template way.
|
// Option for getting the report w/ summary with func template way.
|
||||||
type Option func(options *Options)
|
type Option func(options *Options)
|
||||||
|
|
||||||
// WithCVEAllowlist is an option of setting CVE allowlist.
|
|
||||||
func WithCVEAllowlist(set *models2.CVESet) Option {
|
|
||||||
return func(options *Options) {
|
|
||||||
options.CVEAllowlist = *set
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithArtifactDigest is an option of setting artifact digest
|
// WithArtifactDigest is an option of setting artifact digest
|
||||||
func WithArtifactDigest(artifactDigest string) Option {
|
func WithArtifactDigest(artifactDigest string) Option {
|
||||||
return func(options *Options) {
|
return func(options *Options) {
|
||||||
@ -107,11 +97,6 @@ type SummaryGenerator func(r *scan.Report, options ...Option) (interface{}, erro
|
|||||||
|
|
||||||
// GenerateNativeSummary generates the report summary for the native report.
|
// GenerateNativeSummary generates the report summary for the native report.
|
||||||
func GenerateNativeSummary(r *scan.Report, options ...Option) (interface{}, error) {
|
func GenerateNativeSummary(r *scan.Report, options ...Option) (interface{}, error) {
|
||||||
ops := &Options{}
|
|
||||||
for _, op := range options {
|
|
||||||
op(ops)
|
|
||||||
}
|
|
||||||
|
|
||||||
sum := &vuln.NativeReportSummary{}
|
sum := &vuln.NativeReportSummary{}
|
||||||
sum.ReportID = r.UUID
|
sum.ReportID = r.UUID
|
||||||
sum.StartTime = r.StartTime
|
sum.StartTime = r.StartTime
|
||||||
@ -120,9 +105,6 @@ func GenerateNativeSummary(r *scan.Report, options ...Option) (interface{}, erro
|
|||||||
if sum.Duration < 0 {
|
if sum.Duration < 0 {
|
||||||
sum.Duration = 0
|
sum.Duration = 0
|
||||||
}
|
}
|
||||||
if len(ops.CVEAllowlist) > 0 {
|
|
||||||
sum.CVEBypassed = make([]string, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
sum.ScanStatus = job.ErrorStatus.String()
|
sum.ScanStatus = job.ErrorStatus.String()
|
||||||
if job.Status(r.Status).Code() != -1 {
|
if job.Status(r.Status).Code() != -1 {
|
||||||
@ -157,7 +139,7 @@ func GenerateNativeSummary(r *scan.Report, options ...Option) (interface{}, erro
|
|||||||
sum.Severity = rp.Severity
|
sum.Severity = rp.Severity
|
||||||
sum.Scanner = rp.Scanner
|
sum.Scanner = rp.Scanner
|
||||||
|
|
||||||
sum.UpdateSeveritySummaryAndByPassed(rp.GetVulnerabilityItemList(), ops.CVEAllowlist)
|
sum.UpdateSeveritySummary(rp.GetVulnerabilityItemList())
|
||||||
|
|
||||||
return sum, nil
|
return sum, nil
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
models2 "github.com/goharbor/harbor/src/pkg/allowlist/models"
|
|
||||||
"github.com/goharbor/harbor/src/pkg/scan/dao/scan"
|
"github.com/goharbor/harbor/src/pkg/scan/dao/scan"
|
||||||
v1 "github.com/goharbor/harbor/src/pkg/scan/rest/v1"
|
v1 "github.com/goharbor/harbor/src/pkg/scan/rest/v1"
|
||||||
"github.com/goharbor/harbor/src/pkg/scan/vuln"
|
"github.com/goharbor/harbor/src/pkg/scan/vuln"
|
||||||
@ -103,23 +102,6 @@ func (suite *SummaryTestSuite) TestSummaryGenerateSummaryNoOptions() {
|
|||||||
suite.Equal("0.1.0", nativeSummary.Scanner.Version)
|
suite.Equal("0.1.0", nativeSummary.Scanner.Version)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestSummaryGenerateSummaryWithOptions ...
|
|
||||||
func (suite *SummaryTestSuite) TestSummaryGenerateSummaryWithOptions() {
|
|
||||||
cveSet := make(models2.CVESet)
|
|
||||||
cveSet["2019-0980-0909"] = struct{}{}
|
|
||||||
|
|
||||||
summaries, err := GenerateSummary(suite.r, WithCVEAllowlist(&cveSet))
|
|
||||||
require.NoError(suite.T(), err)
|
|
||||||
require.NotNil(suite.T(), summaries)
|
|
||||||
|
|
||||||
nativeSummary, ok := summaries.(*vuln.NativeReportSummary)
|
|
||||||
require.Equal(suite.T(), true, ok)
|
|
||||||
|
|
||||||
suite.Equal(vuln.Medium, nativeSummary.Severity)
|
|
||||||
suite.Equal(1, len(nativeSummary.CVEBypassed))
|
|
||||||
suite.Equal(1, nativeSummary.Summary.Total)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestSummaryGenerateSummaryWrongMime ...
|
// TestSummaryGenerateSummaryWrongMime ...
|
||||||
func (suite *SummaryTestSuite) TestSummaryGenerateSummaryWrongMime() {
|
func (suite *SummaryTestSuite) TestSummaryGenerateSummaryWrongMime() {
|
||||||
suite.r.MimeType = "wrong-mime"
|
suite.r.MimeType = "wrong-mime"
|
||||||
|
@ -18,7 +18,6 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
models2 "github.com/goharbor/harbor/src/pkg/allowlist/models"
|
|
||||||
v1 "github.com/goharbor/harbor/src/pkg/scan/rest/v1"
|
v1 "github.com/goharbor/harbor/src/pkg/scan/rest/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -149,27 +148,19 @@ func (l *VulnerabilityItemList) Add(items ...*VulnerabilityItem) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSeveritySummaryAndByPassed returns the Severity Summary and ByPassed by allowlist for the l
|
// GetSeveritySummary returns the severity and summary of l
|
||||||
func (l *VulnerabilityItemList) GetSeveritySummaryAndByPassed(allowlist models2.CVESet) (Severity, *VulnerabilitySummary, []string) {
|
func (l *VulnerabilityItemList) GetSeveritySummary() (Severity, *VulnerabilitySummary) {
|
||||||
|
if l == nil {
|
||||||
|
return Severity(""), nil
|
||||||
|
}
|
||||||
|
|
||||||
sum := &VulnerabilitySummary{
|
sum := &VulnerabilitySummary{
|
||||||
Total: len(l.Items()),
|
Total: len(l.Items()),
|
||||||
Summary: make(SeveritySummary),
|
Summary: make(SeveritySummary),
|
||||||
}
|
}
|
||||||
|
|
||||||
var bypassed []string
|
|
||||||
|
|
||||||
severity := None
|
severity := None
|
||||||
for _, v := range l.Items() {
|
for _, v := range l.Items() {
|
||||||
if len(allowlist) > 0 && allowlist.Contains(v.ID) {
|
|
||||||
// If allowlist is set, then check if we need to bypass it
|
|
||||||
// Reduce the total
|
|
||||||
sum.Total--
|
|
||||||
// Append the by passed CVEs specified in the allowlist
|
|
||||||
bypassed = append(bypassed, v.ID)
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if num, ok := sum.Summary[v.Severity]; ok {
|
if num, ok := sum.Summary[v.Severity]; ok {
|
||||||
sum.Summary[v.Severity] = num + 1
|
sum.Summary[v.Severity] = num + 1
|
||||||
} else {
|
} else {
|
||||||
@ -180,14 +171,13 @@ func (l *VulnerabilityItemList) GetSeveritySummaryAndByPassed(allowlist models2.
|
|||||||
if v.Severity.Code() > severity.Code() {
|
if v.Severity.Code() > severity.Code() {
|
||||||
severity = v.Severity
|
severity = v.Severity
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the CVE item has a fixable version
|
// If the CVE item has a fixable version
|
||||||
if len(v.FixVersion) > 0 {
|
if len(v.FixVersion) > 0 {
|
||||||
sum.Fixable++
|
sum.Fixable++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return severity, sum, bypassed
|
return severity, sum
|
||||||
}
|
}
|
||||||
|
|
||||||
// VulnerabilityItem represents one found vulnerability
|
// VulnerabilityItem represents one found vulnerability
|
||||||
|
@ -19,7 +19,6 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
models2 "github.com/goharbor/harbor/src/pkg/allowlist/models"
|
|
||||||
v1 "github.com/goharbor/harbor/src/pkg/scan/rest/v1"
|
v1 "github.com/goharbor/harbor/src/pkg/scan/rest/v1"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
@ -80,7 +79,7 @@ func TestReportMarshalJSON(t *testing.T) {
|
|||||||
assert.Contains(string(b), "vulnerabilities")
|
assert.Contains(string(b), "vulnerabilities")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetSummarySeverityAndByPassed(t *testing.T) {
|
func TestGetSummarySeverity(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
vul1 := &VulnerabilityItem{
|
vul1 := &VulnerabilityItem{
|
||||||
@ -102,34 +101,14 @@ func TestGetSummarySeverityAndByPassed(t *testing.T) {
|
|||||||
l := VulnerabilityItemList{}
|
l := VulnerabilityItemList{}
|
||||||
l.Add(vul1, vul2, vul3)
|
l.Add(vul1, vul2, vul3)
|
||||||
|
|
||||||
{
|
|
||||||
s := SeveritySummary{
|
s := SeveritySummary{
|
||||||
Low: 2,
|
Low: 2,
|
||||||
Medium: 1,
|
Medium: 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
severity, sum, byPassed := l.GetSeveritySummaryAndByPassed(models2.CVESet{})
|
severity, sum := l.GetSeveritySummary()
|
||||||
|
assert.Equal(Medium, severity)
|
||||||
assert.Equal(3, sum.Total)
|
assert.Equal(3, sum.Total)
|
||||||
assert.Equal(1, sum.Fixable)
|
assert.Equal(1, sum.Fixable)
|
||||||
assert.Equal(s, sum.Summary)
|
assert.Equal(s, sum.Summary)
|
||||||
assert.Equal(Medium, severity)
|
|
||||||
assert.Empty(byPassed)
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
s := SeveritySummary{
|
|
||||||
Low: 2,
|
|
||||||
}
|
|
||||||
|
|
||||||
cveSet := models2.CVESet{}
|
|
||||||
cveSet.Add("cve3")
|
|
||||||
|
|
||||||
severity, sum, byPassed := l.GetSeveritySummaryAndByPassed(cveSet)
|
|
||||||
assert.Equal(2, sum.Total)
|
|
||||||
assert.Equal(1, sum.Fixable)
|
|
||||||
assert.Equal(s, sum.Summary)
|
|
||||||
assert.Equal(Low, severity)
|
|
||||||
assert.NotEmpty(byPassed)
|
|
||||||
assert.Equal([]string{"cve3"}, byPassed)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/jobservice/job"
|
"github.com/goharbor/harbor/src/jobservice/job"
|
||||||
models2 "github.com/goharbor/harbor/src/pkg/allowlist/models"
|
|
||||||
v1 "github.com/goharbor/harbor/src/pkg/scan/rest/v1"
|
v1 "github.com/goharbor/harbor/src/pkg/scan/rest/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -39,30 +38,12 @@ type NativeReportSummary struct {
|
|||||||
TotalCount int `json:"-"`
|
TotalCount int `json:"-"`
|
||||||
CompleteCount int `json:"-"`
|
CompleteCount int `json:"-"`
|
||||||
VulnerabilityItemList *VulnerabilityItemList `json:"-"`
|
VulnerabilityItemList *VulnerabilityItemList `json:"-"`
|
||||||
CVESet models2.CVESet `json:"-"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateSeveritySummaryAndByPassed update the Severity, Summary and CVEBypassed of the sum from l and s
|
// UpdateSeveritySummary update the Severity, Summary of the sum from l
|
||||||
func (sum *NativeReportSummary) UpdateSeveritySummaryAndByPassed(l *VulnerabilityItemList, s models2.CVESet) {
|
func (sum *NativeReportSummary) UpdateSeveritySummary(l *VulnerabilityItemList) {
|
||||||
sum.VulnerabilityItemList = l
|
sum.VulnerabilityItemList = l
|
||||||
sum.CVESet = s
|
sum.Severity, sum.Summary = l.GetSeveritySummary()
|
||||||
|
|
||||||
if l == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var severity Severity
|
|
||||||
severity, sum.Summary, sum.CVEBypassed = l.GetSeveritySummaryAndByPassed(s)
|
|
||||||
|
|
||||||
if len(s) > 0 {
|
|
||||||
// Override the overall severity of the filtered list if needed.
|
|
||||||
sum.Severity = severity
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsSuccessStatus returns true when the scan status is success
|
|
||||||
func (sum *NativeReportSummary) IsSuccessStatus() bool {
|
|
||||||
return sum.ScanStatus == job.SuccessStatus.String()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merge ...
|
// Merge ...
|
||||||
@ -79,17 +60,17 @@ func (sum *NativeReportSummary) Merge(another *NativeReportSummary) *NativeRepor
|
|||||||
} else {
|
} else {
|
||||||
r.Scanner = another.Scanner
|
r.Scanner = another.Scanner
|
||||||
}
|
}
|
||||||
|
|
||||||
r.TotalCount = sum.TotalCount + another.TotalCount
|
r.TotalCount = sum.TotalCount + another.TotalCount
|
||||||
r.CompleteCount = sum.CompleteCount + another.CompleteCount
|
r.CompleteCount = sum.CompleteCount + another.CompleteCount
|
||||||
r.CompletePercent = r.CompleteCount * 100 / r.TotalCount
|
r.CompletePercent = r.CompleteCount * 100 / r.TotalCount
|
||||||
r.ReportID = mergeReportID(sum.ReportID, another.ReportID)
|
r.ReportID = mergeReportID(sum.ReportID, another.ReportID)
|
||||||
r.Severity = mergeSeverity(sum.Severity, another.Severity)
|
r.ScanStatus = MergeScanStatus(sum.ScanStatus, another.ScanStatus)
|
||||||
r.ScanStatus = mergeScanStatus(sum.ScanStatus, another.ScanStatus)
|
|
||||||
|
|
||||||
r.UpdateSeveritySummaryAndByPassed(
|
if r.ScanStatus != job.RunningStatus.String() {
|
||||||
NewVulnerabilityItemList(sum.VulnerabilityItemList, another.VulnerabilityItemList),
|
l := NewVulnerabilityItemList(sum.VulnerabilityItemList, another.VulnerabilityItemList)
|
||||||
models2.NewCVESet(sum.CVESet, another.CVESet),
|
r.UpdateSeveritySummary(l)
|
||||||
)
|
}
|
||||||
|
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ func TestMergeNativeReportSummary(t *testing.T) {
|
|||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
errorStatus := job.ErrorStatus.String()
|
errorStatus := job.ErrorStatus.String()
|
||||||
runningStatus := job.RunningStatus.String()
|
runningStatus := job.RunningStatus.String()
|
||||||
|
successStatus := job.SuccessStatus.String()
|
||||||
|
|
||||||
v1 := VulnerabilitySummary{
|
v1 := VulnerabilitySummary{
|
||||||
Total: 1,
|
Total: 1,
|
||||||
@ -42,82 +43,135 @@ func TestMergeNativeReportSummary(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
{
|
{
|
||||||
n1 := NativeReportSummary{
|
// running && running
|
||||||
|
n1 := &NativeReportSummary{
|
||||||
ScanStatus: runningStatus,
|
ScanStatus: runningStatus,
|
||||||
Severity: Low,
|
|
||||||
TotalCount: 1,
|
|
||||||
Summary: &v1,
|
|
||||||
VulnerabilityItemList: l,
|
|
||||||
}
|
|
||||||
|
|
||||||
r := n1.Merge(&NativeReportSummary{
|
|
||||||
ScanStatus: errorStatus,
|
|
||||||
Severity: Severity(""),
|
|
||||||
TotalCount: 1,
|
|
||||||
})
|
|
||||||
|
|
||||||
assert.Equal(runningStatus, r.ScanStatus)
|
|
||||||
assert.Equal(Low, r.Severity)
|
|
||||||
assert.Equal(v1, *r.Summary)
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
n1 := NativeReportSummary{
|
|
||||||
ScanStatus: runningStatus,
|
|
||||||
Severity: Severity(""),
|
|
||||||
TotalCount: 1,
|
TotalCount: 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
r := n1.Merge(&NativeReportSummary{
|
r := n1.Merge(&NativeReportSummary{
|
||||||
ScanStatus: errorStatus,
|
ScanStatus: runningStatus,
|
||||||
Severity: Severity(""),
|
|
||||||
TotalCount: 1,
|
TotalCount: 1,
|
||||||
})
|
})
|
||||||
|
|
||||||
assert.Equal(runningStatus, r.ScanStatus)
|
assert.Equal(runningStatus, r.ScanStatus)
|
||||||
assert.Equal(Severity(""), r.Severity)
|
|
||||||
assert.Nil(r.Summary)
|
assert.Nil(r.Summary)
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
n1 := &NativeReportSummary{
|
// running && success
|
||||||
ScanStatus: errorStatus,
|
n1 := NativeReportSummary{
|
||||||
Severity: Severity(""),
|
ScanStatus: runningStatus,
|
||||||
TotalCount: 1,
|
TotalCount: 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
r := n1.Merge(&NativeReportSummary{
|
r := n1.Merge(&NativeReportSummary{
|
||||||
ScanStatus: runningStatus,
|
ScanStatus: successStatus,
|
||||||
Severity: Low,
|
Severity: Low,
|
||||||
TotalCount: 1,
|
TotalCount: 1,
|
||||||
|
CompleteCount: 1,
|
||||||
Summary: &v1,
|
Summary: &v1,
|
||||||
VulnerabilityItemList: l,
|
VulnerabilityItemList: l,
|
||||||
})
|
})
|
||||||
|
|
||||||
assert.Equal(runningStatus, r.ScanStatus)
|
assert.Equal(runningStatus, r.ScanStatus)
|
||||||
assert.Equal(Low, r.Severity)
|
assert.Nil(r.Summary)
|
||||||
assert.Equal(v1, *r.Summary)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
n1 := &NativeReportSummary{
|
// running && error
|
||||||
|
n1 := NativeReportSummary{
|
||||||
ScanStatus: runningStatus,
|
ScanStatus: runningStatus,
|
||||||
|
TotalCount: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
r := n1.Merge(&NativeReportSummary{
|
||||||
|
ScanStatus: errorStatus,
|
||||||
|
TotalCount: 1,
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Equal(runningStatus, r.ScanStatus)
|
||||||
|
assert.Nil(r.Summary)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// success && success
|
||||||
|
n1 := NativeReportSummary{
|
||||||
|
ScanStatus: successStatus,
|
||||||
Severity: Low,
|
Severity: Low,
|
||||||
TotalCount: 1,
|
TotalCount: 1,
|
||||||
|
CompleteCount: 1,
|
||||||
|
Summary: &v1,
|
||||||
|
VulnerabilityItemList: l,
|
||||||
|
}
|
||||||
|
|
||||||
|
l2 := &VulnerabilityItemList{}
|
||||||
|
l2.Add(&VulnerabilityItem{
|
||||||
|
ID: "cve-id-high",
|
||||||
|
Package: "openssl-libs-high",
|
||||||
|
Version: "1:1.1.1g-11.el8",
|
||||||
|
Severity: High,
|
||||||
|
FixVersion: "1:1.1.1g-12.el8_3",
|
||||||
|
})
|
||||||
|
|
||||||
|
r := n1.Merge(&NativeReportSummary{
|
||||||
|
ScanStatus: successStatus,
|
||||||
|
Severity: High,
|
||||||
|
TotalCount: 1,
|
||||||
|
CompleteCount: 1,
|
||||||
|
Summary: &VulnerabilitySummary{
|
||||||
|
Total: 1,
|
||||||
|
Fixable: 1,
|
||||||
|
Summary: map[Severity]int{High: 1},
|
||||||
|
},
|
||||||
|
VulnerabilityItemList: l2,
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Equal(successStatus, r.ScanStatus)
|
||||||
|
assert.Equal(High, r.Severity)
|
||||||
|
assert.Equal(VulnerabilitySummary{
|
||||||
|
Total: 2,
|
||||||
|
Fixable: 2,
|
||||||
|
Summary: map[Severity]int{Low: 1, High: 1},
|
||||||
|
}, *r.Summary)
|
||||||
|
assert.Equal(100, r.CompletePercent)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// success && error
|
||||||
|
n1 := NativeReportSummary{
|
||||||
|
ScanStatus: successStatus,
|
||||||
|
Severity: Low,
|
||||||
|
TotalCount: 1,
|
||||||
|
CompleteCount: 1,
|
||||||
Summary: &v1,
|
Summary: &v1,
|
||||||
VulnerabilityItemList: l,
|
VulnerabilityItemList: l,
|
||||||
}
|
}
|
||||||
|
|
||||||
r := n1.Merge(&NativeReportSummary{
|
r := n1.Merge(&NativeReportSummary{
|
||||||
ScanStatus: runningStatus,
|
ScanStatus: errorStatus,
|
||||||
Severity: Low,
|
|
||||||
TotalCount: 1,
|
TotalCount: 1,
|
||||||
Summary: &v1,
|
|
||||||
VulnerabilityItemList: l,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
assert.Equal(runningStatus, r.ScanStatus)
|
assert.Equal(successStatus, r.ScanStatus)
|
||||||
assert.Equal(Low, r.Severity)
|
assert.Equal(Low, r.Severity)
|
||||||
assert.Equal(v1, *r.Summary)
|
assert.Equal(v1, *r.Summary)
|
||||||
|
assert.Equal(50, r.CompletePercent)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// error && error
|
||||||
|
n1 := NativeReportSummary{
|
||||||
|
ScanStatus: errorStatus,
|
||||||
|
TotalCount: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
r := n1.Merge(&NativeReportSummary{
|
||||||
|
ScanStatus: errorStatus,
|
||||||
|
TotalCount: 1,
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Equal(errorStatus, r.ScanStatus)
|
||||||
|
assert.Nil(r.Summary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,12 +79,14 @@ func mergeSeverity(s1, s2 Severity) Severity {
|
|||||||
return s2
|
return s2
|
||||||
}
|
}
|
||||||
|
|
||||||
func mergeScanStatus(s1, s2 string) string {
|
// MergeScanStatus ...
|
||||||
|
func MergeScanStatus(s1, s2 string) string {
|
||||||
j1, j2 := job.Status(s1), job.Status(s2)
|
j1, j2 := job.Status(s1), job.Status(s2)
|
||||||
|
|
||||||
if j1 == job.RunningStatus || j2 == job.RunningStatus {
|
if j1 == job.RunningStatus || j2 == job.RunningStatus {
|
||||||
return job.RunningStatus.String()
|
return job.RunningStatus.String()
|
||||||
} else if j1 == job.SuccessStatus || j2 == job.SuccessStatus {
|
} else if j1 == job.SuccessStatus || j2 == job.SuccessStatus {
|
||||||
|
// the scan status of the image index will be treated as a success when one of its children is success
|
||||||
return job.SuccessStatus.String()
|
return job.SuccessStatus.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@ func Test_mergeScanStatus(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
if got := mergeScanStatus(tt.args.s1, tt.args.s2); got != tt.want {
|
if got := MergeScanStatus(tt.args.s1, tt.args.s2); got != tt.want {
|
||||||
t.Errorf("mergeScanStatus() = %v, want %v", got, tt.want)
|
t.Errorf("mergeScanStatus() = %v, want %v", got, tt.want)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -25,8 +25,6 @@ import (
|
|||||||
"github.com/goharbor/harbor/src/lib"
|
"github.com/goharbor/harbor/src/lib"
|
||||||
"github.com/goharbor/harbor/src/lib/errors"
|
"github.com/goharbor/harbor/src/lib/errors"
|
||||||
"github.com/goharbor/harbor/src/lib/log"
|
"github.com/goharbor/harbor/src/lib/log"
|
||||||
"github.com/goharbor/harbor/src/pkg/scan/report"
|
|
||||||
v1 "github.com/goharbor/harbor/src/pkg/scan/rest/v1"
|
|
||||||
"github.com/goharbor/harbor/src/pkg/scan/vuln"
|
"github.com/goharbor/harbor/src/pkg/scan/vuln"
|
||||||
"github.com/goharbor/harbor/src/server/middleware"
|
"github.com/goharbor/harbor/src/server/middleware"
|
||||||
"github.com/goharbor/harbor/src/server/middleware/util"
|
"github.com/goharbor/harbor/src/server/middleware/util"
|
||||||
@ -92,28 +90,20 @@ func Middleware() func(http.Handler) http.Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
allowlist := proj.CVEAllowlist.CVESet()
|
allowlist := proj.CVEAllowlist.CVESet()
|
||||||
summaries, err := scanController.GetSummary(ctx, art, []string{v1.MimeTypeNativeReport, v1.MimeTypeGenericVulnerabilityReport}, report.WithCVEAllowlist(&allowlist))
|
|
||||||
if err != nil {
|
|
||||||
logger.Errorf("get vulnerability summary of the artifact %s@%s failed, error: %v", art.RepositoryName, art.Digest, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
projectSeverity := vuln.ParseSeverityVersion3(proj.Severity())
|
projectSeverity := vuln.ParseSeverityVersion3(proj.Severity())
|
||||||
|
|
||||||
rawSummary, ok := summaries[v1.MimeTypeNativeReport]
|
vulnerable, err := scanController.GetVulnerable(ctx, art, allowlist)
|
||||||
if !ok {
|
if err != nil {
|
||||||
rawSummary, ok = summaries[v1.MimeTypeGenericVulnerabilityReport]
|
if errors.IsNotFoundErr(err) {
|
||||||
if !ok {
|
|
||||||
// No report yet?
|
// No report yet?
|
||||||
msg := fmt.Sprintf(`current image without vulnerability scanning cannot be pulled due to configured policy in 'Prevent images with vulnerability severity of "%s" or higher from running.' `+
|
msg := fmt.Sprintf(`current image without vulnerability scanning cannot be pulled due to configured policy in 'Prevent images with vulnerability severity of "%s" or higher from running.' `+
|
||||||
`To continue with pull, please contact your project administrator for help.`, projectSeverity)
|
`To continue with pull, please contact your project administrator for help.`, projectSeverity)
|
||||||
return errors.New(nil).WithCode(errors.PROJECTPOLICYVIOLATION).WithMessage(msg)
|
return errors.New(nil).WithCode(errors.PROJECTPOLICYVIOLATION).WithMessage(msg)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
summary, ok := rawSummary.(*vuln.NativeReportSummary)
|
logger.Errorf("get vulnerability summary of the artifact %s@%s failed, error: %v", art.RepositoryName, art.Digest, err)
|
||||||
if !ok {
|
return err
|
||||||
return fmt.Errorf("report summary is invalid")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if art.IsImageIndex() {
|
if art.IsImageIndex() {
|
||||||
@ -128,37 +118,28 @@ func Middleware() func(http.Handler) http.Handler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !summary.IsSuccessStatus() {
|
if !vulnerable.IsScanSuccess() {
|
||||||
msg := fmt.Sprintf(`current image with "%s" status of vulnerability scanning cannot be pulled due to configured policy in 'Prevent images with vulnerability severity of "%s" or higher from running.' `+
|
msg := fmt.Sprintf(`current image with "%s" status of vulnerability scanning cannot be pulled due to configured policy in 'Prevent images with vulnerability severity of "%s" or higher from running.' `+
|
||||||
`To continue with pull, please contact your project administrator for help.`, summary.ScanStatus, projectSeverity)
|
`To continue with pull, please contact your project administrator for help.`, vulnerable.ScanStatus, projectSeverity)
|
||||||
return errors.New(nil).WithCode(errors.PROJECTPOLICYVIOLATION).WithMessage(msg)
|
return errors.New(nil).WithCode(errors.PROJECTPOLICYVIOLATION).WithMessage(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
if summary.Summary == nil || summary.Summary.Total == 0 {
|
|
||||||
// No vulnerabilities found in the artifact, skip the checking
|
|
||||||
// See https://github.com/goharbor/harbor/issues/11210 to get more details
|
|
||||||
logger.Debugf("no vulnerabilities found in artifact %s@%s, skip the vulnerability prevention checking", art.RepositoryName, art.Digest)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do judgement
|
// Do judgement
|
||||||
if summary.Severity.Code() >= projectSeverity.Code() {
|
if vulnerable.Severity != nil && vulnerable.Severity.Code() >= projectSeverity.Code() {
|
||||||
thing := "vulnerability"
|
thing := "vulnerability"
|
||||||
if summary.Summary.Total > 1 {
|
if vulnerable.VulnerabilitiesCount > 1 {
|
||||||
thing = "vulnerabilities"
|
thing = "vulnerabilities"
|
||||||
}
|
}
|
||||||
msg := fmt.Sprintf(`current image with %d %s cannot be pulled due to configured policy in 'Prevent images with vulnerability severity of "%s" or higher from running.' `+
|
msg := fmt.Sprintf(`current image with %d %s cannot be pulled due to configured policy in 'Prevent images with vulnerability severity of "%s" or higher from running.' `+
|
||||||
`To continue with pull, please contact your project administrator to exempt matched vulnerabilities through configuring the CVE allowlist.`,
|
`To continue with pull, please contact your project administrator to exempt matched vulnerabilities through configuring the CVE allowlist.`,
|
||||||
summary.Summary.Total, thing, projectSeverity)
|
vulnerable.VulnerabilitiesCount, thing, projectSeverity)
|
||||||
return errors.New(nil).WithCode(errors.PROJECTPOLICYVIOLATION).WithMessage(msg)
|
return errors.New(nil).WithCode(errors.PROJECTPOLICYVIOLATION).WithMessage(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print scannerPull CVE list
|
// Print scannerPull CVE list
|
||||||
if len(summary.CVEBypassed) > 0 {
|
for _, cve := range vulnerable.CVEBypassed {
|
||||||
for _, cve := range summary.CVEBypassed {
|
|
||||||
logger.Infof("Vulnerable policy check: bypassed CVE %s", cve)
|
logger.Infof("Vulnerable policy check: bypassed CVE %s", cve)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
@ -28,7 +28,7 @@ import (
|
|||||||
"github.com/goharbor/harbor/src/controller/project"
|
"github.com/goharbor/harbor/src/controller/project"
|
||||||
"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"
|
||||||
v1 "github.com/goharbor/harbor/src/pkg/scan/rest/v1"
|
"github.com/goharbor/harbor/src/lib/errors"
|
||||||
"github.com/goharbor/harbor/src/pkg/scan/vuln"
|
"github.com/goharbor/harbor/src/pkg/scan/vuln"
|
||||||
securitytesting "github.com/goharbor/harbor/src/testing/common/security"
|
securitytesting "github.com/goharbor/harbor/src/testing/common/security"
|
||||||
artifacttesting "github.com/goharbor/harbor/src/testing/controller/artifact"
|
artifacttesting "github.com/goharbor/harbor/src/testing/controller/artifact"
|
||||||
@ -222,7 +222,7 @@ func (suite *MiddlewareTestSuite) TestArtifactNotScanned() {
|
|||||||
mock.OnAnything(suite.artifactController, "GetByReference").Return(suite.artifact, nil)
|
mock.OnAnything(suite.artifactController, "GetByReference").Return(suite.artifact, nil)
|
||||||
mock.OnAnything(suite.projectController, "Get").Return(suite.project, nil)
|
mock.OnAnything(suite.projectController, "Get").Return(suite.project, nil)
|
||||||
mock.OnAnything(suite.checker, "IsScannable").Return(true, nil)
|
mock.OnAnything(suite.checker, "IsScannable").Return(true, nil)
|
||||||
mock.OnAnything(suite.scanController, "GetSummary").Return(nil, nil)
|
mock.OnAnything(suite.scanController, "GetVulnerable").Return(nil, errors.NotFoundError(nil))
|
||||||
|
|
||||||
req := suite.makeRequest()
|
req := suite.makeRequest()
|
||||||
rr := httptest.NewRecorder()
|
rr := httptest.NewRecorder()
|
||||||
@ -235,12 +235,7 @@ func (suite *MiddlewareTestSuite) TestArtifactScanFailed() {
|
|||||||
mock.OnAnything(suite.artifactController, "GetByReference").Return(suite.artifact, nil)
|
mock.OnAnything(suite.artifactController, "GetByReference").Return(suite.artifact, nil)
|
||||||
mock.OnAnything(suite.projectController, "Get").Return(suite.project, nil)
|
mock.OnAnything(suite.projectController, "Get").Return(suite.project, nil)
|
||||||
mock.OnAnything(suite.checker, "IsScannable").Return(true, nil)
|
mock.OnAnything(suite.checker, "IsScannable").Return(true, nil)
|
||||||
mock.OnAnything(suite.scanController, "GetSummary").Return(map[string]interface{}{
|
mock.OnAnything(suite.scanController, "GetVulnerable").Return(&scan.Vulnerable{ScanStatus: "Error"}, nil)
|
||||||
v1.MimeTypeNativeReport: &vuln.NativeReportSummary{
|
|
||||||
ScanStatus: "Error",
|
|
||||||
CVEBypassed: []string{"cve-2020"},
|
|
||||||
},
|
|
||||||
}, nil)
|
|
||||||
|
|
||||||
req := suite.makeRequest()
|
req := suite.makeRequest()
|
||||||
rr := httptest.NewRecorder()
|
rr := httptest.NewRecorder()
|
||||||
@ -249,26 +244,11 @@ func (suite *MiddlewareTestSuite) TestArtifactScanFailed() {
|
|||||||
suite.Equal(rr.Code, http.StatusPreconditionFailed)
|
suite.Equal(rr.Code, http.StatusPreconditionFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *MiddlewareTestSuite) TestGetSummaryFailed() {
|
func (suite *MiddlewareTestSuite) TestGetVulnerableFailed() {
|
||||||
mock.OnAnything(suite.artifactController, "GetByReference").Return(suite.artifact, nil)
|
mock.OnAnything(suite.artifactController, "GetByReference").Return(suite.artifact, nil)
|
||||||
mock.OnAnything(suite.projectController, "Get").Return(suite.project, nil)
|
mock.OnAnything(suite.projectController, "Get").Return(suite.project, nil)
|
||||||
mock.OnAnything(suite.checker, "IsScannable").Return(true, nil)
|
mock.OnAnything(suite.checker, "IsScannable").Return(true, nil)
|
||||||
mock.OnAnything(suite.scanController, "GetSummary").Return(nil, fmt.Errorf("error"))
|
mock.OnAnything(suite.scanController, "GetVulnerable").Return(nil, fmt.Errorf("error"))
|
||||||
|
|
||||||
req := suite.makeRequest()
|
|
||||||
rr := httptest.NewRecorder()
|
|
||||||
|
|
||||||
Middleware()(suite.next).ServeHTTP(rr, req)
|
|
||||||
suite.Equal(rr.Code, http.StatusInternalServerError)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (suite *MiddlewareTestSuite) TestBadSummary() {
|
|
||||||
mock.OnAnything(suite.artifactController, "GetByReference").Return(suite.artifact, nil)
|
|
||||||
mock.OnAnything(suite.projectController, "Get").Return(suite.project, nil)
|
|
||||||
mock.OnAnything(suite.checker, "IsScannable").Return(true, nil)
|
|
||||||
mock.OnAnything(suite.scanController, "GetSummary").Return(map[string]interface{}{
|
|
||||||
v1.MimeTypeNativeReport: "bad report",
|
|
||||||
}, nil)
|
|
||||||
|
|
||||||
req := suite.makeRequest()
|
req := suite.makeRequest()
|
||||||
rr := httptest.NewRecorder()
|
rr := httptest.NewRecorder()
|
||||||
@ -281,32 +261,9 @@ func (suite *MiddlewareTestSuite) TestNoVulnerabilities() {
|
|||||||
mock.OnAnything(suite.artifactController, "GetByReference").Return(suite.artifact, nil)
|
mock.OnAnything(suite.artifactController, "GetByReference").Return(suite.artifact, nil)
|
||||||
mock.OnAnything(suite.projectController, "Get").Return(suite.project, nil)
|
mock.OnAnything(suite.projectController, "Get").Return(suite.project, nil)
|
||||||
mock.OnAnything(suite.checker, "IsScannable").Return(true, nil)
|
mock.OnAnything(suite.checker, "IsScannable").Return(true, nil)
|
||||||
mock.OnAnything(suite.scanController, "GetSummary").Return(map[string]interface{}{
|
mock.OnAnything(suite.scanController, "GetVulnerable").Return(&scan.Vulnerable{
|
||||||
v1.MimeTypeNativeReport: &vuln.NativeReportSummary{
|
|
||||||
ScanStatus: "Success",
|
ScanStatus: "Success",
|
||||||
Severity: vuln.Unknown,
|
|
||||||
CVEBypassed: []string{"cve-2020"},
|
CVEBypassed: []string{"cve-2020"},
|
||||||
},
|
|
||||||
}, nil)
|
|
||||||
|
|
||||||
req := suite.makeRequest()
|
|
||||||
rr := httptest.NewRecorder()
|
|
||||||
|
|
||||||
Middleware()(suite.next).ServeHTTP(rr, req)
|
|
||||||
suite.Equal(rr.Code, http.StatusOK)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (suite *MiddlewareTestSuite) TestTotalVulnerabilitiesIsZero() {
|
|
||||||
mock.OnAnything(suite.artifactController, "GetByReference").Return(suite.artifact, nil)
|
|
||||||
mock.OnAnything(suite.projectController, "Get").Return(suite.project, nil)
|
|
||||||
mock.OnAnything(suite.checker, "IsScannable").Return(true, nil)
|
|
||||||
mock.OnAnything(suite.scanController, "GetSummary").Return(map[string]interface{}{
|
|
||||||
v1.MimeTypeNativeReport: &vuln.NativeReportSummary{
|
|
||||||
ScanStatus: "Success",
|
|
||||||
Severity: vuln.Unknown,
|
|
||||||
Summary: &vuln.VulnerabilitySummary{Total: 0},
|
|
||||||
CVEBypassed: []string{"cve-2020"},
|
|
||||||
},
|
|
||||||
}, nil)
|
}, nil)
|
||||||
|
|
||||||
req := suite.makeRequest()
|
req := suite.makeRequest()
|
||||||
@ -317,16 +274,15 @@ func (suite *MiddlewareTestSuite) TestTotalVulnerabilitiesIsZero() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (suite *MiddlewareTestSuite) TestAllowed() {
|
func (suite *MiddlewareTestSuite) TestAllowed() {
|
||||||
|
low := vuln.Low
|
||||||
mock.OnAnything(suite.artifactController, "GetByReference").Return(suite.artifact, nil)
|
mock.OnAnything(suite.artifactController, "GetByReference").Return(suite.artifact, nil)
|
||||||
mock.OnAnything(suite.projectController, "Get").Return(suite.project, nil)
|
mock.OnAnything(suite.projectController, "Get").Return(suite.project, nil)
|
||||||
mock.OnAnything(suite.checker, "IsScannable").Return(true, nil)
|
mock.OnAnything(suite.checker, "IsScannable").Return(true, nil)
|
||||||
mock.OnAnything(suite.scanController, "GetSummary").Return(map[string]interface{}{
|
mock.OnAnything(suite.scanController, "GetVulnerable").Return(&scan.Vulnerable{
|
||||||
v1.MimeTypeNativeReport: &vuln.NativeReportSummary{
|
|
||||||
ScanStatus: "Success",
|
ScanStatus: "Success",
|
||||||
Severity: vuln.Low,
|
Severity: &low,
|
||||||
Summary: &vuln.VulnerabilitySummary{Total: 1},
|
VulnerabilitiesCount: 1,
|
||||||
CVEBypassed: []string{"cve-2020"},
|
CVEBypassed: []string{"cve-2020"},
|
||||||
},
|
|
||||||
}, nil)
|
}, nil)
|
||||||
|
|
||||||
req := suite.makeRequest()
|
req := suite.makeRequest()
|
||||||
@ -341,14 +297,14 @@ func (suite *MiddlewareTestSuite) TestPrevented() {
|
|||||||
mock.OnAnything(suite.projectController, "Get").Return(suite.project, nil)
|
mock.OnAnything(suite.projectController, "Get").Return(suite.project, nil)
|
||||||
mock.OnAnything(suite.checker, "IsScannable").Return(true, nil)
|
mock.OnAnything(suite.checker, "IsScannable").Return(true, nil)
|
||||||
|
|
||||||
|
critical := vuln.Critical
|
||||||
|
|
||||||
{
|
{
|
||||||
// only one vulnerability
|
// only one vulnerability
|
||||||
mock.OnAnything(suite.scanController, "GetSummary").Return(map[string]interface{}{
|
mock.OnAnything(suite.scanController, "GetVulnerable").Return(&scan.Vulnerable{
|
||||||
v1.MimeTypeNativeReport: &vuln.NativeReportSummary{
|
|
||||||
ScanStatus: "Success",
|
ScanStatus: "Success",
|
||||||
Severity: vuln.Critical,
|
Severity: &critical,
|
||||||
Summary: &vuln.VulnerabilitySummary{Total: 1},
|
VulnerabilitiesCount: 1,
|
||||||
},
|
|
||||||
}, nil).Once()
|
}, nil).Once()
|
||||||
|
|
||||||
req := suite.makeRequest()
|
req := suite.makeRequest()
|
||||||
@ -362,12 +318,10 @@ func (suite *MiddlewareTestSuite) TestPrevented() {
|
|||||||
|
|
||||||
{
|
{
|
||||||
// multiple vulnerabilities
|
// multiple vulnerabilities
|
||||||
mock.OnAnything(suite.scanController, "GetSummary").Return(map[string]interface{}{
|
mock.OnAnything(suite.scanController, "GetVulnerable").Return(&scan.Vulnerable{
|
||||||
v1.MimeTypeNativeReport: &vuln.NativeReportSummary{
|
|
||||||
ScanStatus: "Success",
|
ScanStatus: "Success",
|
||||||
Severity: vuln.Critical,
|
Severity: &critical,
|
||||||
Summary: &vuln.VulnerabilitySummary{Total: 2},
|
VulnerabilitiesCount: 2,
|
||||||
},
|
|
||||||
}, nil).Once()
|
}, nil).Once()
|
||||||
|
|
||||||
req := suite.makeRequest()
|
req := suite.makeRequest()
|
||||||
@ -381,14 +335,15 @@ func (suite *MiddlewareTestSuite) TestPrevented() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (suite *MiddlewareTestSuite) TestArtifactIsImageIndex() {
|
func (suite *MiddlewareTestSuite) TestArtifactIsImageIndex() {
|
||||||
|
critical := vuln.Critical
|
||||||
|
|
||||||
suite.artifact.ManifestMediaType = manifestlist.MediaTypeManifestList
|
suite.artifact.ManifestMediaType = manifestlist.MediaTypeManifestList
|
||||||
mock.OnAnything(suite.artifactController, "GetByReference").Return(suite.artifact, nil)
|
mock.OnAnything(suite.artifactController, "GetByReference").Return(suite.artifact, nil)
|
||||||
mock.OnAnything(suite.projectController, "Get").Return(suite.project, nil)
|
mock.OnAnything(suite.projectController, "Get").Return(suite.project, nil)
|
||||||
mock.OnAnything(suite.checker, "IsScannable").Return(true, nil)
|
mock.OnAnything(suite.checker, "IsScannable").Return(true, nil)
|
||||||
mock.OnAnything(suite.scanController, "GetSummary").Return(map[string]interface{}{
|
mock.OnAnything(suite.scanController, "GetVulnerable").Return(&scan.Vulnerable{
|
||||||
v1.MimeTypeNativeReport: &vuln.NativeReportSummary{
|
ScanStatus: "Success",
|
||||||
Severity: vuln.Critical,
|
Severity: &critical,
|
||||||
},
|
|
||||||
}, nil)
|
}, nil)
|
||||||
|
|
||||||
req := suite.makeRequest()
|
req := suite.makeRequest()
|
||||||
|
@ -351,27 +351,16 @@ func (a *artifactAPI) GetVulnerabilitiesAddition(ctx context.Context, params ope
|
|||||||
return a.SendError(ctx, err)
|
return a.SendError(ctx, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, rp := range reports {
|
vrp, err := report.Reports(reports).ResolveData(mimeType)
|
||||||
// Resolve scan report data only when it is ready
|
if err != nil {
|
||||||
if len(rp.Report) == 0 {
|
return a.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if vrp == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
vrp, err := report.ResolveData(rp.MimeType, []byte(rp.Report), report.WithArtifactDigest(rp.Digest))
|
vulnerabilities[mimeType] = vrp
|
||||||
if err != nil {
|
|
||||||
return a.SendError(ctx, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if v, ok := vulnerabilities[rp.MimeType]; ok {
|
|
||||||
r, err := report.Merge(rp.MimeType, v, vrp)
|
|
||||||
if err != nil {
|
|
||||||
return a.SendError(ctx, err)
|
|
||||||
}
|
|
||||||
vulnerabilities[rp.MimeType] = r
|
|
||||||
} else {
|
|
||||||
vulnerabilities[rp.MimeType] = vrp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(vulnerabilities) != 0 {
|
if len(vulnerabilities) != 0 {
|
||||||
break
|
break
|
||||||
|
@ -11,9 +11,9 @@ import (
|
|||||||
|
|
||||||
mock "github.com/stretchr/testify/mock"
|
mock "github.com/stretchr/testify/mock"
|
||||||
|
|
||||||
pkgscan "github.com/goharbor/harbor/src/pkg/scan"
|
models "github.com/goharbor/harbor/src/pkg/allowlist/models"
|
||||||
|
|
||||||
report "github.com/goharbor/harbor/src/pkg/scan/report"
|
pkgscan "github.com/goharbor/harbor/src/pkg/scan"
|
||||||
|
|
||||||
scan "github.com/goharbor/harbor/src/controller/scan"
|
scan "github.com/goharbor/harbor/src/controller/scan"
|
||||||
)
|
)
|
||||||
@ -90,20 +90,13 @@ func (_m *Controller) GetScanLog(ctx context.Context, uuid string) ([]byte, erro
|
|||||||
return r0, r1
|
return r0, r1
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSummary provides a mock function with given fields: ctx, _a1, mimeTypes, options
|
// GetSummary provides a mock function with given fields: ctx, _a1, mimeTypes
|
||||||
func (_m *Controller) GetSummary(ctx context.Context, _a1 *artifact.Artifact, mimeTypes []string, options ...report.Option) (map[string]interface{}, error) {
|
func (_m *Controller) GetSummary(ctx context.Context, _a1 *artifact.Artifact, mimeTypes []string) (map[string]interface{}, error) {
|
||||||
_va := make([]interface{}, len(options))
|
ret := _m.Called(ctx, _a1, mimeTypes)
|
||||||
for _i := range options {
|
|
||||||
_va[_i] = options[_i]
|
|
||||||
}
|
|
||||||
var _ca []interface{}
|
|
||||||
_ca = append(_ca, ctx, _a1, mimeTypes)
|
|
||||||
_ca = append(_ca, _va...)
|
|
||||||
ret := _m.Called(_ca...)
|
|
||||||
|
|
||||||
var r0 map[string]interface{}
|
var r0 map[string]interface{}
|
||||||
if rf, ok := ret.Get(0).(func(context.Context, *artifact.Artifact, []string, ...report.Option) map[string]interface{}); ok {
|
if rf, ok := ret.Get(0).(func(context.Context, *artifact.Artifact, []string) map[string]interface{}); ok {
|
||||||
r0 = rf(ctx, _a1, mimeTypes, options...)
|
r0 = rf(ctx, _a1, mimeTypes)
|
||||||
} else {
|
} else {
|
||||||
if ret.Get(0) != nil {
|
if ret.Get(0) != nil {
|
||||||
r0 = ret.Get(0).(map[string]interface{})
|
r0 = ret.Get(0).(map[string]interface{})
|
||||||
@ -111,8 +104,31 @@ func (_m *Controller) GetSummary(ctx context.Context, _a1 *artifact.Artifact, mi
|
|||||||
}
|
}
|
||||||
|
|
||||||
var r1 error
|
var r1 error
|
||||||
if rf, ok := ret.Get(1).(func(context.Context, *artifact.Artifact, []string, ...report.Option) error); ok {
|
if rf, ok := ret.Get(1).(func(context.Context, *artifact.Artifact, []string) error); ok {
|
||||||
r1 = rf(ctx, _a1, mimeTypes, options...)
|
r1 = rf(ctx, _a1, mimeTypes)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVulnerable provides a mock function with given fields: ctx, _a1, allowlist
|
||||||
|
func (_m *Controller) GetVulnerable(ctx context.Context, _a1 *artifact.Artifact, allowlist models.CVESet) (*scan.Vulnerable, error) {
|
||||||
|
ret := _m.Called(ctx, _a1, allowlist)
|
||||||
|
|
||||||
|
var r0 *scan.Vulnerable
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, *artifact.Artifact, models.CVESet) *scan.Vulnerable); ok {
|
||||||
|
r0 = rf(ctx, _a1, allowlist)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*scan.Vulnerable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(1).(func(context.Context, *artifact.Artifact, models.CVESet) error); ok {
|
||||||
|
r1 = rf(ctx, _a1, allowlist)
|
||||||
} else {
|
} else {
|
||||||
r1 = ret.Error(1)
|
r1 = ret.Error(1)
|
||||||
}
|
}
|
||||||
@ -162,13 +178,13 @@ func (_m *Controller) ScanAll(ctx context.Context, trigger string, async bool) (
|
|||||||
return r0, r1
|
return r0, r1
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateReport provides a mock function with given fields: ctx, _a1
|
// UpdateReport provides a mock function with given fields: ctx, report
|
||||||
func (_m *Controller) UpdateReport(ctx context.Context, _a1 *pkgscan.CheckInReport) error {
|
func (_m *Controller) UpdateReport(ctx context.Context, report *pkgscan.CheckInReport) error {
|
||||||
ret := _m.Called(ctx, _a1)
|
ret := _m.Called(ctx, report)
|
||||||
|
|
||||||
var r0 error
|
var r0 error
|
||||||
if rf, ok := ret.Get(0).(func(context.Context, *pkgscan.CheckInReport) error); ok {
|
if rf, ok := ret.Get(0).(func(context.Context, *pkgscan.CheckInReport) error); ok {
|
||||||
r0 = rf(ctx, _a1)
|
r0 = rf(ctx, report)
|
||||||
} else {
|
} else {
|
||||||
r0 = ret.Error(0)
|
r0 = ret.Error(0)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user