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

     1  package unpackaged
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"errors"
     7  
     8  	"golang.org/x/exp/slices"
     9  	"golang.org/x/xerrors"
    10  
    11  	sbomatt "github.com/devseccon/trivy/pkg/attestation/sbom"
    12  	"github.com/devseccon/trivy/pkg/fanal/analyzer"
    13  	"github.com/devseccon/trivy/pkg/fanal/artifact"
    14  	"github.com/devseccon/trivy/pkg/fanal/handler"
    15  	"github.com/devseccon/trivy/pkg/fanal/types"
    16  	"github.com/devseccon/trivy/pkg/log"
    17  	"github.com/devseccon/trivy/pkg/sbom"
    18  )
    19  
    20  func init() {
    21  	handler.RegisterPostHandlerInit(types.UnpackagedPostHandler, NewUnpackagedHandler)
    22  }
    23  
    24  const version = 1
    25  
    26  type unpackagedHook struct {
    27  	client sbomatt.Rekor
    28  }
    29  
    30  func NewUnpackagedHandler(opt artifact.Option) (handler.PostHandler, error) {
    31  	c, err := sbomatt.NewRekor(opt.RekorURL)
    32  	if err != nil {
    33  		return nil, xerrors.Errorf("rekor client error: %w", err)
    34  	}
    35  	return unpackagedHook{
    36  		client: c,
    37  	}, nil
    38  }
    39  
    40  // Handle retrieves SBOM of unpackaged executable files in Rekor.
    41  func (h unpackagedHook) Handle(ctx context.Context, res *analyzer.AnalysisResult, blob *types.BlobInfo) error {
    42  	for filePath, digest := range res.Digests {
    43  		// Skip files installed by OS package managers.
    44  		if slices.Contains(res.SystemInstalledFiles, filePath) {
    45  			continue
    46  		}
    47  
    48  		// Retrieve SBOM from Rekor according to the file digest.
    49  		raw, err := h.client.RetrieveSBOM(ctx, digest)
    50  		if errors.Is(err, sbomatt.ErrNoSBOMAttestation) {
    51  			continue
    52  		} else if err != nil {
    53  			return err
    54  		}
    55  
    56  		r := bytes.NewReader(raw)
    57  
    58  		// Detect the SBOM format like CycloneDX, SPDX, etc.
    59  		format, err := sbom.DetectFormat(r)
    60  		if err != nil {
    61  			return err
    62  		}
    63  
    64  		// Parse the fetched SBOM
    65  		bom, err := sbom.Decode(bytes.NewReader(raw), format)
    66  		if err != nil {
    67  			return err
    68  		}
    69  
    70  		if len(bom.Applications) > 0 {
    71  			log.Logger.Infof("Found SBOM attestation in Rekor: %s", filePath)
    72  			// Take the first app since this SBOM should contain a single application.
    73  			app := bom.Applications[0]
    74  			app.FilePath = filePath // Use the original file path rather than the one in the SBOM.
    75  			blob.Applications = append(blob.Applications, app)
    76  		}
    77  	}
    78  
    79  	return nil
    80  }
    81  
    82  func (h unpackagedHook) Version() int {
    83  	return version
    84  }
    85  
    86  func (h unpackagedHook) Type() types.HandlerType {
    87  	return types.UnpackagedPostHandler
    88  }
    89  
    90  func (h unpackagedHook) Priority() int {
    91  	return types.UnpackagedPostHandlerPriority
    92  }