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

     1  package sbom
     2  
     3  import (
     4  	"context"
     5  	"crypto/sha256"
     6  	"encoding/json"
     7  	"os"
     8  	"path/filepath"
     9  
    10  	"github.com/opencontainers/go-digest"
    11  	"golang.org/x/xerrors"
    12  
    13  	"github.com/devseccon/trivy/pkg/fanal/analyzer"
    14  	"github.com/devseccon/trivy/pkg/fanal/artifact"
    15  	"github.com/devseccon/trivy/pkg/fanal/cache"
    16  	"github.com/devseccon/trivy/pkg/fanal/handler"
    17  	"github.com/devseccon/trivy/pkg/fanal/types"
    18  	"github.com/devseccon/trivy/pkg/log"
    19  	"github.com/devseccon/trivy/pkg/sbom"
    20  )
    21  
    22  type Artifact struct {
    23  	filePath       string
    24  	cache          cache.ArtifactCache
    25  	analyzer       analyzer.AnalyzerGroup
    26  	handlerManager handler.Manager
    27  
    28  	artifactOption artifact.Option
    29  }
    30  
    31  func NewArtifact(filePath string, c cache.ArtifactCache, opt artifact.Option) (artifact.Artifact, error) {
    32  	return Artifact{
    33  		filePath:       filepath.Clean(filePath),
    34  		cache:          c,
    35  		artifactOption: opt,
    36  	}, nil
    37  }
    38  
    39  func (a Artifact) Inspect(_ context.Context) (types.ArtifactReference, error) {
    40  	f, err := os.Open(a.filePath)
    41  	if err != nil {
    42  		return types.ArtifactReference{}, xerrors.Errorf("failed to open sbom file error: %w", err)
    43  	}
    44  	defer f.Close()
    45  
    46  	// Format auto-detection
    47  	format, err := sbom.DetectFormat(f)
    48  	if err != nil {
    49  		return types.ArtifactReference{}, xerrors.Errorf("failed to detect SBOM format: %w", err)
    50  	}
    51  	log.Logger.Infof("Detected SBOM format: %s", format)
    52  
    53  	bom, err := sbom.Decode(f, format)
    54  	if err != nil {
    55  		return types.ArtifactReference{}, xerrors.Errorf("SBOM decode error: %w", err)
    56  	}
    57  
    58  	blobInfo := types.BlobInfo{
    59  		SchemaVersion: types.BlobJSONSchemaVersion,
    60  		OS:            bom.OS,
    61  		PackageInfos:  bom.Packages,
    62  		Applications:  bom.Applications,
    63  	}
    64  
    65  	cacheKey, err := a.calcCacheKey(blobInfo)
    66  	if err != nil {
    67  		return types.ArtifactReference{}, xerrors.Errorf("failed to calculate a cache key: %w", err)
    68  	}
    69  
    70  	if err = a.cache.PutBlob(cacheKey, blobInfo); err != nil {
    71  		return types.ArtifactReference{}, xerrors.Errorf("failed to store blob (%s) in cache: %w", cacheKey, err)
    72  	}
    73  
    74  	var artifactType types.ArtifactType
    75  	switch format {
    76  	case sbom.FormatCycloneDXJSON, sbom.FormatCycloneDXXML, sbom.FormatAttestCycloneDXJSON, sbom.FormatLegacyCosignAttestCycloneDXJSON:
    77  		artifactType = types.ArtifactCycloneDX
    78  	case sbom.FormatSPDXTV, sbom.FormatSPDXJSON:
    79  		artifactType = types.ArtifactSPDX
    80  
    81  	}
    82  
    83  	return types.ArtifactReference{
    84  		Name:    a.filePath,
    85  		Type:    artifactType,
    86  		ID:      cacheKey, // use a cache key as pseudo artifact ID
    87  		BlobIDs: []string{cacheKey},
    88  
    89  		// Keep an original report
    90  		CycloneDX: bom.CycloneDX,
    91  	}, nil
    92  }
    93  
    94  func (a Artifact) Clean(reference types.ArtifactReference) error {
    95  	return a.cache.DeleteBlobs(reference.BlobIDs)
    96  }
    97  
    98  func (a Artifact) calcCacheKey(blobInfo types.BlobInfo) (string, error) {
    99  	// calculate hash of JSON and use it as pseudo artifactID and blobID
   100  	h := sha256.New()
   101  	if err := json.NewEncoder(h).Encode(blobInfo); err != nil {
   102  		return "", xerrors.Errorf("json error: %w", err)
   103  	}
   104  
   105  	d := digest.NewDigest(digest.SHA256, h)
   106  	cacheKey, err := cache.CalcKey(d.String(), a.analyzer.AnalyzerVersions(), a.handlerManager.Versions(), a.artifactOption)
   107  	if err != nil {
   108  		return "", xerrors.Errorf("cache key: %w", err)
   109  	}
   110  
   111  	return cacheKey, nil
   112  }