Skip to content

Commit 90a888a

Browse files
committed
enricher: add RHCC enricher
This change introduces a new enricher that reports where rhcc packages exist (if at all), it allows callers to discount vulnerabilities / packages that come from the same layers. This approach helps to keep the index report unchanged and therefore state is less of an issue, it also builds on existing machinary. Signed-off-by: crozzy <joseph.crosland@gmail.com>
1 parent ecc9d8d commit 90a888a

8 files changed

Lines changed: 277 additions & 14 deletions

File tree

enricher/rhcc/rhcc.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package rhcc
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
7+
"github.com/quay/claircore"
8+
"github.com/quay/claircore/libvuln/driver"
9+
"github.com/quay/claircore/rhel/rhcc"
10+
)
11+
12+
type Enricher struct{}
13+
14+
var (
15+
_ driver.Enricher = (*Enricher)(nil)
16+
)
17+
18+
const (
19+
// Type is the type of data returned from the Enricher's Enrich method.
20+
Type = `message/vnd.clair.map.layer; enricher=clair.rhcc`
21+
)
22+
23+
func (e *Enricher) Name() string { return "rhcc" }
24+
25+
func (e *Enricher) Enrich(ctx context.Context, g driver.EnrichmentGetter, r *claircore.VulnerabilityReport) (string, []json.RawMessage, error) {
26+
problematicPkgs := make(map[string]string)
27+
for id, _ := range r.Packages {
28+
if envs, ok := r.Environments[id]; ok {
29+
for _, e := range envs {
30+
for _, repoID := range e.RepositoryIDs {
31+
repo := r.Repositories[repoID]
32+
if repo.Name == rhcc.GoldRepo.Name {
33+
problematicPkgs[id] = e.IntroducedIn.String()
34+
}
35+
}
36+
}
37+
}
38+
}
39+
40+
b, err := json.Marshal(problematicPkgs)
41+
if err != nil {
42+
return Type, nil, err
43+
}
44+
return Type, []json.RawMessage{b}, nil
45+
}

