-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathvalidate.go
More file actions
143 lines (117 loc) · 3.61 KB
/
validate.go
File metadata and controls
143 lines (117 loc) · 3.61 KB
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
package main
import (
"fmt"
"regexp"
"strings"
"time"
"github.com/facebookincubator/nvdtools/cvss3"
"github.com/hashicorp/go-version"
"github.com/pkg/errors"
"github.com/stackrox/istio-cves/types"
)
const (
linkFmt = "https://istio.io/latest/news/security/%s/"
)
var (
firstPublishedCVE = time.Time{}
vulnPattern = regexp.MustCompile(`^ISTIO-SECURITY-(\d{4})-(\d{3})$`)
)
func init() {
var err error
firstPublishedCVE, err = time.Parse(types.TimeLayout, "2019-05-28T00:00Z")
if err != nil {
panic("Should not happen")
}
}
// validate yaml files
func validate(fileName string, vuln *types.Vuln) error {
// validate vuln name.
if !vulnPattern.MatchString(vuln.Name) {
return errors.Errorf("Vuln name must adhere to the pattern %q: %s", vulnPattern.String(), vuln.Name)
}
// validate file name.
if !strings.HasSuffix(fileName, vuln.Name+".yaml") {
return errors.Errorf("file name must match CVE (%q)", vuln.Name)
}
// validate link format
if vuln.Link != fmt.Sprintf(linkFmt, strings.ToLower(vuln.Name)) {
return errors.Errorf("Vuln link must include vlun name %s: %s", vuln.Link, vuln.Name)
}
// validate published.
if vuln.Published.Before(firstPublishedCVE) {
return errors.Errorf("published time must be before %s", firstPublishedCVE.String())
}
// validate description.
if len(strings.TrimSpace(vuln.Description)) == 0 {
return errors.New("description must be defined")
}
// validate CVSS.
if err := validateCVSS(vuln.CVSS); err != nil {
return errors.Wrap(err, "invalid CVSS field")
}
// validate affected.
if err := validateAffected(vuln.Affected); err != nil {
return errors.Wrap(err, "invalid affected field")
}
return nil
}
func validateCVSS(cvss types.CVSS) error {
if cvss.ScoreV3 <= 0.0 {
return errors.New("scoreV3 must be defined and greater than 0.0")
}
if err := validateCVSSv3(cvss.ScoreV3, cvss.VectorV3); err != nil {
return errors.Wrap(err, "invalid CVSS3")
}
return nil
}
func validateCVSSv3(score float64, vector string) error {
v, err := cvss3.VectorFromString(vector)
if err != nil {
return err
}
if err := v.Validate(); err != nil {
return err
}
calculatedScore := v.BaseScore()
if score != calculatedScore {
return errors.Errorf("CVSS3 score differs from calculated vector score: %f != %0.1f", score, calculatedScore)
}
return nil
}
func validateAffected(affects []types.Affected) error {
if len(affects) == 0 {
return errors.New("affected must be defined")
}
affectedSet := make(map[string]bool)
for _, affected := range affects {
trimmedRange := strings.TrimSpace(affected.Range)
if len(trimmedRange) == 0 {
return errors.New("affected range must not be blank")
}
if affectedSet[trimmedRange] {
return errors.Errorf("affected range must not be repeated: %s", trimmedRange)
}
affectedSet[trimmedRange] = true
// It would be nice if we could ensure all ranges are non-overlapping,
// but it doesn't seem very straightforward at the moment.
c, err := version.NewConstraint(trimmedRange)
if err != nil {
return errors.Wrapf(err, "invalid affected range: %s", trimmedRange)
}
trimmedFixedBy := strings.TrimSpace(affected.FixedBy)
if len(trimmedFixedBy) == 0 {
// fixedBy need not be defined.
continue
}
v, err := version.NewVersion(trimmedFixedBy)
if err != nil {
return errors.Wrapf(err, "invalid fixedBy: %s", trimmedFixedBy)
}
// It would be nice if we could ensure the version is above the range,
// but it doesn't seem very straightforward at the moment.
if c.Check(v) {
return errors.Errorf("fixedBy must not be within the given range: %s contains %s", trimmedRange, trimmedFixedBy)
}
}
return nil
}