github.com/devseccon/trivy@v0.47.1-0.20231123133102-bd902a0bd996/pkg/fanal/artifact/vm/file.go (about) 1 package vm 2 3 import ( 4 "context" 5 "crypto/sha256" 6 "encoding/json" 7 "errors" 8 "io" 9 "os" 10 11 lru "github.com/hashicorp/golang-lru/v2" 12 "github.com/opencontainers/go-digest" 13 "golang.org/x/xerrors" 14 15 "github.com/devseccon/trivy/pkg/fanal/cache" 16 "github.com/devseccon/trivy/pkg/fanal/types" 17 "github.com/devseccon/trivy/pkg/fanal/vm" 18 "github.com/devseccon/trivy/pkg/fanal/vm/disk" 19 "github.com/devseccon/trivy/pkg/log" 20 ) 21 22 // default vmdk block size 64 KB 23 // If vm type vmdk max cache memory size 64 MB 24 const storageFILECacheSize = 1024 25 26 // ImageFile represents an local VM image file 27 type ImageFile struct { 28 Storage 29 30 filePath string 31 file *os.File 32 reader *io.SectionReader 33 } 34 35 func newFile(filePath string, storage Storage) (*ImageFile, error) { 36 f, err := os.Open(filePath) 37 if err != nil { 38 return nil, xerrors.Errorf("file open error: %w", err) 39 } 40 41 c, err := lru.New[string, []byte](storageFILECacheSize) 42 if err != nil { 43 return nil, xerrors.Errorf("failed to create new lru cache: %w", err) 44 } 45 46 reader, err := disk.New(f, c) 47 if err != nil { 48 if errors.Is(err, vm.ErrUnsupportedType) { 49 return nil, err 50 } 51 52 log.Logger.Debugf("VM image not detected: %s", err) 53 log.Logger.Debugf("Assume raw image") 54 fi, err := f.Stat() 55 if err != nil { 56 return nil, xerrors.Errorf("file stat error: %w", err) 57 } 58 reader = io.NewSectionReader(f, 0, fi.Size()) 59 } 60 61 return &ImageFile{ 62 Storage: storage, 63 64 filePath: filePath, 65 file: f, 66 reader: reader, 67 }, nil 68 } 69 70 func (a *ImageFile) Inspect(ctx context.Context) (types.ArtifactReference, error) { 71 blobInfo, err := a.Analyze(ctx, a.reader) 72 if err != nil { 73 return types.ArtifactReference{}, xerrors.Errorf("inspection error: %w", err) 74 } 75 76 cacheKey, err := a.calcCacheKey(blobInfo) 77 if err != nil { 78 return types.ArtifactReference{}, xerrors.Errorf("cache calculation error: %w", err) 79 } 80 81 if err = a.cache.PutBlob(cacheKey, blobInfo); err != nil { 82 return types.ArtifactReference{}, xerrors.Errorf("failed to store blob (%s) in cache: %w", cacheKey, err) 83 } 84 85 return types.ArtifactReference{ 86 Name: a.filePath, 87 Type: types.ArtifactVM, 88 ID: cacheKey, // use a cache key as pseudo artifact ID 89 BlobIDs: []string{cacheKey}, 90 }, nil 91 } 92 93 func (a *ImageFile) calcCacheKey(blobInfo types.BlobInfo) (string, error) { 94 // calculate hash of JSON and use it as pseudo artifactID and blobID 95 h := sha256.New() 96 if err := json.NewEncoder(h).Encode(blobInfo); err != nil { 97 return "", xerrors.Errorf("json error: %w", err) 98 } 99 100 d := digest.NewDigest(digest.SHA256, h) 101 cacheKey, err := cache.CalcKey(d.String(), a.analyzer.AnalyzerVersions(), a.handlerManager.Versions(), a.artifactOption) 102 if err != nil { 103 return "", xerrors.Errorf("cache key: %w", err) 104 } 105 106 return cacheKey, nil 107 } 108 109 func (a *ImageFile) Clean(reference types.ArtifactReference) error { 110 _ = a.file.Close() 111 return a.cache.DeleteBlobs(reference.BlobIDs) 112 }