Vector Comparison API
CVSS Skills compares vectors through methods on Cvss3x itself — there is no separate comparator type. The comparison surface spans four levels of granularity, from a single boolean to a full per-metric breakdown.
Comparison Dimensions
Two vectors can be compared at four levels of granularity — from a single boolean to a full per-metric breakdown:
| Question | Method | Returns |
|---|---|---|
| Are the two vectors identical, metric for metric? | Equal(other) | bool |
| Do they produce the same base score? | EqualScore(other) | (bool, error) |
| Do they land in the same severity band? | SameSeverity(other) | (bool, error) |
| Exactly which metrics differ? | Diff(other) | []DiffEntry |
| How numerically close are they? | NewDistanceCalculator(a, b) | distances / similarities |
| Combine metrics from two vectors? | Merge(other) | *Cvss3x |
DiffEntry
Diff() returns a slice of DiffEntry, one per metric whose short value differs between the two vectors:
type DiffEntry struct {
Metric string // short name, e.g. "AV", "PR"
V1 string // vector A's short value, e.g. "N"
V2 string // vector B's short value, e.g. "L"
V1Long string // vector A's long value, e.g. "Network"
V2Long string // vector B's long value, e.g. "Local"
}
func (d DiffEntry) String() string // "AV: N vs L"Diff only reports set, differing metrics
Diff walks the metrics present on both vectors and emits an entry for each position where the short values disagree. Metrics absent from one side are not reported as diffs — use MissingMetrics() to detect absence instead.
Methods
Equal
func (x *Cvss3x) Equal(other *Cvss3x) boolReports whether the two vectors are identical across version, base, temporal, and environmental metrics. A nil receiver compares equal only to a nil argument.
Returns:
bool:trueif every metric group matches exactly
Example:
if v1.Equal(v2) {
fmt.Println("vectors are identical")
}EqualScore
func (x *Cvss3x) EqualScore(other *Cvss3x) (bool, error)Reports whether the two vectors produce the same base score. Two vectors with different metrics can share a score (e.g. different AV/AC combinations that net to the same Exploitability).
Returns:
bool:trueifGetBaseScore()is equal for botherror: non-nil if either vector fails to score (incomplete/invalid base metrics)
Example:
same, err := v1.EqualScore(v2)
if err != nil {
log.Fatalf("cannot compare scores: %v", err)
}
fmt.Printf("same base score: %t\n", same)SameSeverity
func (x *Cvss3x) SameSeverity(other *Cvss3x) (bool, error)Reports whether the two vectors land in the same severity band (None / Low / Medium / High / Critical). Coarser than EqualScore — two scores of 7.1 and 8.9 are different scores but the same severity (High).
Returns:
bool:trueif both vectors' base scores map to the same severityerror: non-nil if either vector fails to score
Example:
same, err := v1.SameSeverity(v2)
if err != nil {
log.Fatal(err)
}
fmt.Printf("same severity band: %t\n", same)Diff
func (x *Cvss3x) Diff(other *Cvss3x) []DiffEntryReturns one DiffEntry per metric whose short value differs between the two vectors.
Returns:
[]DiffEntry: differing metrics (empty if identical;nilif either receiver is nil)
Example:
for _, d := range v1.Diff(v2) {
fmt.Printf(" %s: %s vs %s\n", d.Metric, d.V1, d.V2)
}Merge
func (x *Cvss3x) Merge(other *Cvss3x) *Cvss3xReturns a new *Cvss3x combining x with other: for each metric, if other has it set, the result takes other's value; otherwise it keeps x's. This is how you overlay temporal/environmental metrics from one vector onto a base vector. The receiver is not mutated.
Returns:
*Cvss3x: the merged vector
Example:
// baseOnly has just the 8 base metrics; temporalOnly has E/RL/RC
combined := baseOnly.Merge(temporalOnly)
fmt.Println(combined.String()) // base + temporalComplete Example
package main
import (
"fmt"
"log"
"github.com/scagogogo/cvss-skills/pkg/cvss"
"github.com/scagogogo/cvss-skills/pkg/parser"
)
func main() {
v1, err := parser.ParseString("CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H")
if err != nil {
log.Fatal(err)
}
v2, err := parser.ParseString("CVSS:3.1/AV:L/AC:H/PR:H/UI:R/S:U/C:L/I:L/A:L")
if err != nil {
log.Fatal(err)
}
// Equality
fmt.Printf("Equal: %t\n", v1.Equal(v2))
// Score / severity comparison
sameScore, err := v1.EqualScore(v2)
if err != nil {
log.Fatal(err)
}
sameSev, err := v1.SameSeverity(v2)
if err != nil {
log.Fatal(err)
}
fmt.Printf("EqualScore: %t\n", sameScore)
fmt.Printf("SameSeverity: %t\n", sameSev)
// Per-metric diff
fmt.Println("Differences:")
for _, d := range v1.Diff(v2) {
fmt.Printf(" %s: %s (%s) vs %s (%s)\n", d.Metric, d.V1, d.V1Long, d.V2, d.V2Long)
}
// Numeric distance (see DistanceCalculator docs)
dc := cvss.NewDistanceCalculator(v1, v2)
fmt.Printf("Euclidean: %.3f\n", dc.EuclideanDistance())
fmt.Printf("Score diff: %.3f\n", dc.ScoreDifference())
}Ranking and Clustering
There is no built-in RankVectors or ClusterVectors — these are application-level concerns built from the primitives above. The idiomatic patterns:
Rank by score
sort.Slice(vectors, func(i, j int) bool {
ci, _ := cvss.NewCalculator(vectors[i]).GetBaseScore()
cj, _ := cvss.NewCalculator(vectors[j]).GetBaseScore()
return ci > cj
})Group by severity band
buckets := map[cvss.Severity][]*cvss.Cvss3x{}
for _, v := range vectors {
score, err := cvss.NewCalculator(v).GetBaseScore()
if err != nil {
continue
}
buckets[cvss.GetSeverity(score)] = append(buckets[cvss.GetSeverity(score)], v)
}Cluster by distance threshold
func cluster(vectors []*cvss.Cvss3x, threshold float64) [][]int {
var clusters [][]int
used := make([]bool, len(vectors))
for i, a := range vectors {
if used[i] {
continue
}
cluster := []int{i}
used[i] = true
for j, b := range vectors {
if i == j || used[j] {
continue
}
if cvss.NewDistanceCalculator(a, b).EuclideanDistance() <= threshold {
cluster = append(cluster, j)
used[j] = true
}
}
clusters = append(clusters, cluster)
}
return clusters
}Error Handling
Equal cannot fail (it returns false for nil/invalid). EqualScore and SameSeverity return an error when a vector's base metrics are incomplete and cannot be scored — handle it rather than ignoring:
same, err := v1.SameSeverity(v2)
if err != nil {
// one of the vectors is missing required base metrics
return fmt.Errorf("cannot compare severity: %w", err)
}For structured per-metric validation before comparing, call Validate():
if err := v1.Validate(); err != nil {
if ve, ok := err.(cvss.ValidationErrors); ok {
return fmt.Errorf("v1 missing metrics: %v", ve.MissingMetrics())
}
return err
}Related Documentation
- DistanceCalculator - Mathematical distance and similarity metrics
- Cvss3x Data Structure - The type these methods live on
- Calculator - Scoring (used by EqualScore / SameSeverity)
- Comparison Examples - End-to-end usage examples