mirror of
https://github.com/project-zot/zot.git
synced 2024-12-23 22:27:35 -05:00
ab9a20c1ae
If this GraphQL API is available, try that first, else fallback to the slowpath. Signed-off-by: Roxana Nemulescu <roxana.nemulescu@gmail.com>
205 lines
4.9 KiB
Go
205 lines
4.9 KiB
Go
package cveinfo
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"path"
|
|
"strings"
|
|
|
|
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
|
|
"github.com/aquasecurity/trivy/pkg/commands/artifact"
|
|
"github.com/aquasecurity/trivy/pkg/commands/operation"
|
|
"github.com/aquasecurity/trivy/pkg/report"
|
|
"github.com/aquasecurity/trivy/pkg/types"
|
|
ispec "github.com/opencontainers/image-spec/specs-go/v1"
|
|
"github.com/urfave/cli/v2"
|
|
"zotregistry.io/zot/pkg/extensions/search/common"
|
|
"zotregistry.io/zot/pkg/log"
|
|
"zotregistry.io/zot/pkg/storage"
|
|
)
|
|
|
|
func getRoutePrefix(name string) string {
|
|
names := strings.SplitN(name, "/", 2) //nolint:gomnd
|
|
|
|
if len(names) != 2 { // nolint: gomnd
|
|
// it means route is of global storage e.g "centos:latest"
|
|
if len(names) == 1 {
|
|
return "/"
|
|
}
|
|
}
|
|
|
|
return fmt.Sprintf("/%s", names[0])
|
|
}
|
|
|
|
// UpdateCVEDb ...
|
|
func UpdateCVEDb(dbDir string, log log.Logger) error {
|
|
return operation.DownloadDB("dev", dbDir, false, false, false)
|
|
}
|
|
|
|
// NewTrivyContext set some trivy configuration value and return a context.
|
|
func NewTrivyContext(dir string) *TrivyCtx {
|
|
trivyCtx := &TrivyCtx{}
|
|
|
|
app := &cli.App{}
|
|
|
|
flagSet := &flag.FlagSet{}
|
|
|
|
var cacheDir string
|
|
|
|
flagSet.StringVar(&cacheDir, "cache-dir", dir, "")
|
|
|
|
var vuln string
|
|
|
|
flagSet.StringVar(&vuln, "vuln-type", strings.Join([]string{types.VulnTypeOS, types.VulnTypeLibrary}, ","), "")
|
|
|
|
var severity string
|
|
|
|
flagSet.StringVar(&severity, "severity", strings.Join(dbTypes.SeverityNames, ","), "")
|
|
|
|
flagSet.StringVar(&trivyCtx.Input, "input", "", "")
|
|
|
|
var securityCheck string
|
|
|
|
flagSet.StringVar(&securityCheck, "security-checks", types.SecurityCheckVulnerability, "")
|
|
|
|
var reportFormat string
|
|
|
|
flagSet.StringVar(&reportFormat, "format", "table", "")
|
|
|
|
ctx := cli.NewContext(app, flagSet, nil)
|
|
|
|
trivyCtx.Ctx = ctx
|
|
|
|
return trivyCtx
|
|
}
|
|
|
|
func ScanImage(ctx *cli.Context) (report.Report, error) {
|
|
return artifact.TrivyImageRun(ctx)
|
|
}
|
|
|
|
func GetCVEInfo(storeController storage.StoreController, log log.Logger) (*CveInfo, error) {
|
|
cveController := CveTrivyController{}
|
|
layoutUtils := common.NewBaseOciLayoutUtils(storeController, log)
|
|
|
|
subCveConfig := make(map[string]*TrivyCtx)
|
|
|
|
if storeController.DefaultStore != nil {
|
|
imageStore := storeController.DefaultStore
|
|
|
|
rootDir := imageStore.RootDir()
|
|
|
|
ctx := NewTrivyContext(rootDir)
|
|
|
|
cveController.DefaultCveConfig = ctx
|
|
}
|
|
|
|
if storeController.SubStore != nil {
|
|
for route, storage := range storeController.SubStore {
|
|
rootDir := storage.RootDir()
|
|
|
|
ctx := NewTrivyContext(rootDir)
|
|
|
|
subCveConfig[route] = ctx
|
|
}
|
|
}
|
|
|
|
cveController.SubCveConfig = subCveConfig
|
|
|
|
return &CveInfo{
|
|
Log: log, CveTrivyController: cveController, StoreController: storeController,
|
|
LayoutUtils: layoutUtils,
|
|
}, nil
|
|
}
|
|
|
|
func (cveinfo CveInfo) GetTrivyContext(image string) *TrivyCtx {
|
|
// Split image to get route prefix
|
|
prefixName := getRoutePrefix(image)
|
|
|
|
var trivyCtx *TrivyCtx
|
|
|
|
var ok bool
|
|
|
|
var rootDir string
|
|
|
|
// Get corresponding CVE trivy config, if no sub cve config present that means its default
|
|
trivyCtx, ok = cveinfo.CveTrivyController.SubCveConfig[prefixName]
|
|
if ok {
|
|
imgStore := cveinfo.StoreController.SubStore[prefixName]
|
|
|
|
rootDir = imgStore.RootDir()
|
|
} else {
|
|
trivyCtx = cveinfo.CveTrivyController.DefaultCveConfig
|
|
|
|
imgStore := cveinfo.StoreController.DefaultStore
|
|
|
|
rootDir = imgStore.RootDir()
|
|
}
|
|
|
|
trivyCtx.Input = path.Join(rootDir, image)
|
|
|
|
return trivyCtx
|
|
}
|
|
|
|
func (cveinfo CveInfo) GetImageListForCVE(repo, cvid string, imgStore storage.ImageStore,
|
|
trivyCtx *TrivyCtx,
|
|
) ([]ImageInfoByCVE, error) {
|
|
imgList := make([]ImageInfoByCVE, 0)
|
|
|
|
rootDir := imgStore.RootDir()
|
|
|
|
manifests, err := cveinfo.LayoutUtils.GetImageManifests(repo)
|
|
if err != nil {
|
|
cveinfo.Log.Error().Err(err).Msg("unable to get list of image tag")
|
|
|
|
return imgList, err
|
|
}
|
|
|
|
for _, manifest := range manifests {
|
|
tag := manifest.Annotations[ispec.AnnotationRefName]
|
|
|
|
image := fmt.Sprintf("%s:%s", repo, tag)
|
|
|
|
trivyCtx.Input = path.Join(rootDir, image)
|
|
|
|
isValidImage, _ := cveinfo.LayoutUtils.IsValidImageFormat(image)
|
|
if !isValidImage {
|
|
cveinfo.Log.Debug().Str("image", repo+":"+tag).Msg("image media type not supported for scanning")
|
|
|
|
continue
|
|
}
|
|
|
|
cveinfo.Log.Info().Str("image", repo+":"+tag).Msg("scanning image")
|
|
|
|
report, err := ScanImage(trivyCtx.Ctx)
|
|
if err != nil {
|
|
cveinfo.Log.Error().Err(err).Str("image", repo+":"+tag).Msg("unable to scan image")
|
|
|
|
continue
|
|
}
|
|
|
|
for _, result := range report.Results {
|
|
for _, vulnerability := range result.Vulnerabilities {
|
|
if vulnerability.VulnerabilityID == cvid {
|
|
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
|
|
}
|