cvss3/score.go (168 lines of code) (raw):

// Copyright (c) Facebook, Inc. and its affiliates. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package cvss3 import ( "fmt" "math" ) func roundUp(x float64) float64 { // round up to one decimal return math.Ceil(x*10) / 10 } // Validate should be called before calculating any scores on vector // If there's an error, there's no guarantee that a call to *Score() won't panic func (v Vector) Validate() error { switch { case !v.BaseMetrics.AttackVector.defined(): return fmt.Errorf("base metric attack vector not defined") case !v.BaseMetrics.AttackComplexity.defined(): return fmt.Errorf("base metric attack complexity not defined") case !v.BaseMetrics.PrivilegesRequired.defined(): return fmt.Errorf("base metric privileges required not defined") case !v.BaseMetrics.UserInteraction.defined(): return fmt.Errorf("base metric user interaction not defined") case !v.BaseMetrics.Scope.defined(): return fmt.Errorf("base metric scope not defined") case !v.BaseMetrics.Confidentiality.defined(): return fmt.Errorf("base metric confidentiality not defined") case !v.BaseMetrics.Integrity.defined(): return fmt.Errorf("base metric integrity not defined") case !v.BaseMetrics.Availability.defined(): return fmt.Errorf("base metric availability not defined") default: return nil } } // Score = combined score for the whole Vector func (v Vector) Score() float64 { // combines all of them return v.EnvironmentalScore() } // BaseScore returns base score of the vector func (v Vector) BaseScore() float64 { i, e := v.impactScore(), v.exploitabilityScore() if i < 0 { return 0 } c := 1.0 if v.baseScopeChanged() { c = 1.08 } return roundUp(math.Min(c*(e+i), 10.0)) } func (v Vector) impactScore() float64 { iscBase := 1 - (1-v.BaseMetrics.Confidentiality.weight())* (1-v.BaseMetrics.Integrity.weight())* (1-v.BaseMetrics.Availability.weight()) if v.baseScopeChanged() { return 7.52*(iscBase-0.029) - 3.25*math.Pow((iscBase-0.02), 15) } return 6.42 * iscBase } func (v Vector) exploitabilityScore() float64 { return 8.22 * v.BaseMetrics.AttackVector.weight() * v.BaseMetrics.AttackComplexity.weight() * v.BaseMetrics.PrivilegesRequired.weight(v.baseScopeChanged()) * v.BaseMetrics.UserInteraction.weight() } // TemporalScore returns temporal score of the vector func (v Vector) TemporalScore() float64 { return roundUp(v.BaseScore() * v.TemporalMetrics.ExploitCodeMaturity.weight() * v.TemporalMetrics.RemediationLevel.weight() * v.TemporalMetrics.ReportConfidence.weight()) } // EnvironmentalScore returns environmental score of the vector func (v Vector) EnvironmentalScore() float64 { i, e := v.modifiedImpactScore(), v.modifiedExploitabilityScore() if i < 0 { return 0 } c := 1.0 if v.modifiedScopeChanged() { c = 1.08 } modifiedTemporalMetricsMult := v.modifiedTemporalMetricsMult() return roundUp(roundUp(math.Min(c*(e+i), 10.0)) * modifiedTemporalMetricsMult) } func (v Vector) modifiedTemporalMetricsMult() float64 { var me, mrl, mrc float64 if v.EnvironmentalMetrics.ModifiedExploitCodeMaturity.defined() { me = v.EnvironmentalMetrics.ModifiedExploitCodeMaturity.weight() } else { me = v.TemporalMetrics.ExploitCodeMaturity.weight() } if v.EnvironmentalMetrics.ModifiedRemediationLevel.defined() { mrl = v.EnvironmentalMetrics.ModifiedRemediationLevel.weight() } else { mrl = v.TemporalMetrics.RemediationLevel.weight() } if v.EnvironmentalMetrics.ModifiedReportConfidence.defined() { mrc = v.EnvironmentalMetrics.ModifiedReportConfidence.weight() } else { mrc = v.TemporalMetrics.ReportConfidence.weight() } return me * mrl * mrc } func (v Vector) modifiedImpactScore() float64 { var mc, mi, ma float64 if v.EnvironmentalMetrics.ModifiedConfidentiality.defined() { mc = v.EnvironmentalMetrics.ModifiedConfidentiality.weight() } else { mc = v.BaseMetrics.Confidentiality.weight() } if v.EnvironmentalMetrics.ModifiedIntegrity.defined() { mi = v.EnvironmentalMetrics.ModifiedIntegrity.weight() } else { mi = v.BaseMetrics.Integrity.weight() } if v.EnvironmentalMetrics.ModifiedAvailability.defined() { ma = v.EnvironmentalMetrics.ModifiedAvailability.weight() } else { ma = v.BaseMetrics.Availability.weight() } iscModified := math.Min( 1-(1-mc*v.EnvironmentalMetrics.ConfidentialityRequirement.weight())* (1-mi*v.EnvironmentalMetrics.IntegrityRequirement.weight())* (1-ma*v.EnvironmentalMetrics.AvailabilityRequirement.weight()), 0.915, ) if v.modifiedScopeChanged() { switch v.version { case version(1): return 7.52*(iscModified-0.029) - 3.25*math.Pow((iscModified*0.9731-0.02), 13) case version(0): fallthrough default: return 7.52*(iscModified-0.029) - 3.25*math.Pow((iscModified-0.02), 15) } } else { return 6.42 * iscModified } } func (v Vector) modifiedExploitabilityScore() float64 { var mav, mac, mpr, mui float64 if v.EnvironmentalMetrics.ModifiedAttackVector.defined() { mav = v.EnvironmentalMetrics.ModifiedAttackVector.weight() } else { mav = v.BaseMetrics.AttackVector.weight() } if v.EnvironmentalMetrics.ModifiedAttackComplexity.defined() { mac = v.EnvironmentalMetrics.ModifiedAttackComplexity.weight() } else { mac = v.BaseMetrics.AttackComplexity.weight() } if v.EnvironmentalMetrics.ModifiedPrivilegesRequired.defined() { mpr = v.EnvironmentalMetrics.ModifiedPrivilegesRequired.weight(v.modifiedScopeChanged()) } else { mpr = v.BaseMetrics.PrivilegesRequired.weight(v.modifiedScopeChanged()) } if v.EnvironmentalMetrics.ModifiedUserInteraction.defined() { mui = v.EnvironmentalMetrics.ModifiedUserInteraction.weight() } else { mui = v.BaseMetrics.UserInteraction.weight() } return 8.22 * mav * mac * mpr * mui } // scope functions func (v Vector) baseScopeChanged() bool { return v.BaseMetrics.Scope == ScopeChanged } func (v Vector) modifiedScopeChanged() bool { if v.EnvironmentalMetrics.ModifiedScope.defined() { return v.EnvironmentalMetrics.ModifiedScope == ModifiedScope(ScopeChanged) } return v.baseScopeChanged() }