Gate on how likely, not only on how bad.
A severity threshold treats every critical the same and every medium as safe. The Common Vulnerability Scoring System (CVSS), the Exploit Prediction Scoring System (EPSS) and the Known Exploited Vulnerabilities (KEV) catalogue each answer a different question about a CVE. Terramantle puts all three on every provider finding, so one policy can create a policy to block vulnerable and exploited providers.
Combined, they answer three questions
| Signal | Question it answers | What it is |
|---|---|---|
| CVSS | How bad would exploitation be? | An ordinal 0 to 10 severity scale from the Forum of Incident Response and Security Teams (FIRST). It does not move when exploitation starts. |
| EPSS | How likely is exploitation in the next 30 days? | A calibrated probability, 0 to 1, republished daily by FIRST, with a percentile that ranks the CVE against every other scored CVE. |
| KEV | Has it been exploited? | A curated list from the Cybersecurity and Infrastructure Security Agency (CISA) of CVEs with evidence of exploitation in the wild. US federal agencies must fix a listing by its due date. |
FIRST's guide to using EPSS draws the line between the last two:
One is a record of history. The other is a forecast.
As a general rule of thumb: when a vulnerability appears on CISA KEV, treat it as actively exploited and prioritize accordingly, regardless of EPSS score.
FIRST, Using EPSS
Where severity alone gets it backwards
These are findings from Terramantle's own provider scans, scored against the FIRST feed of 7 November 2026. The severity-only column is a plain CVSS >= 7 rule, and it gets both of the first two rows wrong in the expensive direction.
The 9.8 blocks a pipeline and pulls an engineer onto a fix for a CVE that FIRST gives a 0.4% chance of being exploited this month: a day of work, a delayed release, and no risk removed.
The 5.9 passes, and that CVE is more likely to be exploited than 99.8% of everything FIRST scores, so the one vulnerability an attacker is actually using ships without a review.
CVSS is not wrong in either row. It answers how bad, which is the question it was built for. The policy asked it how likely, and it has no opinion.
| Case | CVSS | EPSS | KEV | Severity only | With EPSS and KEV |
|---|---|---|---|---|---|
Severe, unlikely. CVE-2026-1229 in github.com/ | 9.8 | 0.004 37th percentile | No | Blocks | Advisory |
Moderate, likely. CVE-2023-48795 (Terrapin) in golang.org/ | 5.9 | 0.94 99.8th percentile | No | Passes | Blocks |
Severe, likely. CVE-2023-45288 (HTTP/2 CONTINUATION) in golang.org/ | 7.5 | 0.89 99.8th percentile | No | Blocks | Blocks |
Thresholds: percentiles, not round numbers
The obvious EPSS rule is "block anything above 0.5". FIRST is blunt about it:
You may think that a threshold at 50% is sane, but it's not (since the base rate is around 2%), and setting a threshold at 50% deprioritizes the 98.8% of all published vulnerabilities.
FIRST, Using EPSS
Scores are long-tailed. On the 7 November 2026 feed, 401,675 CVEs had a median score of 0.006, so a cutoff like 0.5 sits far out on the tail. Terramantle's rules threshold on the percentile instead. "The top 1%" still means the top 1% after FIRST recalibrates its model, where a fixed probability would quietly select a different share of CVEs each time.
Before settling on the 99th percentile as the blocking default, Terramantle ran it against the catalogue. It matched 930 of the 1,731 provider versions with CVE findings, where "no critical vulnerabilities" matches 78%. Almost all of those come from two Go CVEs, Terrapin and the HTTP/2 CONTINUATION flood, and it matched only 10 of the 254 provider builds Terramantle has patched itself.
One Terramantle policy, three signals
Each finding f in input.findings carries the per-CVE values, and input.scan.vulnerabilities carries the rollups for the version. Every signal has a way of being absent, and a rule that does not say what it does then fails open:
| Signal | On the policy input | When it is missing |
|---|---|---|
| CVSS | f.meta.cvss_score, f.severity | cvss_score is null when trivy carried no CVSS for the advisory, common for GHSA-only ids. The severity band is still there. |
| EPSS | f.meta.epss, f.meta.epss_percentile; rollups max_epss, epss_floor | Absent below the 0.04 floor. max_epss null: nothing on the version reached the floor. epss_floor null: EPSS could not be fetched. |
| KEV | f.meta.kev; rollup kev_count | kev_count null: the catalogue could not be fetched. |
This policy reads all three. It was checked with opa eval against a clean input and a failing one. CVSS and EPSS meet in the third rule as AND, and the last rule honours the org's on_missing_signal setting for both catalogues:
package terramantle.policy
# KEV: exploitation has been observed. Blocks whatever EPSS or CVSS say.
deny contains msg if {
some f in input.findings
f.meta.kev
msg := sprintf("%s is on the CISA KEV catalogue", [f.rule_id])
}
# EPSS: exploitation is likely. The top 1% of all scored CVEs, whatever the severity.
deny contains msg if {
some f in input.findings
f.category == "vulnerability"
f.meta.epss_percentile >= 0.99
msg := sprintf("%s is in the top 1%% of CVEs by EPSS (%.2f)", [f.rule_id, f.meta.epss])
}
# CVSS AND EPSS: severe if exploited, and at least as likely as a CVSS-Critical-sized
# population (FIRST's 90th-percentile equivalent). Severe-but-unlikely is left to an
# advisory CVSS ceiling rather than blocked. Never multiply the two.
deny contains msg if {
some f in input.findings
f.meta.cvss_score >= 9.0
f.meta.epss_percentile >= 0.90
msg := sprintf("%s is critical (CVSS %.1f) and above the 90th EPSS percentile", [f.rule_id, f.meta.cvss_score])
}
# Missing signals, honouring the org's setting.
deny contains msg if {
input.settings.on_missing_signal == "hard_fail"
some name, value in {"KEV": input.scan.vulnerabilities.kev_count, "EPSS": input.scan.vulnerabilities.epss_floor}
value == null
msg := sprintf("%s data unavailable and the signal-fail gate is set to Hard Fail", [name])
}The Terramantle policy editor ships the rules as separate templates, so each can be enforcing or advisory on its own:
- Block actively exploited CVEs: KEV, covered on the KEV policy page.
- Block likely-exploited CVEs (EPSS): at the 99th percentile.
- Likely and unfixable: at the 90th percentile, for CVEs with no fixed version.
- CVSS ceiling: keeps severe-but-unlikely findings visible.
What this does not do
It does not produce a combined risk score; multiplying CVSS by EPSS is the "score laundering" FIRST warns against. It does not know your environment: EPSS forecasts exploitation across the internet, not whether the vulnerable path is reachable in the way you use a provider.
It is not a live feed; the mirror is daily and keeps no history, so a policy cannot react to a score that jumped this week. It does not make low scores safe, and below the 0.04 floor it cannot tell 0.03 from 0.0001. And it applies to providers only, because modules, state and plans carry no CVE findings for EPSS to score.
Sources
- FIRST: Using EPSS (2026-11-07)
- FIRST: EPSS data (2026-11-07)
- CISA Known Exploited Vulnerabilities catalogue (2026-11-07)
CVSS, EPSS and KEV FAQ
Is EPSS a replacement for CVSS?
No. FIRST describes EPSS as "not a 'severity' score and not a complete risk score". CVSS says how bad exploitation would be; EPSS says how likely it is in the next 30 days. A policy needs both, which is why the rules combine them with AND rather than picking one.
Why not multiply EPSS by CVSS into one risk score?
FIRST calls that score laundering: "Do not multiply an EPSS score by an ordinal (such as CVSS) and think that produces a combined risk score." CVSS is an ordinal scale and EPSS is a calibrated probability, so the product has no meaning. Terramantle does not compute one.
What does a missing EPSS score mean?
Terramantle keeps scores of 0.04 and above, about the 90th percentile. A CVE below that carries no meta.epss. If EPSS could not be fetched at all, scan.vulnerabilities.epss_floor is null, which is a missing signal, and the org setting on_missing_signal decides whether that blocks.
Does a KEV listing still matter if the EPSS score is low?
Yes. FIRST says to treat a KEV-listed vulnerability "as actively exploited and prioritize accordingly, regardless of EPSS score." KEV is a record of exploitation that happened; EPSS is a forecast. The KEV rule stands on its own so that a listing always blocks.
Empower your teams with EPSS and KEV based allow list policies with Terramantle.
Provider scanning and Open Policy Agent (OPA) enforcement are Business-tier features. The EPSS templates are in the policy editor next to the KEV one.
Last reviewed
Terramantle is a private Terraform and OpenTofu registry. See pricing or read the FAQ.