1
0
Fork 0
Rust library to parse and score CVSS vector strings.
Find a file
Paul Duncan 79af494507
All checks were successful
CI / doc (push) Successful in 19s
CI / lint markdown (push) Successful in 22s
CI / build, lint, test (stable) (push) Successful in 1m0s
CI / audit (push) Successful in 27s
CI / build, lint, test (nightly) (push) Successful in 2m13s
CI / build, lint, test (beta) (push) Successful in 3m4s
CI / coverage (push) Successful in 2m44s
Cargo.{lock,toml}: bump version to 0.3.5
2026-03-27 23:20:22 -04:00
.forgejo .forgejo/workflows/ci.yml: remove cargo install cargo-audit, move audit job after coverage 2026-03-27 21:57:22 -04:00
.github/workflows .github/workflows/ci.yml: add audit job 2026-03-27 22:07:52 -04:00
examples cargo fmt fixes: sort imports, sort derives, format cfgs, misc small fixes 2026-02-01 01:31:31 -05:00
src src/v3.rs: remove arithmetic encoding handling code (not used for v3) 2026-03-27 17:28:50 -04:00
.gitignore initial commit. forked from nvd-cves repo 2025-09-18 17:38:41 -04:00
.markdownlint.yaml .markdownlint.yaml: add documentation comments, increase MD013.code_block_line_length to 100 chars 2026-03-27 21:33:35 -04:00
_rustfmt.toml add _rustfmt.toml 2026-02-01 01:45:32 -05:00
Cargo.lock Cargo.{lock,toml}: bump version to 0.3.5 2026-03-27 23:20:22 -04:00
Cargo.toml Cargo.{lock,toml}: bump version to 0.3.5 2026-03-27 23:20:22 -04:00
LICENSE.txt ./LICENSE.txt: update copyright year 2026-03-06 08:59:17 -05:00
README.md README.md: fix lint issues: wrap long line, remove unused links 2026-03-27 20:18:25 -04:00

polycvss

Rust library to parse and score CVSS vector strings.

Features:

  • CVSS v2, CVSS v3, and CVSS v4 support.
  • Version-agnostic parsing and scoring API.
  • Memory efficient: Vectors are 8 bytes. Scores and severities are 1 byte.
  • No dependencies by default except the standard library.
  • Optional serde integration via the serde build feature.
  • Extensive tests: Tested against thousands of vectors and scores from the NVD CVSS calculators.

Links:

Here is an example tool which parses the first command-line argument as a CVSS vector string, then prints the score and severity:

use polycvss::{Err, Score, Severity, Vector};

fn main() -> Result<(), Err> {
  let args: Vec<String> = std::env::args().collect(); // get cli args

  if args.len() == 2 {
    let vec: Vector = args[1].parse()?; // parse string
    let score = Score::from(vec); // get score
    let severity = Severity::from(score); // get severity
    println!("{score} {severity}"); // print score and severity
  } else {
    let name = args.first().map_or("app", |s| s); // get app name
    eprintln!("Usage: {name} [VECTOR]"); // print usage
  }

  Ok(())
}

Here is the example tool output for a CVSS v2 vector string, a CVSS v3 vector string, and a CVSS v4 vector string:

# test with cvss v2 vector string
$ cvss-score "AV:A/AC:H/Au:N/C:C/I:C/A:C"
6.8 MEDIUM

# test with cvss v3 vector string
$ cvss-score "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"
9.8 CRITICAL

# test with cvss v4 vector string
$ cvss-score "CVSS:4.0/AV:L/AC:H/AT:N/PR:N/UI:P/VC:L/VI:L/VA:L/SC:H/SI:H/SA:H"
5.2 MEDIUM

This example tool is included in the Git repository as src/bin/cvss-score.rs.

Examples

Parse vector strings:

// parse CVSS v2 vector string
let v2: Vector = "AV:N/AC:L/Au:N/C:C/I:C/A:C".parse()?;

// parse CVSS v3 vector string
let v3: Vector = "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H".parse()?;

// parse CVSS v4 vector string
let v4: Vector = "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H".parse()?;

Get vector score:

// parse CVSS v4 vector string
let v: Vector = "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H".parse()?;

// get score
let score = Score::from(v);

// check result
assert_eq!(score, Score::from(10.0));

Compare scores:

let a = Score::from(1.2); // first score
let b = Score::from(3.5); // second score
assert!(a < b); // compare scores

Get score severity:

let severity = Severity::from(Score::from(2.3));
assert_eq!(severity, Severity::Low);

Compare severities:

let a = Severity::Low; // first severity
let b = Severity::High; // second severity
assert!(a < b); // compare severities

Get metric from vector by name:

