github.com/noqcks/syft@v0.0.0-20230920222752-a9e2c4e288e5/syft/formats/common/cyclonedxhelpers/component.go (about)

     1  package cyclonedxhelpers
     2  
     3  import (
     4  	"reflect"
     5  
     6  	"github.com/CycloneDX/cyclonedx-go"
     7  
     8  	"github.com/anchore/packageurl-go"
     9  	"github.com/anchore/syft/syft/file"
    10  	"github.com/anchore/syft/syft/formats/common"
    11  	"github.com/anchore/syft/syft/pkg"
    12  )
    13  
    14  func encodeComponent(p pkg.Package) cyclonedx.Component {
    15  	props := encodeProperties(p, "syft:package")
    16  	props = append(props, encodeCPEs(p)...)
    17  	locations := p.Locations.ToSlice()
    18  	if len(locations) > 0 {
    19  		props = append(props, encodeProperties(locations, "syft:location")...)
    20  	}
    21  	if hasMetadata(p) {
    22  		props = append(props, encodeProperties(p.Metadata, "syft:metadata")...)
    23  	}
    24  
    25  	var properties *[]cyclonedx.Property
    26  	if len(props) > 0 {
    27  		properties = &props
    28  	}
    29  
    30  	componentType := cyclonedx.ComponentTypeLibrary
    31  	if p.Type == pkg.BinaryPkg {
    32  		componentType = cyclonedx.ComponentTypeApplication
    33  	}
    34  
    35  	return cyclonedx.Component{
    36  		Type:               componentType,
    37  		Name:               p.Name,
    38  		Group:              encodeGroup(p),
    39  		Version:            p.Version,
    40  		PackageURL:         p.PURL,
    41  		Licenses:           encodeLicenses(p),
    42  		CPE:                encodeSingleCPE(p),
    43  		Author:             encodeAuthor(p),
    44  		Publisher:          encodePublisher(p),
    45  		Description:        encodeDescription(p),
    46  		ExternalReferences: encodeExternalReferences(p),
    47  		Properties:         properties,
    48  		BOMRef:             deriveBomRef(p),
    49  	}
    50  }
    51  
    52  func deriveBomRef(p pkg.Package) string {
    53  	// try and parse the PURL if possible and append syft id to it, to make
    54  	// the purl unique in the BOM.
    55  	// TODO: In the future we may want to dedupe by PURL and combine components with
    56  	// the same PURL while preserving their unique metadata.
    57  	if parsedPURL, err := packageurl.FromString(p.PURL); err == nil {
    58  		parsedPURL.Qualifiers = append(parsedPURL.Qualifiers, packageurl.Qualifier{Key: "package-id", Value: string(p.ID())})
    59  		return parsedPURL.ToString()
    60  	}
    61  	// fallback is to use strictly the ID if there is no valid pURL
    62  	return string(p.ID())
    63  }
    64  
    65  func hasMetadata(p pkg.Package) bool {
    66  	return p.Metadata != nil
    67  }
    68  
    69  func decodeComponent(c *cyclonedx.Component) *pkg.Package {
    70  	values := map[string]string{}
    71  	if c.Properties != nil {
    72  		for _, p := range *c.Properties {
    73  			values[p.Name] = p.Value
    74  		}
    75  	}
    76  
    77  	p := &pkg.Package{
    78  		Name:      c.Name,
    79  		Version:   c.Version,
    80  		Locations: decodeLocations(values),
    81  		Licenses:  pkg.NewLicenseSet(decodeLicenses(c)...),
    82  		CPEs:      decodeCPEs(c),
    83  		PURL:      c.PackageURL,
    84  	}
    85  
    86  	common.DecodeInto(p, values, "syft:package", CycloneDXFields)
    87  
    88  	p.MetadataType = pkg.CleanMetadataType(p.MetadataType)
    89  
    90  	p.Metadata = decodePackageMetadata(values, c, p.MetadataType)
    91  
    92  	if p.Type == "" {
    93  		p.Type = pkg.TypeFromPURL(p.PURL)
    94  	}
    95  
    96  	if p.Language == "" {
    97  		p.Language = pkg.LanguageFromPURL(p.PURL)
    98  	}
    99  
   100  	return p
   101  }
   102  
   103  func decodeLocations(vals map[string]string) file.LocationSet {
   104  	v := common.Decode(reflect.TypeOf([]file.Location{}), vals, "syft:location", CycloneDXFields)
   105  	out, ok := v.([]file.Location)
   106  	if !ok {
   107  		out = nil
   108  	}
   109  	return file.NewLocationSet(out...)
   110  }
   111  
   112  func decodePackageMetadata(vals map[string]string, c *cyclonedx.Component, typ pkg.MetadataType) interface{} {
   113  	if typ != "" && c.Properties != nil {
   114  		metaTyp, ok := pkg.MetadataTypeByName[typ]
   115  		if !ok {
   116  			return nil
   117  		}
   118  		metaPtrTyp := reflect.PtrTo(metaTyp)
   119  		metaPtr := common.Decode(metaPtrTyp, vals, "syft:metadata", CycloneDXFields)
   120  
   121  		// Map all explicit metadata properties
   122  		decodeAuthor(c.Author, metaPtr)
   123  		decodeGroup(c.Group, metaPtr)
   124  		decodePublisher(c.Publisher, metaPtr)
   125  		decodeDescription(c.Description, metaPtr)
   126  		decodeExternalReferences(c, metaPtr)
   127  
   128  		// return the actual interface{} -> struct ... not interface{} -> *struct
   129  		return common.PtrToStruct(metaPtr)
   130  	}
   131  
   132  	return nil
   133  }