Skip to content

Commit 408d115

Browse files
committed
rpmver: move to internal rpm version package
Signed-off-by: Hank Donnay <hdonnay@redhat.com> Change-Id: I5f0bdbae3d18c9e87be919712d86d91f7be3ad35
1 parent 2779b70 commit 408d115

9 files changed

Lines changed: 75 additions & 117 deletions

File tree

aws/matcher.go

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@ package aws
33
import (
44
"context"
55

6-
version "github.com/knqyf263/go-rpm-version"
7-
86
"github.com/quay/claircore"
7+
"github.com/quay/claircore/internal/rpm"
98
"github.com/quay/claircore/libvuln/driver"
109
)
1110

@@ -43,21 +42,6 @@ func (*Matcher) Query() []driver.MatchConstraint {
4342
}
4443
}
4544

46-
func (*Matcher) Vulnerable(_ context.Context, record *claircore.IndexRecord, vuln *claircore.Vulnerability) (bool, error) {
47-
pkgVer := version.NewVersion(record.Package.Version)
48-
var vulnVer version.Version
49-
// Assume the vulnerability record we have is for the last known vulnerable
50-
// version, so greater versions aren't vulnerable.
51-
cmp := func(i int) bool { return i != version.GREATER }
52-
// But if it's explicitly marked as a fixed-in version, it's only vulnerable
53-
// if less than that version.
54-
if vuln.FixedInVersion != "" {
55-
vulnVer = version.NewVersion(vuln.FixedInVersion)
56-
cmp = func(i int) bool { return i == version.LESS }
57-
} else {
58-
// If a vulnerability doesn't have FixedInVersion, assume it is unfixed.
59-
vulnVer = version.NewVersion("65535:0")
60-
}
61-
// compare version and architecture
62-
return cmp(pkgVer.Compare(vulnVer)) && vuln.ArchOperation.Cmp(record.Package.Arch, vuln.Package.Arch), nil
45+
func (*Matcher) Vulnerable(ctx context.Context, record *claircore.IndexRecord, vuln *claircore.Vulnerability) (bool, error) {
46+
return rpm.MatchVulnerable(ctx, record, vuln)
6347
}

go.mod

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ require (
1111
github.com/klauspost/compress v1.18.5
1212
github.com/knqyf263/go-apk-version v0.0.0-20200609155635-041fdbb8563f
1313
github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d
14-
github.com/knqyf263/go-rpm-version v0.0.0-20170716094938-74609b86c936
1514
github.com/package-url/packageurl-go v0.1.5
1615
github.com/prometheus/client_golang v1.23.2
1716
github.com/quay/claircore/toolkit v1.4.0

go.sum

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,6 @@ github.com/knqyf263/go-apk-version v0.0.0-20200609155635-041fdbb8563f h1:GvCU5GX
4545
github.com/knqyf263/go-apk-version v0.0.0-20200609155635-041fdbb8563f/go.mod h1:q59u9px8b7UTj0nIjEjvmTWekazka6xIt6Uogz5Dm+8=
4646
github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d h1:X4cedH4Kn3JPupAwwWuo4AzYp16P0OyLO9d7OnMZc/c=
4747
github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d/go.mod h1:o8sgWoz3JADecfc/cTYD92/Et1yMqMy0utV1z+VaZao=
48-
github.com/knqyf263/go-rpm-version v0.0.0-20170716094938-74609b86c936 h1:HDjRqotkViMNcGMGicb7cgxklx8OwnjtCBmyWEqrRvM=
49-
github.com/knqyf263/go-rpm-version v0.0.0-20170716094938-74609b86c936/go.mod h1:i4sF0l1fFnY1aiw08QQSwVAFxHEm311Me3WsU/X7nL0=
5048
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
5149
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
5250
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=

oracle/matcher.go

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@ package oracle
33
import (
44
"context"
55

6-
version "github.com/knqyf263/go-rpm-version"
7-
86
"github.com/quay/claircore"
7+
"github.com/quay/claircore/internal/rpm"
98
"github.com/quay/claircore/libvuln/driver"
109
)
1110

@@ -14,17 +13,17 @@ const (
1413
OSReleaseName = "Oracle Linux Server"
1514
)
1615

17-
// Matcher implements driver.Matcher
16+
// Matcher is an Oracle Linux matcher.
1817
type Matcher struct{}
1918

2019
var _ driver.Matcher = (*Matcher)(nil)
2120

22-
// Name implements driver.Matcher
21+
// Name implements [driver.Matcher].
2322
func (*Matcher) Name() string {
2423
return "oracle"
2524
}
2625

27-
// Filter implements driver.Matcher
26+
// Filter implements [driver.Matcher].
2827
func (*Matcher) Filter(record *claircore.IndexRecord) bool {
2928
if record.Distribution == nil {
3029
return false
@@ -40,7 +39,7 @@ func (*Matcher) Filter(record *claircore.IndexRecord) bool {
4039
}
4140
}
4241

43-
// Query implements driver.Matcher
42+
// Query implements [driver.Matcher].
4443
func (*Matcher) Query() []driver.MatchConstraint {
4544
return []driver.MatchConstraint{
4645
driver.DistributionDID,
@@ -49,17 +48,7 @@ func (*Matcher) Query() []driver.MatchConstraint {
4948
}
5049
}
5150

52-
// Vulnerable implements driver.Matcher
51+
// Vulnerable implements [driver.Matcher].
5352
func (*Matcher) Vulnerable(ctx context.Context, record *claircore.IndexRecord, vuln *claircore.Vulnerability) (bool, error) {
54-
pkgVer, vulnVer := version.NewVersion(record.Package.Version), version.NewVersion(vuln.Package.Version)
55-
// Assume the vulnerability record we have is for the last known vulnerable
56-
// version, so greater versions aren't vulnerable.
57-
cmp := func(i int) bool { return i != version.GREATER }
58-
// But if it's explicitly marked as a fixed-in version, it't only vulnerable
59-
// if less than that version.
60-
if vuln.FixedInVersion != "" {
61-
vulnVer = version.NewVersion(vuln.FixedInVersion)
62-
cmp = func(i int) bool { return i == version.LESS }
63-
}
64-
return cmp(pkgVer.Compare(vulnVer)) && vuln.ArchOperation.Cmp(record.Package.Arch, vuln.Package.Arch), nil
53+
return rpm.MatchVulnerable(ctx, record, vuln)
6554
}

photon/matcher.go

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,29 +3,28 @@ package photon
33
import (
44
"context"
55

6-
version "github.com/knqyf263/go-rpm-version"
7-
86
"github.com/quay/claircore"
7+
"github.com/quay/claircore/internal/rpm"
98
"github.com/quay/claircore/libvuln/driver"
109
)
1110

12-
// Matcher implements driver.Matcher.
11+
// Matcher implements [driver.Matcher].
1312
type Matcher struct{}
1413

1514
var _ driver.Matcher = (*Matcher)(nil)
1615

17-
// Name implements driver.Matcher.
16+
// Name implements [driver.Matcher].
1817
func (*Matcher) Name() string {
1918
return "photon"
2019
}
2120

22-
// Filter implements driver.Matcher.
21+
// Filter implements [driver.Matcher].
2322
func (*Matcher) Filter(record *claircore.IndexRecord) bool {
2423
return record.Distribution != nil &&
2524
record.Distribution.DID == "photon"
2625
}
2726

28-
// Query implements driver.Matcher.
27+
// Query implements [driver.Matcher].
2928
func (*Matcher) Query() []driver.MatchConstraint {
3029
return []driver.MatchConstraint{
3130
driver.DistributionDID,
@@ -34,17 +33,7 @@ func (*Matcher) Query() []driver.MatchConstraint {
3433
}
3534
}
3635

37-
// Vulnerable implements driver.Matcher.
36+
// Vulnerable implements [driver.Matcher].
3837
func (*Matcher) Vulnerable(ctx context.Context, record *claircore.IndexRecord, vuln *claircore.Vulnerability) (bool, error) {
39-
pkgVer, vulnVer := version.NewVersion(record.Package.Version), version.NewVersion(vuln.Package.Version)
40-
// Assume the vulnerability record we have is for the last known vulnerable
41-
// version, so greater versions aren't vulnerable.
42-
cmp := func(i int) bool { return i != version.GREATER }
43-
// But if it's explicitly marked as a fixed-in version, it't only vulnerable
44-
// if less than that version.
45-
if vuln.FixedInVersion != "" {
46-
vulnVer = version.NewVersion(vuln.FixedInVersion)
47-
cmp = func(i int) bool { return i == version.LESS }
48-
}
49-
return cmp(pkgVer.Compare(vulnVer)), nil
38+
return rpm.MatchVulnerable(ctx, record, vuln)
5039
}

pkg/rhctag/version.go

Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,33 +3,39 @@
33
package rhctag
44

55
import (
6+
"errors"
67
"fmt"
78
"math"
89
"strconv"
910
"strings"
1011

11-
rpmVersion "github.com/knqyf263/go-rpm-version"
12-
1312
"github.com/quay/claircore"
13+
"github.com/quay/claircore/internal/rpmver"
1414
)
1515

16-
// Allows extracting the Major and Minor versions so that we don't compare container tags from different minor versions.
16+
// Version allows extracting the "major" and "minor" versions so that we don't compare container tags from different minor versions.
17+
//
1718
// This is a workaround for another problem where not all minor releases of containers have a unique CPE.
18-
// Take for example ocs4/rook-ceph-rhel8-operator, it shipped at least 2 minor releases into the same container repository.
19-
// Both those major versions use the same CPE "cpe:/a:redhat:openshift_container_storage:4"
20-
// Here are 2 example tags which fixed CVE-2020-8565
21-
// 4.7 minor: 4.7-140.49a6fcf.release_4.7
22-
// 4.8 minor: 4.8-167.9a9db5f.release_4.8
23-
24-
// This class also handles container tags which have a 'v' prefix, for example openshift4/ose-metering-hive
25-
// 4.6 minor: v4.6.0-202112140546.p0.g8b9da97.assembly.stream
26-
// 4.7 minor: v4.7.0-202112140553.p0.g091bb99.assembly.stream
19+
//
20+
// For example, take "ocs4/rook-ceph-rhel8-operator": it shipped at least 2 minor releases into the same container repository.
21+
// Both those major versions use the same CPE: "cpe:/a:redhat:openshift_container_storage:4".
22+
// Here are 2 example tags which fixed CVE-2020-8565:
23+
// - 4.7-140.49a6fcf.release_4.7
24+
// - 4.8-167.9a9db5f.release_4.8
25+
//
26+
// This type also handles container tags which have a 'v' prefix (e.g. "openshift4/ose-metering-hive").
27+
// - v4.6.0-202112140546.p0.g8b9da97.assembly.stream
28+
// - v4.7.0-202112140553.p0.g091bb99.assembly.stream
2729
type Version struct {
2830
Original string
2931
Major int
3032
Minor int
3133
}
3234

35+
// Version turns the reciever into a [claircore.Version].
36+
//
37+
// "Min" controls whether the returned version is the minimum within the minor
38+
// series or the maximum.
3339
func (v *Version) Version(min bool) (c claircore.Version) {
3440
const (
3541
major = 0
@@ -64,7 +70,7 @@ func upToDot(s string) (value int, remainder string, err error) {
6470
if err == nil {
6571
return value, remainder, nil
6672
}
67-
return value, remainder, fmt.Errorf("Could not parse %s as an int", s)
73+
return value, remainder, fmt.Errorf("could not parse %q as int", s)
6874
}
6975

7076
// Parse attempts to extract a Red Hat container registry tag version string
@@ -75,7 +81,7 @@ func Parse(s string) (v Version, err error) {
7581
// remove the leading "v" prefix
7682
canonical = s[1:]
7783
}
78-
//strip revision
84+
// strip revision
7985
dashIndex := strings.Index(canonical, "-")
8086
if dashIndex > 0 {
8187
canonical = canonical[:dashIndex]
@@ -105,13 +111,26 @@ func (v *Version) MinorStart() (start Version) {
105111
return start
106112
}
107113

114+
// Compare is a comparison for the provided [Version]s.
108115
func (v *Version) Compare(x *Version) int {
109-
thisRpmVersion := rpmVersion.NewVersion(v.Original)
110-
otherRpmVersion := rpmVersion.NewVersion(x.Original)
111-
return thisRpmVersion.Compare(otherRpmVersion)
116+
a, aErr := rpmver.Parse(v.toEVR())
117+
b, bErr := rpmver.Parse(x.toEVR())
118+
if err := errors.Join(aErr, bErr); err != nil {
119+
panic(fmt.Errorf("unable to compare versions: %w", err))
120+
}
121+
return rpmver.Compare(&a, &b)
122+
}
123+
124+
func (v *Version) toEVR() string {
125+
s := v.Original
126+
s = strings.TrimPrefix(s, "v")
127+
if !strings.Contains(s, "-") {
128+
s = s + "-0"
129+
}
130+
return s
112131
}
113132

114-
// Versions implements sort.Interface.
133+
// Versions implements [sort.Interface].
115134
type Versions []Version
116135

117136
func (vs Versions) Len() int {

rhel/matcher.go

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@ import (
55
"log/slog"
66
"strings"
77

8-
version "github.com/knqyf263/go-rpm-version"
9-
108
"github.com/quay/claircore"
9+
"github.com/quay/claircore/internal/rpm"
1110
"github.com/quay/claircore/libvuln/driver"
1211
"github.com/quay/claircore/toolkit/types/cpe"
1312
)
@@ -91,21 +90,5 @@ func (m *Matcher) Vulnerable(ctx context.Context, record *claircore.IndexRecord,
9190
return false, nil
9291
}
9392

94-
// TODO(hank) Switch to the [rpmver] package.
95-
pkgVer := version.NewVersion(record.Package.Version)
96-
var vulnVer version.Version
97-
// Assume the vulnerability record we have is for the last known vulnerable
98-
// version, so greater versions aren't vulnerable.
99-
cmp := func(i int) bool { return i != version.GREATER }
100-
// But if it's explicitly marked as a fixed-in version, it's only vulnerable
101-
// if less than that version.
102-
if vuln.FixedInVersion != "" {
103-
vulnVer = version.NewVersion(vuln.FixedInVersion)
104-
cmp = func(i int) bool { return i == version.LESS }
105-
} else {
106-
// If a vulnerability doesn't have FixedInVersion, assume it is unfixed.
107-
vulnVer = version.NewVersion("65535:0")
108-
}
109-
// compare version and architecture
110-
return cmp(pkgVer.Compare(vulnVer)) && vuln.ArchOperation.Cmp(record.Package.Arch, vuln.Package.Arch), nil
93+
return rpm.MatchVulnerable(ctx, record, vuln)
11194
}

rhel/rhcc/matcher.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ package rhcc
22

33
import (
44
"context"
5+
"fmt"
56
"log/slog"
67

7-
rpmVersion "github.com/knqyf263/go-rpm-version"
8-
98
"github.com/quay/claircore"
9+
"github.com/quay/claircore/internal/rpmver"
1010
"github.com/quay/claircore/libvuln/driver"
1111
"github.com/quay/claircore/rhel"
1212
"github.com/quay/claircore/toolkit/types/cpe"
@@ -50,9 +50,17 @@ func (*matcher) Vulnerable(ctx context.Context, record *claircore.IndexRecord, v
5050
return false, nil
5151
}
5252
}
53-
pkgVer, fixedInVer := rpmVersion.NewVersion(record.Package.Version), rpmVersion.NewVersion(vuln.FixedInVersion)
53+
5454
slog.DebugContext(ctx, "comparing versions", "record", record.Package.Version, "vulnerability", vuln.FixedInVersion)
55-
return pkgVer.LessThan(fixedInVer), nil
55+
pkgVer, err := rpmver.Parse(record.Package.Version)
56+
if err != nil {
57+
return false, fmt.Errorf("rhcc: unable to parse version %q: %w", record.Package.Version, err)
58+
}
59+
fixedVer, err := rpmver.Parse(vuln.FixedInVersion)
60+
if err != nil {
61+
return false, fmt.Errorf("rhcc: unable to parse version %q: %w", vuln.FixedInVersion, err)
62+
}
63+
return rpmver.Compare(&pkgVer, &fixedVer) == -1, nil
5664
}
5765

5866
// Implement version filtering to have the database only return results for the

suse/matcher.go

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@ import (
44
"context"
55
"slices"
66

7-
version "github.com/knqyf263/go-rpm-version"
8-
97
"github.com/quay/claircore"
8+
"github.com/quay/claircore/internal/rpm"
109
"github.com/quay/claircore/libvuln/driver"
1110
)
1211

@@ -15,17 +14,17 @@ var (
1514
OSReleaseNames = []string{"SLES", "openSUSE Leap"}
1615
)
1716

18-
// Matcher implements driver.Matcher
17+
// Matcher implements [driver.Matcher]
1918
type Matcher struct{}
2019

2120
var _ driver.Matcher = (*Matcher)(nil)
2221

23-
// Name implements driver.Matcher
22+
// Name implements [driver.Matcher]
2423
func (*Matcher) Name() string {
2524
return "suse"
2625
}
2726

28-
// Filter implements driver.Matcher
27+
// Filter implements [driver.Matcher]
2928
func (*Matcher) Filter(record *claircore.IndexRecord) bool {
3029
if record.Distribution == nil {
3130
return false
@@ -41,7 +40,7 @@ func (*Matcher) Filter(record *claircore.IndexRecord) bool {
4140
}
4241
}
4342

44-
// Query implements driver.Matcher
43+
// Query implements [driver.Matcher]
4544
func (*Matcher) Query() []driver.MatchConstraint {
4645
return []driver.MatchConstraint{
4746
driver.DistributionDID,
@@ -50,17 +49,7 @@ func (*Matcher) Query() []driver.MatchConstraint {
5049
}
5150
}
5251

53-
// Vulnerable implements driver.Matcher
52+
// Vulnerable implements [driver.Matcher]
5453
func (*Matcher) Vulnerable(ctx context.Context, record *claircore.IndexRecord, vuln *claircore.Vulnerability) (bool, error) {
55-
pkgVer, vulnVer := version.NewVersion(record.Package.Version), version.NewVersion(vuln.Package.Version)
56-
// Assume the vulnerability record we have is for the last known vulnerable
57-
// version, so greater versions aren't vulnerable.
58-
cmp := func(i int) bool { return i != version.GREATER }
59-
// But if it's explicitly marked as a fixed-in version, it't only vulnerable
60-
// if less than that version.
61-
if vuln.FixedInVersion != "" {
62-
vulnVer = version.NewVersion(vuln.FixedInVersion)
63-
cmp = func(i int) bool { return i == version.LESS }
64-
}
65-
return cmp(pkgVer.Compare(vulnVer)) && vuln.ArchOperation.Cmp(record.Package.Arch, vuln.Package.Arch), nil
54+
return rpm.MatchVulnerable(ctx, record, vuln)
6655
}

0 commit comments

Comments
 (0)