2020-06-24 14:38:42 -05:00
|
|
|
package cveinfo
|
|
|
|
|
|
|
|
import (
|
2020-08-19 01:03:16 -05:00
|
|
|
"encoding/json"
|
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
|
|
|
"path"
|
2020-08-19 01:53:04 -05:00
|
|
|
"sort"
|
2020-08-19 01:03:16 -05:00
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/anuvu/zot/errors"
|
2020-06-24 14:38:42 -05:00
|
|
|
"github.com/anuvu/zot/pkg/log"
|
|
|
|
integration "github.com/aquasecurity/trivy/integration"
|
|
|
|
config "github.com/aquasecurity/trivy/integration/config"
|
2020-06-26 14:09:10 -05:00
|
|
|
"github.com/aquasecurity/trivy/pkg/report"
|
2020-08-19 01:03:16 -05:00
|
|
|
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"
|
2020-06-24 14:38:42 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
// UpdateCVEDb ...
|
|
|
|
func UpdateCVEDb(dbDir string, log log.Logger) error {
|
|
|
|
config, err := config.NewConfig(dbDir)
|
|
|
|
if err != nil {
|
2020-09-22 12:35:39 -05:00
|
|
|
log.Error().Err(err).Msg("unable to get config")
|
2020-06-24 14:38:42 -05:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = integration.RunTrivyDb(config.TrivyConfig)
|
|
|
|
if err != nil {
|
2020-09-22 12:35:39 -05:00
|
|
|
log.Error().Err(err).Msg("unable to update DB ")
|
2020-06-24 14:38:42 -05:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2020-06-26 14:09:10 -05:00
|
|
|
|
|
|
|
func NewTrivyConfig(dir string) (*config.Config, error) {
|
|
|
|
return config.NewConfig(dir)
|
|
|
|
}
|
|
|
|
|
|
|
|
func ScanImage(config *config.Config) (report.Results, error) {
|
|
|
|
return integration.ScanTrivyImage(config.TrivyConfig)
|
|
|
|
}
|
2020-08-19 01:03:16 -05:00
|
|
|
|
|
|
|
func (cveinfo CveInfo) IsValidImageFormat(imagePath string) (bool, error) {
|
2020-09-04 15:16:15 -05:00
|
|
|
imageDir, inputTag := getImageDirAndTag(imagePath)
|
2020-08-19 01:03:16 -05:00
|
|
|
|
|
|
|
if !dirExists(imageDir) {
|
2020-09-22 12:35:39 -05:00
|
|
|
cveinfo.Log.Error().Msg("image directory doesn't exist")
|
2020-08-19 01:03:16 -05:00
|
|
|
|
|
|
|
return false, errors.ErrRepoNotFound
|
|
|
|
}
|
|
|
|
|
2020-09-04 15:16:15 -05:00
|
|
|
manifests, err := cveinfo.getImageManifests(imageDir)
|
2020-08-19 01:03:16 -05:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
2020-09-04 15:16:15 -05:00
|
|
|
for _, m := range manifests {
|
|
|
|
tag, ok := m.Annotations[ispec.AnnotationRefName]
|
2020-08-19 01:03:16 -05:00
|
|
|
|
2020-09-04 15:16:15 -05:00
|
|
|
if ok && inputTag != "" && tag != inputTag {
|
|
|
|
continue
|
2020-08-19 01:03:16 -05:00
|
|
|
}
|
|
|
|
|
2020-09-04 15:16:15 -05:00
|
|
|
blobManifest, err := cveinfo.getImageBlobManifest(imageDir, m.Digest)
|
|
|
|
if err != nil {
|
2020-08-19 01:03:16 -05:00
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
|
|
|
imageLayers := blobManifest.Layers
|
|
|
|
|
|
|
|
for _, imageLayer := range imageLayers {
|
|
|
|
switch imageLayer.MediaType {
|
|
|
|
case types.OCILayer, types.DockerLayer:
|
|
|
|
return true, nil
|
|
|
|
|
|
|
|
default:
|
2020-09-22 12:35:39 -05:00
|
|
|
cveinfo.Log.Debug().Msg("image media type not supported for scanning")
|
2020-09-04 15:16:15 -05:00
|
|
|
return false, errors.ErrScanNotSupported
|
2020-08-19 01:03:16 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func dirExists(d string) bool {
|
|
|
|
fi, err := os.Stat(d)
|
|
|
|
if err != nil && os.IsNotExist(err) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
return fi.IsDir()
|
|
|
|
}
|
|
|
|
|
2020-09-04 15:16:15 -05:00
|
|
|
func getImageDirAndTag(imageName string) (string, string) {
|
2020-08-19 01:03:16 -05:00
|
|
|
var imageDir string
|
2020-09-04 15:16:15 -05:00
|
|
|
|
|
|
|
var imageTag string
|
|
|
|
|
2020-08-19 01:03:16 -05:00
|
|
|
if strings.Contains(imageName, ":") {
|
2020-09-04 15:16:15 -05:00
|
|
|
splitImageName := strings.Split(imageName, ":")
|
|
|
|
imageDir = splitImageName[0]
|
|
|
|
imageTag = splitImageName[1]
|
2020-08-19 01:03:16 -05:00
|
|
|
} else {
|
|
|
|
imageDir = imageName
|
|
|
|
}
|
|
|
|
|
2020-09-04 15:16:15 -05:00
|
|
|
return imageDir, imageTag
|
2020-08-19 01:03:16 -05:00
|
|
|
}
|
2020-08-19 01:53:04 -05:00
|
|
|
|
|
|
|
// GetImageTagsWithTimestamp returns a list of image tags with timestamp available in the specified repository.
|
|
|
|
func (cveinfo CveInfo) GetImageTagsWithTimestamp(rootDir string, repo string) ([]TagInfo, error) {
|
2020-09-04 15:16:15 -05:00
|
|
|
tagsInfo := make([]TagInfo, 0)
|
|
|
|
|
2020-08-19 01:53:04 -05:00
|
|
|
dir := path.Join(rootDir, repo)
|
|
|
|
if !dirExists(dir) {
|
|
|
|
return nil, errors.ErrRepoNotFound
|
|
|
|
}
|
|
|
|
|
2020-09-04 15:16:15 -05:00
|
|
|
manifests, err := cveinfo.getImageManifests(dir)
|
2020-08-19 01:53:04 -05:00
|
|
|
|
|
|
|
if err != nil {
|
2020-09-22 12:35:39 -05:00
|
|
|
cveinfo.Log.Error().Err(err).Msg("unable to read image manifests")
|
2020-08-19 01:53:04 -05:00
|
|
|
|
2020-09-04 15:16:15 -05:00
|
|
|
return tagsInfo, err
|
2020-08-19 01:53:04 -05:00
|
|
|
}
|
|
|
|
|
2020-09-04 15:16:15 -05:00
|
|
|
for _, manifest := range manifests {
|
|
|
|
digest := manifest.Digest
|
2020-08-19 01:53:04 -05:00
|
|
|
|
|
|
|
v, ok := manifest.Annotations[ispec.AnnotationRefName]
|
2020-09-04 15:16:15 -05:00
|
|
|
if ok {
|
|
|
|
imageBlobManifest, err := cveinfo.getImageBlobManifest(dir, digest)
|
2020-08-19 01:53:04 -05:00
|
|
|
|
2020-09-04 15:16:15 -05:00
|
|
|
if err != nil {
|
2020-09-22 12:35:39 -05:00
|
|
|
cveinfo.Log.Error().Err(err).Msg("unable to read image blob manifest")
|
2020-08-19 01:53:04 -05:00
|
|
|
|
2020-09-04 15:16:15 -05:00
|
|
|
return tagsInfo, err
|
|
|
|
}
|
2020-08-19 01:53:04 -05:00
|
|
|
|
2020-09-04 15:16:15 -05:00
|
|
|
imageInfo, err := cveinfo.getImageInfo(dir, imageBlobManifest.Config.Digest)
|
|
|
|
if err != nil {
|
2020-09-22 12:35:39 -05:00
|
|
|
cveinfo.Log.Error().Err(err).Msg("unable to read image info")
|
2020-08-19 01:53:04 -05:00
|
|
|
|
2020-09-04 15:16:15 -05:00
|
|
|
return tagsInfo, err
|
|
|
|
}
|
2020-08-19 01:53:04 -05:00
|
|
|
|
2020-09-04 15:16:15 -05:00
|
|
|
timeStamp := *imageInfo.History[0].Created
|
2020-08-19 01:53:04 -05:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|
2020-09-04 15:16:15 -05:00
|
|
|
|
|
|
|
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) {
|
2020-09-22 12:35:39 -05:00
|
|
|
cveinfo.Log.Error().Err(err).Msg("index.json doesn't exist")
|
2020-09-04 15:16:15 -05:00
|
|
|
|
|
|
|
return nil, errors.ErrRepoNotFound
|
|
|
|
}
|
|
|
|
|
2020-09-22 12:35:39 -05:00
|
|
|
cveinfo.Log.Error().Err(err).Msg("unable to open index.json")
|
2020-09-04 15:16:15 -05:00
|
|
|
|
|
|
|
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 {
|
2020-09-22 12:35:39 -05:00
|
|
|
cveinfo.Log.Error().Err(err).Msg("unable to open image metadata file")
|
2020-09-04 15:16:15 -05:00
|
|
|
|
|
|
|
return blobIndex, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := json.Unmarshal(blobBuf, &blobIndex); err != nil {
|
2020-09-22 12:35:39 -05:00
|
|
|
cveinfo.Log.Error().Err(err).Msg("unable to marshal blob index")
|
2020-09-04 15:16:15 -05:00
|
|
|
|
|
|
|
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 {
|
2020-09-22 12:35:39 -05:00
|
|
|
cveinfo.Log.Error().Err(err).Msg("unable to open image layers file")
|
2020-09-04 15:16:15 -05:00
|
|
|
|
|
|
|
return imageInfo, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := json.Unmarshal(blobBuf, &imageInfo); err != nil {
|
2020-09-22 12:35:39 -05:00
|
|
|
cveinfo.Log.Error().Err(err).Msg("unable to marshal blob index")
|
2020-09-04 15:16:15 -05:00
|
|
|
|
|
|
|
return imageInfo, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return imageInfo, err
|
|
|
|
}
|