-
Notifications
You must be signed in to change notification settings - Fork 79
/
matcher.go
103 lines (88 loc) · 2.92 KB
/
matcher.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
package python
import (
"context"
"net/url"
"github.com/quay/zlog"
"github.com/quay/claircore"
"github.com/quay/claircore/libvuln/driver"
"github.com/quay/claircore/pkg/pep440"
)
var _ driver.Matcher = (*Matcher)(nil)
// Matcher attempts to correlate discovered python packages with reported
// vulnerabilities.
type Matcher struct{}
// Name implements driver.Matcher.
func (*Matcher) Name() string { return "python" }
// Filter implements driver.Matcher.
func (*Matcher) Filter(record *claircore.IndexRecord) bool {
return record.Package.NormalizedVersion.Kind == "pep440"
}
// Query implements driver.Matcher.
func (*Matcher) Query() []driver.MatchConstraint {
return []driver.MatchConstraint{driver.RepositoryName}
}
// Vulnerable implements driver.Matcher.
func (*Matcher) Vulnerable(ctx context.Context, record *claircore.IndexRecord, vuln *claircore.Vulnerability) (bool, error) {
// TODO(ross): This is a common pattern for OSV vulnerabilities. This should be moved into
// a common place for all OSV vulnerability matchers.
if vuln.FixedInVersion == "" {
return true, nil
}
// Parse the package first. If it cannot be parsed, it cannot properly be analyzed for vulnerabilities.
rv, err := pep440.Parse(record.Package.Version)
if err != nil {
zlog.Warn(ctx).
Str("package", record.Package.Name).
Stringer("version", &rv).
Msg("unable to parse python package version")
return false, err
}
decodedVersions, err := url.ParseQuery(vuln.FixedInVersion)
if err != nil {
return false, err
}
introduced := decodedVersions.Get("introduced")
// If there is an introduced version, check if the package's version is lower.
if introduced != "" {
iv, err := pep440.Parse(introduced)
if err != nil {
zlog.Warn(ctx).
Str("package", vuln.Package.Name).
Str("version", introduced).
Msg("unable to parse python introduced version")
return false, err
}
// If the package's version is less than the introduced version, it's not vulnerable.
if rv.Compare(&iv) < 0 {
return false, nil
}
}
fixedVersion := decodedVersions.Get("fixed")
lastAffected := decodedVersions.Get("lastAffected")
switch {
case fixedVersion != "":
uv, err := pep440.Parse(fixedVersion)
if err != nil {
zlog.Warn(ctx).
Str("package", vuln.Package.Name).
Str("version", fixedVersion).
Msg("unable to parse python fixed version")
return false, err
}
// The package is affected if its version is less than the fixed version.
return rv.Compare(&uv) < 0, nil
case lastAffected != "":
la, err := pep440.Parse(lastAffected)
if err != nil {
zlog.Warn(ctx).
Str("package", vuln.Package.Name).
Str("version", lastAffected).
Msg("unable to parse python last_affected version")
return false, err
}
// The package is affected if its version is less than or equal to the last affected version.
return rv.Compare(&la) <= 0, nil
}
// Just say the package is vulnerable, by default.
return true, nil
}