0
Fork 0
mirror of https://github.com/project-zot/zot.git synced 2024-12-30 22:34:13 -05:00

feat(cve): expand search domain to cve description and package info (#2086)

* feat(cve): add reference url for cve

Signed-off-by: Laurentiu Niculae <niculae.laurentiu1@gmail.com>

* feat(cve): expand search domain to cve description and package info

Signed-off-by: Laurentiu Niculae <niculae.laurentiu1@gmail.com>

---------

Signed-off-by: Laurentiu Niculae <niculae.laurentiu1@gmail.com>
This commit is contained in:
LaurentiuNiculae 2023-11-29 20:59:00 +02:00 committed by GitHub
parent e59d8da454
commit 90d27ff2ac
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 124 additions and 7 deletions

2
go.mod
View file

@ -496,7 +496,7 @@ require (
go.uber.org/atomic v1.11.0 // indirect go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.26.0 // indirect go.uber.org/zap v1.26.0 // indirect
golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect golang.org/x/exp v0.0.0-20231006140011-7918f672742d
golang.org/x/mod v0.13.0 // indirect golang.org/x/mod v0.13.0 // indirect
golang.org/x/net v0.18.0 // indirect golang.org/x/net v0.18.0 // indirect
golang.org/x/term v0.14.0 // indirect golang.org/x/term v0.14.0 // indirect

View file

@ -8,6 +8,7 @@ import (
godigest "github.com/opencontainers/go-digest" godigest "github.com/opencontainers/go-digest"
ispec "github.com/opencontainers/image-spec/specs-go/v1" ispec "github.com/opencontainers/image-spec/specs-go/v1"
"golang.org/x/exp/slices"
zerr "zotregistry.io/zot/errors" zerr "zotregistry.io/zot/errors"
zcommon "zotregistry.io/zot/pkg/common" zcommon "zotregistry.io/zot/pkg/common"
@ -334,7 +335,15 @@ func filterCVEList(cveMap map[string]cvemodel.CVE, searchedCVE string, pageFinde
for _, cve := range cveMap { for _, cve := range cveMap {
if strings.Contains(strings.ToUpper(cve.Title), searchedCVE) || if strings.Contains(strings.ToUpper(cve.Title), searchedCVE) ||
strings.Contains(strings.ToUpper(cve.ID), searchedCVE) { strings.Contains(strings.ToUpper(cve.ID), searchedCVE) ||
strings.Contains(strings.ToUpper(cve.Description), searchedCVE) ||
strings.Contains(strings.ToUpper(cve.Reference), searchedCVE) ||
strings.Contains(strings.ToUpper(cve.Severity), searchedCVE) ||
slices.ContainsFunc(cve.PackageList, func(pack cvemodel.Package) bool {
return strings.Contains(strings.ToUpper(pack.Name), searchedCVE) ||
strings.Contains(strings.ToUpper(pack.FixedVersion), searchedCVE) ||
strings.Contains(strings.ToUpper(pack.InstalledVersion), searchedCVE)
}) {
pageFinder.Add(cve) pageFinder.Add(cve)
} }
} }

View file

@ -17,6 +17,7 @@ type CVE struct {
Description string `json:"Description"` Description string `json:"Description"`
Severity string `json:"Severity"` Severity string `json:"Severity"`
Title string `json:"Title"` Title string `json:"Title"`
Reference string `json:"Reference"`
PackageList []Package `json:"PackageList"` PackageList []Package `json:"PackageList"`
} }

View file

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"os" "os"
"path" "path"
"strings"
"sync" "sync"
"github.com/aquasecurity/trivy-db/pkg/metadata" "github.com/aquasecurity/trivy-db/pkg/metadata"
@ -427,6 +428,7 @@ func (scanner Scanner) scanManifest(ctx context.Context, repo, digest string) (m
ID: vulnerability.VulnerabilityID, ID: vulnerability.VulnerabilityID,
Title: vulnerability.Title, Title: vulnerability.Title,
Description: vulnerability.Description, Description: vulnerability.Description,
Reference: getCVEReference(vulnerability.PrimaryURL, vulnerability.References),
Severity: convertSeverity(vulnerability.Severity), Severity: convertSeverity(vulnerability.Severity),
PackageList: newPkgList, PackageList: newPkgList,
} }
@ -439,6 +441,34 @@ func (scanner Scanner) scanManifest(ctx context.Context, repo, digest string) (m
return cveidMap, nil return cveidMap, nil
} }
func getCVEReference(primaryURL string, references []string) string {
if primaryURL != "" {
return primaryURL
}
if len(references) > 0 {
nvdReference, found := getNVDReference(references)
if found {
return nvdReference
}
return references[0]
}
return ""
}
func getNVDReference(references []string) (string, bool) {
for i := range references {
if strings.Contains(references[i], "nvd.nist.gov") {
return references[i], true
}
}
return "", false
}
func (scanner Scanner) scanIndex(ctx context.Context, repo, digest string) (map[string]cvemodel.CVE, error) { func (scanner Scanner) scanIndex(ctx context.Context, repo, digest string) (map[string]cvemodel.CVE, error) {
if cachedMap := scanner.cache.Get(digest); cachedMap != nil { if cachedMap := scanner.cache.Get(digest); cachedMap != nil {
return cachedMap, nil return cachedMap, nil

View file

@ -488,3 +488,19 @@ func TestIsIndexScannableErrors(t *testing.T) {
}) })
}) })
} }
func TestGetCVEReference(t *testing.T) {
Convey("getCVEReference", t, func() {
ref := getCVEReference("primary", []string{})
So(ref, ShouldResemble, "primary")
ref = getCVEReference("", []string{"secondary"})
So(ref, ShouldResemble, "secondary")
ref = getCVEReference("", []string{""})
So(ref, ShouldResemble, "")
ref = getCVEReference("", []string{"https://nvd.nist.gov/vuln/detail/CVE-2023-2650"})
So(ref, ShouldResemble, "https://nvd.nist.gov/vuln/detail/CVE-2023-2650")
})
}

View file

@ -54,6 +54,7 @@ type ComplexityRoot struct {
Description func(childComplexity int) int Description func(childComplexity int) int
ID func(childComplexity int) int ID func(childComplexity int) int
PackageList func(childComplexity int) int PackageList func(childComplexity int) int
Reference func(childComplexity int) int
Severity func(childComplexity int) int Severity func(childComplexity int) int
Title func(childComplexity int) int Title func(childComplexity int) int
} }
@ -281,6 +282,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.CVE.PackageList(childComplexity), true return e.complexity.CVE.PackageList(childComplexity), true
case "CVE.Reference":
if e.complexity.CVE.Reference == nil {
break
}
return e.complexity.CVE.Reference(childComplexity), true
case "CVE.Severity": case "CVE.Severity":
if e.complexity.CVE.Severity == nil { if e.complexity.CVE.Severity == nil {
break break
@ -1184,6 +1192,10 @@ type CVE {
""" """
Description: String Description: String
""" """
Reference for the given CVE
"""
Reference: String
"""
The impact the CVE has, one of "UNKNOWN", "LOW", "MEDIUM", "HIGH", "CRITICAL" The impact the CVE has, one of "UNKNOWN", "LOW", "MEDIUM", "HIGH", "CRITICAL"
""" """
Severity: String Severity: String
@ -2510,6 +2522,47 @@ func (ec *executionContext) fieldContext_CVE_Description(ctx context.Context, fi
return fc, nil return fc, nil
} }
func (ec *executionContext) _CVE_Reference(ctx context.Context, field graphql.CollectedField, obj *Cve) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_CVE_Reference(ctx, field)
if err != nil {
return graphql.Null
}
ctx = graphql.WithFieldContext(ctx, fc)
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.Reference, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
return graphql.Null
}
res := resTmp.(*string)
fc.Result = res
return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_CVE_Reference(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "CVE",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type String does not have child fields")
},
}
return fc, nil
}
func (ec *executionContext) _CVE_Severity(ctx context.Context, field graphql.CollectedField, obj *Cve) (ret graphql.Marshaler) { func (ec *executionContext) _CVE_Severity(ctx context.Context, field graphql.CollectedField, obj *Cve) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_CVE_Severity(ctx, field) fc, err := ec.fieldContext_CVE_Severity(ctx, field)
if err != nil { if err != nil {
@ -2683,6 +2736,8 @@ func (ec *executionContext) fieldContext_CVEResultForImage_CVEList(ctx context.C
return ec.fieldContext_CVE_Title(ctx, field) return ec.fieldContext_CVE_Title(ctx, field)
case "Description": case "Description":
return ec.fieldContext_CVE_Description(ctx, field) return ec.fieldContext_CVE_Description(ctx, field)
case "Reference":
return ec.fieldContext_CVE_Reference(ctx, field)
case "Severity": case "Severity":
return ec.fieldContext_CVE_Severity(ctx, field) return ec.fieldContext_CVE_Severity(ctx, field)
case "PackageList": case "PackageList":
@ -9407,6 +9462,8 @@ func (ec *executionContext) _CVE(ctx context.Context, sel ast.SelectionSet, obj
out.Values[i] = ec._CVE_Title(ctx, field, obj) out.Values[i] = ec._CVE_Title(ctx, field, obj)
case "Description": case "Description":
out.Values[i] = ec._CVE_Description(ctx, field, obj) out.Values[i] = ec._CVE_Description(ctx, field, obj)
case "Reference":
out.Values[i] = ec._CVE_Reference(ctx, field, obj)
case "Severity": case "Severity":
out.Values[i] = ec._CVE_Severity(ctx, field, obj) out.Values[i] = ec._CVE_Severity(ctx, field, obj)
case "PackageList": case "PackageList":

View file

@ -27,6 +27,8 @@ type Cve struct {
Title *string `json:"Title,omitempty"` Title *string `json:"Title,omitempty"`
// A detailed description of the CVE // A detailed description of the CVE
Description *string `json:"Description,omitempty"` Description *string `json:"Description,omitempty"`
// Reference for the given CVE
Reference *string `json:"Reference,omitempty"`
// The impact the CVE has, one of "UNKNOWN", "LOW", "MEDIUM", "HIGH", "CRITICAL" // The impact the CVE has, one of "UNKNOWN", "LOW", "MEDIUM", "HIGH", "CRITICAL"
Severity *string `json:"Severity,omitempty"` Severity *string `json:"Severity,omitempty"`
// Information on the packages in which the CVE was found // Information on the packages in which the CVE was found

View file

@ -228,6 +228,7 @@ func getCVEListForImage(
desc := cveDetail.Description desc := cveDetail.Description
title := cveDetail.Title title := cveDetail.Title
severity := cveDetail.Severity severity := cveDetail.Severity
referenceURL := cveDetail.Reference
pkgList := make([]*gql_generated.PackageInfo, 0) pkgList := make([]*gql_generated.PackageInfo, 0)
@ -249,6 +250,7 @@ func getCVEListForImage(
Title: &title, Title: &title,
Description: &desc, Description: &desc,
Severity: &severity, Severity: &severity,
Reference: &referenceURL,
PackageList: pkgList, PackageList: pkgList,
}, },
) )

View file

@ -46,6 +46,10 @@ type CVE {
""" """
Description: String Description: String
""" """
Reference for the given CVE
"""
Reference: String
"""
The impact the CVE has, one of "UNKNOWN", "LOW", "MEDIUM", "HIGH", "CRITICAL" The impact the CVE has, one of "UNKNOWN", "LOW", "MEDIUM", "HIGH", "CRITICAL"
""" """
Severity: String Severity: String

View file

@ -20,11 +20,7 @@ func (r *queryResolver) CVEListForImage(ctx context.Context, image string, reque
return &gql_generated.CVEResultForImage{}, zerr.ErrCVESearchDisabled return &gql_generated.CVEResultForImage{}, zerr.ErrCVESearchDisabled
} }
if searchedCve == nil { return getCVEListForImage(ctx, image, r.cveInfo, requestedPage, deref(searchedCve, ""), r.log)
return getCVEListForImage(ctx, image, r.cveInfo, requestedPage, "", r.log)
}
return getCVEListForImage(ctx, image, r.cveInfo, requestedPage, *searchedCve, r.log)
} }
// ImageListForCve is the resolver for the ImageListForCVE field. // ImageListForCve is the resolver for the ImageListForCVE field.