github.com/anchore/syft@v1.38.2/syft/format/decoders_collection.go (about) 1 package format 2 3 import ( 4 "fmt" 5 "io" 6 7 "github.com/anchore/syft/internal/log" 8 "github.com/anchore/syft/syft/format/internal/stream" 9 "github.com/anchore/syft/syft/sbom" 10 ) 11 12 var _ sbom.FormatDecoder = (*DecoderCollection)(nil) 13 14 type DecoderCollection struct { 15 decoders []sbom.FormatDecoder 16 } 17 18 func NewDecoderCollection(decoders ...sbom.FormatDecoder) sbom.FormatDecoder { 19 return &DecoderCollection{ 20 decoders: decoders, 21 } 22 } 23 24 // Decode takes a set of bytes and attempts to decode it into an SBOM relative to the decoders in the collection. 25 func (c *DecoderCollection) Decode(r io.Reader) (*sbom.SBOM, sbom.FormatID, string, error) { 26 if r == nil { 27 return nil, "", "", fmt.Errorf("no SBOM bytes provided") 28 } 29 30 reader, err := stream.SeekableReader(r) 31 if err != nil { 32 return nil, "", "", fmt.Errorf("unable to create a seekable reader: %w", err) 33 } 34 35 var bestID sbom.FormatID 36 for _, d := range c.decoders { 37 _, err = reader.Seek(0, io.SeekStart) 38 if err != nil { 39 return nil, "", "", fmt.Errorf("unable to seek to start of SBOM: %w", err) 40 } 41 id, version := d.Identify(reader) 42 if id == "" || version == "" { 43 if id != "" { 44 bestID = id 45 } 46 continue 47 } 48 49 _, err = reader.Seek(0, io.SeekStart) 50 if err != nil { 51 return nil, "", "", fmt.Errorf("unable to seek to start of SBOM: %w", err) 52 } 53 return d.Decode(reader) 54 } 55 56 if bestID != "" { 57 return nil, bestID, "", fmt.Errorf("sbom format found to be %q but the version is not supported", bestID) 58 } 59 60 return nil, "", "", fmt.Errorf("sbom format not recognized") 61 } 62 63 // Identify takes a set of bytes and attempts to identify the format of the SBOM relative to the decoders in the collection. 64 func (c *DecoderCollection) Identify(r io.Reader) (sbom.FormatID, string) { 65 if r == nil { 66 return "", "" 67 } 68 69 reader, err := stream.SeekableReader(r) 70 if err != nil { 71 log.Debugf("unable to create a seekable reader: %v", err) 72 return "", "" 73 } 74 75 for _, d := range c.decoders { 76 _, err = reader.Seek(0, io.SeekStart) 77 if err != nil { 78 log.Debugf("unable to seek to start of SBOM: %v", err) 79 } 80 id, version := d.Identify(reader) 81 if id != "" && version != "" { 82 return id, version 83 } 84 } 85 return "", "" 86 }