// parse CVSS v4 vector string
let v: Vector = "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H".parse()?;

// get metric
let metric = v.get(Name::V4(v4::Name::AttackVector))?;

// check result
assert_eq!(metric, Metric::V4(v4::Metric::AttackVector(v4::AttackVector::Network)));

Iterate over vector metrics:

// parse CVSS v4 vector string
let v: Vector = "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H".parse()?;

// print metrics
for m in v {
  println!("metric: {m}");
}

Convert a version-agnostic vector to a version-specific vector to access version-specific behavior:

// parse vector string
let v: Vector = "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H".parse()?;

// convert version-agnosic vector to a v4 vector
let v = v4::Vector::from(v);

// get nomenclature
assert_eq!(v4::Nomenclature::from(v), v4::Nomenclature::CvssB);

See the examples/ directory for more examples.

Install

polycvss package page on crates.io

Run cargo add polycvss to add polycvss as a dependency to an exiting Rust project:

$ cargo add polycvss
...

Run cargo install polycvss to install the example cvss-score tool:

# install cvss-score in cargo bin dir (e.g. `~/.cargo/bin`)
$ cargo install polycvss

Build

Run cargo build to create a debug build of the example tool in target/debug/:

$ cargo build
...
$ target/debug/cvss-score "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"
9.8 CRITICAL

Run cargo build --release to create a release build of the example tool in target/release/:

$ cargo build --release
...
$ target/release/cvss-score "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"
9.8 CRITICAL

You can also build the example cvss-score tool in a container using Podman or Docker like this:

$ podman run --rm -t -v "$PWD":/src -w /src docker.io/rust cargo build --release
...
$ target/release/cvss-score "CVSS:4.0/AV:L/AC:H/AT:N/PR:N/UI:P/VC:L/VI:L/VA:L/SC:H/SI:H/SA:H"
5.2 MEDIUM

To build a static binary of the cvss-score tool in a container:

# build cvss-score static binary
$ podman run --rm -it -v .:/src -w /src rust sh -c "
  rustup target add $(arch)-unknown-linux-musl &&
    cargo build --release --target $(arch)-unknown-linux-musl
"
...
$ ldd target/$(arch)-unknown-linux-musl/release/cvss-score
        statically linked
$ du -sh target/$(arch)-unknown-linux-musl/release/cvss-score
604K    target/x86_64-unknown-linux-musl/release/cvss-score

Documentation

polycvss API documentation on docs.rs

Run cargo doc to build the API documentation locally in target/doc/polycvss/:

$ cargo doc
...
$ ls target/doc/polycvss/index.html
target/doc/polycvss/index.html

Run cargo doc --lib build the library documentation and exclude the example tool documentation:

# remove generated docs
# (needed to clean up stale artifacts)
$ cargo clean --doc

# generate library-only docs
$ cargo doc --lib

Tests

Use cargo test to run the test suite:

$ cargo test
...
test result: ok. 369 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.05s
$

Note: The test suite includes a large number of scored CVSS vector string test cases. The test cases were generated using cvss-calcs and can be found in src/v2.rs, src/v3.rs, and src/v4.rs.

Use cargo clippy to run the linter:

$ cargo clippy
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.04s
$

Install cargo-tarpaulin and use cargo tarpaulin to check code coverage:

$ cargo tarpaulin --engine llvm --fail-under 95
...
2026-03-27T03:03:08.336541Z  INFO cargo_tarpaulin::report: Coverage Results:
|| Uncovered Lines:
|| src/bin/cvss-score.rs: 36-37, 39-43, 45-46, 49
|| src/lib.rs: 616, 781-782, 787-788, 793-794, 799-800, 805-806, 811-812, 891-892, 897-898, 903
-904, 1369-1370, 1555-1556
|| src/v2.rs: 2255-2256, 2575, 2803, 2896-2897, 2910, 3281-3282
|| src/v3.rs: 3110-3111, 3912, 3929-3930, 3941
|| src/v4.rs: 1494, 1624, 5338-5339, 5704-5709, 6161-6162, 6638-6646, 6652, 6654, 6660, 6671-66
72, 6674-6675, 6682-6689, 6693, 6695, 6697-6699, 6710, 6715-6717, 6744, 6832, 6907, 7139, 7170,
 7172, 7177-7178, 7181, 7184-7185
|| Tested/Total Lines:
|| src/bin/cvss-score.rs: 0/10 +0.00%
|| src/lib.rs: 149/172 -0.28%
|| src/v2.rs: 439/448 +0.03%
|| src/v3.rs: 628/634 +0.01%
|| src/v4.rs: 1061/1117 +0.06%
||
95.63% coverage, 2277/2381 lines covered, +0.00% change in coverage