port rpm cataloger to new generic cataloger pattern (#1321)

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
This commit is contained in:
Alex Goodman 2022-11-04 10:41:04 -04:00 committed by GitHub
parent 1076281566
commit 3048382bbd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 444 additions and 583 deletions

View File

@ -41,7 +41,7 @@ func ImageCatalogers(cfg Config) []pkg.Cataloger {
javascript.NewJavascriptPackageCataloger(), javascript.NewJavascriptPackageCataloger(),
javascript.NewNodeBinaryCataloger(), javascript.NewNodeBinaryCataloger(),
deb.NewDpkgdbCataloger(), deb.NewDpkgdbCataloger(),
rpm.NewRpmdbCataloger(), rpm.NewRpmDBCataloger(),
java.NewJavaCataloger(cfg.Java()), java.NewJavaCataloger(cfg.Java()),
apkdb.NewApkdbCataloger(), apkdb.NewApkdbCataloger(),
golang.NewGoModuleBinaryCataloger(), golang.NewGoModuleBinaryCataloger(),
@ -61,7 +61,7 @@ func DirectoryCatalogers(cfg Config) []pkg.Cataloger {
javascript.NewJavascriptLockCataloger(), javascript.NewJavascriptLockCataloger(),
javascript.NewNodeBinaryCataloger(), javascript.NewNodeBinaryCataloger(),
deb.NewDpkgdbCataloger(), deb.NewDpkgdbCataloger(),
rpm.NewRpmdbCataloger(), rpm.NewRpmDBCataloger(),
rpm.NewFileCataloger(), rpm.NewFileCataloger(),
java.NewJavaCataloger(cfg.Java()), java.NewJavaCataloger(cfg.Java()),
java.NewJavaPomCataloger(), java.NewJavaPomCataloger(),
@ -90,7 +90,7 @@ func AllCatalogers(cfg Config) []pkg.Cataloger {
javascript.NewJavascriptPackageCataloger(), javascript.NewJavascriptPackageCataloger(),
javascript.NewNodeBinaryCataloger(), javascript.NewNodeBinaryCataloger(),
deb.NewDpkgdbCataloger(), deb.NewDpkgdbCataloger(),
rpm.NewRpmdbCataloger(), rpm.NewRpmDBCataloger(),
rpm.NewFileCataloger(), rpm.NewFileCataloger(),
java.NewJavaCataloger(cfg.Java()), java.NewJavaCataloger(cfg.Java()),
java.NewJavaPomCataloger(), java.NewJavaPomCataloger(),

View File

@ -0,0 +1,22 @@
/*
Package rpm provides a concrete DBCataloger implementation for RPM "Package" DB files and a FileCataloger for RPM files.
*/
package rpm
import (
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/generic"
)
// NewRpmDBCataloger returns a new RPM DB cataloger object.
func NewRpmDBCataloger() *generic.Cataloger {
return generic.NewCataloger("rpm-db-cataloger").
WithParserByGlobs(parseRpmDB, pkg.RpmDBGlob).
WithParserByGlobs(parseRpmManifest, pkg.RpmManifestGlob)
}
// NewFileCataloger returns a new RPM file cataloger object.
func NewFileCataloger() *generic.Cataloger {
return generic.NewCataloger("rpm-file-cataloger").
WithParserByGlobs(parseRpm, "**/*.rpm")
}

View File

@ -1,75 +0,0 @@
/*
Package rpm provides a concrete DBCataloger implementation for RPM "Package" DB files
and a FileCataloger for RPM files.
*/
package rpm
import (
"fmt"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/source"
)
const dbCatalogerName = "rpm-db-cataloger"
type DBCataloger struct{}
// NewRpmdbCataloger returns a new RPM DB cataloger object.
func NewRpmdbCataloger() *DBCataloger {
return &DBCataloger{}
}
// Name returns a string that uniquely describes a cataloger
func (c *DBCataloger) Name() string {
return dbCatalogerName
}
// Catalog is given an object to resolve file references and content, this function returns any discovered Packages after analyzing rpm db installation.
func (c *DBCataloger) Catalog(resolver source.FileResolver) ([]pkg.Package, []artifact.Relationship, error) {
fileMatches, err := resolver.FilesByGlob(pkg.RpmDBGlob)
if err != nil {
return nil, nil, fmt.Errorf("failed to find rpmdb's by glob: %w", err)
}
var pkgs []pkg.Package
for _, location := range fileMatches {
dbContentReader, err := resolver.FileContentsByLocation(location)
if err != nil {
return nil, nil, err
}
discoveredPkgs, err := parseRpmDB(resolver, location, dbContentReader)
internal.CloseAndLogError(dbContentReader, location.VirtualPath)
if err != nil {
return nil, nil, fmt.Errorf("unable to catalog rpmdb package=%+v: %w", location.RealPath, err)
}
pkgs = append(pkgs, discoveredPkgs...)
}
// Additionally look for RPM manifest files to detect packages in CBL-Mariner distroless images
manifestFileMatches, err := resolver.FilesByGlob(pkg.RpmManifestGlob)
if err != nil {
return nil, nil, fmt.Errorf("failed to find rpm manifests by glob: %w", err)
}
for _, location := range manifestFileMatches {
reader, err := resolver.FileContentsByLocation(location)
if err != nil {
return nil, nil, err
}
discoveredPkgs, err := parseRpmManifest(location, reader)
internal.CloseAndLogError(reader, location.VirtualPath)
if err != nil {
return nil, nil, fmt.Errorf("unable to catalog rpm manifest=%+v: %w", location.RealPath, err)
}
pkgs = append(pkgs, discoveredPkgs...)
}
return pkgs, nil, nil
}

View File

@ -1,140 +0,0 @@
package rpm
import (
"fmt"
"strconv"
"strings"
rpmdb "github.com/knqyf263/go-rpmdb/pkg"
"github.com/sassoftware/go-rpmutils"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/source"
)
type FileCataloger struct{}
// NewFileCataloger returns a new RPM file cataloger object.
func NewFileCataloger() *FileCataloger {
return &FileCataloger{}
}
// Name returns a string that uniquely describes a cataloger
func (c *FileCataloger) Name() string {
return "rpm-file-cataloger"
}
// Catalog is given an object to resolve file references and content, this function returns any discovered Packages after analyzing rpm files
//
//nolint:funlen
func (c *FileCataloger) Catalog(resolver source.FileResolver) ([]pkg.Package, []artifact.Relationship, error) {
fileMatches, err := resolver.FilesByGlob("**/*.rpm")
if err != nil {
return nil, nil, fmt.Errorf("failed to find rpm files's by glob: %w", err)
}
var pkgs []pkg.Package
for _, location := range fileMatches {
contentReader, err := resolver.FileContentsByLocation(location)
if err != nil {
return nil, nil, err
}
rpm, err := rpmutils.ReadRpm(contentReader)
if err != nil {
log.Debugf("RPM file found but unable to read: %s (%v)", location.RealPath, err)
continue
}
nevra, err := rpm.Header.GetNEVRA()
if err != nil {
return nil, nil, err
}
licenses, _ := rpm.Header.GetStrings(rpmutils.LICENSE)
sourceRpm, _ := rpm.Header.GetString(rpmutils.SOURCERPM)
vendor, _ := rpm.Header.GetString(rpmutils.VENDOR)
digestAlgorithm := getDigestAlgorithm(rpm.Header)
size, _ := rpm.Header.InstalledSize()
files, _ := rpm.Header.GetFiles()
p := pkg.Package{
Name: nevra.Name,
Version: nevra.Version,
FoundBy: c.Name(),
Licenses: licenses,
Locations: source.NewLocationSet(location),
Type: pkg.RpmPkg,
MetadataType: pkg.RpmMetadataType,
Metadata: pkg.RpmMetadata{
Name: nevra.Name,
Version: nevra.Version,
Epoch: parseEpoch(nevra.Epoch),
Arch: nevra.Arch,
Release: nevra.Release,
SourceRpm: sourceRpm,
Vendor: vendor,
License: strings.Join(licenses, " AND "),
Size: int(size),
Files: mapFiles(files, digestAlgorithm),
},
}
p.SetID()
pkgs = append(pkgs, p)
internal.CloseAndLogError(contentReader, location.VirtualPath)
if err != nil {
return nil, nil, fmt.Errorf("unable to catalog rpm file=%+v: %w", location.RealPath, err)
}
}
return pkgs, nil, nil
}
func getDigestAlgorithm(header *rpmutils.RpmHeader) string {
digestAlgorithm, _ := header.GetString(rpmutils.FILEDIGESTALGO)
if digestAlgorithm != "" {
return digestAlgorithm
}
digestAlgorithms, _ := header.GetUint32s(rpmutils.FILEDIGESTALGO)
if len(digestAlgorithms) > 0 {
digestAlgo := int(digestAlgorithms[0])
return rpmutils.GetFileAlgoName(digestAlgo)
}
return ""
}
func mapFiles(files []rpmutils.FileInfo, digestAlgorithm string) []pkg.RpmdbFileRecord {
var out []pkg.RpmdbFileRecord
for _, f := range files {
digest := file.Digest{}
if f.Digest() != "" {
digest = file.Digest{
Algorithm: digestAlgorithm,
Value: f.Digest(),
}
}
out = append(out, pkg.RpmdbFileRecord{
Path: f.Name(),
Mode: pkg.RpmdbFileMode(f.Mode()),
Size: int(f.Size()),
Digest: digest,
UserName: f.UserName(),
GroupName: f.GroupName(),
Flags: rpmdb.FileFlags(f.Flags()).String(),
})
}
return out
}
func parseEpoch(epoch string) *int {
i, err := strconv.Atoi(epoch)
if err != nil {
return nil
}
return &i
}

View File

@ -0,0 +1,122 @@
package rpm
import (
"fmt"
"strconv"
"strings"
rpmdb "github.com/knqyf263/go-rpmdb/pkg"
"github.com/anchore/packageurl-go"
"github.com/anchore/syft/syft/linux"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/source"
)
func newPackage(dbLocation source.Location, metadata pkg.RpmMetadata, distro *linux.Release) pkg.Package {
p := pkg.Package{
Name: metadata.Name,
Version: toELVersion(metadata),
PURL: packageURL(metadata, distro),
Locations: source.NewLocationSet(dbLocation),
Type: pkg.RpmPkg,
MetadataType: pkg.RpmMetadataType,
Metadata: metadata,
}
if metadata.License != "" {
p.Licenses = append(p.Licenses, metadata.License)
}
p.SetID()
return p
}
func newMetadataFromEntry(entry rpmdb.PackageInfo, files []pkg.RpmdbFileRecord) pkg.RpmMetadata {
return pkg.RpmMetadata{
Name: entry.Name,
Version: entry.Version,
Epoch: entry.Epoch,
Arch: entry.Arch,
Release: entry.Release,
SourceRpm: entry.SourceRpm,
Vendor: entry.Vendor,
License: entry.License,
Size: entry.Size,
ModularityLabel: entry.Modularitylabel,
Files: files,
}
}
func newMetadataFromManifestLine(entry string) (*pkg.RpmMetadata, error) {
parts := strings.Split(entry, "\t")
if len(parts) < 10 {
return nil, fmt.Errorf("unexpected number of fields in line: %s", entry)
}
versionParts := strings.Split(parts[1], "-")
if len(versionParts) != 2 {
return nil, fmt.Errorf("unexpected version field: %s", parts[1])
}
version := versionParts[0]
release := versionParts[1]
converted, err := strconv.Atoi(parts[8])
var epoch *int
if err != nil || parts[5] == "(none)" {
epoch = nil
} else {
epoch = &converted
}
converted, err = strconv.Atoi(parts[6])
var size int
if err == nil {
size = converted
}
return &pkg.RpmMetadata{
Name: parts[0],
Version: version,
Epoch: epoch,
Arch: parts[7],
Release: release,
SourceRpm: parts[9],
Vendor: parts[4],
Size: size,
}, nil
}
// packageURL returns the PURL for the specific RHEL package (see https://github.com/package-url/purl-spec)
func packageURL(m pkg.RpmMetadata, distro *linux.Release) string {
var namespace string
if distro != nil {
namespace = distro.ID
}
qualifiers := map[string]string{
pkg.PURLQualifierArch: m.Arch,
}
if m.Epoch != nil {
qualifiers[pkg.PURLQualifierEpoch] = strconv.Itoa(*m.Epoch)
}
if m.SourceRpm != "" {
qualifiers[pkg.PURLQualifierUpstream] = m.SourceRpm
}
return packageurl.NewPackageURL(
packageurl.TypeRPM,
namespace,
m.Name,
// for purl the epoch is a qualifier, not part of the version
// see https://github.com/package-url/purl-spec/blob/master/PURL-TYPES.rst under the RPM section
fmt.Sprintf("%s-%s", m.Version, m.Release),
pkg.PURLQualifiers(
qualifiers,
distro,
),
"",
).ToString()
}

View File

@ -0,0 +1,84 @@
package rpm
import (
"testing"
"github.com/sergi/go-diff/diffmatchpatch"
"github.com/anchore/syft/syft/linux"
"github.com/anchore/syft/syft/pkg"
)
func Test_packageURL(t *testing.T) {
tests := []struct {
name string
distro *linux.Release
metadata pkg.RpmMetadata
expected string
}{
{
name: "go case",
distro: &linux.Release{
ID: "rhel",
VersionID: "8.4",
},
metadata: pkg.RpmMetadata{
Name: "p",
Version: "v",
Release: "r",
Epoch: nil,
},
expected: "pkg:rpm/rhel/p@v-r?distro=rhel-8.4",
},
{
name: "with arch and epoch",
distro: &linux.Release{
ID: "centos",
VersionID: "7",
},
metadata: pkg.RpmMetadata{
Name: "p",
Version: "v",
Arch: "a",
Release: "r",
Epoch: intRef(1),
},
expected: "pkg:rpm/centos/p@v-r?arch=a&epoch=1&distro=centos-7",
},
{
name: "missing distro",
metadata: pkg.RpmMetadata{
Name: "p",
Version: "v",
Release: "r",
Epoch: nil,
},
expected: "pkg:rpm/p@v-r",
},
{
name: "with upstream source rpm info",
distro: &linux.Release{
ID: "rhel",
VersionID: "8.4",
},
metadata: pkg.RpmMetadata{
Name: "p",
Version: "v",
Release: "r",
SourceRpm: "sourcerpm",
},
expected: "pkg:rpm/rhel/p@v-r?upstream=sourcerpm&distro=rhel-8.4",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
actual := packageURL(test.metadata, test.distro)
if actual != test.expected {
dmp := diffmatchpatch.New()
diffs := dmp.DiffMain(test.expected, actual, true)
t.Errorf("diff: %s", dmp.DiffPrettyText(diffs))
}
})
}
}

View File

@ -0,0 +1,95 @@
package rpm
import (
"fmt"
"strconv"
"strings"
rpmdb "github.com/knqyf263/go-rpmdb/pkg"
"github.com/sassoftware/go-rpmutils"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/generic"
"github.com/anchore/syft/syft/source"
)
// parseRpm parses a single RPM
func parseRpm(_ source.FileResolver, _ *generic.Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
rpm, err := rpmutils.ReadRpm(reader)
if err != nil {
return nil, nil, fmt.Errorf("RPM file found but unable to read: %s (%v)", reader.Location.RealPath, err)
}
nevra, err := rpm.Header.GetNEVRA()
if err != nil {
return nil, nil, err
}
licenses, _ := rpm.Header.GetStrings(rpmutils.LICENSE)
sourceRpm, _ := rpm.Header.GetString(rpmutils.SOURCERPM)
vendor, _ := rpm.Header.GetString(rpmutils.VENDOR)
digestAlgorithm := getDigestAlgorithm(rpm.Header)
size, _ := rpm.Header.InstalledSize()
files, _ := rpm.Header.GetFiles()
metadata := pkg.RpmMetadata{
Name: nevra.Name,
Version: nevra.Version,
Epoch: parseEpoch(nevra.Epoch),
Arch: nevra.Arch,
Release: nevra.Release,
SourceRpm: sourceRpm,
Vendor: vendor,
License: strings.Join(licenses, " AND "), // TODO: AND conjunction is not necessarily correct, but we don't have a way to represent multiple licenses yet
Size: int(size),
Files: mapFiles(files, digestAlgorithm),
}
return []pkg.Package{newPackage(reader.Location, metadata, nil)}, nil, nil
}
func getDigestAlgorithm(header *rpmutils.RpmHeader) string {
digestAlgorithm, _ := header.GetString(rpmutils.FILEDIGESTALGO)
if digestAlgorithm != "" {
return digestAlgorithm
}
digestAlgorithms, _ := header.GetUint32s(rpmutils.FILEDIGESTALGO)
if len(digestAlgorithms) > 0 {
digestAlgo := int(digestAlgorithms[0])
return rpmutils.GetFileAlgoName(digestAlgo)
}
return ""
}
func mapFiles(files []rpmutils.FileInfo, digestAlgorithm string) []pkg.RpmdbFileRecord {
var out []pkg.RpmdbFileRecord
for _, f := range files {
digest := file.Digest{}
if f.Digest() != "" {
digest = file.Digest{
Algorithm: digestAlgorithm,
Value: f.Digest(),
}
}
out = append(out, pkg.RpmdbFileRecord{
Path: f.Name(),
Mode: pkg.RpmdbFileMode(f.Mode()),
Size: int(f.Size()),
Digest: digest,
UserName: f.UserName(),
GroupName: f.GroupName(),
Flags: rpmdb.FileFlags(f.Flags()).String(),
})
}
return out
}
func parseEpoch(epoch string) *int {
i, err := strconv.Atoi(epoch)
if err != nil {
return nil
}
return &i
}

View File

@ -9,16 +9,19 @@ import (
"github.com/anchore/syft/internal" "github.com/anchore/syft/internal"
"github.com/anchore/syft/internal/log" "github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/linux"
"github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/generic"
"github.com/anchore/syft/syft/source" "github.com/anchore/syft/syft/source"
) )
// parseRpmDb parses an "Packages" RPM DB and returns the Packages listed within it. // parseRpmDb parses an "Packages" RPM DB and returns the Packages listed within it.
func parseRpmDB(resolver source.FilePathResolver, dbLocation source.Location, reader io.Reader) ([]pkg.Package, error) { func parseRpmDB(resolver source.FileResolver, env *generic.Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
f, err := os.CreateTemp("", internal.ApplicationName+"-rpmdb") f, err := os.CreateTemp("", internal.ApplicationName+"-rpmdb")
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to create temp rpmdb file: %w", err) return nil, nil, fmt.Errorf("failed to create temp rpmdb file: %w", err)
} }
defer func() { defer func() {
@ -30,26 +33,40 @@ func parseRpmDB(resolver source.FilePathResolver, dbLocation source.Location, re
_, err = io.Copy(f, reader) _, err = io.Copy(f, reader)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to copy rpmdb contents to temp file: %w", err) return nil, nil, fmt.Errorf("failed to copy rpmdb contents to temp file: %w", err)
} }
db, err := rpmdb.Open(f.Name()) db, err := rpmdb.Open(f.Name())
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
pkgList, err := db.ListPackages() pkgList, err := db.ListPackages()
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
var allPkgs []pkg.Package var allPkgs []pkg.Package
var distro *linux.Release
if env != nil {
distro = env.LinuxRelease
}
for _, entry := range pkgList { for _, entry := range pkgList {
p := newPkg(resolver, dbLocation, entry) if entry == nil {
continue
}
p := newPackage(
reader.Location,
newMetadataFromEntry(*entry, extractRpmdbFileRecords(resolver, *entry)),
distro,
)
if !pkg.IsValid(&p) { if !pkg.IsValid(&p) {
log.Warnf("ignoring invalid package found in RPM DB: location=%q name=%q version=%q", dbLocation, entry.Name, entry.Version) log.WithFields("location", reader.RealPath, "pkg", fmt.Sprintf("%s@%s", entry.Name, entry.Version)).
Warn("ignoring invalid package found in RPM DB")
continue continue
} }
@ -57,40 +74,7 @@ func parseRpmDB(resolver source.FilePathResolver, dbLocation source.Location, re
allPkgs = append(allPkgs, p) allPkgs = append(allPkgs, p)
} }
return allPkgs, nil return allPkgs, nil, nil
}
func newPkg(resolver source.FilePathResolver, dbLocation source.Location, entry *rpmdb.PackageInfo) pkg.Package {
metadata := pkg.RpmMetadata{
Name: entry.Name,
Version: entry.Version,
Epoch: entry.Epoch,
Arch: entry.Arch,
Release: entry.Release,
SourceRpm: entry.SourceRpm,
Vendor: entry.Vendor,
License: entry.License,
Size: entry.Size,
ModularityLabel: entry.Modularitylabel,
Files: extractRpmdbFileRecords(resolver, entry),
}
p := pkg.Package{
Name: entry.Name,
Version: toELVersion(metadata),
Locations: source.NewLocationSet(dbLocation),
FoundBy: dbCatalogerName,
Type: pkg.RpmPkg,
MetadataType: pkg.RpmMetadataType,
Metadata: metadata,
}
if entry.License != "" {
p.Licenses = append(p.Licenses, entry.License)
}
p.SetID()
return p
} }
// The RPM naming scheme is [name]-[version]-[release]-[arch], where version is implicitly expands to [epoch]:[version]. // The RPM naming scheme is [name]-[version]-[release]-[arch], where version is implicitly expands to [epoch]:[version].
@ -106,7 +90,7 @@ func toELVersion(metadata pkg.RpmMetadata) string {
return fmt.Sprintf("%s-%s", metadata.Version, metadata.Release) return fmt.Sprintf("%s-%s", metadata.Version, metadata.Release)
} }
func extractRpmdbFileRecords(resolver source.FilePathResolver, entry *rpmdb.PackageInfo) []pkg.RpmdbFileRecord { func extractRpmdbFileRecords(resolver source.FilePathResolver, entry rpmdb.PackageInfo) []pkg.RpmdbFileRecord {
var records = make([]pkg.RpmdbFileRecord, 0) var records = make([]pkg.RpmdbFileRecord, 0)
files, err := entry.InstalledFiles() files, err := entry.InstalledFiles()

View File

@ -2,21 +2,38 @@ package rpm
import ( import (
"fmt" "fmt"
"os" "io"
"testing" "testing"
"github.com/go-test/deep"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
"github.com/anchore/syft/syft/source" "github.com/anchore/syft/syft/source"
) )
var _ source.FileResolver = (*rpmdbTestFileResolverMock)(nil)
type rpmdbTestFileResolverMock struct { type rpmdbTestFileResolverMock struct {
ignorePaths bool ignorePaths bool
} }
func (r rpmdbTestFileResolverMock) FileContentsByLocation(location source.Location) (io.ReadCloser, error) {
//TODO implement me
panic("implement me")
}
func (r rpmdbTestFileResolverMock) AllLocations() <-chan source.Location {
//TODO implement me
panic("implement me")
}
func (r rpmdbTestFileResolverMock) FileMetadataByLocation(location source.Location) (source.FileMetadata, error) {
//TODO implement me
panic("implement me")
}
func newTestFileResolver(ignorePaths bool) *rpmdbTestFileResolverMock { func newTestFileResolver(ignorePaths bool) *rpmdbTestFileResolverMock {
return &rpmdbTestFileResolverMock{ return &rpmdbTestFileResolverMock{
ignorePaths: ignorePaths, ignorePaths: ignorePaths,
@ -54,23 +71,21 @@ func (r *rpmdbTestFileResolverMock) FilesByMIMEType(...string) ([]source.Locatio
} }
func TestParseRpmDB(t *testing.T) { func TestParseRpmDB(t *testing.T) {
dbLocation := source.NewLocation("test-path")
tests := []struct { tests := []struct {
fixture string fixture string
expected map[string]pkg.Package expected []pkg.Package
ignorePaths bool ignorePaths bool
}{ }{
{ {
fixture: "test-fixtures/Packages", fixture: "test-fixtures/Packages",
// we only surface package paths for files that exist (here we DO NOT expect a path) // we only surface package paths for files that exist (here we DO NOT expect a path)
ignorePaths: true, ignorePaths: true,
expected: map[string]pkg.Package{ expected: []pkg.Package{
"dive": { {
Name: "dive", Name: "dive",
Version: "0.9.2-1", Version: "0.9.2-1",
Locations: source.NewLocationSet(dbLocation), PURL: "pkg:rpm/dive@0.9.2-1?arch=x86_64&upstream=dive-0.9.2-1.src.rpm",
FoundBy: dbCatalogerName, Locations: source.NewLocationSet(source.NewLocation("test-fixtures/Packages")),
Type: pkg.RpmPkg, Type: pkg.RpmPkg,
MetadataType: pkg.RpmMetadataType, MetadataType: pkg.RpmMetadataType,
Licenses: []string{"MIT"}, Licenses: []string{"MIT"},
@ -93,12 +108,12 @@ func TestParseRpmDB(t *testing.T) {
fixture: "test-fixtures/Packages", fixture: "test-fixtures/Packages",
// we only surface package paths for files that exist (here we expect a path) // we only surface package paths for files that exist (here we expect a path)
ignorePaths: false, ignorePaths: false,
expected: map[string]pkg.Package{ expected: []pkg.Package{
"dive": { {
Name: "dive", Name: "dive",
Version: "0.9.2-1", Version: "0.9.2-1",
Locations: source.NewLocationSet(dbLocation), PURL: "pkg:rpm/dive@0.9.2-1?arch=x86_64&upstream=dive-0.9.2-1.src.rpm",
FoundBy: dbCatalogerName, Locations: source.NewLocationSet(source.NewLocation("test-fixtures/Packages")),
Type: pkg.RpmPkg, Type: pkg.RpmPkg,
MetadataType: pkg.RpmMetadataType, MetadataType: pkg.RpmMetadataType,
Licenses: []string{"MIT"}, Licenses: []string{"MIT"},
@ -132,34 +147,11 @@ func TestParseRpmDB(t *testing.T) {
for _, test := range tests { for _, test := range tests {
t.Run(test.fixture, func(t *testing.T) { t.Run(test.fixture, func(t *testing.T) {
fixture, err := os.Open(test.fixture) pkgtest.NewCatalogTester().
if err != nil { WithResolver(newTestFileResolver(test.ignorePaths)).
t.Fatalf("failed to open fixture: %+v", err) FromFile(t, test.fixture).
} Expects(test.expected, nil).
TestParser(t, parseRpmDB)
fileResolver := newTestFileResolver(test.ignorePaths)
actual, err := parseRpmDB(fileResolver, dbLocation, fixture)
if err != nil {
t.Fatalf("failed to parse rpmdb: %+v", err)
}
if len(actual) != len(test.expected) {
for _, a := range actual {
t.Log(" ", a)
}
t.Fatalf("unexpected package count: %d!=%d", len(actual), len(test.expected))
}
for _, a := range actual {
e := test.expected[a.Name]
diffs := deep.Equal(a, e)
if len(diffs) > 0 {
for _, d := range diffs {
t.Errorf("diff: %+v", d)
}
}
}
}) })
} }

View File

@ -2,74 +2,18 @@ package rpm
import ( import (
"bufio" "bufio"
"fmt"
"io" "io"
"strconv"
"strings" "strings"
"github.com/anchore/syft/internal/log" "github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/generic"
"github.com/anchore/syft/syft/source" "github.com/anchore/syft/syft/source"
) )
// Parses an entry in an RPM manifest file as used in Mariner distroless containers
// Each line is the output of :
// rpm --query --all --query-format "%{NAME}\t%{VERSION}-%{RELEASE}\t%{INSTALLTIME}\t%{BUILDTIME}\t%{VENDOR}\t%{EPOCH}\t%{SIZE}\t%{ARCH}\t%{EPOCHNUM}\t%{SOURCERPM}\n"
// https://github.com/microsoft/CBL-Mariner/blob/3df18fac373aba13a54bd02466e64969574f13af/toolkit/docs/how_it_works/5_misc.md?plain=1#L150
func parseRpmManifestEntry(entry string, location source.Location) (*pkg.Package, error) {
parts := strings.Split(entry, "\t")
if len(parts) < 10 {
return nil, fmt.Errorf("unexpected number of fields in line: %s", entry)
}
versionParts := strings.Split(parts[1], "-")
if len(versionParts) != 2 {
return nil, fmt.Errorf("unexpected version field: %s", parts[1])
}
version := versionParts[0]
release := versionParts[1]
converted, err := strconv.Atoi(parts[8])
var epoch *int
if err != nil || parts[5] == "(none)" {
epoch = nil
} else {
epoch = &converted
}
converted, err = strconv.Atoi(parts[6])
var size int
if err == nil {
size = converted
}
metadata := pkg.RpmMetadata{
Name: parts[0],
Version: version,
Epoch: epoch,
Arch: parts[7],
Release: release,
SourceRpm: parts[9],
Vendor: parts[4],
Size: size,
}
p := pkg.Package{
Name: parts[0],
Version: toELVersion(metadata),
Locations: source.NewLocationSet(location),
FoundBy: dbCatalogerName,
Type: pkg.RpmPkg,
MetadataType: pkg.RpmMetadataType,
Metadata: metadata,
}
p.SetID()
return &p, nil
}
// Parses an RPM manifest file, as used in Mariner distroless containers, and returns the Packages listed // Parses an RPM manifest file, as used in Mariner distroless containers, and returns the Packages listed
func parseRpmManifest(dbLocation source.Location, reader io.Reader) ([]pkg.Package, error) { func parseRpmManifest(_ source.FileResolver, _ *generic.Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
r := bufio.NewReader(reader) r := bufio.NewReader(reader)
allPkgs := make([]pkg.Package, 0) allPkgs := make([]pkg.Package, 0)
@ -79,14 +23,14 @@ func parseRpmManifest(dbLocation source.Location, reader io.Reader) ([]pkg.Packa
if err == io.EOF { if err == io.EOF {
break break
} }
return nil, err return nil, nil, err
} }
if line == "" { if line == "" {
continue continue
} }
p, err := parseRpmManifestEntry(strings.TrimSuffix(line, "\n"), dbLocation) p, err := parseRpmManifestEntry(strings.TrimSuffix(line, "\n"), reader.Location)
if err != nil { if err != nil {
log.Warnf("unable to parse RPM manifest entry: %w", err) log.Warnf("unable to parse RPM manifest entry: %w", err)
continue continue
@ -100,5 +44,24 @@ func parseRpmManifest(dbLocation source.Location, reader io.Reader) ([]pkg.Packa
allPkgs = append(allPkgs, *p) allPkgs = append(allPkgs, *p)
} }
return allPkgs, nil return allPkgs, nil, nil
}
// Parses an entry in an RPM manifest file as used in Mariner distroless containers
// Each line is the output of :
// rpm --query --all --query-format "%{NAME}\t%{VERSION}-%{RELEASE}\t%{INSTALLTIME}\t%{BUILDTIME}\t%{VENDOR}\t%{EPOCH}\t%{SIZE}\t%{ARCH}\t%{EPOCHNUM}\t%{SOURCERPM}\n"
// https://github.com/microsoft/CBL-Mariner/blob/3df18fac373aba13a54bd02466e64969574f13af/toolkit/docs/how_it_works/5_misc.md?plain=1#L150
func parseRpmManifestEntry(entry string, location source.Location) (*pkg.Package, error) {
metadata, err := newMetadataFromManifestLine(entry)
if err != nil {
return nil, err
}
if metadata == nil {
return nil, nil
}
p := newPackage(location, *metadata, nil)
return &p, nil
} }

View File

@ -1,25 +1,22 @@
package rpm package rpm
import ( import (
"os"
"testing" "testing"
"github.com/go-test/deep"
"github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
"github.com/anchore/syft/syft/source" "github.com/anchore/syft/syft/source"
) )
func TestParseRpmManifest(t *testing.T) { func TestParseRpmManifest(t *testing.T) {
location := source.NewLocation("test-path") fixture := "test-fixtures/container-manifest-2"
location := source.NewLocation(fixture)
fixture_path := "test-fixtures/container-manifest-2" expected := []pkg.Package{
expected := map[string]pkg.Package{ {
"mariner-release": {
Name: "mariner-release", Name: "mariner-release",
Version: "2.0-12.cm2", Version: "2.0-12.cm2",
PURL: "pkg:rpm/mariner-release@2.0-12.cm2?arch=noarch&upstream=mariner-release-2.0-12.cm2.src.rpm",
Locations: source.NewLocationSet(location), Locations: source.NewLocationSet(location),
FoundBy: dbCatalogerName,
Type: pkg.RpmPkg, Type: pkg.RpmPkg,
MetadataType: pkg.RpmMetadataType, MetadataType: pkg.RpmMetadataType,
Metadata: pkg.RpmMetadata{ Metadata: pkg.RpmMetadata{
@ -33,11 +30,11 @@ func TestParseRpmManifest(t *testing.T) {
Vendor: "Microsoft Corporation", Vendor: "Microsoft Corporation",
}, },
}, },
"filesystem": { {
Name: "filesystem", Name: "filesystem",
Version: "1.1-9.cm2", Version: "1.1-9.cm2",
PURL: "pkg:rpm/filesystem@1.1-9.cm2?arch=x86_64&upstream=filesystem-1.1-9.cm2.src.rpm",
Locations: source.NewLocationSet(location), Locations: source.NewLocationSet(location),
FoundBy: dbCatalogerName,
Type: pkg.RpmPkg, Type: pkg.RpmPkg,
MetadataType: pkg.RpmMetadataType, MetadataType: pkg.RpmMetadataType,
Metadata: pkg.RpmMetadata{ Metadata: pkg.RpmMetadata{
@ -51,11 +48,11 @@ func TestParseRpmManifest(t *testing.T) {
Vendor: "Microsoft Corporation", Vendor: "Microsoft Corporation",
}, },
}, },
"glibc": { {
Name: "glibc", Name: "glibc",
Version: "2.35-2.cm2", Version: "2.35-2.cm2",
PURL: "pkg:rpm/glibc@2.35-2.cm2?arch=x86_64&upstream=glibc-2.35-2.cm2.src.rpm",
Locations: source.NewLocationSet(location), Locations: source.NewLocationSet(location),
FoundBy: dbCatalogerName,
Type: pkg.RpmPkg, Type: pkg.RpmPkg,
MetadataType: pkg.RpmMetadataType, MetadataType: pkg.RpmMetadataType,
Metadata: pkg.RpmMetadata{ Metadata: pkg.RpmMetadata{
@ -69,11 +66,11 @@ func TestParseRpmManifest(t *testing.T) {
Vendor: "Microsoft Corporation", Vendor: "Microsoft Corporation",
}, },
}, },
"openssl-libs": { {
Name: "openssl-libs", Name: "openssl-libs",
Version: "1.1.1k-15.cm2", Version: "1.1.1k-15.cm2",
PURL: "pkg:rpm/openssl-libs@1.1.1k-15.cm2?arch=x86_64&upstream=openssl-1.1.1k-15.cm2.src.rpm",
Locations: source.NewLocationSet(location), Locations: source.NewLocationSet(location),
FoundBy: dbCatalogerName,
Type: pkg.RpmPkg, Type: pkg.RpmPkg,
MetadataType: pkg.RpmMetadataType, MetadataType: pkg.RpmMetadataType,
Metadata: pkg.RpmMetadata{ Metadata: pkg.RpmMetadata{
@ -89,30 +86,9 @@ func TestParseRpmManifest(t *testing.T) {
}, },
} }
fixture, err := os.Open(fixture_path) pkgtest.NewCatalogTester().
if err != nil { FromFile(t, fixture).
t.Fatalf("failed to open fixture: %+v", err) Expects(expected, nil).
} TestParser(t, parseRpmManifest)
actual, err := parseRpmManifest(location, fixture)
if err != nil {
t.Fatalf("failed to parse rpm manifest: %+v", err)
}
if len(actual) != 12 {
for _, a := range actual {
t.Log(" ", a)
}
t.Fatalf("unexpected package count: %d!=%d", len(actual), len(expected))
}
for _, a := range actual[0:4] {
e := expected[a.Name]
diffs := deep.Equal(a, e)
if len(diffs) > 0 {
for _, d := range diffs {
t.Errorf("diff: %+v", d)
}
}
}
} }

View File

@ -3,26 +3,25 @@ package rpm
import ( import (
"testing" "testing"
"github.com/go-test/deep"
"github.com/stretchr/testify/require"
"github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
"github.com/anchore/syft/syft/source" "github.com/anchore/syft/syft/source"
) )
func TestParseRpmFiles(t *testing.T) { func TestParseRpmFiles(t *testing.T) {
tests := []struct { tests := []struct {
fixture string fixture string
expected map[string]pkg.Package expected []pkg.Package
}{ }{
{ {
fixture: "test-fixtures/rpms", fixture: "test-fixtures/rpms",
expected: map[string]pkg.Package{ expected: []pkg.Package{
"abc": { {
Name: "abc", Name: "abc",
Version: "1.01", Version: "0:1.01-9.hg20160905.el7",
Locations: source.NewLocationSet(), PURL: "pkg:rpm/abc@1.01-9.hg20160905.el7?arch=x86_64&epoch=0&upstream=abc-1.01-9.hg20160905.el7.src.rpm",
Locations: source.NewLocationSet(source.NewLocation("abc-1.01-9.hg20160905.el7.x86_64.rpm")),
FoundBy: "rpm-file-cataloger", FoundBy: "rpm-file-cataloger",
Type: pkg.RpmPkg, Type: pkg.RpmPkg,
MetadataType: pkg.RpmMetadataType, MetadataType: pkg.RpmMetadataType,
@ -46,10 +45,11 @@ func TestParseRpmFiles(t *testing.T) {
}, },
}, },
}, },
"zork": { {
Name: "zork", Name: "zork",
Version: "1.0.3", Version: "0:1.0.3-1.el7",
Locations: source.NewLocationSet(), PURL: "pkg:rpm/zork@1.0.3-1.el7?arch=x86_64&epoch=0&upstream=zork-1.0.3-1.el7.src.rpm",
Locations: source.NewLocationSet(source.NewLocation("zork-1.0.3-1.el7.x86_64.rpm")),
FoundBy: "rpm-file-cataloger", FoundBy: "rpm-file-cataloger",
Type: pkg.RpmPkg, Type: pkg.RpmPkg,
MetadataType: pkg.RpmMetadataType, MetadataType: pkg.RpmMetadataType,
@ -86,24 +86,10 @@ func TestParseRpmFiles(t *testing.T) {
for _, test := range tests { for _, test := range tests {
t.Run(test.fixture, func(t *testing.T) { t.Run(test.fixture, func(t *testing.T) {
s, err := source.NewFromDirectory(test.fixture) pkgtest.NewCatalogTester().
require.NoError(t, err) FromDirectory(t, test.fixture).
Expects(test.expected, nil).
r, err := s.FileResolver(source.SquashedScope) TestCataloger(t, NewFileCataloger())
require.NoError(t, err)
packages, _, err := NewFileCataloger().Catalog(r)
require.NoError(t, err)
for _, a := range packages {
e := test.expected[a.Name]
diffs := deep.Equal(e, a)
if len(diffs) > 0 {
for _, d := range diffs {
t.Errorf("diff: %+v", d)
}
}
}
}) })
} }
} }

View File

@ -2,11 +2,4 @@ mariner-release 2.0-12.cm2 1653816591 1653753130 Microsoft Corporation (none) 58
filesystem 1.1-9.cm2 1653816591 1653628924 Microsoft Corporation (none) 7596 x86_64 0 filesystem-1.1-9.cm2.src.rpm filesystem 1.1-9.cm2 1653816591 1653628924 Microsoft Corporation (none) 7596 x86_64 0 filesystem-1.1-9.cm2.src.rpm
glibc 2.35-2.cm2 1653816591 1653628955 Microsoft Corporation (none) 10855265 x86_64 0 glibc-2.35-2.cm2.src.rpm glibc 2.35-2.cm2 1653816591 1653628955 Microsoft Corporation (none) 10855265 x86_64 0 glibc-2.35-2.cm2.src.rpm
openssl-libs 1.1.1k-15.cm2 1653816591 1653631609 Microsoft Corporation (none) 4365048 x86_64 0 openssl-1.1.1k-15.cm2.src.rpm openssl-libs 1.1.1k-15.cm2 1653816591 1653631609 Microsoft Corporation (none) 4365048 x86_64 0 openssl-1.1.1k-15.cm2.src.rpm
libgcc 11.2.0-2.cm2 1653816591 1650702349 Microsoft Corporation (none) 103960 x86_64 0 gcc-11.2.0-2.cm2.src.rpm
openssl 1.1.1k-15.cm2 1653816591 1653631609 Microsoft Corporation (none) 1286337 x86_64 0 openssl-1.1.1k-15.cm2.src.rpm
glibc-iconv 2.35-2.cm2 1653816591 1653628955 Microsoft Corporation (none) 8397230 x86_64 0 glibc-2.35-2.cm2.src.rpm
iana-etc 20211115-1.cm2 1653816591 1650711959 Microsoft Corporation (none) 4380680 noarch 0 iana-etc-20211115-1.cm2.src.rpm
tzdata 2022a-1.cm2 1653816591 1653752882 Microsoft Corporation (none) 1535764 noarch 0 tzdata-2022a-1.cm2.src.rpm
prebuilt-ca-certificates-base 2.0.0-3.cm2 1653816591 1653771776 Microsoft Corporation (none) 65684 noarch 1 prebuilt-ca-certificates-base-2.0.0-3.cm2.src.rpm
distroless-packages-minimal 0.1-2.cm2 1653816591 1650712132 Microsoft Corporation (none) 0 x86_64 0 distroless-packages-0.1-2.cm2.src.rpm
distroless-packages-base 0.1-2.cm2 1653816591 1650712132 Microsoft Corporation (none) 0 x86_64 0 distroless-packages-0.1-2.cm2.src.rpm

View File

@ -1,15 +1,11 @@
package pkg package pkg
import ( import (
"fmt"
"sort" "sort"
"strconv"
"github.com/scylladb/go-set/strset" "github.com/scylladb/go-set/strset"
"github.com/anchore/packageurl-go"
"github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/linux"
) )
// Packages is the legacy Berkely db based format // Packages is the legacy Berkely db based format
@ -20,10 +16,7 @@ const RpmDBGlob = "**/var/lib/rpm/{Packages,Packages.db,rpmdb.sqlite}"
// Used in CBL-Mariner distroless images // Used in CBL-Mariner distroless images
const RpmManifestGlob = "**/var/lib/rpmmanifest/container-manifest-2" const RpmManifestGlob = "**/var/lib/rpmmanifest/container-manifest-2"
var ( var _ FileOwner = (*RpmMetadata)(nil)
_ FileOwner = (*RpmMetadata)(nil)
_ urlIdentifier = (*RpmMetadata)(nil)
)
// RpmMetadata represents all captured data for a RPM DB package entry. // RpmMetadata represents all captured data for a RPM DB package entry.
type RpmMetadata struct { type RpmMetadata struct {
@ -54,40 +47,6 @@ type RpmdbFileRecord struct {
// RpmdbFileMode is the raw file mode for a single file. This can be interpreted as the linux stat.h mode (see https://pubs.opengroup.org/onlinepubs/007908799/xsh/sysstat.h.html) // RpmdbFileMode is the raw file mode for a single file. This can be interpreted as the linux stat.h mode (see https://pubs.opengroup.org/onlinepubs/007908799/xsh/sysstat.h.html)
type RpmdbFileMode uint16 type RpmdbFileMode uint16
// PackageURL returns the PURL for the specific RHEL package (see https://github.com/package-url/purl-spec)
func (m RpmMetadata) PackageURL(distro *linux.Release) string {
var namespace string
if distro != nil {
namespace = distro.ID
}
qualifiers := map[string]string{
PURLQualifierArch: m.Arch,
}
if m.Epoch != nil {
qualifiers[PURLQualifierEpoch] = strconv.Itoa(*m.Epoch)
}
if m.SourceRpm != "" {
qualifiers[PURLQualifierUpstream] = m.SourceRpm
}
return packageurl.NewPackageURL(
packageurl.TypeRPM,
namespace,
m.Name,
// for purl the epoch is a qualifier, not part of the version
// see https://github.com/package-url/purl-spec/blob/master/PURL-TYPES.rst under the RPM section
fmt.Sprintf("%s-%s", m.Version, m.Release),
PURLQualifiers(
qualifiers,
distro,
),
"",
).ToString()
}
func (m RpmMetadata) OwnedFiles() (result []string) { func (m RpmMetadata) OwnedFiles() (result []string) {
s := strset.New() s := strset.New()
for _, f := range m.Files { for _, f := range m.Files {

View File

@ -5,85 +5,8 @@ import (
"testing" "testing"
"github.com/go-test/deep" "github.com/go-test/deep"
"github.com/sergi/go-diff/diffmatchpatch"
"github.com/anchore/syft/syft/linux"
) )
func TestRpmMetadata_pURL(t *testing.T) {
tests := []struct {
name string
distro *linux.Release
metadata RpmMetadata
expected string
}{
{
name: "go case",
distro: &linux.Release{
ID: "rhel",
VersionID: "8.4",
},
metadata: RpmMetadata{
Name: "p",
Version: "v",
Release: "r",
Epoch: nil,
},
expected: "pkg:rpm/rhel/p@v-r?distro=rhel-8.4",
},
{
name: "with arch and epoch",
distro: &linux.Release{
ID: "centos",
VersionID: "7",
},
metadata: RpmMetadata{
Name: "p",
Version: "v",
Arch: "a",
Release: "r",
Epoch: intRef(1),
},
expected: "pkg:rpm/centos/p@v-r?arch=a&epoch=1&distro=centos-7",
},
{
name: "missing distro",
metadata: RpmMetadata{
Name: "p",
Version: "v",
Release: "r",
Epoch: nil,
},
expected: "pkg:rpm/p@v-r",
},
{
name: "with upstream source rpm info",
distro: &linux.Release{
ID: "rhel",
VersionID: "8.4",
},
metadata: RpmMetadata{
Name: "p",
Version: "v",
Release: "r",
SourceRpm: "sourcerpm",
},
expected: "pkg:rpm/rhel/p@v-r?upstream=sourcerpm&distro=rhel-8.4",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
actual := test.metadata.PackageURL(test.distro)
if actual != test.expected {
dmp := diffmatchpatch.New()
diffs := dmp.DiffMain(test.expected, actual, true)
t.Errorf("diff: %s", dmp.DiffPrettyText(diffs))
}
})
}
}
func TestRpmMetadata_FileOwner(t *testing.T) { func TestRpmMetadata_FileOwner(t *testing.T) {
tests := []struct { tests := []struct {
metadata RpmMetadata metadata RpmMetadata
@ -123,7 +46,3 @@ func TestRpmMetadata_FileOwner(t *testing.T) {
}) })
} }
} }
func intRef(i int) *int {
return &i
}

View File

@ -35,26 +35,6 @@ func TestPackageURL(t *testing.T) {
}, },
expected: "pkg:npm/name@v0.1.0", expected: "pkg:npm/name@v0.1.0",
}, },
{
name: "rpm",
distro: &linux.Release{
ID: "centos",
VersionID: "7",
},
pkg: Package{
Name: "bad-name",
Version: "bad-v0.1.0",
Type: RpmPkg,
Metadata: RpmMetadata{
Name: "name",
Version: "0.1.0",
Epoch: intRef(2),
Arch: "amd64",
Release: "3",
},
},
expected: "pkg:rpm/centos/name@0.1.0-3?arch=amd64&epoch=2&distro=centos-7",
},
{ {
name: "cargo", name: "cargo",
pkg: Package{ pkg: Package{
@ -128,6 +108,7 @@ func TestPackageURL(t *testing.T) {
expectedTypes.Remove(string(BinaryPkg)) expectedTypes.Remove(string(BinaryPkg))
expectedTypes.Remove(string(PhpComposerPkg)) expectedTypes.Remove(string(PhpComposerPkg))
expectedTypes.Remove(string(PythonPkg)) expectedTypes.Remove(string(PythonPkg))
expectedTypes.Remove(string(RpmPkg))
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {