Skip to content

Commit a6881c2

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 c3ca57f commit a6881c2

2 files changed

Lines changed: 285 additions & 0 deletions

File tree

enricher/rhcc/rhcc.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package rhcc
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
8+
"github.com/quay/claircore"
9+
"github.com/quay/claircore/libvuln/driver"
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.RepositoryHint == "rhcc" && p.Kind == claircore.BINARY {
29+
if envs, ok := r.Environments[id]; ok {
30+
for _, e := range envs {
31+
problematicPkgs[e.IntroducedIn.String()] = id
32+
}
33+
} else {
34+
return Type, nil, fmt.Errorf("no environment found for package %s", id)
35+
}
36+
}
37+
}
38+
39+
b, err := json.Marshal(problematicPkgs)
40+
if err != nil {
41+
return Type, nil, err
42+
}
43+
return Type, []json.RawMessage{b}, nil
44+
}

enricher/rhcc/rhcc_test.go

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

0 commit comments

Comments
 (0)