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