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
Shivam Mishra d63f715fe5 search/cve: exclude unsupported images from fixed-tag list.
If image vulnerability scan does not support any media type, considering those images as an infected image and now this images will not be shown in fixed images list.

Fixes issue #130
2020-09-22 09:24:04 -07:00

247 lines
5.8 KiB
Go

package cveinfo
import (
"encoding/json"
"io/ioutil"
"os"
"path"
"sort"
"strings"
"github.com/anuvu/zot/errors"
"github.com/anuvu/zot/pkg/log"
integration "github.com/aquasecurity/trivy/integration"
config "github.com/aquasecurity/trivy/integration/config"
"github.com/aquasecurity/trivy/pkg/report"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/types"
godigest "github.com/opencontainers/go-digest"
ispec "github.com/opencontainers/image-spec/specs-go/v1"
)
// UpdateCVEDb ...
func UpdateCVEDb(dbDir string, log log.Logger) error {
config, err := config.NewConfig(dbDir)
if err != nil {
log.Error().Err(err).Msg("Unable to get config")
return err
}
err = integration.RunTrivyDb(config.TrivyConfig)
if err != nil {
log.Error().Err(err).Msg("Unable to update DB ")
return err
}
return nil
}
func NewTrivyConfig(dir string) (*config.Config, error) {
return config.NewConfig(dir)
}
func ScanImage(config *config.Config) (report.Results, error) {
return integration.ScanTrivyImage(config.TrivyConfig)
}
func (cveinfo CveInfo) IsValidImageFormat(imagePath string) (bool, error) {
imageDir, inputTag := getImageDirAndTag(imagePath)
if !dirExists(imageDir) {
cveinfo.Log.Error().Msg("Image Directory not exists")
return false, errors.ErrRepoNotFound
}
manifests, err := cveinfo.getImageManifests(imageDir)
if err != nil {
return false, err
}
for _, m := range manifests {
tag, ok := m.Annotations[ispec.AnnotationRefName]
if ok && inputTag != "" && tag != inputTag {
continue
}
blobManifest, err := cveinfo.getImageBlobManifest(imageDir, m.Digest)
if err != nil {
return false, err
}
imageLayers := blobManifest.Layers
for _, imageLayer := range imageLayers {
switch imageLayer.MediaType {
case types.OCILayer, types.DockerLayer:
return true, nil
default:
cveinfo.Log.Debug().Msg("Image media type not supported for scanning")
return false, errors.ErrScanNotSupported
}
}
}
return false, nil
}
func dirExists(d string) bool {
fi, err := os.Stat(d)
if err != nil && os.IsNotExist(err) {
return false
}
return fi.IsDir()
}
func getImageDirAndTag(imageName string) (string, string) {
var imageDir string
var imageTag string
if strings.Contains(imageName, ":") {
splitImageName := strings.Split(imageName, ":")
imageDir = splitImageName[0]
imageTag = splitImageName[1]
} else {
imageDir = imageName
}
return imageDir, imageTag
}
// GetImageTagsWithTimestamp returns a list of image tags with timestamp available in the specified repository.
func (cveinfo CveInfo) GetImageTagsWithTimestamp(rootDir string, repo string) ([]TagInfo, error) {
tagsInfo := make([]TagInfo, 0)
dir := path.Join(rootDir, repo)
if !dirExists(dir) {
return nil, errors.ErrRepoNotFound
}
manifests, err := cveinfo.getImageManifests(dir)
if err != nil {
cveinfo.Log.Error().Err(err).Msg("Unable to read image manifests")
return tagsInfo, err
}
for _, manifest := range manifests {
digest := manifest.Digest
v, ok := manifest.Annotations[ispec.AnnotationRefName]
if ok {
imageBlobManifest, err := cveinfo.getImageBlobManifest(dir, digest)
if err != nil {
cveinfo.Log.Error().Err(err).Msg("Unable to read image blob manifest")
return tagsInfo, err
}
imageInfo, err := cveinfo.getImageInfo(dir, imageBlobManifest.Config.Digest)
if err != nil {
cveinfo.Log.Error().Err(err).Msg("Unable to read image info")
return tagsInfo, err
}
timeStamp := *imageInfo.History[0].Created
tagsInfo = append(tagsInfo, TagInfo{Name: v, Timestamp: timeStamp})
}
}
return tagsInfo, nil
}
func GetFixedTags(allTags []TagInfo, infectedTags []TagInfo) []TagInfo {
sort.Slice(allTags, func(i, j int) bool {
return allTags[i].Timestamp.Before(allTags[j].Timestamp)
})
latestInfected := TagInfo{}
for _, tag := range infectedTags {
if !tag.Timestamp.Before(latestInfected.Timestamp) {
latestInfected = tag
}
}
var fixedTags []TagInfo
for _, tag := range allTags {
if tag.Timestamp.After(latestInfected.Timestamp) {
fixedTags = append(fixedTags, tag)
}
}
return fixedTags
}
func (cveinfo CveInfo) getImageManifests(imagePath string) ([]ispec.Descriptor, error) {
buf, err := ioutil.ReadFile(path.Join(imagePath, "index.json"))
if err != nil {
if os.IsNotExist(err) {
cveinfo.Log.Error().Err(err).Msg("Index.json does not exist")
return nil, errors.ErrRepoNotFound
}
cveinfo.Log.Error().Err(err).Msg("Unable to open index.json")
return nil, errors.ErrRepoNotFound
}
var index ispec.Index
if err := json.Unmarshal(buf, &index); err != nil {
cveinfo.Log.Error().Err(err).Str("dir", imagePath).Msg("invalid JSON")
return nil, errors.ErrRepoNotFound
}
return index.Manifests, nil
}
func (cveinfo CveInfo) getImageBlobManifest(imageDir string, digest godigest.Digest) (v1.Manifest, error) {
var blobIndex v1.Manifest
blobBuf, err := ioutil.ReadFile(path.Join(imageDir, "blobs", digest.Algorithm().String(), digest.Encoded()))
if err != nil {
cveinfo.Log.Error().Err(err).Msg("Unable to open image Metadata file")
return blobIndex, err
}
if err := json.Unmarshal(blobBuf, &blobIndex); err != nil {
cveinfo.Log.Error().Err(err).Msg("Unable to marshal blob index")
return blobIndex, err
}
return blobIndex, nil
}
func (cveinfo CveInfo) getImageInfo(imageDir string, hash v1.Hash) (ispec.Image, error) {
var imageInfo ispec.Image
blobBuf, err := ioutil.ReadFile(path.Join(imageDir, "blobs", hash.Algorithm, hash.Hex))
if err != nil {
cveinfo.Log.Error().Err(err).Msg("Unable to open image Layers file")
return imageInfo, err
}
if err := json.Unmarshal(blobBuf, &imageInfo); err != nil {
cveinfo.Log.Error().Err(err).Msg("Unable to marshal blob index")
return imageInfo, err
}
return imageInfo, err
}