Skip to content

Commit da2ddd0

Browse files
committed
enricher: add RHCC enricher
This change introduces an enricher who's purpose is to scope down the vulnerability report for layers that have RHCC packages. 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 dd6c601 commit da2ddd0

File tree

2 files changed

+291
-0
lines changed

2 files changed

+291
-0
lines changed

enricher/rhcc/rhcc.go

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
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+
)
10+
11+
type Enricher struct{}
12+
13+
var (
14+
_ driver.Enricher = (*Enricher)(nil)
15+
)
16+
17+
func (e *Enricher) Name() string { return "rhcc" }
18+
19+
func (e *Enricher) Enrich(ctx context.Context, g driver.EnrichmentGetter, r *claircore.VulnerabilityReport) (string, []json.RawMessage, error) {
20+
rhLayers := []claircore.Digest{}
21+
// TODO: Should we look at repos here? Nicer to find but harder to use.
22+
for id, p := range r.Packages {
23+
if p.RepositoryHint == "rhcc" {
24+
// Grab the layers where rhcc packages exist.
25+
for _, e := range r.Environments[id] {
26+
rhLayers = append(rhLayers, e.IntroducedIn)
27+
}
28+
}
29+
}
30+
problematicPkgIDs := make(map[string]struct{})
31+
// Check which packages come from those layers.
32+
for pkgID, es := range r.Environments {
33+
for _, e := range es {
34+
for _, rhl := range rhLayers {
35+
if e.IntroducedIn.String() == rhl.String() {
36+
problematicPkgIDs[pkgID] = struct{}{}
37+
}
38+
}
39+
}
40+
}
41+
42+
problematicVulnIDs := make(map[string]string)
43+
// Check if the packages have any vulnerabilities
44+
for pkgID, pkgV := range r.PackageVulnerabilities {
45+
if _, ok := problematicPkgIDs[pkgID]; ok {
46+
for _, vID := range pkgV {
47+
problematicVulnIDs[vID] = pkgID
48+
}
49+
}
50+
}
51+
52+
// Scope down the vulnerabilities
53+
finalVulnerabilities := make(map[string]*claircore.Vulnerability)
54+
finalPackageVulnerabilities := make(map[string][]string)
55+
for vID, vuln := range r.Vulnerabilities {
56+
if pkgID, ok := problematicVulnIDs[vID]; !ok || r.Packages[pkgID].RepositoryHint == "rhcc" {
57+
finalVulnerabilities[vID] = vuln
58+
finalPackageVulnerabilities[pkgID] = append(finalPackageVulnerabilities[pkgID], vID)
59+
}
60+
}
61+
r.Vulnerabilities = finalVulnerabilities
62+
r.PackageVulnerabilities = finalPackageVulnerabilities
63+
return "", nil, nil
64+
}

enricher/rhcc/rhcc_test.go

