cvss2/vector.go (154 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 cvss2
import (
"fmt"
"strings"
)
const (
partSeparator = "/"
metricSeparator = ":"
prefix = "("
suffix = ")"
)
// Vector represents a CVSSv3 vector, holds all metrics inside (base, temporal and environmental)
type Vector struct {
BaseMetrics
TemporalMetrics
EnvironmentalMetrics
}
// For some metrics, "not defined" is equivalent to specifying another value. eg.
// E:X is equivalent to E:H.
var undefinedEquivalent = map[string]string{
// Temporal metrics
"E": "H",
"RL": "U",
"RC": "C",
// Environmental metrics
"CDP": "N",
"TD": "H",
"CR": "M",
"IR": "M",
"AR": "M",
}
func equivalent(metric, value string) string {
if value != "ND" {
return value
}
e, ok := undefinedEquivalent[metric]
if !ok {
return value
}
return e
}
// Equal returns true if o represents the same vector as v.
//
// Note that the definition of equal here means that two vectors with different
// string representations can still be equal. For instance TD:ND is defined as
// the same as TD:H.
func (v Vector) Equal(o Vector) bool {
vDefs := v.definables()
oDefs := o.definables()
for _, metric := range order {
a := equivalent(metric, vDefs[metric].String())
b := equivalent(metric, oDefs[metric].String())
if a != b {
return false
}
}
return true
}
// String returns this vectors representation as a string
// it shouldn't depend on the order of metrics
func (v Vector) String() string {
var sb strings.Builder
fmt.Fprint(&sb, prefix)
defineables := v.definables()
first := true
for _, metric := range order {
def := defineables[metric]
if !def.defined() {
continue
}
if !first {
fmt.Fprint(&sb, partSeparator)
} else {
first = false
}
fmt.Fprintf(&sb, "%s%s%s", metric, metricSeparator, def)
}
fmt.Fprint(&sb, suffix)
return sb.String()
}
// VectorFromString will parse a string into a Vector, or return an error if it can't be parsed
func VectorFromString(str string) (Vector, error) {
var v Vector
str = strings.TrimPrefix(str, prefix)
str = strings.TrimSuffix(str, suffix)
parseables := v.parseables()
for _, part := range strings.Split(str, partSeparator) {
tmp := strings.Split(part, metricSeparator)
if len(tmp) != 2 {
return v, fmt.Errorf("need two values separated by %s, got %q", metricSeparator, part)
}
metric, value := tmp[0], tmp[1]
if p, ok := parseables[metric]; !ok {
return v, fmt.Errorf("undefined metric %s with value %s", metric, value)
} else if err := p.parse(value); err != nil {
return v, fmt.Errorf("error occurred while parsing metric %s: %v", metric, err)
}
}
return v, nil
}
// Absorb will override only metrics in the current vector from the one given which are defined
// If the other vector specifies only a single metric with all others undefined, the resulting
// vector will contain all metrics it previously did, with only the new one overriden
func (v *Vector) Absorb(other Vector) {
parseables := v.parseables()
for metric, defineable := range other.definables() {
if defineable.defined() {
parseables[metric].parse(defineable.String())
}
}
}
// AbsorbIfDefined is like Absorb but will not override vector components that
// are not present in v.
func (v *Vector) AbsorbIfDefined(other Vector) {
parseables := v.parseables()
old := v.definables()
for metric, defineable := range other.definables() {
if old[metric].defined() && defineable.defined() {
parseables[metric].parse(defineable.String())
}
}
}
// helpers
var order = []string{"AV", "AC", "Au", "C", "I", "A", "E", "RL", "RC", "CDP", "TD", "CR", "IR", "AR", "ME", "MRL", "MRC"}
type defineable interface {
defined() bool
String() string
}
type parseable interface {
parse(string) error
}
func (v *Vector) definables() map[string]defineable {
return map[string]defineable{
// base metrics
"AV": v.BaseMetrics.AccessVector,
"AC": v.BaseMetrics.AccessComplexity,
"Au": v.BaseMetrics.Authentication,
"C": v.BaseMetrics.ConfidentialityImpact,
"I": v.BaseMetrics.IntegrityImpact,
"A": v.BaseMetrics.AvailabilityImpact,
// temporal metrics
"E": v.TemporalMetrics.Exploitablity,
"RL": v.TemporalMetrics.RemediationLevel,
"RC": v.TemporalMetrics.ReportConfidence,
// environmental metrics
"CDP": v.EnvironmentalMetrics.CollateralDamagePotential,
"TD": v.EnvironmentalMetrics.TargetDistribution,
"CR": v.EnvironmentalMetrics.ConfidentialityRequirement,
"IR": v.EnvironmentalMetrics.IntegrityRequirement,
"AR": v.EnvironmentalMetrics.AvailabilityRequirement,
// extended environmental metrics
"ME": v.EnvironmentalMetrics.ModifiedExploitablity,
"MRL": v.EnvironmentalMetrics.ModifiedRemediationLevel,
"MRC": v.EnvironmentalMetrics.ModifiedReportConfidence,
}
}
func (v *Vector) parseables() map[string]parseable {
return map[string]parseable{
// base metrics
"AV": &v.BaseMetrics.AccessVector,
"AC": &v.BaseMetrics.AccessComplexity,
"Au": &v.BaseMetrics.Authentication,
"C": &v.BaseMetrics.ConfidentialityImpact,
"I": &v.BaseMetrics.IntegrityImpact,
"A": &v.BaseMetrics.AvailabilityImpact,
// temporal metrics
"E": &v.TemporalMetrics.Exploitablity,
"RL": &v.TemporalMetrics.RemediationLevel,
"RC": &v.TemporalMetrics.ReportConfidence,
// environmental metrics
"CDP": &v.EnvironmentalMetrics.CollateralDamagePotential,
"TD": &v.EnvironmentalMetrics.TargetDistribution,
"CR": &v.EnvironmentalMetrics.ConfidentialityRequirement,
"IR": &v.EnvironmentalMetrics.IntegrityRequirement,
"AR": &v.EnvironmentalMetrics.AvailabilityRequirement,
// extended environmental metrics
"ME": &v.EnvironmentalMetrics.ModifiedExploitablity,
"MRL": &v.EnvironmentalMetrics.ModifiedRemediationLevel,
"MRC": &v.EnvironmentalMetrics.ModifiedReportConfidence,
}
}