package cyclonedxhelpers import ( "time" "github.com/CycloneDX/cyclonedx-go" "github.com/anchore/syft/internal" "github.com/anchore/syft/internal/version" "github.com/anchore/syft/syft/linux" "github.com/anchore/syft/syft/sbom" "github.com/anchore/syft/syft/source" "github.com/google/uuid" ) func ToFormatModel(s sbom.SBOM) *cyclonedx.BOM { cdxBOM := cyclonedx.NewBOM() versionInfo := version.FromBuild() // NOTE(jonasagx): cycloneDX requires URN uuids (URN returns the RFC 2141 URN form of uuid): // https://github.com/CycloneDX/specification/blob/master/schema/bom-1.3-strict.schema.json#L36 // "pattern": "^urn:uuid:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$" cdxBOM.SerialNumber = uuid.New().URN() cdxBOM.Metadata = toBomDescriptor(internal.ApplicationName, versionInfo.Version, s.Source) packages := s.Artifacts.PackageCatalog.Sorted() components := make([]cyclonedx.Component, len(packages)) for i, p := range packages { components[i] = Component(p) } components = append(components, toOSComponent(s.Artifacts.LinuxDistribution)...) cdxBOM.Components = &components return cdxBOM } func toOSComponent(distro *linux.Release) []cyclonedx.Component { if distro == nil { return []cyclonedx.Component{} } eRefs := &[]cyclonedx.ExternalReference{} if distro.BugReportURL != "" { *eRefs = append(*eRefs, cyclonedx.ExternalReference{ URL: distro.BugReportURL, Type: cyclonedx.ERTypeIssueTracker, }) } if distro.HomeURL != "" { *eRefs = append(*eRefs, cyclonedx.ExternalReference{ URL: distro.HomeURL, Type: cyclonedx.ERTypeWebsite, }) } if distro.SupportURL != "" { *eRefs = append(*eRefs, cyclonedx.ExternalReference{ URL: distro.SupportURL, Type: cyclonedx.ERTypeOther, Comment: "support", }) } if distro.PrivacyPolicyURL != "" { *eRefs = append(*eRefs, cyclonedx.ExternalReference{ URL: distro.PrivacyPolicyURL, Type: cyclonedx.ERTypeOther, Comment: "privacyPolicy", }) } if len(*eRefs) == 0 { eRefs = nil } props := getCycloneDXProperties(*distro) if len(*props) == 0 { props = nil } return []cyclonedx.Component{ { Type: cyclonedx.ComponentTypeOS, Name: distro.Name, Version: distro.Version, CPE: distro.CPEName, ExternalReferences: eRefs, Properties: props, }, } } // NewBomDescriptor returns a new BomDescriptor tailored for the current time and "syft" tool details. func toBomDescriptor(name, version string, srcMetadata source.Metadata) *cyclonedx.Metadata { return &cyclonedx.Metadata{ Timestamp: time.Now().Format(time.RFC3339), Tools: &[]cyclonedx.Tool{ { Vendor: "anchore", Name: name, Version: version, }, }, Component: toBomDescriptorComponent(srcMetadata), } } func toBomDescriptorComponent(srcMetadata source.Metadata) *cyclonedx.Component { switch srcMetadata.Scheme { case source.ImageScheme: return &cyclonedx.Component{ Type: cyclonedx.ComponentTypeContainer, Name: srcMetadata.ImageMetadata.UserInput, Version: srcMetadata.ImageMetadata.ManifestDigest, } case source.DirectoryScheme, source.FileScheme: return &cyclonedx.Component{ Type: cyclonedx.ComponentTypeFile, Name: srcMetadata.Path, } } return nil }