SSVC.pm: Stakeholder-Specific Vulnerability Categorization on CPAN
If you work anywhere near vulnerability management, you've run into the CVSS problem: a single severity number that tells you almost nothing about what to actually do. CVSS was never designed to be a triage tool, and treating it like one is how organizations end up "patching everything" or, worse, patching nothing because everything looks equally urgent.
SSVC (Stakeholder-Specific Vulnerability Categorization), developed by Carnegie Mellon's SEI (CERT/CC) and later adapted by CISA into its own decision tree, takes a different approach: instead of a score, it's a decision tree. You feed in a handful of factors specific to your role - exploitation status and technical impact for some methodologies, safety and market-share considerations for others - and it hands you back an actual decision: track it, act on it, attend to it now. Which factors matter, and what decision comes out, depends on who you are in the vulnerability management chain - a vendor, a deployer, a coordinator, a finder - because a coordinator publishing an advisory and a deployer patching a fleet of servers are answering completely different questions with completely different inputs.
I've just published SSVC.pm on CPAN, a Perl implementation of the methodology - including the CISA decision tree, but not limited to it.
Methodologies included in SSVC.pm
cisa- SSVC::CISAcisa_bod_26_04- SSVC::CISA::BOD2604coordinator_publication- SSVC::CoordinatorPublicationcoordinator_triage- SSVC::CoordinatorTriagedeployer- SSVC::Deployersupplier- SSVC::Supplier
Need a methodology that isn't one of the six shipped ones - an internal, customized decision tree for your org? SSVC->register_methodology() lets you plugin your own class as long as it extends SSVC::Base.
Usage
use SSVC;
my $ssvc = SSVC->new(
cisa => {
exploitation => 'active',
automatable => 'yes',
technical_impact => 'partial',
mission_prevalence => 'minimal',
public_well_being_impact => 'irreversible',
}
);
say $ssvc->decision; # act
SSVC::CISA also understands vector strings, so you can parse and re-emit CISA's compact SSVCv2/... notation directly:
my $ssvc = SSVC::CISA->from_vector_string(
'SSVCv2/E:A/A:Y/T:P/P:M/B:I/M:H/D:C/2025-01-01T00:00:00'
);
say $ssvc; # round-trips back to the vector string
say $ssvc->public_well_being_impact; # irreversible
A worked example: scoring a real CVE
Theory is nice, but let's do something with it. CISA's Vulnrichment project already scores three of the five CISA decision points (exploitation, automatable, technicalImpact) for CVEs it triages, and that data now flows through into the NVD API itself, under metrics.ssvcV203.
The remaining two CISA decision points - mission_prevalence and public_well_being_impact - are about your environment and your mission, so by design no external feed can fill them in for you. That's the whole point of SSVC: it forces a human, stakeholder-specific judgment call precisely where a generic feed can't substitute for one.
Here's a small calculator that pulls the three CISA already scored via Net::NVD, takes the two that are yours to make as command-line arguments, and produces a full decision, vector string, and a ready-to-share CISA calculator URL:
#!perl
# simple-cve-ssvc-calculator.pl - Simple CVE SSVC Calculator
#
# (C) 2026, Giuseppe Di Terlizzi <giuseppe.diterlizzi@gmail.com>
# License MIT
use strict;
use warnings;
use v5.10;
use SSVC;
use Net::NVD;
my $usage = "Usage $0 <CVE> <MISSION PREVALENCE> <PUBLIC WELL BEIGN IMPACT>\n";
my $cve_id = shift || Carp::croak "$usage\nMissing CVE id";
my $mission_prevalence = shift || Carp::croak "$usage\nMissing mission_prevalence";
my $public_well_being_impact = shift || Carp::croak "$usage\nMissing public_well_being_impact";
my $nvd = Net::NVD->new;
my $cve = $nvd->get($cve_id);
unless ($cve) {
Carp::croak "CVE not found!";
}
unless (defined $cve->{metrics}->{ssvcV203}) {
Carp::croak "NO SSVC Metrics for $cve_id";
}
my $data = $cve->{metrics}->{ssvcV203}->[0]->{ssvcData}->{options};
my %decision_points = ();
foreach (@{$data}) {
my ($decision_point, $value) = %{$_};
$decision_point = 'technical_impact' if ($decision_point eq 'technicalImpact');
$decision_points{$decision_point} = $value;
}
$decision_points{mission_prevalence} = $mission_prevalence;
$decision_points{public_well_being_impact} = $public_well_being_impact;
my $ssvc = SSVC::CISA->new(%decision_points);
my $mission_well_being_impact = $ssvc->mission_well_being_impact;
my $decision = $ssvc->decision;
my $cisa_url = "https://www.cisa.gov/ssvc-calculator#$ssvc&$cve_id&Coordinator";
say "Simple SSVC calculator for $cve_id\n";
say sprintf "%s\n", $cve->{descriptions}[0]{value};
say sprintf "(E) Exploitation (CISA) %s", $decision_points{exploitation};
say sprintf "(A) Automatable (CISA) %s", $decision_points{automatable};
say sprintf "(T) Technical Impact (CISA) %s", $decision_points{technical_impact};
say sprintf "(P) Mission Prevalence (USER) %s", $decision_points{mission_prevalence};
say sprintf "(B) Public Well-being Impact (USER) %s", $decision_points{public_well_being_impact};
say sprintf "(M) Mission Well-being %s", $mission_well_being_impact;
say sprintf "(D) Decision [ %s ]", $decision;
say sprintf " Vector String %s", $ssvc;
say sprintf " URL %s", $cisa_url;
exit 0;
Run it for Log4Shell (CVE-2021-44228):
$ perl simple-cve-ssvc-calculator.pl CVE-2021-44228 minimal minimal
Simple SSVC calculator for CVE-2021-44228
Apache Log4j2 2.0-beta9 through 2.15.0 (excluding security releases 2.12.2, 2.12.3, and 2.3.1) JNDI features used in configuration, log messages, and parameters do not protect against attacker controlled LDAP and other JNDI related endpoints. An attacker who can control log messages or log message parameters can execute arbitrary code loaded from LDAP servers when message lookup substitution is enabled. From log4j 2.15.0, this behavior has been disabled by default. From version 2.16.0 (along with 2.12.2, 2.12.3, and 2.3.1), this functionality has been completely removed. Note that this vulnerability is specific to log4j-core and does not affect log4net, log4cxx, or other Apache Logging Services projects.
(E) Exploitation (CISA) active
(A) Automatable (CISA) yes
(T) Technical Impact (CISA) total
(P) Mission Prevalence (USER) minimal
(B) Public Well-being Impact (USER) minimal
(M) Mission Well-being low
(D) Decision [ attend ]
Vector String SSVCv2/E:A/A:Y/T:T/P:M/B:M/M:L/D:A/2026-08-06T19:49:38.000Z
URL https://www.cisa.gov/ssvc-calculator#SSVCv2/E:A/A:Y/T:T/P:M/B:M/M:L/D:A/2026-08-06T19:49:38.000Z&CVE-2021-44228&Coordinator
CISA already told us this is being actively exploited, requires no special skill to automate (automatable: yes), and has total technical impact - but plugged into a minimal/minimal mission and safety context, SSVC brings the actual decision down to attend rather than treating every actively-exploited bug as an equal five-alarm fire. That's the whole value proposition: the CVE data alone never tells you the decision, only the CVE data plus where it actually lives does.
Get it
cpanm SSVC
- CPAN: https://metacpan.org/pod/SSVC
- Source / issues: https://github.com/giterlizzi/perl-SSVC
Enjoy! GDT
I blog about Perl.
Leave a comment