github.com/devseccon/trivy@v0.47.1-0.20231123133102-bd902a0bd996/pkg/fanal/artifact/vm/ebs.go (about)

     1  package vm
     2  
     3  import (
     4  	"context"
     5  	"io"
     6  
     7  	lru "github.com/hashicorp/golang-lru/v2"
     8  	ebsfile "github.com/masahiro331/go-ebs-file"
     9  	"golang.org/x/xerrors"
    10  
    11  	"github.com/devseccon/trivy/pkg/cloud/aws/config"
    12  	"github.com/devseccon/trivy/pkg/fanal/cache"
    13  	"github.com/devseccon/trivy/pkg/fanal/types"
    14  	"github.com/devseccon/trivy/pkg/log"
    15  )
    16  
    17  // default block size 512 KB
    18  // Max cache memory size 64 MB
    19  const storageEBSCacheSize = 128
    20  
    21  // EBS represents an artifact for AWS EBS snapshots
    22  type EBS struct {
    23  	Storage
    24  	snapshotID string
    25  	ebs        ebsfile.EBSAPI
    26  }
    27  
    28  func newEBS(snapshotID string, vm Storage, region, endpoint string) (*EBS, error) {
    29  	ebs, err := ebsfile.New(context.TODO(), config.MakeAWSOptions(region, endpoint)...)
    30  	if err != nil {
    31  		return nil, xerrors.Errorf("new ebsfile error: %w", err)
    32  	}
    33  
    34  	return &EBS{
    35  		Storage:    vm,
    36  		snapshotID: snapshotID,
    37  		ebs:        ebs,
    38  	}, nil
    39  }
    40  
    41  func (a *EBS) Inspect(ctx context.Context) (types.ArtifactReference, error) {
    42  	sr, err := a.openEBS(ctx)
    43  	if err != nil {
    44  		return types.ArtifactReference{}, xerrors.Errorf("EBS open error: %w", err)
    45  	}
    46  
    47  	cacheKey, err := a.calcCacheKey(a.snapshotID)
    48  	if err != nil {
    49  		return types.ArtifactReference{}, xerrors.Errorf("cache key calculation error: %w", err)
    50  	}
    51  
    52  	if a.hasCache(cacheKey) {
    53  		return types.ArtifactReference{
    54  			Name:    a.snapshotID,
    55  			Type:    types.ArtifactVM,
    56  			ID:      cacheKey, // use a cache key as pseudo artifact ID
    57  			BlobIDs: []string{cacheKey},
    58  		}, nil
    59  	}
    60  
    61  	blobInfo, err := a.Analyze(ctx, sr)
    62  	if err != nil {
    63  		return types.ArtifactReference{}, xerrors.Errorf("inspection error: %w", err)
    64  	}
    65  
    66  	if err = a.cache.PutBlob(cacheKey, blobInfo); err != nil {
    67  		return types.ArtifactReference{}, xerrors.Errorf("failed to store blob (%s) in cache: %w", cacheKey, err)
    68  	}
    69  
    70  	return types.ArtifactReference{
    71  		Name:    a.snapshotID,
    72  		Type:    types.ArtifactVM,
    73  		ID:      cacheKey, // use a cache key as pseudo artifact ID
    74  		BlobIDs: []string{cacheKey},
    75  	}, nil
    76  }
    77  
    78  func (a *EBS) openEBS(ctx context.Context) (*io.SectionReader, error) {
    79  	c, err := lru.New[string, []byte](storageEBSCacheSize)
    80  	if err != nil {
    81  		return nil, xerrors.Errorf("lru cache error: %w", err)
    82  	}
    83  
    84  	r, err := ebsfile.Open(a.snapshotID, ctx, c, a.ebs)
    85  	if err != nil {
    86  		return nil, xerrors.Errorf("EBS error: %w", err)
    87  	}
    88  	return r, nil
    89  }
    90  
    91  func (a *EBS) Clean(_ types.ArtifactReference) error {
    92  	return nil
    93  }
    94  
    95  func (a *EBS) SetEBS(ebs ebsfile.EBSAPI) {
    96  	a.ebs = ebs
    97  }
    98  
    99  func (a *EBS) calcCacheKey(key string) (string, error) {
   100  	s, err := cache.CalcKey(key, a.analyzer.AnalyzerVersions(), a.handlerManager.Versions(), a.artifactOption)
   101  	if err != nil {
   102  		return "", xerrors.Errorf("failed to calculate cache key: %w", err)
   103  	}
   104  	return s, nil
   105  }
   106  
   107  func (a *EBS) hasCache(cacheKey string) bool {
   108  	_, missingCacheKeys, err := a.cache.MissingBlobs(cacheKey, []string{cacheKey})
   109  	if err != nil {
   110  		log.Logger.Debugf("Unable to query missing cache: %s", err)
   111  		return false
   112  	}
   113  
   114  	// Cache exists
   115  	if len(missingCacheKeys) == 0 {
   116  		return true
   117  	}
   118  
   119  	log.Logger.Debugf("Missing virtual machine cache: %s", cacheKey)
   120  	return false
   121  }