diff --git a/v2/metric/environmental.go b/v2/metric/environmental.go index d6a468a..2aebac6 100644 --- a/v2/metric/environmental.go +++ b/v2/metric/environmental.go @@ -120,6 +120,9 @@ func (m *Environmental) GetError() error { if err := m.Temporal.GetError(); err != nil { return errs.Wrap(err) } + if m.IsEmpty() { + return nil + } switch true { case !m.CDP.IsValid(), !m.TD.IsValid(), !m.CR.IsValid(), !m.IR.IsValid(), !m.AR.IsValid(): return errs.Wrap(cvsserr.ErrNoEnvironmentalMetrics) @@ -128,6 +131,11 @@ func (m *Environmental) GetError() error { } } +// IsEmpty returns true if all elements of Temporal Metrics are empty. +func (m *Environmental) IsEmpty() bool { + return !m.names[metricCDP] && !m.names[metricTD] && !m.names[metricCR] && !m.names[metricIR] && !m.names[metricAR] +} + // Encode returns CVSSv2 vector string func (m *Environmental) Encode() (string, error) { if m == nil { @@ -165,9 +173,22 @@ func (m *Environmental) Score() float64 { if err := m.GetError(); err != nil { return 0 } - adjustedImpact := math.Min(10.0, 10.41*(1-(1-m.C.Value()*m.CR.Value())*(1-m.I.Value()*m.IR.Value())*(1-m.A.Value()*m.AR.Value()))) - baseScore := m.Base.score(adjustedImpact) - adjustedTemporal := m.Temporal.score(baseScore) + var baseScore float64 + if m.IsEmpty() { + baseScore = m.Base.Score() + } else { + adjustedImpact := math.Min(10.0, 10.41*(1-(1-m.C.Value()*m.CR.Value())*(1-m.I.Value()*m.IR.Value())*(1-m.A.Value()*m.AR.Value()))) + baseScore = m.Base.score(adjustedImpact) + } + var adjustedTemporal float64 + if m.Temporal.IsEmpty() { + adjustedTemporal = baseScore + } else { + adjustedTemporal = m.Temporal.score(baseScore) + } + if m.IsEmpty() { + return adjustedTemporal + } return math.Round((adjustedTemporal+(10-adjustedTemporal)*m.CDP.Value()*m.TD.Value())*10) / 10 } diff --git a/v2/metric/metric_test.go b/v2/metric/metric_test.go index 1cbdecd..3f6c61f 100644 --- a/v2/metric/metric_test.go +++ b/v2/metric/metric_test.go @@ -157,6 +157,12 @@ func TestBaseTemporalScore(t *testing.T) { base: 6.2, temp: 4.9, }, + { + name: "CVE-2003-0062-baseonly", + vector: "AV:L/AC:H/Au:N/C:C/I:C/A:C", + base: 6.2, + temp: 6.2, + }, } for _, tt := range tests { @@ -208,6 +214,27 @@ func TestEnvEnvironmentalScore(t *testing.T) { temp: 4.9, env: 7.5, }, + { + name: "CVE-2003-0062-baseonly", + vector: "AV:L/AC:H/Au:N/C:C/I:C/A:C", + base: 6.2, + temp: 6.2, + env: 6.2, + }, + { + name: "CVE-2003-0062-temporal", + vector: "AV:L/AC:H/Au:N/C:C/I:C/A:C/E:POC/RL:OF/RC:C", + base: 6.2, + temp: 4.9, + env: 4.9, + }, + { + name: "CVE-2003-0062-skip-temporal", + vector: "AV:L/AC:H/Au:N/C:C/I:C/A:C/CDP:H/TD:H/CR:M/IR:M/AR:M", + base: 6.2, + temp: 6.2, + env: 8.1, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/v2/metric/temporal.go b/v2/metric/temporal.go index 1959928..6dc9cb1 100644 --- a/v2/metric/temporal.go +++ b/v2/metric/temporal.go @@ -104,6 +104,9 @@ func (m *Temporal) GetError() error { if err := m.Base.GetError(); err != nil { return errs.Wrap(err) } + if m.IsEmpty() { + return nil + } switch true { case !m.E.IsValid(), !m.RL.IsValid(), !m.RC.IsValid(): return errs.Wrap(cvsserr.ErrNoTemporalMetrics) @@ -112,6 +115,11 @@ func (m *Temporal) GetError() error { } } +// IsEmpty returns true if all elements of Temporal Metrics are empty. +func (m *Temporal) IsEmpty() bool { + return !m.names[metricE] && !m.names[metricRL] && !m.names[metricRC] +} + // Encode returns CVSSv2 vector string func (m *Temporal) Encode() (string, error) { if m == nil { @@ -143,7 +151,11 @@ func (m *Temporal) Score() float64 { if err := m.GetError(); err != nil { return 0 } - return m.score(m.Base.Score()) + bs := m.Base.Score() + if m.IsEmpty() { + return bs + } + return m.score(bs) } func (m *Temporal) score(baseScore float64) float64 {