Skip to content

Commit b25c003

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 f2b7d47 commit b25c003

File tree

2 files changed

+275
-0
lines changed

2 files changed

+275
-0
lines changed

enricher/rhcc/rhcc.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
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+
rhccPkgs := make(map[string]string)
27+
for id, p := range r.Packages {
28+
if envs, ok := r.Environments[id]; ok && p.Kind == claircore.BINARY {
29+
for _, e := range envs {
30+
for _, repoID := range e.RepositoryIDs {
31+
repo := r.Repositories[repoID]
32+
if repo.Name == rhcc.GoldRepo.Name {
33+
rhccPkgs[id] = e.IntroducedIn.String()
34+
break
35+
}
36+
}
37+
}
38+
}
39+
}
40+
41+
if len(rhccPkgs) == 0 {
42+
return Type, nil, nil
43+
}
44+
b, err := json.Marshal(rhccPkgs)
45+
if err != nil {
46+
return Type, nil, err
47+
}
48+
return Type, []json.RawMessage{b}, nil
49+
}

enricher/rhcc/rhcc_test.go

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

0 commit comments

Comments
 (0)