enricher/rhcc/rhcc_test.go

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
package rhcc
2+
3+
import (
4+
"context"
5+
"crypto/sha256"
6+
"encoding/json"
7+
"io"
8+
"testing"
9+
10+
"github.com/google/go-cmp/cmp"
11+
"github.com/quay/zlog"
12+
13+
"github.com/quay/claircore"
14+
"github.com/quay/claircore/libvuln/driver"
15+
)
16+
17+
func Digest(name string) claircore.Digest {
18+
h := sha256.New()
19+
io.WriteString(h, name)
20+
d, err := claircore.NewDigest("sha256", h.Sum(nil))
21+
if err != nil {
22+
panic(err)
23+
}
24+
return d
25+
}
26+
27+
func TestEnrich(t *testing.T) {
28+
t.Parallel()
29+
ctx := zlog.Test(context.Background(), t)
30+
firstLayerHash := Digest("first layer")
31+
secondLayerHash := Digest("second layer")
32+
tests := []struct {
33+
name string
34+
vr *claircore.VulnerabilityReport
35+
layers []*claircore.Layer
36+
want map[string]string
37+
}{
38+
{
39+
name: "one package that is a layer one that isn't",
40+
vr: &claircore.VulnerabilityReport{
41+
Packages: map[string]*claircore.Package{
42+
"1": {
43+
Name: "some-rh-package-slash-image",
44+
Version: "v1.0.0",
45+
Kind: claircore.BINARY,
46+
},
47+
"2": {
48+
Name: "grafana",
49+
Version: "v4.7.0",
50+
Kind: claircore.BINARY,
51+
},
52+
},
53+
Environments: map[string][]*claircore.Environment{
54+
"1": {{IntroducedIn: firstLayerHash, RepositoryIDs: []string{"1"}}},
55+
"2": {{IntroducedIn: secondLayerHash}},
56+
},
57+
Repositories: map[string]*claircore.Repository{
58+
"1": {
59+
ID: "1",
60+
Name: "Red Hat Container Catalog",
61+
URI: "https://catalog.redhat.com/software/containers/explore",
62+
},
63+
},
64+
},
65+
layers: []*claircore.Layer{
66+
{Hash: firstLayerHash},
67+
{Hash: secondLayerHash},
68+
},
69+
want: map[string]string{"1": firstLayerHash.String()},
70+
},
71+
{
72+
name: "two packages, neither are layers",
73+
vr: &claircore.VulnerabilityReport{
74+
Packages: map[string]*claircore.Package{
75+
"1": {
76+
Name: "cool app",
77+
Version: "v1.0.0",
78+
Kind: claircore.BINARY,
79+
},
80+
"2": {
81+
Name: "grafana",
82+
Version: "v4.7.0",
83+
Kind: claircore.BINARY,
84+
},
85+
},
86+
Environments: map[string][]*claircore.Environment{
87+
"1": {{IntroducedIn: firstLayerHash}},
88+
"2": {{IntroducedIn: firstLayerHash}},
89+
},
90+
},
91+
layers: []*claircore.Layer{
92+
{Hash: firstLayerHash},
93+
{Hash: secondLayerHash},
94+
},
95+
want: map[string]string{},
96+
},
97+
{
98+
name: "multiple rhcc packages in different layers",
99+
vr: &claircore.VulnerabilityReport{
100+
Packages: map[string]*claircore.Package{
101+
"1": {
102+
Name: "some-rh-package-slash-image",
103+
RepositoryHint: "rhcc",
104+
Version: "v1.0.0",
105+
Kind: claircore.BINARY,
106+
},
107+
"2": {
108+
Name: "some-other-rh-package-slash-image",
109+
RepositoryHint: "rhcc",
110+
Version: "v1.0.0",
111+
Kind: claircore.BINARY,
112+
},
113+
"3": {
114+
Name: "grafana",
115+
Version: "v4.7.0",
116+
Kind: claircore.BINARY,
117+
},
118+
},
119+
Environments: map[string][]*claircore.Environment{
120+
"1": {{IntroducedIn: firstLayerHash, RepositoryIDs: []string{"1"}}},
121+
"2": {{IntroducedIn: secondLayerHash, RepositoryIDs: []string{"1"}}},
122+
"3": {{IntroducedIn: firstLayerHash}},
123+
},
124+
Repositories: map[string]*claircore.Repository{
125+
"1": {
126+
ID: "1",
127+
Name: "Red Hat Container Catalog",
128+
URI: "https://catalog.redhat.com/software/containers/explore",
129+
},
130+
},
131+
},
132+
layers: []*claircore.Layer{
133+
{Hash: firstLayerHash},
134+
{Hash: secondLayerHash},
135+
},
136+
want: map[string]string{"1": firstLayerHash.String(), "2": secondLayerHash.String()},
137+
},
138+
{
139+
name: "multiple rhcc packages in same layers (source and binary)",
140+
vr: &claircore.VulnerabilityReport{
141+
Packages: map[string]*claircore.Package{
142+
"1": {
143+
Name: "some-rh-package-slash-image-binary",
144+
Version: "v1.0.0",
145+
Kind: claircore.BINARY,
146+
Source: &claircore.Package{
147+
Name: "some-rh-package-slash-image-source",
148+
Version: "v1.0.0",
149+
Kind: claircore.SOURCE,
150+
},
151+
},
152+
"2": {
153+
Name: "some-rh-package-slash-image-source",
154+
Version: "v1.0.0",
155+
Kind: claircore.SOURCE,
156+
},
157+
"3": {
158+
Name: "grafana",
159+
Version: "v4.7.0",
160+
Kind: claircore.BINARY,
161+
},
162+
},
163+
Environments: map[string][]*claircore.Environment{
164+
"1": {{IntroducedIn: firstLayerHash, RepositoryIDs: []string{"1"}}},
165+
"2": {{IntroducedIn: firstLayerHash, RepositoryIDs: []string{"1"}}},
166+
"3": {{IntroducedIn: secondLayerHash}},
167+
},
168+
Repositories: map[string]*claircore.Repository{
169+
"1": {
170+
ID: "1",
171+
Name: "Red Hat Container Catalog",
172+
URI: "https://catalog.redhat.com/software/containers/explore",
173+
},
174+
},
175+
},
176+
layers: []*claircore.Layer{
177+
{Hash: firstLayerHash},
178+
{Hash: secondLayerHash},
179+
},
180+
want: map[string]string{"1": firstLayerHash.String(), "2": firstLayerHash.String()},
181+
},
182+
}
183+
184+
e := &Enricher{}
185+
nog := &noopGetter{}
186+
for _, tc := range tests {
187+
t.Run(tc.name, func(t *testing.T) {
188+
tp, data, err := e.Enrich(ctx, nog, tc.vr)
189+
if err != nil {
190+
t.Fatal(err)
191+
}
192+
if tp != "message/vnd.clair.map.layer; enricher=clair.rhcc" {
193+
t.Fatal("wrong type")
194+
}
195+
got := make(map[string]string)
196+
if err := json.Unmarshal(data[0], &got); err != nil {
197+
t.Error(err)
198+
}
199+
if !cmp.Equal(got, tc.want) {
200+
t.Error(cmp.Diff(got, tc.want))
201+
}
202+
})
203+
204+
}
205+
}
206+
207+
func TestName(t *testing.T) {
208+
e := &Enricher{}
209+
if e.Name() != "rhcc" {
210+
t.Fatal("name should be rhcc")
211+
}
212+
}
213+
214+
type noopGetter struct{}
215+
216+
func (f *noopGetter) GetEnrichment(ctx context.Context, tags []string) ([]driver.EnrichmentRecord, error) {
217+
return nil, nil
218+
}

