mirror of
https://github.com/project-zot/zot.git
synced 2026-06-17 04:48:26 +08:00
feat(cve): implemented trivy image scan for multiarch images (#1510)
Signed-off-by: Laurentiu Niculae <niculae.laurentiu1@gmail.com>
This commit is contained in:
+137
-8
@@ -20,6 +20,7 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -45,6 +46,7 @@ import (
|
||||
"oras.land/oras-go/v2/registry/remote"
|
||||
"oras.land/oras-go/v2/registry/remote/auth"
|
||||
|
||||
zerr "zotregistry.io/zot/errors"
|
||||
"zotregistry.io/zot/pkg/meta/repodb"
|
||||
"zotregistry.io/zot/pkg/storage"
|
||||
"zotregistry.io/zot/pkg/test/inject"
|
||||
@@ -56,6 +58,8 @@ const (
|
||||
SleepTime = 100 * time.Millisecond
|
||||
)
|
||||
|
||||
var vulnerableLayer []byte //nolint: gochecknoglobals
|
||||
|
||||
var NotationPathLock = new(sync.Mutex) //nolint: gochecknoglobals
|
||||
|
||||
// which: manifest, config, layer
|
||||
@@ -604,15 +608,8 @@ func GetRandomImageComponents(layerSize int) (ispec.Image, [][]byte, ispec.Manif
|
||||
|
||||
configDigest := godigest.FromBytes(configBlob)
|
||||
|
||||
layer := make([]byte, layerSize)
|
||||
|
||||
_, err = rand.Read(layer)
|
||||
if err != nil {
|
||||
return ispec.Image{}, [][]byte{}, ispec.Manifest{}, err
|
||||
}
|
||||
|
||||
layers := [][]byte{
|
||||
layer,
|
||||
GetRandomLayer(layerSize),
|
||||
}
|
||||
|
||||
schemaVersion := 2
|
||||
@@ -639,6 +636,138 @@ func GetRandomImageComponents(layerSize int) (ispec.Image, [][]byte, ispec.Manif
|
||||
return config, layers, manifest, nil
|
||||
}
|
||||
|
||||
// These are the 2 vulnerabilities found for the returned image by the GetVulnImage function.
|
||||
const (
|
||||
Vulnerability1ID = "CVE-2023-2650"
|
||||
Vulnerability2ID = "CVE-2023-1255"
|
||||
)
|
||||
|
||||
func GetVulnImage(ref string) (Image, error) {
|
||||
const skipStackFrame = 2
|
||||
|
||||
vulnerableLayer, err := GetLayerWithVulnerability(skipStackFrame)
|
||||
if err != nil {
|
||||
return Image{}, err
|
||||
}
|
||||
|
||||
vulnerableConfig := ispec.Image{
|
||||
Platform: ispec.Platform{
|
||||
Architecture: "amd64",
|
||||
OS: "linux",
|
||||
},
|
||||
Config: ispec.ImageConfig{
|
||||
Env: []string{"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"},
|
||||
Cmd: []string{"/bin/sh"},
|
||||
},
|
||||
RootFS: ispec.RootFS{
|
||||
Type: "layers",
|
||||
DiffIDs: []godigest.Digest{"sha256:f1417ff83b319fbdae6dd9cd6d8c9c88002dcd75ecf6ec201c8c6894681cf2b5"},
|
||||
},
|
||||
}
|
||||
|
||||
img, err := GetImageWithComponents(
|
||||
vulnerableConfig,
|
||||
[][]byte{
|
||||
vulnerableLayer,
|
||||
})
|
||||
if err != nil {
|
||||
return Image{}, err
|
||||
}
|
||||
|
||||
img.Reference = ref
|
||||
|
||||
return img, err
|
||||
}
|
||||
|
||||
func GetVulnImageWithConfig(ref string, config ispec.Image) (Image, error) {
|
||||
const skipStackFrame = 2
|
||||
|
||||
vulnerableLayer, err := GetLayerWithVulnerability(skipStackFrame)
|
||||
if err != nil {
|
||||
return Image{}, err
|
||||
}
|
||||
|
||||
vulnerableConfig := ispec.Image{
|
||||
Platform: config.Platform,
|
||||
Config: config.Config,
|
||||
RootFS: ispec.RootFS{
|
||||
Type: "layers",
|
||||
DiffIDs: []godigest.Digest{"sha256:f1417ff83b319fbdae6dd9cd6d8c9c88002dcd75ecf6ec201c8c6894681cf2b5"},
|
||||
},
|
||||
Created: config.Created,
|
||||
History: config.History,
|
||||
}
|
||||
|
||||
img, err := GetImageWithComponents(
|
||||
vulnerableConfig,
|
||||
[][]byte{
|
||||
vulnerableLayer,
|
||||
})
|
||||
if err != nil {
|
||||
return Image{}, err
|
||||
}
|
||||
|
||||
img.Reference = ref
|
||||
|
||||
return img, err
|
||||
}
|
||||
|
||||
func GetLayerWithVulnerability(skip int) ([]byte, error) {
|
||||
if vulnerableLayer != nil {
|
||||
return vulnerableLayer, nil
|
||||
}
|
||||
|
||||
_, b, _, ok := runtime.Caller(skip)
|
||||
if !ok {
|
||||
return []byte{}, zerr.ErrCallerInfo
|
||||
}
|
||||
|
||||
absoluteCallerpath := filepath.Dir(b)
|
||||
fmt.Println(absoluteCallerpath)
|
||||
|
||||
// we know pkg folder inside zot must exist, and since all tests are called from within pkg we'll use it as reference
|
||||
relCallerPath := absoluteCallerpath[strings.LastIndex(absoluteCallerpath, "pkg"):]
|
||||
|
||||
relCallerSlice := strings.Split(relCallerPath, string(os.PathSeparator))
|
||||
fmt.Println(relCallerPath, relCallerSlice)
|
||||
|
||||
// we'll calculate how many folder we should go back to reach the root of the zot folder relative
|
||||
// to the callers position
|
||||
backPathSlice := make([]string, len(relCallerSlice))
|
||||
|
||||
for i := 0; i < len(backPathSlice); i++ {
|
||||
backPathSlice[i] = ".."
|
||||
}
|
||||
|
||||
backPath := filepath.Join(backPathSlice...)
|
||||
|
||||
// this is the path of the blob relative to the root of the zot folder
|
||||
vulnBlobPath := "test/data/alpine/blobs/sha256/f56be85fc22e46face30e2c3de3f7fe7c15f8fd7c4e5add29d7f64b87abdaa09"
|
||||
|
||||
var err error
|
||||
|
||||
x, _ := filepath.Abs(filepath.Join(backPath, vulnBlobPath))
|
||||
_ = x
|
||||
|
||||
vulnerableLayer, err = os.ReadFile(filepath.Join(backPath, vulnBlobPath)) //nolint: lll
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return vulnerableLayer, nil
|
||||
}
|
||||
|
||||
func GetRandomLayer(size int) []byte {
|
||||
layer := make([]byte, size)
|
||||
|
||||
_, err := rand.Read(layer)
|
||||
if err != nil {
|
||||
return layer
|
||||
}
|
||||
|
||||
return layer
|
||||
}
|
||||
|
||||
func GetRandomImage(reference string) (Image, error) {
|
||||
const layerSize = 20
|
||||
|
||||
|
||||
@@ -2,17 +2,18 @@ package mocks
|
||||
|
||||
import (
|
||||
"zotregistry.io/zot/pkg/common"
|
||||
cveinfo "zotregistry.io/zot/pkg/extensions/search/cve"
|
||||
cvemodel "zotregistry.io/zot/pkg/extensions/search/cve/model"
|
||||
)
|
||||
|
||||
type CveInfoMock struct {
|
||||
GetImageListForCVEFn func(repo, cveID string) ([]cvemodel.TagInfo, error)
|
||||
GetImageListWithCVEFixedFn func(repo, cveID string) ([]cvemodel.TagInfo, error)
|
||||
GetCVEListForImageFn func(repo string, reference string, searchedCVE string, pageInput cveinfo.PageInput,
|
||||
GetCVEListForImageFn func(repo string, reference string, searchedCVE string, pageInput cvemodel.PageInput,
|
||||
) ([]cvemodel.CVE, common.PageInfo, error)
|
||||
GetCVESummaryForImageFn func(repo string, reference string,
|
||||
) (cveinfo.ImageCVESummary, error)
|
||||
) (cvemodel.ImageCVESummary, error)
|
||||
GetCVESummaryForImageMediaFn func(repo string, digest, mediaType string,
|
||||
) (cvemodel.ImageCVESummary, error)
|
||||
CompareSeveritiesFn func(severity1, severity2 string) int
|
||||
UpdateDBFn func() error
|
||||
}
|
||||
@@ -34,7 +35,7 @@ func (cveInfo CveInfoMock) GetImageListWithCVEFixed(repo, cveID string) ([]cvemo
|
||||
}
|
||||
|
||||
func (cveInfo CveInfoMock) GetCVEListForImage(repo string, reference string,
|
||||
searchedCVE string, pageInput cveinfo.PageInput,
|
||||
searchedCVE string, pageInput cvemodel.PageInput,
|
||||
) (
|
||||
[]cvemodel.CVE,
|
||||
common.PageInfo,
|
||||
@@ -48,12 +49,21 @@ func (cveInfo CveInfoMock) GetCVEListForImage(repo string, reference string,
|
||||
}
|
||||
|
||||
func (cveInfo CveInfoMock) GetCVESummaryForImage(repo string, reference string,
|
||||
) (cveinfo.ImageCVESummary, error) {
|
||||
) (cvemodel.ImageCVESummary, error) {
|
||||
if cveInfo.GetCVESummaryForImageFn != nil {
|
||||
return cveInfo.GetCVESummaryForImageFn(repo, reference)
|
||||
}
|
||||
|
||||
return cveinfo.ImageCVESummary{}, nil
|
||||
return cvemodel.ImageCVESummary{}, nil
|
||||
}
|
||||
|
||||
func (cveInfo CveInfoMock) GetCVESummaryForImageMedia(repo, digest, mediaType string,
|
||||
) (cvemodel.ImageCVESummary, error) {
|
||||
if cveInfo.GetCVESummaryForImageMediaFn != nil {
|
||||
return cveInfo.GetCVESummaryForImageMediaFn(repo, digest, mediaType)
|
||||
}
|
||||
|
||||
return cvemodel.ImageCVESummary{}, nil
|
||||
}
|
||||
|
||||
func (cveInfo CveInfoMock) CompareSeverities(severity1, severity2 string) int {
|
||||
@@ -74,6 +84,7 @@ func (cveInfo CveInfoMock) UpdateDB() error {
|
||||
|
||||
type CveScannerMock struct {
|
||||
IsImageFormatScannableFn func(repo string, reference string) (bool, error)
|
||||
IsImageMediaScannableFn func(repo string, digest, mediaType string) (bool, error)
|
||||
ScanImageFn func(image string) (map[string]cvemodel.CVE, error)
|
||||
CompareSeveritiesFn func(severity1, severity2 string) int
|
||||
UpdateDBFn func() error
|
||||
@@ -87,6 +98,14 @@ func (scanner CveScannerMock) IsImageFormatScannable(repo string, reference stri
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (scanner CveScannerMock) IsImageMediaScannable(repo string, digest, mediaType string) (bool, error) {
|
||||
if scanner.IsImageMediaScannableFn != nil {
|
||||
return scanner.IsImageMediaScannableFn(repo, digest, mediaType)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (scanner CveScannerMock) ScanImage(image string) (map[string]cvemodel.CVE, error) {
|
||||
if scanner.ScanImageFn != nil {
|
||||
return scanner.ScanImageFn(image)
|
||||
|
||||
@@ -216,7 +216,7 @@ func (olu BaseOciLayoutUtils) GetImageTagsWithTimestamp(repo string) ([]cvemodel
|
||||
|
||||
tagsInfo = append(tagsInfo,
|
||||
cvemodel.TagInfo{
|
||||
Name: val,
|
||||
Tag: val,
|
||||
Timestamp: timeStamp,
|
||||
Descriptor: cvemodel.Descriptor{
|
||||
Digest: digest,
|
||||
|
||||
@@ -450,7 +450,7 @@ func TestTagsInfo(t *testing.T) {
|
||||
allTags := make([]cvemodel.TagInfo, 0)
|
||||
|
||||
firstTag := cvemodel.TagInfo{
|
||||
Name: "1.0.0",
|
||||
Tag: "1.0.0",
|
||||
Descriptor: cvemodel.Descriptor{
|
||||
Digest: "sha256:eca04f027f414362596f2632746d8a178362170b9ac9af772011fedcc3877ebb",
|
||||
MediaType: ispec.MediaTypeImageManifest,
|
||||
@@ -458,7 +458,7 @@ func TestTagsInfo(t *testing.T) {
|
||||
Timestamp: time.Now(),
|
||||
}
|
||||
secondTag := cvemodel.TagInfo{
|
||||
Name: "1.0.1",
|
||||
Tag: "1.0.1",
|
||||
Descriptor: cvemodel.Descriptor{
|
||||
Digest: "sha256:eca04f027f414362596f2632746d8a179362170b9ac9af772011fedcc3877ebb",
|
||||
MediaType: ispec.MediaTypeImageManifest,
|
||||
@@ -466,7 +466,7 @@ func TestTagsInfo(t *testing.T) {
|
||||
Timestamp: time.Now(),
|
||||
}
|
||||
thirdTag := cvemodel.TagInfo{
|
||||
Name: "1.0.2",
|
||||
Tag: "1.0.2",
|
||||
Descriptor: cvemodel.Descriptor{
|
||||
Digest: "sha256:eca04f027f414362596f2632746d8a170362170b9ac9af772011fedcc3877ebb",
|
||||
MediaType: ispec.MediaTypeImageManifest,
|
||||
@@ -474,7 +474,7 @@ func TestTagsInfo(t *testing.T) {
|
||||
Timestamp: time.Now(),
|
||||
}
|
||||
fourthTag := cvemodel.TagInfo{
|
||||
Name: "1.0.3",
|
||||
Tag: "1.0.3",
|
||||
Descriptor: cvemodel.Descriptor{
|
||||
Digest: "sha256:eca04f027f414362596f2632746d8a171362170b9ac9af772011fedcc3877ebb",
|
||||
MediaType: ispec.MediaTypeImageManifest,
|
||||
@@ -485,6 +485,6 @@ func TestTagsInfo(t *testing.T) {
|
||||
allTags = append(allTags, firstTag, secondTag, thirdTag, fourthTag)
|
||||
|
||||
latestTag := ocilayout.GetLatestTag(allTags)
|
||||
So(latestTag.Name, ShouldEqual, "1.0.3")
|
||||
So(latestTag.Tag, ShouldEqual, "1.0.3")
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user