Skip to content

Commit 27eb16c

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 <[email protected]>
1 parent b6aa249 commit 27eb16c

File tree

8 files changed

+301
-14
lines changed

8 files changed

+301
-14
lines changed

enricher/rhcc/rhcc.go

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
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 schema=??`
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, p := range r.Packages {
28+
if p.Kind == "binary" {
29+
if envs, ok := r.Environments[id]; ok {
30+
for _, e := range envs {
31+
for _, repoID := range e.RepositoryIDs {
32+
repo := r.Repositories[repoID]
33+
if repo.Name == rhcc.GoldRepo.Name {
34+
problematicPkgs[e.IntroducedIn.String()] = id
35+
}
36+
}
37+
}
38+
}
39+
}
40+
}
41+
42+
b, err := json.Marshal(problematicPkgs)
43+
if err != nil {
44+
return Type, nil, err
45+
}
46+
return Type, []json.RawMessage{b}, nil
47+
}

enricher/rhcc/rhcc_test.go

+240
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
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: "vuln in package in different layer from rhcc package",
40+
vr: &claircore.VulnerabilityReport{
41+
Packages: map[string]*claircore.Package{
42+
"1": {
43+
Name: "some-rh-package-slash-image",
44+
RepositoryHint: "rhcc",
45+
Version: "v1.0.0",
46+
Kind: claircore.BINARY,
47+
},
48+
"2": {
49+
Name: "grafana",
50+
Version: "v4.7.0",
51+
Kind: claircore.BINARY,
52+
},
53+
},
54+
Environments: map[string][]*claircore.Environment{
55+
"1": {{IntroducedIn: firstLayerHash}},
56+
"2": {{IntroducedIn: secondLayerHash}},
57+
},
58+
Vulnerabilities: map[string]*claircore.Vulnerability{
59+
"4": {
60+
Name: "something bad with grafana",
61+
FixedInVersion: "v100.0.0",
62+
},
63+
},
64+
PackageVulnerabilities: map[string][]string{
65+
"2": {"4"},
66+
},
67+
},
68+
layers: []*claircore.Layer{
69+
{Hash: firstLayerHash},
70+
{Hash: secondLayerHash},
71+
},
72+
want: map[string]string{firstLayerHash.String(): "1"},
73+
},
74+
{
75+
name: "vuln in package in same layer as rhcc package",
76+
vr: &claircore.VulnerabilityReport{
77+
Packages: map[string]*claircore.Package{
78+
"1": {
79+
Name: "some-rh-package-slash-image",
80+
RepositoryHint: "rhcc",
81+
Version: "v1.0.0",
82+
Kind: claircore.BINARY,
83+
},
84+
"2": {
85+
Name: "grafana",
86+
Version: "v4.7.0",
87+
Kind: claircore.BINARY,
88+
},
89+
},
90+
Environments: map[string][]*claircore.Environment{
91+
"1": {{IntroducedIn: firstLayerHash}},
92+
"2": {{IntroducedIn: firstLayerHash}},
93+
},
94+
Vulnerabilities: map[string]*claircore.Vulnerability{
95+
"4": {
96+
Name: "something bad with grafana",
97+
FixedInVersion: "v100.0.0",
98+
},
99+
},
100+
PackageVulnerabilities: map[string][]string{
101+
"2": {"4"},
102+
},
103+
},
104+
layers: []*claircore.Layer{
105+
{Hash: firstLayerHash},
106+
{Hash: secondLayerHash},
107+
},
108+
want: map[string]string{firstLayerHash.String(): "1"},
109+
},
110+
{
111+
name: "vuln in package in same layer as rhcc package and rhcc vuln in same layer",
112+
vr: &claircore.VulnerabilityReport{
113+
Packages: map[string]*claircore.Package{
114+
"1": {
115+
Name: "some-rh-package-slash-image",
116+
RepositoryHint: "rhcc",
117+
Version: "v1.0.0",
118+
Kind: claircore.BINARY,
119+
},
120+
"2": {
121+
Name: "grafana",
122+
Version: "v4.7.0",
123+
Kind: claircore.BINARY,
124+
},
125+
},
126+
Environments: map[string][]*claircore.Environment{
127+
"1": {{IntroducedIn: firstLayerHash}},
128+
"2": {{IntroducedIn: firstLayerHash}},
129+
},
130+
Vulnerabilities: map[string]*claircore.Vulnerability{
131+
"4": {
132+
Name: "something bad with grafana",
133+
FixedInVersion: "v100.0.0",
134+
},
135+
"5": {
136+
Name: "something bad ubi",
137+
FixedInVersion: "v100.0.0",
138+
},
139+
},
140+
PackageVulnerabilities: map[string][]string{
141+
"2": {"4"},
142+
"1": {"5"},
143+
},
144+
},
145+
layers: []*claircore.Layer{
146+
{Hash: firstLayerHash},
147+
{Hash: secondLayerHash},
148+
},
149+
want: map[string]string{firstLayerHash.String(): "1"},
150+
},
151+
{
152+
name: "multiple rhcc packages in different layers",
153+
vr: &claircore.VulnerabilityReport{
154+
Packages: map[string]*claircore.Package{
155+
"1": {
156+
Name: "some-rh-package-slash-image",
157+
RepositoryHint: "rhcc",
158+
Version: "v1.0.0",
159+
Kind: claircore.BINARY,
160+
},
161+
"2": {
162+
Name: "some-other-rh-package-slash-image",
163+
RepositoryHint: "rhcc",
164+
Version: "v1.0.0",
165+
Kind: claircore.BINARY,
166+
},
167+
"3": {
168+
Name: "grafana",
169+
Version: "v4.7.0",
170+
Kind: claircore.BINARY,
171+
},
172+
},
173+
Environments: map[string][]*claircore.Environment{
174+
"1": {{IntroducedIn: firstLayerHash}},
175+
"2": {{IntroducedIn: secondLayerHash}},
176+
"3": {{IntroducedIn: firstLayerHash}},
177+
},
178+
Vulnerabilities: map[string]*claircore.Vulnerability{
179+
"4": {
180+
Name: "something bad with grafana",
181+
FixedInVersion: "v100.0.0",
182+
},
183+
"5": {
184+
Name: "something bad ubi",
185+
FixedInVersion: "v100.0.0",
186+
},
187+
"6": {
188+
Name: "something bad s2i",
189+
FixedInVersion: "v100.0.0",
190+
},
191+
},
192+
PackageVulnerabilities: map[string][]string{
193+
"3": {"4"},
194+
"1": {"5"},
195+
"2": {"6"},
196+
},
197+
},
198+
layers: []*claircore.Layer{
199+
{Hash: firstLayerHash},
200+
{Hash: secondLayerHash},
201+
},
202+
want: map[string]string{firstLayerHash.String(): "1", secondLayerHash.String(): "2"},
203+
},
204+
}
205+
206+
e := &Enricher{}
207+
nog := &noopGetter{}
208+
for _, tc := range tests {
209+
t.Run(tc.name, func(t *testing.T) {
210+
tp, data, err := e.Enrich(ctx, nog, tc.vr)
211+
if err != nil {
212+
t.Fatal(err)
213+
}
214+
if tp != "message/vnd.clair.map.layer; enricher=clair.rhcc schema=??" {
215+
t.Fatal("wrong type")
216+
}
217+
got := make(map[string]string)
218+
if err := json.Unmarshal(data[0], &got); err != nil {
219+
t.Error(err)
220+
}
221+
if !cmp.Equal(got, tc.want) {
222+
t.Error(cmp.Diff(got, tc.want))
223+
}
224+
})
225+
226+
}
227+
}
228+
229+
func TestName(t *testing.T) {
230+
e := &Enricher{}
231+
if e.Name() != "rhcc" {
232+
t.Fatal("name should be rhcc")
233+
}
234+
}
235+
236+
type noopGetter struct{}
237+
238+
func (f *noopGetter) GetEnrichment(ctx context.Context, tags []string) ([]driver.EnrichmentRecord, error) {
239+
return nil, nil
240+
}

rhel/rhcc/coalescer_test.go

+2-2
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

+1-1
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

+8-8
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

+1-1
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
}

0 commit comments

Comments
 (0)