rhel/rhcc/coalescer_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ func TestCoalescer(t *testing.T) {
2121
// Mark them as if they came from this package's package scanner
2222
p.RepositoryHint = `rhcc`
2323
}
24-
repo := []*claircore.Repository{&goldRepo}
24+
repo := []*claircore.Repository{&GoldRepo}
2525
layerArtifacts := []*indexer.LayerArtifacts{
2626
{
2727
Hash: test.RandomSHA256Digest(t),
@@ -67,7 +67,7 @@ func TestCoalescer(t *testing.T) {
6767
}
6868
for _, id := range e.RepositoryIDs {
6969
r := ir.Repositories[id]
70-
if got, want := r.Name, goldRepo.Name; got != want {
70+
if got, want := r.Name, GoldRepo.Name; got != want {
7171
t.Errorf("got: %q, want: %q", got, want)
7272
}
7373
}

rhel/rhcc/matcher.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ func (*matcher) Name() string { return "rhel-container-matcher" }
2626
// Filter implements [driver.Matcher].
2727
func (*matcher) Filter(r *claircore.IndexRecord) bool {
2828
return r.Repository != nil &&
29-
r.Repository.Name == goldRepo.Name
29+
r.Repository.Name == GoldRepo.Name
3030
}
3131

3232
// Query implements [driver.Matcher].

rhel/rhcc/parser_test.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ func TestDB(t *testing.T) {
3939
Links: "https://access.redhat.com/errata/RHSA-2021:3665 https://access.redhat.com/security/cve/CVE-2021-3762",
4040
NormalizedSeverity: claircore.High,
4141
FixedInVersion: "v3.5.7-8",
42-
Repo: &goldRepo,
42+
Repo: &GoldRepo,
4343
Range: &claircore.Range{
4444
Lower: claircore.Version{
4545
Kind: "rhctag",
@@ -81,7 +81,7 @@ func TestDB(t *testing.T) {
8181
},
8282
},
8383
FixedInVersion: "v4.6.0-202112140546.p0.g8b9da97.assembly.stream",
84-
Repo: &goldRepo,
84+
Repo: &GoldRepo,
8585
},
8686
{
8787
Name: "RHSA-2021:5107",
@@ -103,7 +103,7 @@ func TestDB(t *testing.T) {
103103
},
104104
},
105105
FixedInVersion: "v4.7.0-202112140553.p0.g091bb99.assembly.stream",
106-
Repo: &goldRepo,
106+
Repo: &GoldRepo,
107107
},
108108
{
109109
Name: "RHSA-2021:5108",
@@ -125,7 +125,7 @@ func TestDB(t *testing.T) {
125125
},
126126
},
127127
FixedInVersion: "v4.8.0-202112132154.p0.g57dd03a.assembly.stream",
128-
Repo: &goldRepo,
128+
Repo: &GoldRepo,
129129
},
130130
},
131131
},
@@ -153,7 +153,7 @@ func TestDB(t *testing.T) {
153153
},
154154
},
155155
FixedInVersion: "v6.8.1-65",
156-
Repo: &goldRepo,
156+
Repo: &GoldRepo,
157157
},
158158
{
159159
Name: "RHSA-2021:5137",
@@ -175,7 +175,7 @@ func TestDB(t *testing.T) {
175175
},
176176
},
177177
FixedInVersion: "v5.0.10-1",
178-
Repo: &goldRepo,
178+
Repo: &GoldRepo,
179179
},
180180
},
181181
},
@@ -203,7 +203,7 @@ func TestDB(t *testing.T) {
203203
},
204204
},
205205
FixedInVersion: "4.8-167.9a9db5f.release_4.8",
206-
Repo: &goldRepo,
206+
Repo: &GoldRepo,
207207
},
208208
{
209209
Name: "RHSA-2021:2041",
@@ -225,7 +225,7 @@ func TestDB(t *testing.T) {
225225
},
226226
},
227227
FixedInVersion: "4.7-140.49a6fcf.release_4.7",
228-
Repo: &goldRepo,
228+
Repo: &GoldRepo,
229229
},
230230
},
231231
},

rhel/rhcc/rhcc.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import (
1414
"github.com/quay/claircore/toolkit/types/cpe"
1515
)
1616

17-
var goldRepo = claircore.Repository{
17+
var GoldRepo = claircore.Repository{
1818
Name: "Red Hat Container Catalog",
1919
URI: `https://catalog.redhat.com/software/containers/explore`,
2020
}

rhel/rhcc/scanner.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,5 +299,5 @@ func (s *reposcanner) Scan(ctx context.Context, l *claircore.Layer) ([]*claircor
299299
}
300300
zlog.Debug(ctx).
301301
Msg("found buildinfo Dockerfile")
302-
return []*claircore.Repository{&goldRepo}, nil
302+
return []*claircore.Repository{&GoldRepo}, nil
303303
}

rhel/rhcc/updater.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ func (u *updater) Parse(ctx context.Context, r io.ReadCloser) ([]*claircore.Vuln
288288
Severity: releasesByMinor[minor].Severity,
289289
NormalizedSeverity: common.NormalizeSeverity(releasesByMinor[minor].Severity),
290290
Package: p,
291-
Repo: &goldRepo,
291+
Repo: &GoldRepo,
292292
Links: links,
293293
FixedInVersion: firstPatch.Original,
294294
Range: r,

0 commit comments

Comments
 (0)