Skip to content

Commit

Permalink
rpm: implement an RPM Resolver
Browse files Browse the repository at this point in the history
The Resolver compares file paths gleaned from RPM DBs and compares them
to a Package.Filepath to try and determine if a package needs to be
removed from an index report because its RPM counterpart has already
been included.

Signed-off-by: crozzy <[email protected]>
  • Loading branch information
crozzy committed Jul 10, 2024
1 parent 6df03d3 commit 41d429f
Show file tree
Hide file tree
Showing 4 changed files with 392 additions and 1 deletion.
1 change: 1 addition & 0 deletions libindex/libindex.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ func New(ctx context.Context, opts *Options, cl *http.Client) (*Libindex, error)
opts.Ecosystems = append(opts.Ecosystems, whiteout.NewEcosystem(ctx))
opts.Resolvers = []indexer.Resolver{
&whiteout.Resolver{},
&rhel.Resolver{},
}

if cl == nil {
Expand Down
56 changes: 56 additions & 0 deletions rhel/resolver.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package rhel

import (
"context"

"github.com/quay/zlog"

"github.com/quay/claircore"
"github.com/quay/claircore/indexer"
)

var (
_ indexer.Resolver = (*Resolver)(nil)
)

type Resolver struct{}

// Resolve takes a claircore.IndexReport and uses the rpm Files
// to determine if the package originate from an RPM. If the package
// was deemed to have been installed via RPM it isn't included in the
// final report.
func (r *Resolver) Resolve(ctx context.Context, ir *claircore.IndexReport, layers []*claircore.Layer) *claircore.IndexReport {
for pkgID, pkg := range ir.Packages {
isRPMPackage := false
envLoop:
for _, env := range ir.Environments[pkgID] {
if env.RepositoryIDs != nil {
for _, rID := range env.RepositoryIDs {
r := ir.Repositories[rID]
if r.Key == repositoryKey {
isRPMPackage = true
break envLoop
}
}
}
}
if !isRPMPackage {
filesLoop:
for _, fs := range ir.Files {
for _, f := range fs {
if f.Kind == claircore.FileKindRPM && pkg.Filepath == f.Path {
zlog.Debug(ctx).
Str("package name", pkg.Name).
Str("package file", pkg.Filepath).
Str("rpm file", f.Path).
Msg("package determined to have come from RPM, deleting")
delete(ir.Packages, pkgID)
delete(ir.Environments, pkgID)
break filesLoop
}
}
}
}
}
return ir
}
334 changes: 334 additions & 0 deletions rhel/resolver_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,334 @@
package rhel

import (
"context"
"strings"
"testing"

"github.com/quay/zlog"

"github.com/quay/claircore"
"github.com/quay/claircore/pkg/cpe"
)

var resolverTestcases = []struct {
name string
expectedPackages int
indexReport *claircore.IndexReport
}{
{
name: "No files",
expectedPackages: 2,
indexReport: &claircore.IndexReport{
Hash: claircore.MustParseDigest(`sha256:` + strings.Repeat(`a`, 64)),
Packages: map[string]*claircore.Package{
"123": {
ID: "123",
Name: "package A",
Version: "v1.0.0",
Source: &claircore.Package{
ID: "122",
Name: "package B source",
Kind: claircore.SOURCE,
Version: "v1.0.0",
},
Kind: claircore.BINARY,
},
"456": {
ID: "456",
Name: "package B",
Version: "v2.0.0",
Kind: claircore.BINARY,
},
},
Environments: map[string][]*claircore.Environment{
"123": {
{
PackageDB: "bdb:var/lib/rpm",
IntroducedIn: claircore.MustParseDigest(`sha256:` + strings.Repeat(`b`, 64)),
RepositoryIDs: []string{"11"},
DistributionID: "13",
},
},
"456": {
{
PackageDB: "maven:opt/couchbase/lib/cbas/repo/eventstream-1.0.1.jar",
IntroducedIn: claircore.MustParseDigest(`sha256:` + strings.Repeat(`c`, 64)),
RepositoryIDs: []string{"12"},
},
},
},
Repositories: map[string]*claircore.Repository{
"11": {
ID: "11",
Name: "cpe:/a:redhat:rhel_eus:8.6::appstream",
Key: repositoryKey,
CPE: cpe.MustUnbind("cpe:2.3:a:redhat:rhel_eus:8.6:*:appstream:*:*:*:*:*"),
},
"12": {
ID: "12",
Name: "maven",
URI: "https://repo1.maven.apache.org/maven2",
},
},
Distributions: map[string]*claircore.Distribution{
"13": {
ID: "13",
DID: "rhel",
Name: "Red Hat Enterprise Linux Server",
Version: "7",
VersionID: "7",
CPE: cpe.MustUnbind("cpe:2.3:o:redhat:enterprise_linux:7:*:*:*:*:*:*:*"),
PrettyName: "Red Hat Enterprise Linux Server 7",
},
},
Success: true,
},
},
{
name: "Non-matching files",
expectedPackages: 2,
indexReport: &claircore.IndexReport{
Hash: claircore.MustParseDigest(`sha256:` + strings.Repeat(`a`, 64)),
Packages: map[string]*claircore.Package{
"123": {
ID: "123",
Name: "package A",
Version: "v1.0.0",
Source: &claircore.Package{
ID: "122",
Name: "package B source",
Kind: claircore.SOURCE,
Version: "v1.0.0",
},
Kind: claircore.BINARY,
},
"456": {
ID: "456",
Name: "package B",
Version: "v2.0.0",
Kind: claircore.BINARY,
Filepath: "some/non-rpm-filepath.java",
},
},
Files: map[string][]claircore.File{
"111": {
{Kind: claircore.FileKindRPM, Path: "some/rpm/filepath/one.java"},
{Kind: claircore.FileKindRPM, Path: "some/rpm/filepath/two.java"},
{Kind: claircore.FileKindRPM, Path: "an/actual/rpm/filepath.java"},
},
},
Environments: map[string][]*claircore.Environment{
"123": {
{
PackageDB: "bdb:var/lib/rpm",
IntroducedIn: claircore.MustParseDigest(`sha256:` + strings.Repeat(`b`, 64)),
RepositoryIDs: []string{"11"},
DistributionID: "13",
},
},
"456": {
{
PackageDB: "maven:opt/couchbase/lib/cbas/repo/eventstream-1.0.1.jar",
IntroducedIn: claircore.MustParseDigest(`sha256:` + strings.Repeat(`c`, 64)),
RepositoryIDs: []string{"12"},
},
},
},
Repositories: map[string]*claircore.Repository{
"11": {
ID: "11",
Name: "cpe:/a:redhat:rhel_eus:8.6::appstream",
Key: repositoryKey,
CPE: cpe.MustUnbind("cpe:2.3:a:redhat:rhel_eus:8.6:*:appstream:*:*:*:*:*"),
},
"12": {
ID: "12",
Name: "maven",
URI: "https://repo1.maven.apache.org/maven2",
},
},
Distributions: map[string]*claircore.Distribution{
"13": {
ID: "13",
DID: "rhel",
Name: "Red Hat Enterprise Linux Server",
Version: "7",
VersionID: "7",
CPE: cpe.MustUnbind("cpe:2.3:o:redhat:enterprise_linux:7:*:*:*:*:*:*:*"),
PrettyName: "Red Hat Enterprise Linux Server 7",
},
},
Success: true,
},
},
{
name: "an RPM and a native JAVA package",
expectedPackages: 1,
indexReport: &claircore.IndexReport{
Hash: claircore.MustParseDigest(`sha256:` + strings.Repeat(`a`, 64)),
Packages: map[string]*claircore.Package{
"123": {
ID: "123",
Name: "rpm java package A",
Version: "v2.0.0-1-1",
Source: &claircore.Package{
ID: "122",
Name: "rpm java package A source",
Kind: claircore.SOURCE,
Version: "v2.0.0-1-1",
},
Kind: claircore.BINARY,
Filepath: "some/rpm/filepath.rpm",
},
"456": {
ID: "456",
Name: "java package A",
Version: "v2.0.0",
Kind: claircore.BINARY,
Filepath: "an/actual/rpm/filepath.java",
},
},
Files: map[string][]claircore.File{
"111": {
{Kind: claircore.FileKindRPM, Path: "some/rpm/filepath/one.java"},
{Kind: claircore.FileKindRPM, Path: "some/rpm/filepath/two.java"},
{Kind: claircore.FileKindRPM, Path: "an/actual/rpm/filepath.java"},
},
},
Environments: map[string][]*claircore.Environment{
"123": {
{
PackageDB: "bdb:var/lib/rpm",
IntroducedIn: claircore.MustParseDigest(`sha256:` + strings.Repeat(`b`, 64)),
RepositoryIDs: []string{"11"},
DistributionID: "13",
},
},
"456": {
{
PackageDB: "maven:opt/couchbase/lib/cbas/repo/eventstream-1.0.1.jar",
IntroducedIn: claircore.MustParseDigest(`sha256:` + strings.Repeat(`c`, 64)),
RepositoryIDs: []string{"12"},
},
},
},
Repositories: map[string]*claircore.Repository{
"11": {
ID: "11",
Name: "cpe:/a:redhat:rhel_eus:8.6::appstream",
Key: repositoryKey,
CPE: cpe.MustUnbind("cpe:2.3:a:redhat:rhel_eus:8.6:*:appstream:*:*:*:*:*"),
},
"12": {
ID: "12",
Name: "maven",
URI: "https://repo1.maven.apache.org/maven2",
},
},
Distributions: map[string]*claircore.Distribution{
"13": {
ID: "13",
DID: "rhel",
Name: "Red Hat Enterprise Linux Server",
Version: "7",
VersionID: "7",
CPE: cpe.MustUnbind("cpe:2.3:o:redhat:enterprise_linux:7:*:*:*:*:*:*:*"),
PrettyName: "Red Hat Enterprise Linux Server 7",
},
},
Success: true,
},
},
{
name: "an RPM and a Java package but wrong file Kind",
expectedPackages: 2,
indexReport: &claircore.IndexReport{
Hash: claircore.MustParseDigest(`sha256:` + strings.Repeat(`a`, 64)),
Packages: map[string]*claircore.Package{
"123": {
ID: "123",
Name: "rpm java package A",
Version: "v2.0.0-1-1",
Source: &claircore.Package{
ID: "122",
Name: "rpm java package A source",
Kind: claircore.SOURCE,
Version: "v2.0.0-1-1",
},
Kind: claircore.BINARY,
Filepath: "some/rpm/filepath.rpm",
},
"456": {
ID: "456",
Name: "java package A",
Version: "v2.0.0",
Kind: claircore.BINARY,
Filepath: "an/actual/rpm/filepath.java",
},
},
Files: map[string][]claircore.File{
"111": {
{Kind: claircore.FileKindRPM, Path: "some/rpm/filepath/one.java"},
{Kind: claircore.FileKindRPM, Path: "some/rpm/filepath/two.java"},
{Kind: claircore.FileKindWhiteout, Path: "an/actual/rpm/filepath.java"},
},
},
Environments: map[string][]*claircore.Environment{
"123": {
{
PackageDB: "bdb:var/lib/rpm",
IntroducedIn: claircore.MustParseDigest(`sha256:` + strings.Repeat(`b`, 64)),
RepositoryIDs: []string{"11"},
DistributionID: "13",
},
},
"456": {
{
PackageDB: "maven:opt/couchbase/lib/cbas/repo/eventstream-1.0.1.jar",
IntroducedIn: claircore.MustParseDigest(`sha256:` + strings.Repeat(`c`, 64)),
RepositoryIDs: []string{"12"},
},
},
},
Repositories: map[string]*claircore.Repository{
"11": {
ID: "11",
Name: "cpe:/a:redhat:rhel_eus:8.6::appstream",
Key: repositoryKey,
CPE: cpe.MustUnbind("cpe:2.3:a:redhat:rhel_eus:8.6:*:appstream:*:*:*:*:*"),
},
"12": {
ID: "12",
Name: "maven",
URI: "https://repo1.maven.apache.org/maven2",
},
},
Distributions: map[string]*claircore.Distribution{
"13": {
ID: "13",
DID: "rhel",
Name: "Red Hat Enterprise Linux Server",
Version: "7",
VersionID: "7",
CPE: cpe.MustUnbind("cpe:2.3:o:redhat:enterprise_linux:7:*:*:*:*:*:*:*"),
PrettyName: "Red Hat Enterprise Linux Server 7",
},
},
Success: true,
},
},
}

func TestResolver(t *testing.T) {
ctx := zlog.Test(context.Background(), t)
for _, tt := range resolverTestcases {
t.Run(tt.name, func(t *testing.T) {
r := &Resolver{}
ir := r.Resolve(ctx, tt.indexReport, nil)
if len(ir.Packages) != tt.expectedPackages {
t.Errorf("expected %d packages but got %d", tt.expectedPackages, len(ir.Packages))
}
})
}
}
Loading

0 comments on commit 41d429f

Please sign in to comment.