0
Fork 0
mirror of https://github.com/project-zot/zot.git synced 2024-12-23 22:27:35 -05:00
zot/pkg/extensions/search/cve/cve.go
Andrei Aaron e0d808b196
Include image vulnerability information in ImageSummary (#798)
Return this data as part of GlobalSearch and RepoListWithNewestImage
query results.
This commit also includes refactoring of the CVE scanning logic in
order to better encapsulate trivy specific logic, remove CVE scanning
logic from the graphql resolver.

Signed-off-by: Andrei Aaron <andaaron@cisco.com>
2022-09-28 11:39:54 -07:00

207 lines
5.1 KiB
Go

package cveinfo
import (
"fmt"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/opencontainers/go-digest"
ispec "github.com/opencontainers/image-spec/specs-go/v1"
"zotregistry.io/zot/pkg/extensions/search/common"
cvemodel "zotregistry.io/zot/pkg/extensions/search/cve/model"
"zotregistry.io/zot/pkg/extensions/search/cve/trivy"
"zotregistry.io/zot/pkg/log"
"zotregistry.io/zot/pkg/storage"
)
type CveInfo interface {
GetImageListForCVE(repo, cveID string) ([]ImageInfoByCVE, error)
GetImageListWithCVEFixed(repo, cveID string) ([]common.TagInfo, error)
GetCVEListForImage(image string) (map[string]cvemodel.CVE, error)
GetCVESummaryForImage(image string) (ImageCVESummary, error)
UpdateDB() error
}
type Scanner interface {
ScanImage(image string) (map[string]cvemodel.CVE, error)
IsImageFormatScannable(image string) (bool, error)
CompareSeverities(severity1, severity2 string) int
UpdateDB() error
}
type ImageInfoByCVE struct {
Tag string
Digest digest.Digest
Manifest v1.Manifest
}
type ImageCVESummary struct {
Count int
MaxSeverity string
}
type BaseCveInfo struct {
Log log.Logger
Scanner Scanner
LayoutUtils common.OciLayoutUtils
}
func NewCVEInfo(storeController storage.StoreController, log log.Logger) *BaseCveInfo {
layoutUtils := common.NewBaseOciLayoutUtils(storeController, log)
scanner := trivy.NewScanner(storeController, layoutUtils, log)
return &BaseCveInfo{Log: log, Scanner: scanner, LayoutUtils: layoutUtils}
}
func (cveinfo BaseCveInfo) GetImageListForCVE(repo, cveID string) ([]ImageInfoByCVE, error) {
imgList := make([]ImageInfoByCVE, 0)
manifests, err := cveinfo.LayoutUtils.GetImageManifests(repo)
if err != nil {
cveinfo.Log.Error().Err(err).Str("repo", repo).Msg("unable to get list of tags from repo")
return imgList, err
}
for _, manifest := range manifests {
tag := manifest.Annotations[ispec.AnnotationRefName]
image := fmt.Sprintf("%s:%s", repo, tag)
isValidImage, _ := cveinfo.Scanner.IsImageFormatScannable(image)
if !isValidImage {
continue
}
cveMap, err := cveinfo.Scanner.ScanImage(image)
if err != nil {
continue
}
for id := range cveMap {
if id == cveID {
digest := manifest.Digest
imageBlobManifest, err := cveinfo.LayoutUtils.GetImageBlobManifest(repo, digest)
if err != nil {
cveinfo.Log.Error().Err(err).Msg("unable to read image blob manifest")
return []ImageInfoByCVE{}, err
}
imgList = append(imgList, ImageInfoByCVE{
Tag: tag,
Digest: digest,
Manifest: imageBlobManifest,
})
break
}
}
}
return imgList, nil
}
func (cveinfo BaseCveInfo) GetImageListWithCVEFixed(repo, cveID string) ([]common.TagInfo, error) {
tagsInfo, err := cveinfo.LayoutUtils.GetImageTagsWithTimestamp(repo)
if err != nil {
cveinfo.Log.Error().Err(err).Str("repo", repo).Msg("unable to get list of tags from repo")
return []common.TagInfo{}, err
}
vulnerableTags := make([]common.TagInfo, 0)
var hasCVE bool
for _, tag := range tagsInfo {
image := fmt.Sprintf("%s:%s", repo, tag.Name)
tagInfo := common.TagInfo{Name: tag.Name, Timestamp: tag.Timestamp, Digest: tag.Digest}
isValidImage, _ := cveinfo.Scanner.IsImageFormatScannable(image)
if !isValidImage {
cveinfo.Log.Debug().Str("image", image).
Msg("image media type not supported for scanning, adding as a vulnerable image")
vulnerableTags = append(vulnerableTags, tagInfo)
continue
}
cveMap, err := cveinfo.Scanner.ScanImage(image)
if err != nil {
cveinfo.Log.Debug().Str("image", image).
Msg("scanning failed, adding as a vulnerable image")
vulnerableTags = append(vulnerableTags, tagInfo)
continue
}
hasCVE = false
for id := range cveMap {
if id == cveID {
hasCVE = true
break
}
}
if hasCVE {
vulnerableTags = append(vulnerableTags, tagInfo)
}
}
if len(vulnerableTags) != 0 {
cveinfo.Log.Info().Str("repo", repo).Msg("comparing fixed tags timestamp")
tagsInfo = common.GetFixedTags(tagsInfo, vulnerableTags)
} else {
cveinfo.Log.Info().Str("repo", repo).Str("cve-id", cveID).
Msg("image does not contain any tag that have given cve")
}
return tagsInfo, nil
}
func (cveinfo BaseCveInfo) GetCVEListForImage(image string) (map[string]cvemodel.CVE, error) {
cveMap := make(map[string]cvemodel.CVE)
isValidImage, err := cveinfo.Scanner.IsImageFormatScannable(image)
if !isValidImage {
return cveMap, err
}
return cveinfo.Scanner.ScanImage(image)
}
func (cveinfo BaseCveInfo) GetCVESummaryForImage(image string) (ImageCVESummary, error) {
imageCVESummary := ImageCVESummary{
Count: 0,
MaxSeverity: "UNKNOWN",
}
isValidImage, err := cveinfo.Scanner.IsImageFormatScannable(image)
if !isValidImage {
return imageCVESummary, err
}
cveMap, err := cveinfo.Scanner.ScanImage(image)
if err != nil {
return imageCVESummary, err
}
for _, cve := range cveMap {
if cveinfo.Scanner.CompareSeverities(imageCVESummary.MaxSeverity, cve.Severity) > 0 {
imageCVESummary.MaxSeverity = cve.Severity
}
}
imageCVESummary.Count = len(cveMap)
return imageCVESummary, nil
}
func (cveinfo BaseCveInfo) UpdateDB() error {
return cveinfo.Scanner.UpdateDB()
}