// CVSS vector parser. package cvss import ( "fmt" "strings" ) //go:generate stringer -linecomment -type=v2Metric //go:generate stringer -linecomment -type=v3Key //go:generate stringer -linecomment -type=v3Metric // CVSS v2 metric value type v2Metric byte const ( v2AVNetwork v2Metric = iota // AV:N v2AVAdjacentNetwork // AV:A v2AVLocal // AV:L v2ACLow // AC:L v2ACMedium // AC:L v2ACHigh // AC:H v2AuMultiple // Au:M v2AuSingle // Au:S v2AuNone // Au:N v2CNone // C:N v2CPartial // C:P v2CComplete // C:C v2INone // I:N v2IPartial // I:P v2IComplete // I:C v2ANone // A:N v2APartial // A:P v2AComplete // A:C v2ENotDefined // E:ND v2EUnproven // E:U v2EProofOfConcept // E:POC v2EFunctional // E:F v2EHigh // E:H v2RLOfficialFix // RL:OF v2RLTemporaryFix // RL:TF v2RLWorkaround // RL:W v2RLUnavailable // RL:U v2RLNotDefined // RL:ND v2RCUnconfirmed // RC:UC v2RCUncorroborated // RC:UR v2RCConfirmed // RC:C v2RCNotDefined // RC:ND v2CDPNone // CDP:N v2CDPLow // CDP:L v2CDPLowMedium // CDP:LM v2CDPMediumHigh // CDP:MH v2CDPHigh // CDP:H v2CDPNotDefined // CDP:ND v2TDNone // TD:N v2TDLow // TD:L v2TDMedium // TD:M v2TDHigh // TD:H v2TDNotDefined // TD:ND v2CRLow // CR:L v2CRMedium // CR:M v2CRHigh // CR:H v2CRNotDefined // CR:ND v2IRLow // IR:L v2IRMedium // IR:M v2IRHigh // IR:H v2IRNotDefined // IR:ND v2ARLow // AR:L v2ARMedium // AR:M v2ARHigh // AR:H v2ARNotDefined // AR:ND v2InvalidMetric // invalid ) // map of metrics to metric keys var v2KeyLut = map[v2Metric]v2Key { v2AVNetwork: v2AccessVector, v2AVAdjacentNetwork: v2AccessVector, v2AVLocal: v2AccessVector, v2ACLow: v2AccessComplexity, v2ACMedium: v2AccessComplexity, v2ACHigh: v2AccessComplexity, v2AuMultiple: v2Authentication, v2AuSingle: v2Authentication, v2AuNone: v2Authentication, v2CNone: v2ConfidentialityImpact, v2CPartial: v2ConfidentialityImpact, v2CComplete: v2ConfidentialityImpact, v2INone: v2IntegrityImpact, v2IPartial: v2IntegrityImpact, v2IComplete: v2IntegrityImpact, v2ANone: v2AvailabilityImpact, v2APartial: v2AvailabilityImpact, v2AComplete: v2AvailabilityImpact, v2ENotDefined: v2Exploitability, v2EUnproven: v2Exploitability, v2EProofOfConcept: v2Exploitability, v2EFunctional: v2Exploitability, v2EHigh: v2Exploitability, v2RLOfficialFix: v2RemediationLevel, v2RLTemporaryFix: v2RemediationLevel, v2RLWorkaround: v2RemediationLevel, v2RLUnavailable: v2RemediationLevel, v2RLNotDefined: v2RemediationLevel, v2RCUnconfirmed: v2ReportConfidence, v2RCUncorroborated: v2ReportConfidence, v2RCConfirmed: v2ReportConfidence, v2RCNotDefined: v2ReportConfidence, v2CDPNone: v2CollateralDamagePotential, v2CDPLow: v2CollateralDamagePotential, v2CDPLowMedium: v2CollateralDamagePotential, v2CDPMediumHigh: v2CollateralDamagePotential, v2CDPHigh: v2CollateralDamagePotential, v2CDPNotDefined: v2CollateralDamagePotential, v2TDNone: v2TargetDistribution, v2TDLow: v2TargetDistribution, v2TDMedium: v2TargetDistribution, v2TDHigh: v2TargetDistribution, v2TDNotDefined: v2TargetDistribution, v2CRLow: v2ConfidentialityRequirement, v2CRMedium: v2ConfidentialityRequirement, v2CRHigh: v2ConfidentialityRequirement, v2CRNotDefined: v2ConfidentialityRequirement, v2IRLow: v2IntegrityRequirement, v2IRMedium: v2IntegrityRequirement, v2IRHigh: v2IntegrityRequirement, v2IRNotDefined: v2IntegrityRequirement, v2ARLow: v2AvailabilityRequirement, v2ARMedium: v2AvailabilityRequirement, v2ARHigh: v2AvailabilityRequirement, v2ARNotDefined: v2AvailabilityRequirement, } // map of metric strings to metrics var v2MetricStrLut = map[string]v2Metric { "AV:N": v2AVNetwork, "AV:A": v2AVAdjacentNetwork, "AV:L": v2AVLocal, "AC:L": v2ACLow, "AC:M": v2ACMedium, "AC:H": v2ACHigh, "Au:M": v2AuMultiple, "Au:S": v2AuSingle, "Au:N": v2AuNone, "C:N": v2CNone, "C:P": v2CPartial, "C:C": v2CComplete, "I:N": v2INone, "I:P": v2IPartial, "I:C": v2IComplete, "A:N": v2ANone, "A:P": v2APartial, "A:C": v2AComplete, "E:ND": v2ENotDefined, "E:U": v2EUnproven, "E:POC": v2EProofOfConcept, "E:F": v2EFunctional, "E:H": v2EHigh, "RL:OF": v2RLOfficialFix, "RL:TF": v2RLTemporaryFix, "RL:W": v2RLWorkaround, "RL:U": v2RLUnavailable, "RL:ND": v2RLNotDefined, "RC:UC": v2RCUnconfirmed, "RC:UR": v2RCUncorroborated, "RC:C": v2RCConfirmed, "RC:ND": v2RCNotDefined, "CDP:N": v2CDPNone, "CDP:L": v2CDPLow, "CDP:LM": v2CDPLowMedium, "CDP:MH": v2CDPMediumHigh, "CDP:H": v2CDPHigh, "CDP:ND": v2CDPNotDefined, "TD:N": v2TDNone, "TD:L": v2TDLow, "TD:M": v2TDMedium, "TD:H": v2TDHigh, "TD:ND": v2TDNotDefined, "CR:L": v2CRLow, "CR:M": v2CRMedium, "CR:H": v2CRHigh, "CR:ND": v2CRNotDefined, "IR:L": v2IRLow, "IR:M": v2IRMedium, "IR:H": v2IRHigh, "IR:ND": v2IRNotDefined, "AR:L": v2ARLow, "AR:M": v2ARMedium, "AR:H": v2ARHigh, "AR:ND": v2ARNotDefined, } // Convert string to CVSS 2.0 metric. func getV2MetricFromString(s string) (v2Metric, error) { // get metric m, ok := v2MetricStrLut[s] if !ok { return v2InvalidMetric, fmt.Errorf("invalid metric: %s", s) } // return success return m, nil } // Get CVSS 2.0 metric key. func (m v2Metric) Key() Key { k, _ := v2KeyLut[m] return k } // CVSS 2.0 vector. type v2Vector []v2Metric // Convert vector to string func (v v2Vector) String() string { // convert to slice of metrics metrics := []v2Metric(v) // build vector r := make([]string, len(metrics)) for i, m := range(metrics) { r[i] = m.String() } // build and return string return strings.Join(r, "/") } // Return CVSS version. func (v2Vector) Version() Version { return V20 } // Return metrics in this vector. func (v v2Vector) Metrics() []Metric { // build result r := make([]Metric, len(v)) for i, m := range(v) { r[i] = m } // return result return r } // create CVSS 2.0 vector from string func newV2Vector(s string) (Vector, error) { strs := strings.Split(s, "/") r := make([]v2Metric, len(strs)) // walk metric strings for i, ms := range(strs) { // convert string to vector m, err := getV2MetricFromString(ms) if err != nil { return nil, err } // add to results r[i] = m } // build and return vector return v2Vector(r), nil } // CVSS v3 metric key type v3Key byte const ( v3AttackVector v3Key = iota // AV v3AttackComplexity // AC v3PrivilegesRequired // PR v3UserInteraction // UI v3Scope // S v3Confidentiality // C v3Integrity // I v3Availability // A v3ExploitCodeMaturity // E v3RemediationLevel // RL v3ReportConfidence // RC v3ConfidentialityRequirement // CR v3IntegrityRequirement // IR v3AvailabilityRequirement // AR v3ModifiedAttackVector // MAV v3ModifiedAttackComplexity // MAC v3ModifiedPrivilegesRequired // MPR v3ModifiedUserInteraction // MUI v3ModifiedScope // MS v3ModifiedConfidentiality // MC v3ModifiedIntegrity // MI v3ModifiedAvailability // MA v3InvalidKey // invalid ) // CVSS v3 metric key info lut var v3Keys = map[v3Key]struct { Name string Category Category } { v3AttackVector: { "Attack Vector", Base }, v3AttackComplexity: { "Attack Complexity", Base }, v3PrivilegesRequired: { "Privileges Required", Base }, v3UserInteraction: { "User Interaction", Base }, v3Scope: { "Scope", Base }, v3Confidentiality: { "Confidentiality", Base }, v3Integrity: { "Integrity", Base }, v3Availability: { "Availability", Base }, v3ExploitCodeMaturity: { "Exploit Code Maturity", Temporal }, v3RemediationLevel: { "Remediation Level", Temporal }, v3ReportConfidence: { "Report Confidence", Temporal }, v3ConfidentialityRequirement: { "Confidentiality Requirement", Environmental }, v3IntegrityRequirement: { "Integrity Requirement", Environmental }, v3AvailabilityRequirement: { "Availability Requirement", Environmental }, v3ModifiedAttackVector: { "Modified Attack Vector", Environmental }, v3ModifiedAttackComplexity: { "Modified Attack Complexity", Environmental }, v3ModifiedPrivilegesRequired: { "Modified Privileges Required", Environmental }, v3ModifiedUserInteraction: { "Modified User Interaction", Environmental }, v3ModifiedScope: { "Modified Scope", Environmental }, v3ModifiedConfidentiality: { "Modified Confidentiality", Environmental }, v3ModifiedIntegrity: { "Modified Integrity", Environmental }, v3ModifiedAvailability: { "Modified Availability", Environmental }, } // metric key IDs lut var v3KeyIds = map[string]v3Key { "AV": v3AttackVector, "AC": v3AttackComplexity, "PR": v3PrivilegesRequired, "UI": v3UserInteraction, "S": v3Scope, "C": v3Confidentiality, "I": v3Integrity, "A": v3Availability, "E": v3ExploitCodeMaturity, "RL": v3RemediationLevel, "RC": v3ReportConfidence, "CR": v3ConfidentialityRequirement, "IR": v3IntegrityRequirement, "AR": v3AvailabilityRequirement, "MAV": v3ModifiedAttackVector, "MAC": v3ModifiedAttackComplexity, "MPR": v3ModifiedPrivilegesRequired, "MUI": v3ModifiedUserInteraction, "MS": v3ModifiedScope, "MC": v3ModifiedConfidentiality, "MI": v3ModifiedIntegrity, "MA": v3ModifiedAvailability, } // // Get metric key from string. // func getV3KeyFromString(s string) (v3Key, error) { // k, ok := v3KeyIds[s] // if ok { // return k, nil // } else { // return v3InvalidKey, newBadKey(V30, s) // } // } // Get metric key name. func (k v3Key) Name() string { return v3Keys[k].Name } // Get metric key category. func (k v3Key) Category() Category { return v3Keys[k].Category } // metric value type v3Metric byte const ( v3AVNetwork v3Metric = iota // AV:N v3AVAdjacentNetwork // AV:A v3AVLocal // AV:L v3AVPhysical // AV:P v3ACLow // AC:L v3ACHigh // AC:H v3PRNone // PR:N v3PRLow // PR:L v3PRHigh // PR:H v3UINone // UI:N v3UIRequired // UI:R v3SUnchanged // S:U v3SChanged // S:C v3CHigh // C:H v3CLow // C:L v3CNone // C:N v3IHigh // I:H v3ILow // I:L v3INone // I:N v3AHigh // A:H v3ALow // A:L v3ANone // A:N v3ENotDefined // E:X v3EHigh // E:H v3EFunctional // E:F v3EProofOfConcept // E:P v3EUnproven // E:U v3RLNotDefined // RL:X v3RLUnavailable // RL:U v3RLWorkaround // RL:W v3RLTemporaryFix // RL:T v3RLOfficialFix // RL:O v3RCNotDefined // RC:X v3RCConfirmed // RC:C v3RCReasonable // RC:R v3RCUnknown // RC:U v3CRNotDefined // CR:X v3CRHigh // CR:H v3CRMedium // CR:M v3CRLow // CR:L v3IRNotDefined // IR:X v3IRHigh // IR:H v3IRMedium // IR:M v3IRLow // IR:L v3ARNotDefined // AR:X v3ARHigh // AR:H v3ARMedium // AR:M v3ARLow // AR:L v3MAVNotDefined // MAV:X v3MAVNetwork // MAV:N v3MAVAdjacentNetwork // MAV:A v3MAVLocal // MAV:L v3MAVPhysical // MAV:P v3MACNotDefined // MAC:X v3MACLow // MAC:L v3MACHigh // MAC:H v3MMRNotDefined // MPR:X v3MPRLow // MPR:L v3MPRHigh // MPR:H v3MUINotDefined // MUI:X v3MUINone // MUI:N v3MUIRequired // MUI:R v3MSNotDefined // MMS:X v3MSUnchanged // MMS:U v3MSChanged // MMS:C v3MCNotDefined // MC:X v3MCHigh // MC:H v3MCLow // MC:L v3MCNone // MC:N v3MINotDefined // MI:X v3MIHigh // MI:H v3MILow // MI:L v3MINone // MI:N v3MANotDefined // MA:X v3MAHigh // MA:H v3MALow // MA:L v3MANone // MA:N v3InvalidMetric // invalid ) // map of metrics to metric keys var v3KeyLut = map[v3Metric]v3Key { v3AVNetwork: v3AttackVector, // AV:N v3AVAdjacentNetwork: v3AttackVector, // AV:A v3AVLocal: v3AttackVector, // AV:L v3AVPhysical: v3AttackVector, // AV:P v3ACLow: v3AttackComplexity, // AC:L v3ACHigh: v3AttackComplexity, // AC:H v3PRNone: v3PrivilegesRequired, // PR:N v3PRLow: v3PrivilegesRequired, // PR:L v3PRHigh: v3PrivilegesRequired, // PR:H v3UINone: v3UserInteraction, // UI:N v3UIRequired: v3UserInteraction, // UI:R v3SUnchanged: v3Scope, // S:U v3SChanged: v3Scope, // S:C v3CHigh: v3Confidentiality, // C:H v3CLow: v3Confidentiality, // C:L v3CNone: v3Confidentiality, // C:N v3IHigh: v3Integrity, // I:H v3ILow: v3Integrity, // I:L v3INone: v3Integrity, // I:N v3AHigh: v3Availability, // A:H v3ALow: v3Availability, // A:L v3ANone: v3Availability, // A:N v3ENotDefined: v3ExploitCodeMaturity, // E:X v3EHigh: v3ExploitCodeMaturity, // E:H v3EFunctional: v3ExploitCodeMaturity, // E:F v3EProofOfConcept: v3ExploitCodeMaturity, // E:P v3EUnproven: v3ExploitCodeMaturity, // E:U v3RLNotDefined: v3RemediationLevel, // RL:X v3RLUnavailable: v3RemediationLevel, // RL:U v3RLWorkaround: v3RemediationLevel, // RL:W v3RLTemporaryFix: v3RemediationLevel, // RL:T v3RLOfficialFix: v3RemediationLevel, // RL:O v3RCNotDefined: v3ReportConfidence, // RC:X v3RCConfirmed: v3ReportConfidence, // RC:C v3RCReasonable: v3ReportConfidence, // RC:R v3RCUnknown: v3ReportConfidence, // RC:U v3CRNotDefined: v3ConfidentialityRequirement, // CR:X v3CRHigh: v3ConfidentialityRequirement, // CR:H v3CRMedium: v3ConfidentialityRequirement, // CR:M v3CRLow: v3ConfidentialityRequirement, // CR:L v3IRNotDefined: v3IntegrityRequirement, // IR:X v3IRHigh: v3IntegrityRequirement, // IR:H v3IRMedium: v3IntegrityRequirement, // IR:M v3IRLow: v3IntegrityRequirement, // IR:L v3ARNotDefined: v3AvailabilityRequirement, // AR:X v3ARHigh: v3AvailabilityRequirement, // AR:H v3ARMedium: v3AvailabilityRequirement, // AR:M v3ARLow: v3AvailabilityRequirement, // AR:L v3MAVNotDefined: v3ModifiedAttackVector, // MAV:X v3MAVNetwork: v3ModifiedAttackVector, // MAV:N v3MAVAdjacentNetwork: v3ModifiedAttackVector, // MAV:A v3MAVLocal: v3ModifiedAttackVector, // MAV:L v3MAVPhysical: v3ModifiedAttackVector, // MAV:P v3MACNotDefined: v3ModifiedAttackComplexity, // MAC:X v3MACLow: v3ModifiedAttackComplexity, // MAC:L v3MACHigh: v3ModifiedAttackComplexity, // MAC:H v3MMRNotDefined: v3ModifiedPrivilegesRequired, // MPR:X v3MPRLow: v3ModifiedPrivilegesRequired, // MPR:L v3MPRHigh: v3ModifiedPrivilegesRequired, // MPR:H v3MUINotDefined: v3ModifiedUserInteraction, // MUI:X v3MUINone: v3ModifiedUserInteraction, // MUI:N v3MUIRequired: v3ModifiedUserInteraction, // MUI:R v3MSNotDefined: v3ModifiedScope, // MMS:X v3MSUnchanged: v3ModifiedConfidentiality, // MMS:U v3MSChanged: v3ModifiedIntegrity, // MMS:C v3MCNotDefined: v3ModifiedConfidentiality, // MC:X v3MCHigh: v3ModifiedConfidentiality, // MC:H v3MCLow: v3ModifiedConfidentiality, // MC:L v3MCNone: v3ModifiedConfidentiality, // MC:N v3MINotDefined: v3ModifiedIntegrity, // MI:X v3MIHigh: v3ModifiedIntegrity, // MI:H v3MILow: v3ModifiedIntegrity, // MI:L v3MINone: v3ModifiedIntegrity, // MI:N v3MANotDefined: v3ModifiedAvailability, // MA:X v3MAHigh: v3ModifiedAvailability, // MA:H v3MALow: v3ModifiedAvailability, // MA:L v3MANone: v3ModifiedAvailability, // MA:N } // map of metric strings to metrics var v3MetricStrLut = map[string]v3Metric { "AV:N": v3AVNetwork, "AV:A": v3AVAdjacentNetwork, "AV:L": v3AVLocal, "AV:P": v3AVPhysical, "AC:L": v3ACLow, "AC:H": v3ACHigh, "PR:N": v3PRNone, "PR:L": v3PRLow, "PR:H": v3PRHigh, "UI:N": v3UINone, "UI:R": v3UIRequired, "S:U": v3SUnchanged, "S:C": v3SChanged, "C:H": v3CHigh, "C:L": v3CLow, "C:N": v3CNone, "I:H": v3IHigh, "I:L": v3ILow, "I:N": v3INone, "A:H": v3AHigh, "A:L": v3ALow, "A:N": v3ANone, "E:X": v3ENotDefined, "E:H": v3EHigh, "E:F": v3EFunctional, "E:P": v3EProofOfConcept, "E:U": v3EUnproven, "RL:X": v3RLNotDefined, "RL:U": v3RLUnavailable, "RL:W": v3RLWorkaround, "RL:T": v3RLTemporaryFix, "RL:O": v3RLOfficialFix, "RC:X": v3RCNotDefined, "RC:C": v3RCConfirmed, "RC:R": v3RCReasonable, "RC:U": v3RCUnknown, "CR:X": v3CRNotDefined, "CR:H": v3CRHigh, "CR:M": v3CRMedium, "CR:L": v3CRLow, "IR:X": v3IRNotDefined, "IR:H": v3IRHigh, "IR:M": v3IRMedium, "IR:L": v3IRLow, "AR:X": v3ARNotDefined, "AR:H": v3ARHigh, "AR:M": v3ARMedium, "AR:L": v3ARLow, "MAV:X": v3MAVNotDefined, "MAV:N": v3MAVNetwork, "MAV:A": v3MAVAdjacentNetwork, "MAV:L": v3MAVLocal, "MAV:P": v3MAVPhysical, "MAC:X": v3MACNotDefined, "MAC:L": v3MACLow, "MAC:H": v3MACHigh, "MPR:X": v3MMRNotDefined, "MPR:L": v3MPRLow, "MPR:H": v3MPRHigh, "MUI:X": v3MUINotDefined, "MUI:N": v3MUINone, "MUI:R": v3MUIRequired, "MMS:X": v3MSNotDefined, "MMS:U": v3MSUnchanged, "MMS:C": v3MSChanged, "MC:X": v3MCNotDefined, "MC:H": v3MCHigh, "MC:L": v3MCLow, "MC:N": v3MCNone, "MI:X": v3MINotDefined, "MI:H": v3MIHigh, "MI:L": v3MILow, "MI:N": v3MINone, "MA:X": v3MANotDefined, "MA:H": v3MAHigh, "MA:L": v3MALow, "MA:N": v3MANone, } // Get CVSS 3.x metric key. func (m v3Metric) Key() Key { k, _ := v3KeyLut[m] return k } // Convert string to CVSS 3.1 metric. func getV3Metric(s string) (v3Metric, error) { // get metric m, ok := v3MetricStrLut[s] if !ok { return v3InvalidMetric, fmt.Errorf("invalid metric: %s", s) } // return success return m, nil } // CVSS v3.0 prefix var v30Prefix = "CVSS:3.0/" // CVSS 3.0 vector. type v30Vector []v3Metric // Convert vector to string func (v v30Vector) String() string { // convert to slice of metrics metrics := []v3Metric(v) // build vector r := make([]string, len(metrics)) for i, m := range(metrics) { r[i] = m.String() } // build and return string return v30Prefix + strings.Join(r, "/") } // Return CVSS version. func (v30Vector) Version() Version { return V30 } // Return metrics in this vector. func (v v30Vector) Metrics() []Metric { // build result r := make([]Metric, len(v)) for i, m := range(v) { r[i] = m } // return result return r } // create CVSS 3.0 vector from string func newV30Vector(s string) (Vector, error) { strs := strings.Split(s, "/") r := make([]v3Metric, len(strs)) // walk metric strings for i, ms := range(strs) { // convert metric string to metric m, err := getV3Metric(ms) if err != nil { return nil, err } // add to results r[i] = m } // build and return vector return v30Vector(r), nil } // CVSS v3.1 prefix var v31Prefix = "CVSS:3.1/" // CVSS 3.1 vector. type v31Vector []v3Metric // Convert vector to string func (v v31Vector) String() string { // convert to slice of metrics metrics := []v3Metric(v) // build vector r := make([]string, len(metrics)) for i, m := range(metrics) { r[i] = m.String() } // build and return string return v31Prefix + strings.Join(r, "/") } // Return CVSS version. func (v31Vector) Version() Version { return V31 } // Return metrics in this vector. func (v v31Vector) Metrics() []Metric { // build result r := make([]Metric, len(v)) for i, m := range(v) { r[i] = m } // return result return r } // create CVSS 3.1 vector from string func newV31Vector(s string) (Vector, error) { strs := strings.Split(s, "/") r := make([]v3Metric, len(strs)) // walk metric strings for i, ms := range(strs) { // get metric from string m, err := getV3Metric(ms) if err != nil { return nil, err } // add to results r[i] = m } // build and return vector return v31Vector(r), nil } // Metric key. type Key interface { // Get full name. Name() string // Get category. Category() Category // Return string representation. String() string } // CVSS metric. type Metric interface { // Get metric key. Key() Key // Return string representation of metric. String() string } // CVSS metric vector. type Vector interface { // Get CVSS version. Version() Version // Get CVSS vector string. String() string // Return metrics in this vector. Metrics() []Metric } // Create new CVSS vector from vector string. func NewVector(s string) (Vector, error) { if len(s) > len(v31Prefix) && s[:len(v31Prefix)] == v31Prefix { // create CVSS v2.0 vector. return newV31Vector(s[len(v31Prefix):]) } else if len(s) > len(v30Prefix) && s[:len(v30Prefix)] == v30Prefix { // create CVSS v3.0 vector. return newV30Vector(s[len(v30Prefix):]) } else { // create CVSS V2 vector return newV2Vector(s) } }