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  }