github.com/google/osv-scalibr@v0.4.1/converter/converter.go (about) 1 // Copyright 2025 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package converter provides utility functions for converting SCALIBR's scan results to 16 // standardized inventory formats. 17 package converter 18 19 import ( 20 "time" 21 22 "github.com/CycloneDX/cyclonedx-go" 23 "github.com/google/osv-scalibr/converter/spdx" 24 "github.com/google/osv-scalibr/extractor" 25 cdxmeta "github.com/google/osv-scalibr/extractor/filesystem/sbom/cdx/metadata" 26 spdxmeta "github.com/google/osv-scalibr/extractor/filesystem/sbom/spdx/metadata" 27 "github.com/google/osv-scalibr/purl" 28 "github.com/google/osv-scalibr/result" 29 "github.com/google/uuid" 30 "github.com/spdx/tools-golang/spdx/v2/v2_3" 31 ) 32 33 // ToPURL converts a SCALIBR package structure into a package URL. 34 func ToPURL(p *extractor.Package) *purl.PackageURL { 35 return p.PURL() 36 } 37 38 // ToSPDX23 converts the SCALIBR scan results into an SPDX v2.3 document. 39 func ToSPDX23(r *result.ScanResult, c spdx.Config) *v2_3.Document { 40 return spdx.ToSPDX23(r, c) 41 } 42 43 // CDXConfig describes custom settings that should be applied to the generated CDX file. 44 type CDXConfig struct { 45 ComponentName string 46 ComponentVersion string 47 ComponentType string 48 Authors []string 49 } 50 51 // ToCDX converts the SCALIBR scan results into a CycloneDX document. 52 func ToCDX(r *result.ScanResult, c CDXConfig) *cyclonedx.BOM { 53 bom := cyclonedx.NewBOM() 54 bom.Metadata = &cyclonedx.Metadata{ 55 Timestamp: time.Now().UTC().Format("2006-01-02T15:04:05Z"), 56 Component: &cyclonedx.Component{ 57 Name: c.ComponentName, 58 Version: c.ComponentVersion, 59 Type: cyclonedx.ComponentType(c.ComponentType), 60 BOMRef: uuid.New().String(), 61 }, 62 Tools: &cyclonedx.ToolsChoice{ 63 Components: &[]cyclonedx.Component{ 64 { 65 Type: cyclonedx.ComponentTypeApplication, 66 Name: "SCALIBR", 67 ExternalReferences: &[]cyclonedx.ExternalReference{ 68 { 69 URL: "https://github.com/google/osv-scalibr", 70 Type: cyclonedx.ERTypeWebsite, 71 }, 72 }, 73 }, 74 }, 75 }, 76 } 77 if len(c.Authors) > 0 { 78 authors := make([]cyclonedx.OrganizationalContact, 0, len(c.Authors)) 79 for _, author := range c.Authors { 80 authors = append(authors, cyclonedx.OrganizationalContact{ 81 Name: author, 82 }) 83 } 84 bom.Metadata.Authors = &authors 85 } 86 87 comps := make([]cyclonedx.Component, 0, len(r.Inventory.Packages)) 88 for _, pkg := range r.Inventory.Packages { 89 comp := cyclonedx.Component{ 90 BOMRef: uuid.New().String(), 91 Type: cyclonedx.ComponentTypeLibrary, 92 Name: pkg.Name, 93 Version: pkg.Version, 94 } 95 if p := ToPURL(pkg); p != nil { 96 comp.PackageURL = p.String() 97 } 98 if cpes := extractCPEs(pkg); len(cpes) > 0 { 99 comp.CPE = cpes[0] 100 } 101 if len(pkg.Locations) > 0 { 102 occ := make([]cyclonedx.EvidenceOccurrence, 0, len((pkg.Locations))) 103 for _, loc := range pkg.Locations { 104 occ = append(occ, cyclonedx.EvidenceOccurrence{ 105 Location: loc, 106 }) 107 } 108 comp.Evidence = &cyclonedx.Evidence{ 109 Occurrences: &occ, 110 } 111 } 112 comps = append(comps, comp) 113 } 114 bom.Components = &comps 115 116 return bom 117 } 118 119 func extractCPEs(p *extractor.Package) []string { 120 // Only the two SBOM package types support storing CPEs. 121 if m, ok := p.Metadata.(*spdxmeta.Metadata); ok { 122 return m.CPEs 123 } 124 if m, ok := p.Metadata.(*cdxmeta.Metadata); ok { 125 return m.CPEs 126 } 127 return nil 128 }