github.com/lineaje-labs/syft@v0.98.1-0.20231227153149-9e393f60ff1b/syft/format/cyclonedxxml/decoder.go (about)

     1  package cyclonedxxml
     2  
     3  import (
     4  	"encoding/xml"
     5  	"fmt"
     6  	"io"
     7  	"strings"
     8  
     9  	"github.com/CycloneDX/cyclonedx-go"
    10  
    11  	"github.com/anchore/syft/syft/format/common/cyclonedxhelpers"
    12  	"github.com/anchore/syft/syft/sbom"
    13  	"github.com/lineaje-labs/syft/internal/log"
    14  	"github.com/lineaje-labs/syft/syft/format/internal/cyclonedxutil"
    15  )
    16  
    17  var _ sbom.FormatDecoder = (*decoder)(nil)
    18  
    19  type decoder struct {
    20  	decoder cyclonedxutil.Decoder
    21  }
    22  
    23  func NewFormatDecoder() sbom.FormatDecoder {
    24  	return decoder{
    25  		decoder: cyclonedxutil.NewDecoder(cyclonedx.BOMFileFormatXML),
    26  	}
    27  }
    28  
    29  func (d decoder) Decode(reader io.ReadSeeker) (*sbom.SBOM, sbom.FormatID, string, error) {
    30  	if reader == nil {
    31  		return nil, "", "", fmt.Errorf("no SBOM bytes provided")
    32  	}
    33  
    34  	id, version := d.Identify(reader)
    35  	if id != ID {
    36  		return nil, "", "", fmt.Errorf("not a cyclonedx xml document")
    37  	}
    38  	if version == "" {
    39  		return nil, "", "", fmt.Errorf("unsupported cyclonedx xml document version")
    40  	}
    41  
    42  	doc, err := d.decoder.Decode(reader)
    43  	if err != nil {
    44  		return nil, id, version, fmt.Errorf("unable to decode cyclonedx xml document: %w", err)
    45  	}
    46  
    47  	s, err := cyclonedxhelpers.ToSyftModel(doc)
    48  	if err != nil {
    49  		return nil, id, version, err
    50  	}
    51  
    52  	return s, id, version, nil
    53  }
    54  
    55  func (d decoder) Identify(reader io.ReadSeeker) (sbom.FormatID, string) {
    56  	if reader == nil {
    57  		return "", ""
    58  	}
    59  
    60  	if _, err := reader.Seek(0, io.SeekStart); err != nil {
    61  		log.Debugf("unable to seek to start of CycloneDX XML SBOM: %+v", err)
    62  		return "", ""
    63  	}
    64  
    65  	type Document struct {
    66  		XMLNS string `xml:"xmlns,attr"`
    67  	}
    68  
    69  	dec := xml.NewDecoder(reader)
    70  
    71  	var doc Document
    72  	err := dec.Decode(&doc)
    73  	if err != nil {
    74  		// maybe not xml? maybe not valid? doesn't matter, we won't process it.
    75  		return "", ""
    76  	}
    77  
    78  	id, version := getFormatInfo(doc.XMLNS)
    79  	if version == "" || id != ID {
    80  		// not a cyclonedx xml document that we support
    81  		return "", ""
    82  	}
    83  
    84  	return id, version
    85  }
    86  
    87  func getFormatInfo(xmlns string) (sbom.FormatID, string) {
    88  	version := getVersionFromXMLNS(xmlns)
    89  
    90  	if !strings.Contains(xmlns, "cyclonedx.org/schema/bom") {
    91  		// not a cyclonedx xml document
    92  		return "", ""
    93  	}
    94  
    95  	spec, err := cyclonedxutil.SpecVersionFromString(version)
    96  	if spec < 0 || err != nil {
    97  		// not a supported version, but is cyclonedx xml
    98  		return ID, ""
    99  	}
   100  	return ID, version
   101  }
   102  
   103  func getVersionFromXMLNS(xmlns string) string {
   104  	fields := strings.Split(xmlns, "/")
   105  	return fields[len(fields)-1]
   106  }