-
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathcvss.go
More file actions
179 lines (165 loc) · 3.98 KB
/
Copy pathcvss.go
File metadata and controls
179 lines (165 loc) · 3.98 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
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
170
171
172
173
174
175
176
177
178
179
package vulns
import (
"strings"
gocvss20 "github.com/pandatix/go-cvss/20"
gocvss30 "github.com/pandatix/go-cvss/30"
gocvss31 "github.com/pandatix/go-cvss/31"
gocvss40 "github.com/pandatix/go-cvss/40"
)
const (
LevelCritical = "critical"
LevelHigh = "high"
LevelMedium = "medium"
LevelLow = "low"
LevelNone = "none"
thresholdCritical = 9.0
thresholdHigh = 7.0
thresholdMedium = 4.0
thresholdLow = 0.1
)
// CVSS holds parsed CVSS information.
type CVSS struct {
Version string
Vector string
Score float64
Level string
}
// ParseCVSS parses a CVSS vector string and returns structured CVSS information.
// Supports CVSS v2.0, v3.0, v3.1, and v4.0 vectors.
func ParseCVSS(vector string) (*CVSS, error) {
vector = strings.TrimSpace(vector)
// Try to detect version from prefix
switch {
case strings.HasPrefix(vector, "CVSS:4.0/"):
return parseCVSS40(vector)
case strings.HasPrefix(vector, "CVSS:3.1/"):
return parseCVSS31(vector)
case strings.HasPrefix(vector, "CVSS:3.0/"):
return parseCVSS30(vector)
case strings.HasPrefix(vector, "AV:") || strings.HasPrefix(vector, "(AV:"):
return parseCVSS20(vector)
default:
// Try parsing as a bare score
var score float64
if _, err := scanFloat(vector, &score); err == nil && score >= 0 && score <= 10 {
return &CVSS{
Score: score,
Level: scoreToLevel(score),
}, nil
}
// Try v3.1 as most common
if c, err := parseCVSS31(vector); err == nil {
return c, nil
}
// Try v4.0
if c, err := parseCVSS40(vector); err == nil {
return c, nil
}
return nil, &parseError{}
}
}
func parseCVSS20(vector string) (*CVSS, error) {
// Remove parentheses if present
vector = strings.Trim(vector, "()")
cvss, err := gocvss20.ParseVector(vector)
if err != nil {
return nil, err
}
score := cvss.BaseScore()
return &CVSS{
Version: "2.0",
Vector: vector,
Score: score,
Level: scoreToLevel(score),
}, nil
}
func parseCVSS30(vector string) (*CVSS, error) {
cvss, err := gocvss30.ParseVector(vector)
if err != nil {
return nil, err
}
score := cvss.BaseScore()
return &CVSS{
Version: "3.0",
Vector: vector,
Score: score,
Level: scoreToLevel(score),
}, nil
}
func parseCVSS31(vector string) (*CVSS, error) {
cvss, err := gocvss31.ParseVector(vector)
if err != nil {
return nil, err
}
score := cvss.BaseScore()
return &CVSS{
Version: "3.1",
Vector: vector,
Score: score,
Level: scoreToLevel(score),
}, nil
}
func parseCVSS40(vector string) (*CVSS, error) {
cvss, err := gocvss40.ParseVector(vector)
if err != nil {
return nil, err
}
score := cvss.Score()
return &CVSS{
Version: "4.0",
Vector: vector,
Score: score,
Level: scoreToLevel40(score),
}, nil
}
// scoreToLevel converts a CVSS v2/v3 score to a severity level.
func scoreToLevel(score float64) string {
switch {
case score >= thresholdCritical:
return LevelCritical
case score >= thresholdHigh:
return LevelHigh
case score >= thresholdMedium:
return LevelMedium
case score > 0:
return LevelLow
default:
return LevelNone
}
}
// scoreToLevel40 converts a CVSS v4.0 score to a severity level.
// CVSS 4.0 uses different thresholds.
func scoreToLevel40(score float64) string {
switch {
case score >= thresholdCritical:
return LevelCritical
case score >= thresholdHigh:
return LevelHigh
case score >= thresholdMedium:
return LevelMedium
case score >= thresholdLow:
return LevelLow
default:
return LevelNone
}
}
// CVSSFromSeverity extracts and parses CVSS information from a Severity entry.
func CVSSFromSeverity(sev Severity) (*CVSS, error) {
switch sev.Type {
case "CVSS_V2":
return parseCVSS20(sev.Score)
case "CVSS_V3", "CVSS_V31":
// First try parsing as vector, then as bare score
if c, err := parseCVSS31(sev.Score); err == nil {
return c, nil
}
if c, err := parseCVSS30(sev.Score); err == nil {
return c, nil
}
return ParseCVSS(sev.Score)
case "CVSS_V4", "CVSS_V40":
return parseCVSS40(sev.Score)
default:
return ParseCVSS(sev.Score)
}
}