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