🔍 Validation Workflow
⏱️ 12 min · beginner · CLI + SDK
You will deliberately write a broken vector, watch the CLI reject it two different ways, fix it metric by metric, and learn why Check and Validate exist as separate operations.
Prerequisites
- The
cvssbinary (or./cvss-cli) - Finish getting-started and your-first-vector
Flow
Step 1 — Start from a wrong vector
Suppose a junior analyst hands you this for a "remote crash" bug:
CVSS:3.1/AV:N/AC:L/PR:N/S:U/C:H/I:H/A:XTwo problems are hiding in it:
UIis missing (a required base metric).A:X—Xis not a legal Availability value (legal values areH/L/N).
Let the CLI find them.
Step 2 — parse it
parse is the permissive reader. It accepts the string and tells you what it understood:
cvss parse "CVSS:3.1/AV:N/AC:L/PR:N/S:U/C:H/I:H/A:X"Parse error: unknown availability value: Xparse stops at the first illegal value it encounters — here, A:X. It does not yet complain about the missing UI, because it never got that far.
parse fails fast on bad values
parse validates values as it reads them. Use it when you want the most direct "where does the string break" answer.
Step 3 — validate it
validate is the strict gatekeeper. Try the same broken vector:
cvss validate "CVSS:3.1/AV:N/AC:L/PR:N/S:U/C:H/I:H/A:X"Validation failed: unknown availability value: XSame value error. Now remove the illegal A:X segment entirely and watch what validate reports next:
cvss validate "CVSS:3.1/AV:N/AC:L/PR:N/S:U/C:H/I:H"Validation failed: validation failed: metric UI: is required but not set; metric A: is required but not setTwo missing metrics surface at once: UI (which was never in the vector) and A (which we just removed). The value error is gone; what remains is purely structural — required base metrics that are absent.
validate collects all structural gaps
Unlike parse, which stops at the first problem, validate reports every missing required metric in one pass. That is what makes it the right tool for a CI gate.
Step 4 — Restore Availability
Add A back with a legal value — A:H (full availability loss, matching a crash):
cvss validate "CVSS:3.1/AV:N/AC:L/PR:N/S:U/C:H/I:H/A:H"Validation failed: validation failed: metric UI: is required but not setThe A gap is closed; only the real missing metric, UI, remains.
Step 5 — Add the missing metric
UI:N (no user interaction needed):
cvss validate "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"Valid [PASS]
Version: 3.1
Complete: trueFixed. Score it to confirm:
cvss score "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"9.8 (Critical)Step 6 — Check vs Validate (the SDK distinction)
In the Go SDK there are two related operations. The CLI's validate command runs both; in code you call them separately:
| Operation | What it checks | Example failure |
|---|---|---|
parser.ParseString | Reads the string; rejects illegal values | unknown availability value: X |
Cvss3x.Check() | Structural completeness after parsing | Availability can not empty |
Crucially, ParseString does not require all base metrics to be present — it succeeds on a partial vector:
package main
import (
"errors"
"fmt"
"github.com/scagogogo/cvss-skills/pkg/cvss"
"github.com/scagogogo/cvss-skills/pkg/parser"
)
func main() {
// Partial: AV/AC/PR/UI/S/C/I present, A missing
cv, err := parser.ParseString("CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H")
fmt.Println("ParseString err:", err) // <nil> — parsing succeeded
fmt.Println("Check():", cv.Check()) // Availability can not empty
// Validate collects all problems instead of short-circuiting.
if err := cv.Validate(); err != nil {
var ve cvss.ValidationErrors
if errors.As(err, &ve) {
fmt.Println("Missing:", ve.MissingMetrics()) // [A]
}
}
}ParseString err: <nil>
Check(): Availability can not emptySo the pattern is:
cv, err := parser.ParseString(input) // 1. value-level errors
if err != nil {
return err
}
if err := cv.Check(); err != nil { // 2. structure-level errors
return err
}
// cv is now safe to scoreWhy split them?
You sometimes want to parse a partial vector on purpose — to compute a score range or to merge metrics later. ParseString lets you do that; Check() is the explicit "is this complete enough to score?" question.
Step 7 — Batch-validate a file
When you have many vectors, validate them all at once:
cat > /tmp/vec.txt <<'EOF'
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:X
CVSS:3.1/AV:N/AC:L/PR:N/S:U/C:H/I:H/A:H
EOF
cvss batch validate /tmp/vec.txtPASS Line 1: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
FAIL Line 2: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:X
- unknown availability value: X
FAIL Line 3: CVSS:3.1/AV:N/AC:L/PR:N/S:U/C:H/I:H/A:H
- metric UI: is required but not setLine 1 passes; line 2 fails on the illegal A:X value; line 3 fails on the missing UI metric. batch validate runs the full validation (values + completeness) on every line and prints the specific reason under each failure.
Recap
parsefails fast on illegal values; use it to find the first bad value.validateis the full gatekeeper: value errors and missing-metric errors.- In the SDK,
parser.ParseString(values) andCvss3x.Check()(completeness) are separate on purpose, so you can work with partial vectors. - Fix value errors before chasing missing-metric errors — a malformed segment can cascade.
Next
- Compare and merge vectors in comparison-guide
- Run validation at scale in batch-scripting