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