🔀 Diff, Merge & Description
🔀 Feature · pkg/cvss
Diff returns the metrics where two vectors differ; Merge produces a copy of one vector with its gaps filled from another (never overwriting set values); Description renders a flat, human-readable string of every set metric. Together they cover the three things you do when comparing vectors: see the deltas, reconcile them, and narrate them.
Synopsis
diffs := a.Diff(b) // []DiffEntry — only the differing metrics
merged := a.Merge(b) // a's gaps filled from b; a's set values kept
desc := a.Description() // "Attack Vector: Network, Attack Complexity: Low, ..."API Reference
DiffEntry
type DiffEntry struct {
Metric string // short name, e.g. "AV"
V1, V2 string // short values, e.g. "N" / "L"; "-" when unset on one side
V1Long, V2Long string // long names, e.g. "Network" / "Local"
}
func (d DiffEntry) String() string // "AV: N vs L"Diff
func (x *Cvss3x) Diff(other *Cvss3x) []DiffEntryWalks Base, Temporal, and Environmental metrics. A metric set on one side but not the other is a diff (the missing side shows "-" / "-"). A metric set on both but with different short values is a diff. A metric unset on both sides is not a diff. Returns nil if either receiver is nil.
diffs := a.Diff(b)
for _, d := range diffs { fmt.Println(d) } // AV: N vs LMerge
func (x *Cvss3x) Merge(other *Cvss3x) *Cvss3xReturns a copy of x with every metric that is nil on x but set on other filled in from other. Metrics already set on x are never overwritten — Merge is a left-biased fill, not an overwrite. Sub-structs (Cvss3xTemporal, Cvss3xEnvironmental) are lazily allocated on the copy when other contributes a metric from that group. A nil receiver returns other.Clone(); a nil other returns x.Clone().
Merge never overwrites
Because Merge only fills nil slots, calling a.Merge(b) then a.Merge(c) is order-independent for any metric set in b or c: the first contributor wins. To force an overwrite, use SetMetricValue or the With*Method family.
Description
func (x *Cvss3x) Description() stringReturns "Attack Vector: Network, Attack Complexity: Low, Privileges Required: None, ..." — every set metric across all three groups, in CVSS canonical order, joined by ", ". Unset metrics are skipped. Returns "" for a nil receiver. This is the format the CLI describe command prints.
fmt.Println(cv.Description())
// Attack Vector: Network, Attack Complexity: Low, Privileges Required: None, ...Example
package main
import (
"fmt"
"github.com/scagogogo/cvss-skills/pkg/parser"
)
func main() {
a, 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 {
panic(err)
}
b, err := parser.ParseString("CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H/E:F/RL:U/RC:C")
if err != nil {
panic(err)
}
// Diff: AV differs (N vs L); E/RL/RC are set on b but not a.
for _, d := range a.Diff(b) {
fmt.Println(d)
}
// AV: N vs L
// E: - vs F
// RL: - vs U
// RC: - vs C
// Merge: a keeps AV:N, gains E/RL/RC from b.
merged := a.Merge(b)
fmt.Println(merged.GetTemporalVectorString()) // .../A:H/E:F/RL:U/RC:C
fmt.Println(merged.Equal(a)) // false — merged has temporal
// Merge does not overwrite: AV stays N even though b has AV:L.
s, _, _ := merged.GetMetricValue("AV")
fmt.Printf("AV after merge: %c\n", s) // N
// Description: a flat, human-readable summary.
fmt.Println(a.Description())
// Attack Vector: Network, Attack Complexity: Low, Privileges Required: None, ...
}Related
- Accessor —
GetMetricValue/SetMetricValuefor forced overwrites - With-Method — immutable per-metric setters
- Convenience —
Equalfor whole-vector equality - CLI:
diff,merge,describe