-
Notifications
You must be signed in to change notification settings - Fork 80
/
dpkg.go
169 lines (156 loc) · 4.92 KB
/
dpkg.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
package ovalutil
import (
"context"
"errors"
"fmt"
"github.com/quay/goval-parser/oval"
"github.com/quay/zlog"
"go.opentelemetry.io/otel/baggage"
"go.opentelemetry.io/otel/label"
"github.com/quay/claircore"
)
// DpkgDefsToVulns iterates over the definitions in an oval root and assumes DpkgInfo objects and states.
//
// Each Criterion encountered with an EVR string will be translated into a claircore.Vulnerability
func DpkgDefsToVulns(ctx context.Context, root *oval.Root, protoVulns ProtoVulnsFunc) ([]*claircore.Vulnerability, error) {
ctx = baggage.ContextWithValues(ctx,
label.String("component", "ovalutil/DpkgDefsToVulns"))
vulns := make([]*claircore.Vulnerability, 0, 10000)
pkgcache := map[string]*claircore.Package{}
cris := []*oval.Criterion{}
for _, def := range root.Definitions.Definitions {
// create our prototype vulnerability
protoVulns, err := protoVulns(def)
if err != nil {
zlog.Debug(ctx).
Err(err).
Str("def_id", def.ID).
Msg("could not create prototype vulnerabilities")
continue
}
// recursively collect criterions for this definition
cris := cris[:0]
walkCriterion(ctx, &def.Criteria, &cris)
// unpack criterions into vulnerabilities
for _, criterion := range cris {
test, err := TestLookup(root, criterion.TestRef, func(kind string) bool {
if kind != "dpkginfo_test" {
return false
}
return true
})
switch {
case errors.Is(err, nil):
case errors.Is(err, errTestSkip):
continue
default:
zlog.Debug(ctx).Str("test_ref", criterion.TestRef).Msg("test ref lookup failure. moving to next criterion")
continue
}
objRefs := test.ObjectRef()
stateRefs := test.StateRef()
// from the dpkginfo_test specification found here: https://oval.mitre.org/language/version5.7/ovaldefinition/documentation/linux-definitions-schema.html
// The required object element references a dpkginfo_object and the optional state element specifies the data to check.
// The evaluation of the test is guided by the check attribute that is inherited from the TestType.
//
// thus we *should* only need to care about a single dpkginfo_object and optionally a state object providing the package's fixed-in version.
objRef := objRefs[0].ObjectRef
object, err := dpkgObjectLookup(root, objRef)
switch {
case errors.Is(err, nil):
case errors.Is(err, errObjectSkip):
// We only handle dpkginfo_objects.
continue
default:
if err != nil {
zlog.Debug(ctx).
Err(err).
Str("object_ref", objRef).
Msg("failed object lookup. moving to next criterion")
continue
}
}
var state *oval.DpkgInfoState
if len(stateRefs) > 0 {
stateRef := stateRefs[0].StateRef
state, err = dpkgStateLookup(root, stateRef)
if err != nil {
zlog.Debug(ctx).
Err(err).
Str("state_ref", stateRef).
Msg("failed state lookup. moving to next criterion")
continue
}
// if EVR tag not present this is not a linux package
// see oval definitions for more details
if state.EVR == nil {
continue
}
}
for _, protoVuln := range protoVulns {
name := object.Name
// if the dpkginfo_object>name field has a var_ref it indicates
// a variable lookup for all packages affected by this vuln is necessary.
//
// if the name.Ref field is empty it indicates a single package is affected
// by the vuln and that package's name is in name.Body.
var ns []string
if len(name.Ref) > 0 {
_, i, err := root.Variables.Lookup(name.Ref)
if err != nil {
zlog.Error(ctx).Err(err).Msg("could not lookup variable id")
continue
}
consts := root.Variables.ConstantVariables[i]
for _, v := range consts.Values {
ns = append(ns, v.Body)
}
} else {
ns = append(ns, name.Body)
}
for _, n := range ns {
vuln := *protoVuln
if state != nil {
vuln.FixedInVersion = state.EVR.Body
if state.Arch != nil {
vuln.ArchOperation = mapArchOp(state.Arch.Operation)
vuln.Package.Arch = state.Arch.Body
}
}
if pkg, ok := pkgcache[n]; !ok {
p := &claircore.Package{
Name: n,
Kind: claircore.BINARY,
}
pkgcache[n] = p
vuln.Package = p
} else {
vuln.Package = pkg
}
vulns = append(vulns, &vuln)
}
}
}
}
return vulns, nil
}
func dpkgStateLookup(root *oval.Root, ref string) (*oval.DpkgInfoState, error) {
kind, i, err := root.States.Lookup(ref)
if err != nil {
return nil, err
}
if kind != "dpkginfo_state" {
return nil, fmt.Errorf("oval: got kind %q: %w", kind, errStateSkip)
}
return &root.States.DpkgInfoStates[i], nil
}
func dpkgObjectLookup(root *oval.Root, ref string) (*oval.DpkgInfoObject, error) {
kind, i, err := root.Objects.Lookup(ref)
if err != nil {
return nil, err
}
if kind != "dpkginfo_object" {
return nil, fmt.Errorf("oval: got kind %q: %w", kind, errObjectSkip)
}
return &root.Objects.DpkgInfoObjects[i], nil
}