github.com/noqcks/syft@v0.0.0-20230920222752-a9e2c4e288e5/syft/pkg/cataloger/rpm/package.go (about) 1 package rpm 2 3 import ( 4 "fmt" 5 "strconv" 6 "strings" 7 8 rpmdb "github.com/knqyf263/go-rpmdb/pkg" 9 10 "github.com/anchore/packageurl-go" 11 "github.com/anchore/syft/syft/file" 12 "github.com/anchore/syft/syft/linux" 13 "github.com/anchore/syft/syft/pkg" 14 ) 15 16 func newPackage(dbOrRpmLocation file.Location, pd parsedData, distro *linux.Release) pkg.Package { 17 p := pkg.Package{ 18 Name: pd.Name, 19 Version: toELVersion(pd.RpmMetadata), 20 Licenses: pkg.NewLicenseSet(pd.Licenses...), 21 PURL: packageURL(pd.RpmMetadata, distro), 22 Locations: file.NewLocationSet(dbOrRpmLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)), 23 Type: pkg.RpmPkg, 24 MetadataType: pkg.RpmMetadataType, 25 Metadata: pd.RpmMetadata, 26 } 27 28 p.SetID() 29 return p 30 } 31 32 type parsedData struct { 33 Licenses []pkg.License 34 pkg.RpmMetadata 35 } 36 37 func newParsedDataFromEntry(licenseLocation file.Location, entry rpmdb.PackageInfo, files []pkg.RpmdbFileRecord) parsedData { 38 return parsedData{ 39 Licenses: pkg.NewLicensesFromLocation(licenseLocation, entry.License), 40 RpmMetadata: pkg.RpmMetadata{ 41 Name: entry.Name, 42 Version: entry.Version, 43 Epoch: entry.Epoch, 44 Arch: entry.Arch, 45 Release: entry.Release, 46 SourceRpm: entry.SourceRpm, 47 Vendor: entry.Vendor, 48 Size: entry.Size, 49 ModularityLabel: entry.Modularitylabel, 50 Files: files, 51 }, 52 } 53 } 54 55 func newMetadataFromManifestLine(entry string) (*parsedData, error) { 56 parts := strings.Split(entry, "\t") 57 if len(parts) < 10 { 58 return nil, fmt.Errorf("unexpected number of fields in line: %s", entry) 59 } 60 61 versionParts := strings.Split(parts[1], "-") 62 if len(versionParts) != 2 { 63 return nil, fmt.Errorf("unexpected version field: %s", parts[1]) 64 } 65 version := versionParts[0] 66 release := versionParts[1] 67 68 converted, err := strconv.Atoi(parts[8]) 69 var epoch *int 70 if err != nil || parts[5] == "(none)" { 71 epoch = nil 72 } else { 73 epoch = &converted 74 } 75 76 converted, err = strconv.Atoi(parts[6]) 77 var size int 78 if err == nil { 79 size = converted 80 } 81 return &parsedData{ 82 RpmMetadata: pkg.RpmMetadata{ 83 Name: parts[0], 84 Version: version, 85 Epoch: epoch, 86 Arch: parts[7], 87 Release: release, 88 SourceRpm: parts[9], 89 Vendor: parts[4], 90 Size: size, 91 }, 92 }, nil 93 } 94 95 // packageURL returns the PURL for the specific RHEL package (see https://github.com/package-url/purl-spec) 96 func packageURL(m pkg.RpmMetadata, distro *linux.Release) string { 97 var namespace string 98 if distro != nil { 99 namespace = distro.ID 100 } 101 102 qualifiers := map[string]string{ 103 pkg.PURLQualifierArch: m.Arch, 104 } 105 106 if m.Epoch != nil { 107 qualifiers[pkg.PURLQualifierEpoch] = strconv.Itoa(*m.Epoch) 108 } 109 110 if m.SourceRpm != "" { 111 qualifiers[pkg.PURLQualifierUpstream] = m.SourceRpm 112 } 113 114 return packageurl.NewPackageURL( 115 packageurl.TypeRPM, 116 namespace, 117 m.Name, 118 // for purl the epoch is a qualifier, not part of the version 119 // see https://github.com/package-url/purl-spec/blob/master/PURL-TYPES.rst under the RPM section 120 fmt.Sprintf("%s-%s", m.Version, m.Release), 121 pkg.PURLQualifiers( 122 qualifiers, 123 distro, 124 ), 125 "", 126 ).ToString() 127 }