- Rust 100%
|
Some checks failed
CI / build, lint, test (nightly) (push) Successful in 49s
CI / doc (push) Successful in 23s
CI / build, lint, test (stable) (push) Successful in 1m7s
CI / lint markdown (push) Successful in 23s
CI / build, lint, test (beta) (push) Successful in 2m6s
CI / coverage (push) Failing after 37s
CI / audit (push) Successful in 27s
|
||
|---|---|---|
| .forgejo | ||
| .github/workflows | ||
| examples | ||
| fuzz | ||
| src | ||
| .gitignore | ||
| .markdownlint.yaml | ||
| _rustfmt.toml | ||
| Cargo.lock | ||
| Cargo.toml | ||
| LICENSE.txt | ||
| README.md | ||
polycvss
CVSS vector string parser and and score calculator.
Features:
- Written in Rust.
- 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 Rust standard library.
- Optional serde integration via the
serdebuild 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.
Linter
Use cargo clippy to run the linter:
$ cargo clippy
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.04s
Coverage
Install cargo-tarpaulin and use cargo tarpaulin to check code
coverage:
$ cargo tarpaulin --engine llvm --fail-under 98
...
2026-06-28T05:32:52.705636Z INFO cargo_tarpaulin::report: Coverage Results:
|| Uncovered Lines:
|| src/bin/cvss-score.rs: 36-37, 39-43, 45-46, 49
|| src/v3.rs: 3929
|| src/v4.rs: 6896, 7128, 7159, 7161, 7166-7167, 7170, 7173-7174
|| Tested/Total Lines:
|| src/bin/cvss-score.rs: 0/10 +0.00%
|| src/lib.rs: 172/172 +0.00%
|| src/v2.rs: 450/450 +0.00%
|| src/v3.rs: 629/630 +0.00%
|| src/v4.rs: 1106/1115 +0.00%
||
99.16% coverage, 2357/2377 lines covered, +0.00% change in coverage
Fuzzing
Install cargo-fuzz and use cargo fuzz to run the fuzz tests:
# fuzz vector parser
$ cargo fuzz run --sanitizer none vector
Use cargo fuzz list to list the available fuzz targets:
# list fuzz targets
$ cargo fuzz list
v2-vector
v3-vector
v4-vector
vector