+227
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
package rhcc
2+
3+
import (
4+
"context"
5+
"crypto/sha256"
6+
"io"
7+
"testing"
8+
9+
"github.com/quay/zlog"
10+
11+
"github.com/quay/claircore"
12+
"github.com/quay/claircore/libvuln/driver"
13+
)
14+
15+
func Digest(name string) claircore.Digest {
16+
h := sha256.New()
17+
io.WriteString(h, name)
18+
d, err := claircore.NewDigest("sha256", h.Sum(nil))
19+
if err != nil {
20+
panic(err)
21+
}
22+
return d
23+
}
24+
25+
func TestEnrich(t *testing.T) {
26+
t.Parallel()
27+
ctx := zlog.Test(context.Background(), t)
28+
firstLayerHash := Digest("first layer")
29+
secondLayerHash := Digest("second layer")
30+
//thirdLayerHash := Digest("third layer")
31+
tests := []struct {
32+
name string
33+
vr *claircore.VulnerabilityReport
34+
layers []*claircore.Layer
35+
lenVulns, lenPkgVulns int
36+
}{
37+
{
38+
name: "vuln in package in different layer from rhcc package",
39+
vr: &claircore.VulnerabilityReport{
40+
Packages: map[string]*claircore.Package{
41+
"1": {
42+
Name: "some-rh-package-slash-image",
43+
RepositoryHint: "rhcc",
44+
Version: "v1.0.0",
45+
},
46+
"2": {
47+
Name: "grafana",
48+
Version: "v4.7.0",
49+
},
50+
},
51+
Environments: map[string][]*claircore.Environment{
52+
"1": {{IntroducedIn: firstLayerHash}},
53+
"2": {{IntroducedIn: secondLayerHash}},
54+
},
55+
Vulnerabilities: map[string]*claircore.Vulnerability{
56+
"4": {
57+
Name: "something bad with grafana",
58+
FixedInVersion: "v100.0.0",
59+
},
60+
},
61+
PackageVulnerabilities: map[string][]string{
62+
"2": {"4"},
63+
},
64+
},
65+
layers: []*claircore.Layer{
66+
{Hash: firstLayerHash},
67+
{Hash: secondLayerHash},
68+
},
69+
lenVulns: 1,
70+
lenPkgVulns: 1,
71+
},
72+
{
73+
name: "vuln in package in same layer as rhcc package",
74+
vr: &claircore.VulnerabilityReport{
75+
Packages: map[string]*claircore.Package{
76+
"1": {
77+
Name: "some-rh-package-slash-image",
78+
RepositoryHint: "rhcc",
79+
Version: "v1.0.0",
80+
},
81+
"2": {
82+
Name: "grafana",
83+
Version: "v4.7.0",
84+
},
85+
},
86+
Environments: map[string][]*claircore.Environment{
87+
"1": {{IntroducedIn: firstLayerHash}},
88+
"2": {{IntroducedIn: firstLayerHash}},
89+
},
90+
Vulnerabilities: map[string]*claircore.Vulnerability{
91+
"4": {
92+
Name: "something bad with grafana",
93+
FixedInVersion: "v100.0.0",
94+
},
95+
},
96+
PackageVulnerabilities: map[string][]string{
97+
"2": {"4"},
98+
},
99+
},
100+
layers: []*claircore.Layer{
101+
{Hash: firstLayerHash},
102+
{Hash: secondLayerHash},
103+
},
104+
lenVulns: 0,
105+
lenPkgVulns: 0,
106+
},
107+
{
108+
name: "vuln in package in same layer as rhcc package and rhcc vuln in same layer",
109+
vr: &claircore.VulnerabilityReport{
110+
Packages: map[string]*claircore.Package{
111+
"1": {
112+
Name: "some-rh-package-slash-image",
113+
RepositoryHint: "rhcc",
114+
Version: "v1.0.0",
115+
},
116+
"2": {
117+
Name: "grafana",
118+
Version: "v4.7.0",
119+
},
120+
},
121+
Environments: map[string][]*claircore.Environment{
122+
"1": {{IntroducedIn: firstLayerHash}},
123+
"2": {{IntroducedIn: firstLayerHash}},
124+
},
125+
Vulnerabilities: map[string]*claircore.Vulnerability{
126+
"4": {
127+
Name: "something bad with grafana",
128+
FixedInVersion: "v100.0.0",
129+
},
130+
"5": {
131+
Name: "something bad ubi",
132+
FixedInVersion: "v100.0.0",
133+
},
134+
},
135+
PackageVulnerabilities: map[string][]string{
136+
"2": {"4"},
137+
"1": {"5"},
138+
},
139+
},
140+
layers: []*claircore.Layer{
141+
{Hash: firstLayerHash},
142+
{Hash: secondLayerHash},
143+
},
144+
lenVulns: 1,
145+
lenPkgVulns: 1,
146+
},
147+
{
148+
name: "multiple rhcc packages in different layers",
149+
vr: &claircore.VulnerabilityReport{
150+
Packages: map[string]*claircore.Package{
151+
"1": {
152+
Name: "some-rh-package-slash-image",
153+
RepositoryHint: "rhcc",
154+
Version: "v1.0.0",
155+
},
156+
"2": {
157+
Name: "some-other-rh-package-slash-image",
158+
RepositoryHint: "rhcc",
159+
Version: "v1.0.0",
160+
},
161+
"3": {
162+
Name: "grafana",
163+
Version: "v4.7.0",
164+
},
165+
},
166+
Environments: map[string][]*claircore.Environment{
167+
"1": {{IntroducedIn: firstLayerHash}},
168+
"2": {{IntroducedIn: firstLayerHash}},
169+
"3": {{IntroducedIn: firstLayerHash}},
170+
},
171+
Vulnerabilities: map[string]*claircore.Vulnerability{
172+
"4": {
173+
Name: "something bad with grafana",
174+
FixedInVersion: "v100.0.0",
175+
},
176+
"5": {
177+
Name: "something bad ubi",
178+
FixedInVersion: "v100.0.0",
179+
},
180+
"6": {
181+
Name: "something bad s2i",
182+
FixedInVersion: "v100.0.0",
183+
},
184+
},
185+
PackageVulnerabilities: map[string][]string{
186+
"3": {"4"},
187+
"1": {"5"},
188+
"2": {"6"},
189+
},
190+
},
191+
layers: []*claircore.Layer{
192+
{Hash: firstLayerHash},
193+
{Hash: secondLayerHash},
194+
},
195+
lenVulns: 2,
196+
lenPkgVulns: 2,
197+
},
198+
}
199+
200+
e := &Enricher{}
201+
nog := &noopGetter{}
202+
for _, tc := range tests {
203+
t.Run(tc.name, func(t *testing.T) {
204+
_, _, err := e.Enrich(ctx, nog, tc.vr)
205+
if err != nil {
206+
t.Fatal(err)
207+
}
208+
if len(tc.vr.Vulnerabilities) != tc.lenVulns {
209+
t.Fatalf("wrong number of vulns: expected: %d got: %d", tc.lenVulns, len(tc.vr.Vulnerabilities))
210+
}
211+
lenPkgVulns := 0
212+
for _, vulns := range tc.vr.PackageVulnerabilities {
213+
lenPkgVulns = lenPkgVulns + len(vulns)
214+
}
215+
if lenPkgVulns != tc.lenPkgVulns {
216+
t.Fatalf("wrong number of vulns: expected: %d got: %d", tc.lenPkgVulns, lenPkgVulns)
217+
}
218+
})
219+
220+
}
221+
}
222+
223+
type noopGetter struct{}
224+
225+
func (f *noopGetter) GetEnrichment(ctx context.Context, tags []string) ([]driver.EnrichmentRecord, error) {
226+
return nil, nil
227+
}

0 commit comments

Comments
 (0)