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 }