github.com/anchore/syft@v1.4.2-0.20240516191711-1bec1fc5d397/syft/pkg/cataloger/sbom/cataloger.go (about) 1 /* 2 Package sbom provides a concrete Cataloger implementation for capturing packages embedded within SBOM files. 3 */ 4 package sbom 5 6 import ( 7 "bytes" 8 "context" 9 "fmt" 10 "io" 11 12 "github.com/anchore/syft/internal/log" 13 "github.com/anchore/syft/syft/artifact" 14 "github.com/anchore/syft/syft/file" 15 "github.com/anchore/syft/syft/format" 16 "github.com/anchore/syft/syft/pkg" 17 "github.com/anchore/syft/syft/pkg/cataloger/generic" 18 ) 19 20 const catalogerName = "sbom-cataloger" 21 22 // NewCataloger returns a new SBOM cataloger object loaded from saved SBOM JSON. 23 func NewCataloger() pkg.Cataloger { 24 return generic.NewCataloger(catalogerName). 25 WithParserByGlobs(parseSBOM, 26 "**/*.syft.json", 27 "**/*.bom.*", 28 "**/*.bom", 29 "**/bom", 30 "**/*.sbom.*", 31 "**/*.sbom", 32 "**/sbom", 33 "**/*.cdx.*", 34 "**/*.cdx", 35 "**/*.spdx.*", 36 "**/*.spdx", 37 ) 38 } 39 40 func parseSBOM(_ context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { 41 readSeeker, err := adaptToReadSeeker(reader) 42 if err != nil { 43 return nil, nil, fmt.Errorf("unable to read SBOM file %q: %w", reader.Location.RealPath, err) 44 } 45 s, _, _, err := format.Decode(readSeeker) 46 if err != nil { 47 return nil, nil, err 48 } 49 50 if s == nil { 51 log.WithFields("path", reader.Location.RealPath).Trace("file is not an SBOM") 52 return nil, nil, nil 53 } 54 55 var pkgs []pkg.Package 56 relationships := s.Relationships 57 for _, p := range s.Artifacts.Packages.Sorted() { 58 // replace all locations on the package with the location of the SBOM file. 59 // Why not keep the original list of locations? Since the "locations" field is meant to capture 60 // where there is evidence of this file, and the catalogers have not run against any file other than, 61 // the SBOM, this is the only location that is relevant for this cataloger. 62 p.Locations = file.NewLocationSet( 63 reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), 64 ) 65 p.FoundBy = catalogerName 66 67 pkgs = append(pkgs, p) 68 relationships = append(relationships, artifact.Relationship{ 69 From: p, 70 To: reader.Location.Coordinates, 71 Type: artifact.DescribedByRelationship, 72 }) 73 } 74 75 return pkgs, relationships, nil 76 } 77 78 func adaptToReadSeeker(reader io.Reader) (io.ReadSeeker, error) { 79 // with the stereoscope API and default file.Resolver implementation here in syft, odds are very high that 80 // the underlying reader is already a ReadSeeker, so we can just return it as-is. We still want to 81 if rs, ok := reader.(io.ReadSeeker); ok { 82 return rs, nil 83 } 84 85 log.Debug("SBOM cataloger reader is not a ReadSeeker, reading entire SBOM into memory") 86 87 var buff bytes.Buffer 88 _, err := io.Copy(&buff, reader) 89 return bytes.NewReader(buff.Bytes()), err 90 }